From 69211f1e2af4828ea0e6101f786100e6bb360b32 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@55e972cd-5a19-0410-ae62-a4d7a52db4cd> Date: Fri, 23 Jul 2004 06:28:41 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'tor-0_0_8pre1'. svn:r2121 --- tags/tor-0_0_8pre1/.cvsignore | 20 + tags/tor-0_0_8pre1/AUTHORS | 34 + tags/tor-0_0_8pre1/ChangeLog | 575 ++++ tags/tor-0_0_8pre1/Doxyfile | 996 +++++++ tags/tor-0_0_8pre1/INSTALL | 54 + tags/tor-0_0_8pre1/LICENSE | 29 + tags/tor-0_0_8pre1/Makefile.am | 24 + tags/tor-0_0_8pre1/README | 63 + tags/tor-0_0_8pre1/Win32Build/or/or.dsp | 266 ++ tags/tor-0_0_8pre1/Win32Build/or/or.dsw | 29 + tags/tor-0_0_8pre1/Win32Build/tor/tor.dsw | 29 + tags/tor-0_0_8pre1/autogen.sh | 8 + tags/tor-0_0_8pre1/configure.in | 222 ++ tags/tor-0_0_8pre1/contrib/.cvsignore | 4 + tags/tor-0_0_8pre1/contrib/Makefile.am | 9 + tags/tor-0_0_8pre1/contrib/mdd.py | 171 ++ tags/tor-0_0_8pre1/contrib/tor-resolve | 126 + tags/tor-0_0_8pre1/contrib/tor-tsocks.conf | 7 + tags/tor-0_0_8pre1/contrib/tor.sh.in | 112 + tags/tor-0_0_8pre1/contrib/tor.spec | 150 + tags/tor-0_0_8pre1/contrib/torify.1 | 23 + tags/tor-0_0_8pre1/contrib/torify.in | 7 + tags/tor-0_0_8pre1/debian/README.privoxy | 18 + tags/tor-0_0_8pre1/debian/TODO | 9 + tags/tor-0_0_8pre1/debian/changelog | 196 ++ tags/tor-0_0_8pre1/debian/compat | 1 + tags/tor-0_0_8pre1/debian/control | 48 + tags/tor-0_0_8pre1/debian/copyright | 102 + tags/tor-0_0_8pre1/debian/patches/00list | 3 + .../patches/01_uncomment_datadirectory.dpatch | 37 + .../02_add_debian_files_in_manpage.dpatch | 41 + .../03_tor_manpage_in_section_8.dpatch | 45 + tags/tor-0_0_8pre1/debian/rules | 113 + tags/tor-0_0_8pre1/debian/tor.default | 13 + tags/tor-0_0_8pre1/debian/tor.dirs | 7 + tags/tor-0_0_8pre1/debian/tor.docs | 3 + tags/tor-0_0_8pre1/debian/tor.init | 104 + .../tor-0_0_8pre1/debian/tor.lintian-override | 1 + tags/tor-0_0_8pre1/debian/tor.logrotate | 11 + tags/tor-0_0_8pre1/debian/tor.postinst | 54 + tags/tor-0_0_8pre1/debian/tor.postrm | 11 + tags/tor-0_0_8pre1/doc/.cvsignore | 8 + tags/tor-0_0_8pre1/doc/CLIENTS | 66 + tags/tor-0_0_8pre1/doc/FAQ | 121 + tags/tor-0_0_8pre1/doc/HACKING | 580 ++++ tags/tor-0_0_8pre1/doc/Makefile.am | 4 + tags/tor-0_0_8pre1/doc/TODO | 300 ++ tags/tor-0_0_8pre1/doc/cell-struct.eps | 189 ++ tags/tor-0_0_8pre1/doc/cell-struct.fig | 49 + tags/tor-0_0_8pre1/doc/cell-struct.pdf | Bin 0 -> 6175 bytes tags/tor-0_0_8pre1/doc/cell-struct.png | Bin 0 -> 6088 bytes tags/tor-0_0_8pre1/doc/codecon04.mgp | 357 +++ tags/tor-0_0_8pre1/doc/interaction.eps | 463 +++ tags/tor-0_0_8pre1/doc/interaction.fig | 122 + tags/tor-0_0_8pre1/doc/interaction.pdf | Bin 0 -> 35540 bytes tags/tor-0_0_8pre1/doc/interaction.png | Bin 0 -> 29360 bytes tags/tor-0_0_8pre1/doc/latex8.bst | 1124 ++++++++ tags/tor-0_0_8pre1/doc/latex8.sty | 170 ++ tags/tor-0_0_8pre1/doc/rend-spec.txt | 333 +++ tags/tor-0_0_8pre1/doc/rump-fc04.mgp | 175 ++ tags/tor-0_0_8pre1/doc/socks-extensions.txt | 60 + tags/tor-0_0_8pre1/doc/tor-design.bib | 1093 ++++++++ tags/tor-0_0_8pre1/doc/tor-design.html | 2486 +++++++++++++++++ tags/tor-0_0_8pre1/doc/tor-design.pdf | Bin 0 -> 175246 bytes tags/tor-0_0_8pre1/doc/tor-design.tex | 1988 +++++++++++++ tags/tor-0_0_8pre1/doc/tor-doc.css | 89 + tags/tor-0_0_8pre1/doc/tor-doc.html | 341 +++ tags/tor-0_0_8pre1/doc/tor-spec.txt | 657 +++++ tags/tor-0_0_8pre1/doc/tor.1.in | 212 ++ tags/tor-0_0_8pre1/doc/usenix.sty | 98 + tags/tor-0_0_8pre1/src/.cvsignore | 2 + tags/tor-0_0_8pre1/src/Makefile.am | 6 + tags/tor-0_0_8pre1/src/common/.cvsignore | 3 + tags/tor-0_0_8pre1/src/common/Makefile.am | 8 + tags/tor-0_0_8pre1/src/common/aes.c | 819 ++++++ tags/tor-0_0_8pre1/src/common/aes.h | 36 + tags/tor-0_0_8pre1/src/common/crypto.c | 1491 ++++++++++ tags/tor-0_0_8pre1/src/common/crypto.h | 153 + tags/tor-0_0_8pre1/src/common/fakepoll.c | 124 + tags/tor-0_0_8pre1/src/common/fakepoll.h | 56 + tags/tor-0_0_8pre1/src/common/log.c | 301 ++ tags/tor-0_0_8pre1/src/common/log.h | 83 + tags/tor-0_0_8pre1/src/common/strlcat.c | 73 + tags/tor-0_0_8pre1/src/common/strlcpy.c | 64 + tags/tor-0_0_8pre1/src/common/test.h | 143 + tags/tor-0_0_8pre1/src/common/torint.h | 191 ++ tags/tor-0_0_8pre1/src/common/tortls.c | 790 ++++++ tags/tor-0_0_8pre1/src/common/tortls.h | 55 + tags/tor-0_0_8pre1/src/common/util.c | 1826 ++++++++++++ tags/tor-0_0_8pre1/src/common/util.h | 283 ++ tags/tor-0_0_8pre1/src/config/.cvsignore | 5 + tags/tor-0_0_8pre1/src/config/Makefile.am | 5 + tags/tor-0_0_8pre1/src/config/dirservers | 110 + tags/tor-0_0_8pre1/src/config/moria1-orrc | 12 + tags/tor-0_0_8pre1/src/config/moria2-orrc | 12 + tags/tor-0_0_8pre1/src/config/moria3-orrc | 12 + tags/tor-0_0_8pre1/src/config/torrc.sample.in | 78 + tags/tor-0_0_8pre1/src/or/.cvsignore | 5 + tags/tor-0_0_8pre1/src/or/Makefile.am | 28 + tags/tor-0_0_8pre1/src/or/buffers.c | 628 +++++ tags/tor-0_0_8pre1/src/or/circuitbuild.c | 1203 ++++++++ tags/tor-0_0_8pre1/src/or/circuitlist.c | 503 ++++ tags/tor-0_0_8pre1/src/or/circuituse.c | 859 ++++++ tags/tor-0_0_8pre1/src/or/command.c | 289 ++ tags/tor-0_0_8pre1/src/or/config.c | 968 +++++++ tags/tor-0_0_8pre1/src/or/connection.c | 1382 +++++++++ tags/tor-0_0_8pre1/src/or/connection_edge.c | 1152 ++++++++ tags/tor-0_0_8pre1/src/or/connection_or.c | 433 +++ tags/tor-0_0_8pre1/src/or/cpuworker.c | 384 +++ tags/tor-0_0_8pre1/src/or/directory.c | 729 +++++ tags/tor-0_0_8pre1/src/or/dirserv.c | 762 +++++ tags/tor-0_0_8pre1/src/or/dns.c | 783 ++++++ tags/tor-0_0_8pre1/src/or/main.c | 1142 ++++++++ tags/tor-0_0_8pre1/src/or/onion.c | 326 +++ tags/tor-0_0_8pre1/src/or/or.h | 1443 ++++++++++ tags/tor-0_0_8pre1/src/or/relay.c | 1012 +++++++ tags/tor-0_0_8pre1/src/or/rendclient.c | 459 +++ tags/tor-0_0_8pre1/src/or/rendcommon.c | 345 +++ tags/tor-0_0_8pre1/src/or/rendmid.c | 294 ++ tags/tor-0_0_8pre1/src/or/rendservice.c | 955 +++++++ tags/tor-0_0_8pre1/src/or/rephist.c | 484 ++++ tags/tor-0_0_8pre1/src/or/router.c | 767 +++++ tags/tor-0_0_8pre1/src/or/routerlist.c | 892 ++++++ tags/tor-0_0_8pre1/src/or/routerparse.c | 1424 ++++++++++ tags/tor-0_0_8pre1/src/or/test.c | 946 +++++++ tags/tor-0_0_8pre1/src/or/tor_main.c | 26 + tags/tor-0_0_8pre1/src/or/tree.h | 677 +++++ tags/tor-0_0_8pre1/src/win32/orconfig.h | 196 ++ 128 files changed, 43097 insertions(+) create mode 100644 tags/tor-0_0_8pre1/.cvsignore create mode 100644 tags/tor-0_0_8pre1/AUTHORS create mode 100644 tags/tor-0_0_8pre1/ChangeLog create mode 100644 tags/tor-0_0_8pre1/Doxyfile create mode 100644 tags/tor-0_0_8pre1/INSTALL create mode 100644 tags/tor-0_0_8pre1/LICENSE create mode 100644 tags/tor-0_0_8pre1/Makefile.am create mode 100644 tags/tor-0_0_8pre1/README create mode 100644 tags/tor-0_0_8pre1/Win32Build/or/or.dsp create mode 100644 tags/tor-0_0_8pre1/Win32Build/or/or.dsw create mode 100644 tags/tor-0_0_8pre1/Win32Build/tor/tor.dsw create mode 100755 tags/tor-0_0_8pre1/autogen.sh create mode 100644 tags/tor-0_0_8pre1/configure.in create mode 100644 tags/tor-0_0_8pre1/contrib/.cvsignore create mode 100644 tags/tor-0_0_8pre1/contrib/Makefile.am create mode 100755 tags/tor-0_0_8pre1/contrib/mdd.py create mode 100755 tags/tor-0_0_8pre1/contrib/tor-resolve create mode 100644 tags/tor-0_0_8pre1/contrib/tor-tsocks.conf create mode 100644 tags/tor-0_0_8pre1/contrib/tor.sh.in create mode 100644 tags/tor-0_0_8pre1/contrib/tor.spec create mode 100644 tags/tor-0_0_8pre1/contrib/torify.1 create mode 100755 tags/tor-0_0_8pre1/contrib/torify.in create mode 100644 tags/tor-0_0_8pre1/debian/README.privoxy create mode 100644 tags/tor-0_0_8pre1/debian/TODO create mode 100644 tags/tor-0_0_8pre1/debian/changelog create mode 100644 tags/tor-0_0_8pre1/debian/compat create mode 100644 tags/tor-0_0_8pre1/debian/control create mode 100644 tags/tor-0_0_8pre1/debian/copyright create mode 100644 tags/tor-0_0_8pre1/debian/patches/00list create mode 100755 tags/tor-0_0_8pre1/debian/patches/01_uncomment_datadirectory.dpatch create mode 100755 tags/tor-0_0_8pre1/debian/patches/02_add_debian_files_in_manpage.dpatch create mode 100755 tags/tor-0_0_8pre1/debian/patches/03_tor_manpage_in_section_8.dpatch create mode 100755 tags/tor-0_0_8pre1/debian/rules create mode 100644 tags/tor-0_0_8pre1/debian/tor.default create mode 100644 tags/tor-0_0_8pre1/debian/tor.dirs create mode 100644 tags/tor-0_0_8pre1/debian/tor.docs create mode 100644 tags/tor-0_0_8pre1/debian/tor.init create mode 100644 tags/tor-0_0_8pre1/debian/tor.lintian-override create mode 100644 tags/tor-0_0_8pre1/debian/tor.logrotate create mode 100644 tags/tor-0_0_8pre1/debian/tor.postinst create mode 100644 tags/tor-0_0_8pre1/debian/tor.postrm create mode 100644 tags/tor-0_0_8pre1/doc/.cvsignore create mode 100644 tags/tor-0_0_8pre1/doc/CLIENTS create mode 100644 tags/tor-0_0_8pre1/doc/FAQ create mode 100644 tags/tor-0_0_8pre1/doc/HACKING create mode 100644 tags/tor-0_0_8pre1/doc/Makefile.am create mode 100644 tags/tor-0_0_8pre1/doc/TODO create mode 100644 tags/tor-0_0_8pre1/doc/cell-struct.eps create mode 100644 tags/tor-0_0_8pre1/doc/cell-struct.fig create mode 100644 tags/tor-0_0_8pre1/doc/cell-struct.pdf create mode 100644 tags/tor-0_0_8pre1/doc/cell-struct.png create mode 100644 tags/tor-0_0_8pre1/doc/codecon04.mgp create mode 100644 tags/tor-0_0_8pre1/doc/interaction.eps create mode 100644 tags/tor-0_0_8pre1/doc/interaction.fig create mode 100644 tags/tor-0_0_8pre1/doc/interaction.pdf create mode 100644 tags/tor-0_0_8pre1/doc/interaction.png create mode 100644 tags/tor-0_0_8pre1/doc/latex8.bst create mode 100644 tags/tor-0_0_8pre1/doc/latex8.sty create mode 100644 tags/tor-0_0_8pre1/doc/rend-spec.txt create mode 100644 tags/tor-0_0_8pre1/doc/rump-fc04.mgp create mode 100644 tags/tor-0_0_8pre1/doc/socks-extensions.txt create mode 100644 tags/tor-0_0_8pre1/doc/tor-design.bib create mode 100644 tags/tor-0_0_8pre1/doc/tor-design.html create mode 100644 tags/tor-0_0_8pre1/doc/tor-design.pdf create mode 100644 tags/tor-0_0_8pre1/doc/tor-design.tex create mode 100644 tags/tor-0_0_8pre1/doc/tor-doc.css create mode 100644 tags/tor-0_0_8pre1/doc/tor-doc.html create mode 100644 tags/tor-0_0_8pre1/doc/tor-spec.txt create mode 100644 tags/tor-0_0_8pre1/doc/tor.1.in create mode 100644 tags/tor-0_0_8pre1/doc/usenix.sty create mode 100644 tags/tor-0_0_8pre1/src/.cvsignore create mode 100644 tags/tor-0_0_8pre1/src/Makefile.am create mode 100644 tags/tor-0_0_8pre1/src/common/.cvsignore create mode 100644 tags/tor-0_0_8pre1/src/common/Makefile.am create mode 100644 tags/tor-0_0_8pre1/src/common/aes.c create mode 100644 tags/tor-0_0_8pre1/src/common/aes.h create mode 100644 tags/tor-0_0_8pre1/src/common/crypto.c create mode 100644 tags/tor-0_0_8pre1/src/common/crypto.h create mode 100644 tags/tor-0_0_8pre1/src/common/fakepoll.c create mode 100644 tags/tor-0_0_8pre1/src/common/fakepoll.h create mode 100644 tags/tor-0_0_8pre1/src/common/log.c create mode 100644 tags/tor-0_0_8pre1/src/common/log.h create mode 100644 tags/tor-0_0_8pre1/src/common/strlcat.c create mode 100644 tags/tor-0_0_8pre1/src/common/strlcpy.c create mode 100644 tags/tor-0_0_8pre1/src/common/test.h create mode 100644 tags/tor-0_0_8pre1/src/common/torint.h create mode 100644 tags/tor-0_0_8pre1/src/common/tortls.c create mode 100644 tags/tor-0_0_8pre1/src/common/tortls.h create mode 100644 tags/tor-0_0_8pre1/src/common/util.c create mode 100644 tags/tor-0_0_8pre1/src/common/util.h create mode 100644 tags/tor-0_0_8pre1/src/config/.cvsignore create mode 100644 tags/tor-0_0_8pre1/src/config/Makefile.am create mode 100644 tags/tor-0_0_8pre1/src/config/dirservers create mode 100644 tags/tor-0_0_8pre1/src/config/moria1-orrc create mode 100644 tags/tor-0_0_8pre1/src/config/moria2-orrc create mode 100644 tags/tor-0_0_8pre1/src/config/moria3-orrc create mode 100644 tags/tor-0_0_8pre1/src/config/torrc.sample.in create mode 100644 tags/tor-0_0_8pre1/src/or/.cvsignore create mode 100644 tags/tor-0_0_8pre1/src/or/Makefile.am create mode 100644 tags/tor-0_0_8pre1/src/or/buffers.c create mode 100644 tags/tor-0_0_8pre1/src/or/circuitbuild.c create mode 100644 tags/tor-0_0_8pre1/src/or/circuitlist.c create mode 100644 tags/tor-0_0_8pre1/src/or/circuituse.c create mode 100644 tags/tor-0_0_8pre1/src/or/command.c create mode 100644 tags/tor-0_0_8pre1/src/or/config.c create mode 100644 tags/tor-0_0_8pre1/src/or/connection.c create mode 100644 tags/tor-0_0_8pre1/src/or/connection_edge.c create mode 100644 tags/tor-0_0_8pre1/src/or/connection_or.c create mode 100644 tags/tor-0_0_8pre1/src/or/cpuworker.c create mode 100644 tags/tor-0_0_8pre1/src/or/directory.c create mode 100644 tags/tor-0_0_8pre1/src/or/dirserv.c create mode 100644 tags/tor-0_0_8pre1/src/or/dns.c create mode 100644 tags/tor-0_0_8pre1/src/or/main.c create mode 100644 tags/tor-0_0_8pre1/src/or/onion.c create mode 100644 tags/tor-0_0_8pre1/src/or/or.h create mode 100644 tags/tor-0_0_8pre1/src/or/relay.c create mode 100644 tags/tor-0_0_8pre1/src/or/rendclient.c create mode 100644 tags/tor-0_0_8pre1/src/or/rendcommon.c create mode 100644 tags/tor-0_0_8pre1/src/or/rendmid.c create mode 100644 tags/tor-0_0_8pre1/src/or/rendservice.c create mode 100644 tags/tor-0_0_8pre1/src/or/rephist.c create mode 100644 tags/tor-0_0_8pre1/src/or/router.c create mode 100644 tags/tor-0_0_8pre1/src/or/routerlist.c create mode 100644 tags/tor-0_0_8pre1/src/or/routerparse.c create mode 100644 tags/tor-0_0_8pre1/src/or/test.c create mode 100644 tags/tor-0_0_8pre1/src/or/tor_main.c create mode 100644 tags/tor-0_0_8pre1/src/or/tree.h create mode 100644 tags/tor-0_0_8pre1/src/win32/orconfig.h diff --git a/tags/tor-0_0_8pre1/.cvsignore b/tags/tor-0_0_8pre1/.cvsignore new file mode 100644 index 0000000000..e99de92461 --- /dev/null +++ b/tags/tor-0_0_8pre1/.cvsignore @@ -0,0 +1,20 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +configure +orconfig.h +orconfig.h.in +config.cache +config.log +config.status +config.guess +config.sub +conftest* +stamp-h +stamp-h1 +tor.sh +depcomp +install-sh +missing +mkinstalldirs diff --git a/tags/tor-0_0_8pre1/AUTHORS b/tags/tor-0_0_8pre1/AUTHORS new file mode 100644 index 0000000000..dc32abebe9 --- /dev/null +++ b/tags/tor-0_0_8pre1/AUTHORS @@ -0,0 +1,34 @@ + +Main authors: +------------- + +Roger Dingledine overhauled all of the code, did +a bunch of new design work, etc. + +Nick Mathewson wrote lots of stuff too, in +particular the router and descriptor parsing, and the crypto and tls +wrappers. + +Matej Pfajfar wrote the first version of the code +(called OR) in 2001-2002. + +Contributors: +------------- + +John Bashinski contributed the initial rpm spec file. + +Christian Grothoff contributed better daemonizing +behavior. + +Steven Hazel made 'make install' do the right +thing. + +Jason Holt contributed patches to the instructions +and the man page. + +Peter Palfrader maintains everything that's +debian-specific. + +Aaron Turner contributed the first version of +the tor.sh initscripts shell script. + diff --git a/tags/tor-0_0_8pre1/ChangeLog b/tags/tor-0_0_8pre1/ChangeLog new file mode 100644 index 0000000000..47125bfbc4 --- /dev/null +++ b/tags/tor-0_0_8pre1/ChangeLog @@ -0,0 +1,575 @@ +Release notes in progress for 0.0.8: +pre1: + o Bugfixes: + - Made our unit tests compile again on OpenBSD 3.5, and tor + itself compile again on OpenBSD on a sparc64. + - We were neglecting milliseconds when logging on win32, so + everything appeared to happen at the beginning of each second. + + o Protocol changes: + - 'Extend' relay cell payloads now include the digest of the + intended next hop's identity key. Now we can verify that we're + extending to the right router, and also extend to routers we + hadn't heard of before. + + o Features: + - Tor nodes can now act as relays (with an advertised ORPort) + without being manually verified by the dirserver operators. + - Uploaded descriptors of unverified routers are now accepted + by the dirservers, and included in the directory. + - Verified routers are listed by nickname in the running-routers + list; unverified routers are listed as "$". + - We now use hash-of-identity-key in most places rather than + nickname or addr:port, for improved security/flexibility. + - To avoid Sybil attacks, paths still use only verified servers. + But now we have a chance to play around with hybrid approaches. + - Nodes track bandwidth usage to estimate capacity (not used yet). + - ClientOnly option for nodes that never want to become servers. + - Directory caching. + - "AuthoritativeDir 1" option for the official dirservers. + - Now other nodes (clients and servers) will cache the latest + directory they've pulled down. + - They can enable their DirPort to serve it to others. + - Clients will pull down a directory from any node with an open + DirPort, and check the signature/timestamp correctly. + - Authoritative dirservers now fetch directories from other + authdirservers, to stay better synced. + - Running-routers list tells who's down also, along with noting + if they're verified (listed by nickname) or unverified (listed + by hash-of-key). + - Allow dirservers to serve running-router list separately. + This isn't used yet. + - ORs connect-on-demand to other ORs + - If you get an extend cell to an OR you're not connected to, + connect, handshake, and forward the create cell. + - The authoritative dirservers stay connected to everybody, + and everybody stays connected to 0.0.7 servers, but otherwise + clients/servers expire unused connections after 5 minutes. + - When servers get a sigint, they delay 30 seconds (refusing new + connections) then exit. A second sigint causes immediate exit. + - File and name management: + - Look for .torrc if no CONFDIR "torrc" is found. + - If no datadir is defined, then choose, make, and secure ~/.tor + as datadir. + - If torrc not found, exitpolicy reject *:*. + - Expands ~/ in filenames to $HOME/ (but doesn't yet expand ~arma). + - If no nickname is defined, derive default from hostname. + - Rename secret key files, e.g. identity.key -> secret_id_key, + to discourage people from mailing their identity key to tor-ops. + - Refuse to build a circuit before the directory has arrived -- + it won't work anyway, since you won't know the right onion keys + to use. + - Try other dirservers immediately if the one you try is down. This + should tolerate down dirservers better now. + - Parse tor version numbers so we can do an is-newer-than check + rather than an is-in-the-list check. + - New socks command 'resolve', to let us shim gethostbyname() + locally. + - A 'tor_resolve' script to access the socks resolve functionality. + - A new socks-extensions.txt doc file to describe our + interpretation and extensions to the socks protocols. + - Add a ContactInfo option, which gets published in descriptor. + - Publish OR uptime in descriptor (and thus in directory) too. + - Write tor version at the top of each log file + - New docs in the tarball: + - tor-doc.html. + - Document that you should proxy your SSL traffic too. + + +Changes in version 0.0.7.2 - 2004-07-07 + o A better fix for the 0.0.0.0 problem, that will hopefully + eliminate the remaining related assertion failures. + + +Changes in version 0.0.7.1 - 2004-07-04 + o When an address resolves to 0.0.0.0, treat it as a failed resolve, + since internally we use 0.0.0.0 to signify "not yet resolved". + + +Changes in version 0.0.7 - 2004-06-07 + o Fixes for crashes and other obnoxious bugs: + - Fix an epipe bug: sometimes when directory connections failed + to connect, we would give them a chance to flush before closing + them. + - When we detached from a circuit because of resolvefailed, we + would immediately try the same circuit twice more, and then + give up on the resolve thinking we'd tried three different + exit nodes. + - Limit the number of intro circuits we'll attempt to build for a + hidden service per 15-minute period. + - Check recommended-software string *early*, before actually parsing + the directory. Thus we can detect an obsolete version and exit, + even if the new directory format doesn't parse. + o Fixes for security bugs: + - Remember which nodes are dirservers when you startup, and if a + random OR enables his dirport, don't automatically assume he's + a trusted dirserver. + o Other bugfixes: + - Directory connections were asking the wrong poll socket to + start writing, and not asking themselves to start writing. + - When we detached from a circuit because we sent a begin but + didn't get a connected, we would use it again the first time; + but after that we would correctly switch to a different one. + - Stop warning when the first onion decrypt attempt fails; they + will sometimes legitimately fail now that we rotate keys. + - Override unaligned-access-ok check when $host_cpu is ia64 or + arm. Apparently they allow it but the kernel whines. + - Dirservers try to reconnect periodically too, in case connections + have failed. + - Fix some memory leaks in directory servers. + - Allow backslash in Win32 filenames. + - Made Tor build complain-free on FreeBSD, hopefully without + breaking other BSD builds. We'll see. + - Check directory signatures based on name of signer, not on whom + we got the directory from. This will let us cache directories more + easily. + - Rotate dnsworkers and cpuworkers on SIGHUP, so they get new config + settings too. + o Features: + - Doxygen markup on all functions and global variables. + - Make directory functions update routerlist, not replace it. So + now directory disagreements are not so critical a problem. + - Remove the upper limit on number of descriptors in a dirserver's + directory (not that we were anywhere close). + - Allow multiple logfiles at different severity ranges. + - Allow *BindAddress to specify ":port" rather than setting *Port + separately. Allow multiple instances of each BindAddress config + option, so you can bind to multiple interfaces if you want. + - Allow multiple exit policy lines, which are processed in order. + Now we don't need that huge line with all the commas in it. + - Enable accept/reject policies on SOCKS connections, so you can bind + to 0.0.0.0 but still control who can use your OP. + - Updated the man page to reflect these features. + + +Changes in version 0.0.6.2 - 2004-05-16 + o Our integrity-checking digest was checking only the most recent cell, + not the previous cells like we'd thought. + Thanks to Stefan Mark for finding the flaw! + + +Changes in version 0.0.6.1 - 2004-05-06 + o Fix two bugs in our AES counter-mode implementation (this affected + onion-level stream encryption, but not TLS-level). It turns + out we were doing something much more akin to a 16-character + polyalphabetic cipher. Oops. + Thanks to Stefan Mark for finding the flaw! + o Retire moria3 as a directory server, and add tor26 as a directory + server. + + +Changes in version 0.0.6 - 2004-05-02 + o Features: + - Hidden services and rendezvous points are implemented. Go to + http://6sxoyfb3h2nvok2d.onion/ for an index of currently available + hidden services. (This only works via a socks4a proxy such as + Privoxy, and currently it's quite slow.) + - We now rotate link (tls context) keys and onion keys. + - CREATE cells now include oaep padding, so you can tell + if you decrypted them correctly. + - Retry stream correctly when we fail to connect because of + exit-policy-reject (should try another) or can't-resolve-address. + - When we hup a dirserver and we've *removed* a server from the + approved-routers list, now we remove that server from the + in-memory directories too. + - Add bandwidthburst to server descriptor. + - Directories now say which dirserver signed them. + - Use a tor_assert macro that logs failed assertions too. + - Since we don't support truncateds much, don't bother sending them; + just close the circ. + - Fetch randomness from /dev/urandom better (not via fopen/fread) + - Better debugging for tls errors + - Set Content-Type on the directory and hidserv descriptor. + - Remove IVs from cipher code, since AES-ctr has none. + o Bugfixes: + - Fix an assert trigger for exit nodes that's been plaguing us since + the days of 0.0.2prexx (thanks weasel!) + - Fix a bug where we were closing tls connections intermittently. + It turns out openssl keeps its errors around -- so if an error + happens, and you don't ask about it, and then another openssl + operation happens and succeeds, and you ask if there was an error, + it tells you about the first error. + - Fix a bug that's been lurking since 27 may 03 (!) + When passing back a destroy cell, we would use the wrong circ id. + - Don't crash if a conn that sent a begin has suddenly lost its circuit. + - Some versions of openssl have an SSL_pending function that erroneously + returns bytes when there is a non-application record pending. + - Win32 fixes. Tor now compiles on win32 with no warnings/errors. + o We were using an array of length zero in a few places. + o Win32's gethostbyname can't resolve an IP to an IP. + o Win32's close can't close a socket. + o Handle windows socket errors correctly. + o Portability: + - check for so we build on FreeBSD again, and + for NetBSD. + + +Changes in version 0.0.5 - 2004-03-30 + o Install torrc as torrc.sample -- we no longer clobber your + torrc. (Woo!) + o Fix mangled-state bug in directory fetching (was causing sigpipes). + o Only build circuits after we've fetched the directory: clients were + using only the directory servers before they'd fetched a directory. + This also means longer startup time; so it goes. + o Fix an assert trigger where an OP would fail to handshake, and we'd + expect it to have a nickname. + o Work around a tsocks bug: do a socks reject when AP connection dies + early, else tsocks goes into an infinite loop. + o Hold socks connection open until reply is flushed (if possible) + o Make exit nodes resolve IPs to IPs immediately, rather than asking + the dns farm to do it. + o Fix c99 aliasing warnings in rephist.c + o Don't include server descriptors that are older than 24 hours in the + directory. + o Give socks 'reject' replies their whole 15s to attempt to flush, + rather than seeing the 60s timeout and assuming the flush had failed. + o Clean automake droppings from the cvs repository + o Add in a 'notice' log level for things the operator should hear + but that aren't warnings + + +Changes in version 0.0.4 - 2004-03-26 + o When connecting to a dirserver or OR and the network is down, + we would crash. + + +Changes in version 0.0.3 - 2004-03-26 + o Warn and fail if server chose a nickname with illegal characters + o Port to Solaris and Sparc: + - include missing header fcntl.h + - have autoconf find -lsocket -lnsl automatically + - deal with hardware word alignment + - make uname() work (solaris has a different return convention) + - switch from using signal() to sigaction() + o Preliminary work on reputation system: + - Keep statistics on success/fail of connect attempts; they're published + by kill -USR1 currently. + - Add a RunTesting option to try to learn link state by creating test + circuits, even when SocksPort is off. + - Remove unused open circuits when there are too many. + + +Changes in version 0.0.2 - 2004-03-19 + - Include strlcpy and strlcat for safer string ops + - define INADDR_NONE so we compile (but still not run) on solaris + + +Changes in version 0.0.2pre27 - 2004-03-14 + o Bugfixes: + - Allow internal tor networks (we were rejecting internal IPs, + now we allow them if they're set explicitly). + - And fix a few endian issues. + + +Changes in version 0.0.2pre26 - 2004-03-14 + o New features: + - If a stream times out after 15s without a connected cell, don't + try that circuit again: try a new one. + - Retry streams at most 4 times. Then give up. + - When a dirserver gets a descriptor from an unknown router, it + logs its fingerprint (so the dirserver operator can choose to + accept it even without mail from the server operator). + - Inform unapproved servers when we reject their descriptors. + - Make tor build on Windows again. It works as a client, who knows + about as a server. + - Clearer instructions in the torrc for how to set up a server. + - Be more efficient about reading fd's when our global token bucket + (used for rate limiting) becomes empty. + o Bugfixes: + - Stop asserting that computers always go forward in time. It's + simply not true. + - When we sent a cell (e.g. destroy) and then marked an OR connection + expired, we might close it before finishing a flush if the other + side isn't reading right then. + - Don't allow dirservers to start if they haven't defined + RecommendedVersions + - We were caching transient dns failures. Oops. + - Prevent servers from publishing an internal IP as their address. + - Address a strcat vulnerability in circuit.c + + +Changes in version 0.0.2pre25 - 2004-03-04 + o New features: + - Put the OR's IP in its router descriptor, not its fqdn. That way + we'll stop being stalled by gethostbyname for nodes with flaky dns, + e.g. poblano. + o Bugfixes: + - If the user typed in an address that didn't resolve, the server + crashed. + + +Changes in version 0.0.2pre24 - 2004-03-03 + o Bugfixes: + - Fix an assertion failure in dns.c, where we were trying to dequeue + a pending dns resolve even if it wasn't pending + - Fix a spurious socks5 warning about still trying to write after the + connection is finished. + - Hold certain marked_for_close connections open until they're finished + flushing, rather than losing bytes by closing them too early. + - Correctly report the reason for ending a stream + - Remove some duplicate calls to connection_mark_for_close + - Put switch_id and start_daemon earlier in the boot sequence, so it + will actually try to chdir() to options.DataDirectory + - Make 'make test' exit(1) if a test fails; fix some unit tests + - Make tor fail when you use a config option it doesn't know about, + rather than warn and continue. + - Make --version work + - Bugfixes on the rpm spec file and tor.sh, so it's more up to date + + +Changes in version 0.0.2pre23 - 2004-02-29 + o New features: + - Print a statement when the first circ is finished, so the user + knows it's working. + - If a relay cell is unrecognized at the end of the circuit, + send back a destroy. (So attacks to mutate cells are more + clearly thwarted.) + - New config option 'excludenodes' to avoid certain nodes for circuits. + - When it daemonizes, it chdir's to the DataDirectory rather than "/", + so you can collect coredumps there. + o Bugfixes: + - Fix a bug in tls flushing where sometimes data got wedged and + didn't flush until more data got sent. Hopefully this bug was + a big factor in the random delays we were seeing. + - Make 'connected' cells include the resolved IP, so the client + dns cache actually gets populated. + - Disallow changing from ORPort=0 to ORPort>0 on hup. + - When we time-out on a stream and detach from the circuit, send an + end cell down it first. + - Only warn about an unknown router (in exitnodes, entrynodes, + excludenodes) after we've fetched a directory. + + +Changes in version 0.0.2pre22 - 2004-02-26 + o New features: + - Servers publish less revealing uname information in descriptors. + - More memory tracking and assertions, to crash more usefully when + errors happen. + - If the default torrc isn't there, just use some default defaults. + Plus provide an internal dirservers file if they don't have one. + - When the user tries to use Tor as an http proxy, give them an http + 501 failure explaining that we're a socks proxy. + - Dump a new router.desc on hup, to help confused people who change + their exit policies and then wonder why router.desc doesn't reflect + it. + - Clean up the generic tor.sh init script that we ship with. + o Bugfixes: + - If the exit stream is pending on the resolve, and a destroy arrives, + then the stream wasn't getting removed from the pending list. I + think this was the one causing recent server crashes. + - Use a more robust poll on OSX 10.3, since their poll is flaky. + - When it couldn't resolve any dirservers, it was useless from then on. + Now it reloads the RouterFile (or default dirservers) if it has no + dirservers. + - Move the 'tor' binary back to /usr/local/bin/ -- it turns out + many users don't even *have* a /usr/local/sbin/. + + +Changes in version 0.0.2pre21 - 2004-02-18 + o New features: + - There's a ChangeLog file that actually reflects the changelog. + - There's a 'torify' wrapper script, with an accompanying + tor-tsocks.conf, that simplifies the process of using tsocks for + tor. It even has a man page. + - The tor binary gets installed to sbin rather than bin now. + - Retry streams where the connected cell hasn't arrived in 15 seconds + - Clean up exit policy handling -- get the default out of the torrc, + so we can update it without forcing each server operator to fix + his/her torrc. + - Allow imaps and pop3s in default exit policy + o Bugfixes: + - Prevent picking middleman nodes as the last node in the circuit + + +Changes in version 0.0.2pre20 - 2004-01-30 + o New features: + - We now have a deb package, and it's in debian unstable. Go to + it, apt-getters. :) + - I've split the TotalBandwidth option into BandwidthRate (how many + bytes per second you want to allow, long-term) and + BandwidthBurst (how many bytes you will allow at once before the cap + kicks in). This better token bucket approach lets you, say, set + BandwidthRate to 10KB/s and BandwidthBurst to 10MB, allowing good + performance while not exceeding your monthly bandwidth quota. + - Push out a tls record's worth of data once you've got it, rather + than waiting until you've read everything waiting to be read. This + may improve performance by pipelining better. We'll see. + - Add an AP_CONN_STATE_CONNECTING state, to allow streams to detach + from failed circuits (if they haven't been connected yet) and attach + to new ones. + - Expire old streams that haven't managed to connect. Some day we'll + have them reattach to new circuits instead. + + o Bugfixes: + - Fix several memory leaks that were causing servers to become bloated + after a while. + - Fix a few very rare assert triggers. A few more remain. + - Setuid to User _before_ complaining about running as root. + + +Changes in version 0.0.2pre19 - 2004-01-07 + o Bugfixes: + - Fix deadlock condition in dns farm. We were telling a child to die by + closing the parent's file descriptor to him. But newer children were + inheriting the open file descriptor from the parent, and since they + weren't closing it, the socket never closed, so the child never read + eof, so he never knew to exit. Similarly, dns workers were holding + open other sockets, leading to all sorts of chaos. + - New cleaner daemon() code for forking and backgrounding. + - If you log to a file, it now prints an entry at the top of the + logfile so you know it's working. + - The onionskin challenge length was 30 bytes longer than necessary. + - Started to patch up the spec so it's not quite so out of date. + + +Changes in version 0.0.2pre18 - 2004-01-02 + o Bugfixes: + - Fix endian issues with the 'integrity' field in the relay header. + - Fix a potential bug where connections in state + AP_CONN_STATE_CIRCUIT_WAIT might unexpectedly ask to write. + + +Changes in version 0.0.2pre17 - 2003-12-30 + o Bugfixes: + - Made --debuglogfile (or any second log file, actually) work. + - Resolved an edge case in get_unique_circ_id_by_conn where a smart + adversary could force us into an infinite loop. + + o Features: + - Each onionskin handshake now includes a hash of the computed key, + to prove the server's identity and help perfect forward secrecy. + - Changed cell size from 256 to 512 bytes (working toward compatibility + with MorphMix). + - Changed cell length to 2 bytes, and moved it to the relay header. + - Implemented end-to-end integrity checking for the payloads of + relay cells. + - Separated streamid from 'recognized' (otherwise circuits will get + messed up when we try to have streams exit from the middle). We + use the integrity-checking to confirm that a cell is addressed to + this hop. + - Randomize the initial circid and streamid values, so an adversary who + breaks into a node can't learn how many circuits or streams have + been made so far. + + +Changes in version 0.0.2pre16 - 2003-12-14 + o Bugfixes: + - Fixed a bug that made HUP trigger an assert + - Fixed a bug where a circuit that immediately failed wasn't being + counted as a failed circuit in counting retries. + + o Features: + - Now we close the circuit when we get a truncated cell: otherwise we're + open to an anonymity attack where a bad node in the path truncates + the circuit and then we open streams at him. + - Add port ranges to exit policies + - Add a conservative default exit policy + - Warn if you're running tor as root + - on HUP, retry OR connections and close/rebind listeners + - options.EntryNodes: try these nodes first when picking the first node + - options.ExitNodes: if your best choices happen to include any of + your preferred exit nodes, you choose among just those preferred + exit nodes. + - options.ExcludedNodes: nodes that are never picked in path building + + +Changes in version 0.0.2pre15 - 2003-12-03 + o Robustness and bugfixes: + - Sometimes clients would cache incorrect DNS resolves, which would + really screw things up. + - An OP that goes offline would slowly leak all its sockets and stop + working. + - A wide variety of bugfixes in exit node selection, exit policy + handling, and processing pending streams when a new circuit is + established. + - Pick nodes for a path only from those the directory says are up + - Choose randomly from all running dirservers, not always the first one + - Increase allowed http header size for directory fetch. + - Stop writing to stderr (if we're daemonized it will be closed). + - Enable -g always, so cores will be more useful to me. + - Switch "-lcrypto -lssl" to "-lssl -lcrypto" for broken distributions. + + o Documentation: + - Wrote a man page. It lists commonly used options. + + o Configuration: + - Change default loglevel to warn. + - Make PidFile default to null rather than littering in your CWD. + - OnionRouter config option is now obsolete. Instead it just checks + ORPort>0. + - Moved to a single unified torrc file for both clients and servers. + + +Changes in version 0.0.2pre14 - 2003-11-29 + o Robustness and bugfixes: + - Force the admin to make the DataDirectory himself + - to get ownership/permissions right + - so clients no longer make a DataDirectory and then never use it + - fix bug where a client who was offline for 45 minutes would never + pull down a directory again + - fix (or at least hide really well) the dns assert bug that was + causing server crashes + - warnings and improved robustness wrt clockskew for certs + - use the native daemon(3) to daemonize, when available + - exit if bind() fails + - exit if neither socksport nor orport is defined + - include our own tor_timegm (Win32 doesn't have its own) + - bugfix for win32 with lots of connections + - fix minor bias in PRNG + - make dirserver more robust to corrupt cached directory + + o Documentation: + - Wrote the design document (woo) + + o Circuit building and exit policies: + - Circuits no longer try to use nodes that the directory has told them + are down. + - Exit policies now support bitmasks (18.0.0.0/255.0.0.0) and + bitcounts (18.0.0.0/8). + - Make AP connections standby for a circuit if no suitable circuit + exists, rather than failing + - Circuits choose exit node based on addr/port, exit policies, and + which AP connections are standing by + - Bump min pathlen from 2 to 3 + - Relay end cells have a payload to describe why the stream ended. + - If the stream failed because of exit policy, try again with a new + circuit. + - Clients have a dns cache to remember resolved addresses. + - Notice more quickly when we have no working circuits + + o Configuration: + - APPort is now called SocksPort + - SocksBindAddress, ORBindAddress, DirBindAddress let you configure + where to bind + - RecommendedVersions is now a config variable rather than + hardcoded (for dirservers) + - Reloads config on HUP + - Usage info on -h or --help + - If you set User and Group config vars, it'll setu/gid to them. + +Changes in version 0.0.2pre13 - 2003-10-19 + o General stability: + - SSL_write no longer fails when it returns WANTWRITE and the number + of bytes in the buf has changed by the next SSL_write call. + - Fix segfault fetching directory when network is down + - Fix a variety of minor memory leaks + - Dirservers reload the fingerprints file on HUP, so I don't have + to take down the network when I approve a new router + - Default server config file has explicit Address line to specify fqdn + + o Buffers: + - Buffers grow and shrink as needed (Cut process size from 20M to 2M) + - Make listener connections not ever alloc bufs + + o Autoconf improvements: + - don't clobber an external CFLAGS in ./configure + - Make install now works + - create var/lib/tor on make install + - autocreate a tor.sh initscript to help distribs + - autocreate the torrc and sample-server-torrc with correct paths + + o Log files and Daemonizing now work: + - If --DebugLogFile is specified, log to it at -l debug + - If --LogFile is specified, use it instead of commandline + - If --RunAsDaemon is set, tor forks and backgrounds on startup + diff --git a/tags/tor-0_0_8pre1/Doxyfile b/tags/tor-0_0_8pre1/Doxyfile new file mode 100644 index 0000000000..e44a6b87ff --- /dev/null +++ b/tags/tor-0_0_8pre1/Doxyfile @@ -0,0 +1,996 @@ +# $Id$ + +# Doxyfile 1.2.18 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = tor + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.0.7pre1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./doc/doxygen + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +#changed +BRIEF_MEMBER_DESC = NO + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src/common src/or + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = *.c *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = tree.h + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non empty doxygen will try to run +# the html help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/tags/tor-0_0_8pre1/INSTALL b/tags/tor-0_0_8pre1/INSTALL new file mode 100644 index 0000000000..c9bc5404dc --- /dev/null +++ b/tags/tor-0_0_8pre1/INSTALL @@ -0,0 +1,54 @@ +Quickstart version for users: + +0) Download the absolute newest version. No, really. + http://freehaven.net/tor/dist/ +1) tar xvf it, and then cd into the directory. +2) ./configure +3) make +4) make install (as root if necessary) +5) tor (if it doesn't work, give it the whole path or fix your path) + You don't need to run this as root, and you probably shouldn't. + (If you're having problems, try running it with "-l info" to get + more details.) +6) point your browser to socks4 or socks5 proxy at localhost port + 9050. In mozilla, this is in edit|preferences|advanced|proxies. This + allows you to test to make sure tor is installed correctly. + (If you have a personal firewall, be sure to allow local connections + to port 9050.) + (If your firewall blocks outgoing connections, punch a hole so it + can connect to TCP *:9001-9004 and *:9031-9033) + (If you're using Safari as your browser, keep in mind that OS X before + 10.3 claims to support socks but does not. You must do step 8.) +7) make sure you've set it up correctly: go to + http://www.junkbusters.com/cgi-bin/privacy and see what IP it says + you're coming from. If it works, you should probably go on to step 8, + to get better privacy. + +8) Optionally, install privoxy (www.privoxy.org), and add the line + "forward-socks4a / localhost:9050 ." (without the quotes -- don't forget + the dot) to its config file. Then change your mozilla to http proxy + at localhost port 8118 (and no socks proxy). You should also set your + SSL proxy to the same thing, to hide your https traffic. Using privoxy + will give you good html scrubbing as well. + (See doc/CLIENTS for why direct socks gives you less anonymity.) + +*****If this works for you, you can stop reading here****** + +If you got the source from cvs: + + Run "./autogen.sh", which will run the various auto* programs and then + run ./configure for you. From there, start at step 3 in the quickstart + list above. + +If the quickstart doesn't work for you: + + If you have problems finding libraries, try + CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" \ + ./configure + rather than simply ./configure. + + Check out the list archives at http://archives.seul.org/or/dev/ and see + if somebody else has reported your problem. If not, please subscribe + and let us know what you did to fix it, or give us the details and + we'll see what we can do. + diff --git a/tags/tor-0_0_8pre1/LICENSE b/tags/tor-0_0_8pre1/LICENSE new file mode 100644 index 0000000000..49f6271f08 --- /dev/null +++ b/tags/tor-0_0_8pre1/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2001-2004, Roger Dingledine + +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 names of the copyright owners 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. diff --git a/tags/tor-0_0_8pre1/Makefile.am b/tags/tor-0_0_8pre1/Makefile.am new file mode 100644 index 0000000000..5d7dd0a81c --- /dev/null +++ b/tags/tor-0_0_8pre1/Makefile.am @@ -0,0 +1,24 @@ +AUTOMAKE_OPTIONS = foreign + # else it keeps trying to put COPYING back in + +SUBDIRS = src doc contrib + +DIST_SUBDIRS = src doc contrib + +EXTRA_DIST = INSTALL README AUTHORS LICENSE ChangeLog + +#install-data-local: +# $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor + +dist-rpm: dist + rm -rf /tmp/tor-rpm-build + mkdir /tmp/tor-rpm-build + for subdir in BUILD RPMS SOURCES SPECS SRPMS; do \ + mkdir /tmp/tor-rpm-build/$$subdir; \ + done + cp tor-$(VERSION).tar.gz /tmp/tor-rpm-build/SOURCES + rpmbuild -ba --define '_topdir /tmp/tor-rpm-build' contrib/tor.spec + mv /tmp/tor-rpm-build/*RPMS/* . + +doxygen: + doxygen && cd doc/doxygen/latex && make \ No newline at end of file diff --git a/tags/tor-0_0_8pre1/README b/tags/tor-0_0_8pre1/README new file mode 100644 index 0000000000..5ca9aea793 --- /dev/null +++ b/tags/tor-0_0_8pre1/README @@ -0,0 +1,63 @@ + +'tor' is an implementation of The Onion Routing system, as +described in a bit more detail at http://www.onion-router.net/. You +can read list archives, and subscribe to the mailing list, at +http://archives.seul.org/or/dev/. + +Is your question in the FAQ? Should it be? + +************************************************************************** +See the INSTALL file for a quickstart. That is all you will probably need. +************************************************************************** + +************************************************************************** +You only need to look beyond this point if the quickstart in the INSTALL +doesn't work for you. +************************************************************************** + +Do you want to run a tor server? + + We're looking for people with reasonably reliable Internet connections, + that have at least 768kbit each way. Currently we don't use all of that, + but we want it available for burst traffic. + + First, copy torrc.sample to torrc (by default it's in + /usr/local/etc/tor/), and edit the middle part. Create the + DataDirectory, and make sure it's owned by whoever will be running + tor. Fix your system clock so it's not too far off. Make sure name + resolution works. + + Then run tor to generate keys. One of the files generated + in your DataDirectory is your 'fingerprint' file. Mail it to + tor-ops@freehaven.net. + + Please also tell us in that mail who you are, so we know whom to contact + if there's any problem. Also describe what kind of connectivity the new + server will have. If possible PGP sign your mail. + + NOTE: You won't be able to use tor as a client or server + in this configuration until you've been added to the directory + and can authenticate to the other nodes. + +Do you want to run a hidden service? + + Copy torrc.sample to torrc (by default it's in /usr/local/etc/tor/), and + edit the bottom part. Then run Tor. It will create each HiddenServiceDir + you have configured, and it will create a 'hostname' file which + specifies the url (xyz.onion) for that service. You can tell people + the url, and they can connect to it via their Tor client. + +Configuring tsocks: + + If you want to use Tor for protocols that can't use Privoxy, or + with applications that are not socksified, then download tsocks + (tsocks.sourceforge.net) and configure it to talk to localhost:9050 + as a socks4 server. My /etc/tsocks.conf simply has: + server_port = 9050 + server = 127.0.0.1 + (I had to "cd /usr/lib; ln -s /lib/libtsocks.so" to get the tsocks + library working after install, since my libpath didn't include /lib.) + Then you can do "tsocks ssh arma@moria.mit.edu". But note that if + ssh is suid root, you either need to do this as root, or cp a local + version of ssh that isn't suid. + diff --git a/tags/tor-0_0_8pre1/Win32Build/or/or.dsp b/tags/tor-0_0_8pre1/Win32Build/or/or.dsp new file mode 100644 index 0000000000..b52f69b044 --- /dev/null +++ b/tags/tor-0_0_8pre1/Win32Build/or/or.dsp @@ -0,0 +1,266 @@ +# Microsoft Developer Studio Project File - Name="or" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=or - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "or.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "or.mak" CFG="or - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "or - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "or - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "or - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "d:\openssl\include ..\win32" /I "..\win32" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib libeay32.lib ssleay32.lib /nologo /subsystem:console /machine:I386 /libpath:"d:\openssl\lib\vc" + +!ELSEIF "$(CFG)" == "or - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\src\win32" /I "D:\openssl\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib libeay32.lib ssleay32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/tor.exe" /pdbtype:sept /libpath:"d:\openssl\lib\vc" + +!ENDIF + +# Begin Target + +# Name "or - Win32 Release" +# Name "or - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\src\common\aes.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\buffers.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\circuitbuild.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\circuitlist.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\circuituse.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\command.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\config.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection_edge.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection_or.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\cpuworker.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\crypto.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\directory.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\dirserv.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\dns.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\fakepoll.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\log.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\main.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\onion.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\relay.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\rendclient.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\rendcommon.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\rendmid.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\rendservice.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\rephist.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\router.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\routerlist.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\routerparse.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\tor_main.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\tortls.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\util.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\src\common\aes.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\crypto.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\fakepoll.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\log.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\or.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\win32\orconfig.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\test.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\torint.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\tortls.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\tree.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\common\util.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/tags/tor-0_0_8pre1/Win32Build/or/or.dsw b/tags/tor-0_0_8pre1/Win32Build/or/or.dsw new file mode 100644 index 0000000000..3ff4c8e840 --- /dev/null +++ b/tags/tor-0_0_8pre1/Win32Build/or/or.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "or"=".\or.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/tags/tor-0_0_8pre1/Win32Build/tor/tor.dsw b/tags/tor-0_0_8pre1/Win32Build/tor/tor.dsw new file mode 100644 index 0000000000..ae4da0804f --- /dev/null +++ b/tags/tor-0_0_8pre1/Win32Build/tor/tor.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "or"="..\or\or.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/tags/tor-0_0_8pre1/autogen.sh b/tags/tor-0_0_8pre1/autogen.sh new file mode 100755 index 0000000000..efe6d360d9 --- /dev/null +++ b/tags/tor-0_0_8pre1/autogen.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +aclocal && + autoheader && + autoconf && + automake --add-missing --copy +./configure + diff --git a/tags/tor-0_0_8pre1/configure.in b/tags/tor-0_0_8pre1/configure.in new file mode 100644 index 0000000000..d04f5ce291 --- /dev/null +++ b/tags/tor-0_0_8pre1/configure.in @@ -0,0 +1,222 @@ + +AC_INIT +AM_INIT_AUTOMAKE(tor, 0.0.8pre1) +AM_CONFIG_HEADER(orconfig.h) + +AC_CANONICAL_HOST + +CFLAGS="$CFLAGS -Wall -g -O2" + +if test -f /etc/redhat-release; then + CFLAGS="$CFLAGS -I/usr/kerberos/include" +fi + +AC_ARG_ENABLE(debug, +[ --enable-debug compiles with debugging info], +[if test x$enableval = xyes; then + CFLAGS="$CFLAGS -g" +fi]) + +AC_PROG_CC +AC_PROG_MAKE_SET +AC_PROG_RANLIB + +# The big search for OpenSSL +# copied from openssh's configure.ac +AC_ARG_WITH(ssl-dir, + [ --with-ssl-dir=PATH Specify path to OpenSSL installation ], + [ + if test "x$withval" != "xno" ; then + tryssldir=$withval + fi + ] +) + +AC_SEARCH_LIBS(socket, [socket]) +AC_SEARCH_LIBS(gethostbyname, [nsl]) + +saved_LIBS="$LIBS" +saved_LDFLAGS="$LDFLAGS" +saved_CPPFLAGS="$CPPFLAGS" +if test "x$prefix" != "xNONE" ; then + tryssldir="$tryssldir $prefix" +fi +AC_CACHE_CHECK([for OpenSSL directory], ac_cv_openssldir, [ + for ssldir in $tryssldir "" /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/pkg /opt /opt/openssl ; do + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" + LIBS="$saved_LIBS -lssl -lcrypto" + + # Skip directories if they don't exist + if test ! -z "$ssldir" -a ! -d "$ssldir" ; then + continue; + fi + if test ! -z "$ssldir" -a "x$ssldir" != "x/usr"; then + # Try to use $ssldir/lib if it exists, otherwise + # $ssldir + if test -d "$ssldir/lib" ; then + LDFLAGS="-L$ssldir/lib $saved_LDFLAGS" + if test ! -z "$need_dash_r" ; then + LDFLAGS="-R$ssldir/lib $LDFLAGS" + fi + else + LDFLAGS="-L$ssldir $saved_LDFLAGS" + if test ! -z "$need_dash_r" ; then + LDFLAGS="-R$ssldir $LDFLAGS" + fi + fi + # Try to use $ssldir/include if it exists, otherwise + # $ssldir + if test -d "$ssldir/include" ; then + CPPFLAGS="-I$ssldir/include $saved_CPPFLAGS" + else + CPPFLAGS="-I$ssldir $saved_CPPFLAGS" + fi + fi + + # Basic test to check for compatible version and correct linking + # *does not* test for RSA - that comes later. + AC_TRY_RUN( + [ +#include +#include +int main(void) +{ + char a[2048]; + memset(a, 0, sizeof(a)); + RAND_add(a, sizeof(a), sizeof(a)); + return(RAND_status() <= 0); +} + ], + [ + found_crypto=1 + break; + ], [] + ) + + if test ! -z "$found_crypto" ; then + break; + fi + done + + if test -z "$found_crypto" ; then + AC_MSG_ERROR([Could not find working OpenSSL library, please install or check config.log]) + fi + if test -z "$ssldir" ; then + ssldir="(system)" + fi + + ac_cv_openssldir=$ssldir +]) +if (test ! -z "$ac_cv_openssldir" && test "x$ac_cv_openssldir" != "x(system)") ; + then + dnl Need to recover ssldir - test above runs in subshell + ssldir=$ac_cv_openssldir + if test ! -z "$ssldir" -a "x$ssldir" != "x/usr"; then + # Try to use $ssldir/lib if it exists, otherwise + # $ssldir + if test -d "$ssldir/lib" ; then + LDFLAGS="-L$ssldir/lib $saved_LDFLAGS" + if test ! -z "$need_dash_r" ; then + LDFLAGS="-R$ssldir/lib $LDFLAGS" + fi + else + LDFLAGS="-L$ssldir $saved_LDFLAGS" + if test ! -z "$need_dash_r" ; then + LDFLAGS="-R$ssldir $LDFLAGS" + fi + fi + # Try to use $ssldir/include if it exists, otherwise + # $ssldir + if test -d "$ssldir/include" ; then + CPPFLAGS="-I$ssldir/include $saved_CPPFLAGS" + else + CPPFLAGS="-I$ssldir $saved_CPPFLAGS" + fi + fi +fi +LIBS="$saved_LIBS -lssl -lcrypto" + +dnl The warning message here is no longer strictly accurate. + +AC_CHECK_HEADERS(unistd.h string.h signal.h netdb.h ctype.h poll.h sys/stat.h sys/poll.h sys/types.h fcntl.h sys/fcntl.h sys/ioctl.h sys/socket.h sys/time.h netinet/in.h arpa/inet.h errno.h assert.h time.h pwd.h grp.h, , AC_MSG_WARN(some headers were not found, compilation may fail)) + +dnl These headers are not essential + +AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h) + +AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime) +AC_REPLACE_FUNCS(strlcat strlcpy) + +dnl In case we aren't given a working stdint.h, we'll need to grow our own. +dnl Watch out. + +AC_CHECK_SIZEOF(int8_t) +AC_CHECK_SIZEOF(int16_t) +AC_CHECK_SIZEOF(int32_t) +AC_CHECK_SIZEOF(int64_t) +AC_CHECK_SIZEOF(uint8_t) +AC_CHECK_SIZEOF(uint16_t) +AC_CHECK_SIZEOF(uint32_t) +AC_CHECK_SIZEOF(uint64_t) +AC_CHECK_SIZEOF(intptr_t) +AC_CHECK_SIZEOF(uintptr_t) + +dnl AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t, intptr_t, uintptr_t]) + +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(__int64) +AC_CHECK_SIZEOF(void *) + +# Now, let's see about alignment requirements. On some platforms, we override +# the default. +case $host in + ia64-*-* | arm-*-* ) + tor_cv_unaligned_ok=no + ;; + *) +AC_CACHE_CHECK([whether unaligned int access is allowed], tor_cv_unaligned_ok, +[AC_RUN_IFELSE([AC_LANG_SOURCE( +[[int main () { char s[] = "A\x00\x00\x00\x00\x00\x00\x00"; +return *(int*)(&s[1]); }]])], + [tor_cv_unaligned_ok=yes], + [tor_cv_unaligned_ok=no], + [tor_cv_unaligned_ok=cross])]) +esac + +if test $tor_cv_unaligned_ok = yes; then + AC_DEFINE([UNALIGNED_INT_ACCESS_OK], 1, + [Define to 1 iff unaligned int access is allowed]) +fi + +# $prefix stores the value of the --prefix command line option, or +# NONE if the option wasn't set. In the case that it wasn't set, make +# it be the default, so that we can use it to expand directories now. +if test "x$prefix" = "xNONE"; then + prefix=$ac_default_prefix +fi + +# and similarly for $exec_prefix +if test "x$exec_prefix" = "xNONE"; then + exec_prefix=$prefix +fi + +CONFDIR=`eval echo $sysconfdir/tor` +AC_SUBST(CONFDIR) +AC_DEFINE_UNQUOTED(CONFDIR,"$CONFDIR") +AC_DEFINE([CONFDIR], [], [tor's configuration directory]) + +BINDIR=`eval echo $bindir` +AC_SUBST(BINDIR) + +LOCALSTATEDIR=`eval echo $localstatedir` +AC_SUBST(LOCALSTATEDIR) + +echo "confdir: $CONFDIR" + +AC_OUTPUT(Makefile contrib/tor.sh contrib/torify contrib/Makefile src/config/torrc.sample doc/tor.1 src/Makefile doc/Makefile src/config/Makefile src/common/Makefile src/or/Makefile) + diff --git a/tags/tor-0_0_8pre1/contrib/.cvsignore b/tags/tor-0_0_8pre1/contrib/.cvsignore new file mode 100644 index 0000000000..bd9dc78554 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +tor.sh +torify diff --git a/tags/tor-0_0_8pre1/contrib/Makefile.am b/tags/tor-0_0_8pre1/contrib/Makefile.am new file mode 100644 index 0000000000..ca07ed0277 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/Makefile.am @@ -0,0 +1,9 @@ +confdir = $(sysconfdir)/tor + +EXTRA_DIST = tor-tsocks.conf torify.1 tor.spec tor-resolve + +conf_DATA = tor-tsocks.conf + +bin_SCRIPTS = torify tor-resolve + +man_MANS = torify.1 diff --git a/tags/tor-0_0_8pre1/contrib/mdd.py b/tags/tor-0_0_8pre1/contrib/mdd.py new file mode 100755 index 0000000000..d3ce5c37db --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/mdd.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python2.3 + +import re, sys +import textwrap + +files = sys.argv[1:] +funcDeclaredIn = {} +fileDeclares = {} +functionCalls = {} +funcCalledByFile = {} +funcCalledByFunc = {} + +cpp_re = re.compile(r'//.*$') +c_re = re.compile(r'/[*]+(?:[^*]+|[*]+[^/*])*[*]+/', re.M|re.S) + +for fname in files: + f = open(fname, 'r') + curFunc = "???" + functionCalls.setdefault(curFunc,{}) + lineno = 0 + body = f.read() + body = cpp_re.sub(" ",body) + body = c_re.sub(" ",body) + #if fname == 'dns.c': print body + for line in body.split("\n"): + lineno += 1 + m = re.match(r'^[^\s/].*\s(\w+)\([^;]*$', line) + if m: + #print line, "->", m.group(1) + curFunc = m.group(1) + if curFunc[0] == '_': curFunc = curFunc[1:] + functionCalls.setdefault(curFunc,{}) + funcDeclaredIn[m.group(1)] = fname + fileDeclares.setdefault(fname, {})[m.group(1)] = 1 + continue + m = re.match(r'^(\w+)\([^;]', line) + if m: + #print line, "->", m.group(1) + curFunc = m.group(1) + if curFunc[0] == '_': curFunc = curFunc[1:] + functionCalls.setdefault(curFunc,{}) + funcDeclaredIn[m.group(1)] = fname + fileDeclares.setdefault(fname, {})[m.group(1)] = 1 + continue + while line: + m = re.search(r'(\w+)\(', line) + if not m: break + #print fname, line, curFunc, "->", m.group(1) + fn = m.group(1) + if fn[0] == '_': + fn = fn[1:] + functionCalls[curFunc][m.group(1)] = 1 + #if curFunc == "???": + # print ">>!!!!! at %s:%s"%(fname,lineno) + funcCalledByFunc.setdefault(m.group(1), {})[curFunc]=1 + funcCalledByFile.setdefault(m.group(1), {})[fname]=1 + line = line[m.end():] + + f.close() + +fileUsers = {} +fileUses = {} + +for fname in files: + print "%s:"%fname + users = {} + for func in fileDeclares[fname]: + cb = funcCalledByFile.get(func,{}).keys() + for f in cb: users[f] = 1 + #print "users[%s] = %s"%(f,users[f]) + users = users.keys() + users.sort() + fileUsers[fname] = users + for user in users: + fileUses.setdefault(user,[]).append(fname) + if user == fname: continue + print " from %s:"%user + for func in fileDeclares[fname]: + if funcCalledByFile.get(func,{}).get(user,0): + print " %s()"%func + +def wrap(s, pre): + return textwrap.fill(s, + width=77, initial_indent=pre, + subsequent_indent=" "*len(pre)) + +for fname in files: + print + print "===== %s"%fname + print wrap(" ".join(fileUses[fname]), + " Calls: ") + print wrap(" ".join(fileUsers[fname]), + " Called by: ") + +print "==============================" + + +funcnames = functionCalls.keys() +funcnames.sort() + +if 1: + for func in funcnames: + print "===== %s"%func + callers = [c for c in funcCalledByFunc.get(func,{}).keys() + if c != "???"] + callers.sort() + called = [c for c in functionCalls[func].keys() if c != "???" and + c in funcnames] + called.sort() + print wrap(" ".join(callers), + " Called by:") + print wrap(" ".join(called), + " Calls:") + +# simple topological sort. +functionDepth = {} +while 1: + BIG = 1000000 + any = 0 + for func in funcnames: + if functionDepth.has_key(func): + continue + called = [c for c in functionCalls[func] if c != func and + functionCalls.has_key(c)] + if len(called) == 0: + functionDepth[func] = 0 + #print "Depth(%s)=%s"%(func,0) + any = 1 + continue + calledDepths = [ functionDepth.get(c,BIG) for c in called ] + if max(calledDepths) < BIG: + d = functionDepth[func] = max(calledDepths)+1 + #print "Depth(%s)=%s"%(func,d) + any = 1 + continue + if not any: + break + +# compute lexical closure. +cycCalls = {} +for func in funcnames: + if not functionDepth.has_key(func): + calls = [ c for c in functionCalls[func] if c != func and + functionCalls.has_key(c) and not functionDepth.has_key(c)] + cycCalls[func] = d = {} + for c in calls: + d[c]=1 + + +cycNames = cycCalls.keys() +while 1: + any = 0 + for func in cycNames: + L = len(cycCalls[func]) + for called in cycCalls[func].keys(): + cycCalls[func].update(cycCalls[called]) + if L != len(cycCalls[func]): + any = 1 + if not any: + break + +depthList = [ (v,k) for k,v in functionDepth.items() ] +depthList.sort() +cycList = [ (len(v),k) for k,v in cycCalls.items() ] +cycList.sort() +for depth,name in depthList: + print "Depth[%s]=%s"%(name,depth) +for bredth,name in cycList: + print "Width[%s]=%s"%(name,bredth) + +print "Sorted %s / %s"%(len(functionDepth),len(funcnames)) diff --git a/tags/tor-0_0_8pre1/contrib/tor-resolve b/tags/tor-0_0_8pre1/contrib/tor-resolve new file mode 100755 index 0000000000..31b44af9d4 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/tor-resolve @@ -0,0 +1,126 @@ +#!/usr/bin/python +#$Id$ + +import socket +import struct +import sys + +def socks4AResolveRequest(hostname): + version = 4 + command = 0xF0 + port = 0 + addr = 0x0000001 + username = "" + reqheader = struct.pack("!BBHL", version, command, port, addr) + return "%s%s\x00%s\x00"%(reqheader,username,hostname) + +def socks4AParseResponse(response): + RESPONSE_LEN = 8 + if len(response) < RESPONSE_LEN: + print "return none", len(response) + return None + assert len(response) >= RESPONSE_LEN + version,status,port = struct.unpack("!BBH",response[:4]) + assert version == 0 + assert port == 0 + if status == 90: + return "%d.%d.%d.%d"%tuple(map(ord, response[4:])) + else: + return "ERROR (status %d)"%status + +def socks5Hello(): + return "\x05\x01\x00" +def socks5ParseHello(response): + if response != "\x05\x00": + raise ValueError("Bizarre socks5 response") +def socks5ResolveRequest(hostname): + version = 5 + command = 0xF0 + rsv = 0 + port = 0 + atype = 0x03 + reqheader = struct.pack("!BBBB",version, command, rsv, atype) + portstr = struct.pach("!H",port) + return "%s%s\0%s"%(reqheader,hostname,port) +def socks5ParseResponse(r): + if len(r)<8: return None + version, reply, rsv, atype = struct.unpack("!BBBB",r[:4]) + assert version==5 + assert rsv==0 + if reply != 0x00: + return "ERROR" + assert atype in (0x01,0x04) + expected_len = 4 + ({1:4,4:16}[atype]) + 2 + if len(r) < expected_len: + return None + elif len(r) > expected_len: + raise ValueError("Overlong socks5 reply!") + addr = r[4:-2] + if atype == 0x01: + return "%d.%d.%d.%d"%tuple(map(ord,addr)) + else: + # not really the right way to format IPv6 + return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr])) + +def parseHostAndPort(h): + host, port = "localhost", 9050 + if ":" in h: + i = h.index(":") + host = h[:i] + try: + port = int(h[i+1:]) + except ValueError: + print "Bad hostname %r"%h + sys.exit(1) + elif h: + try: + port = int(h) + except ValueError: + host = h + + return host, port + +def resolve(hostname, sockshost, socksport, socksver=4): + assert socksver in (4,5) + if socksver == 4: + fmt = socks4AResolveRequest + parse = socks4AParseResponse + else: + fmt = socks5ResolveRequest + parse = socks5ParseResponse + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((sockshost,socksport)) + if socksver == 5: + s.send(socks5Hello()) + socksParseHello(s.recv(2)) + s.send(fmt(hostname)) + answer = s.recv(8) + result = parse(answer) + while result is None: + more = s.recv(1) + if not more: + print "Connection closed; dying." + return None + answer += more + result = parse(answer) + print "Got answer",result + m = s.recv(1) + if m: + print "Got extra data too! Ick." + return result + +if __name__ == '__main__': + if len(sys.argv) not in (2,3,4): + print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]" + sys.exit(0) + if sys.argv[1] in ("-4", "-5"): + socksver = int(sys.argv[1][1]) + del sys.argv[1] + if len(sys.argv) == 4: + print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]" + sys.exit(0) + if len(sys.argv) == 3: + sh,sp = parseHostAndPort(sys.argv[2]) + else: + sh,sp = parseHostAndPort("") + resolve(sys.argv[1], sh, sp) diff --git a/tags/tor-0_0_8pre1/contrib/tor-tsocks.conf b/tags/tor-0_0_8pre1/contrib/tor-tsocks.conf new file mode 100644 index 0000000000..b3293969f7 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/tor-tsocks.conf @@ -0,0 +1,7 @@ +# This is the configuration for libtsocks (transparent socks) for use +# with tor, which is providing a socks server on port 9050 by default. +# +# See tsocks.conf(5) and torify(1) manpages. + +server = 127.0.0.1 +server_port = 9050 diff --git a/tags/tor-0_0_8pre1/contrib/tor.sh.in b/tags/tor-0_0_8pre1/contrib/tor.sh.in new file mode 100644 index 0000000000..dfde1f6a45 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/tor.sh.in @@ -0,0 +1,112 @@ +#!/bin/sh +# +#tor The Onion Router +# +# chkconfig: 2345 90 10 +# description: Onion Router + +TORUSER= +TORGROUP= +TORBIN=@BINDIR@/tor +TORPID=@LOCALSTATEDIR@/run/tor/tor.pid +TORLOG=@LOCALSTATEDIR@/log/tor/tor.log +TORCONF=@CONFDIR@/torrc +# Strictly speaking, we don't need to su if we have --user and --group. +# "Belt and suspenders," says jbash. +TORARGS="--pidfile $TORPID --logfile $TORLOG --runasdaemon 1" +if [ "x$TORUSER" != "x" ]; then + TORARGS="$TORARGS --user $TORUSER" +fi +if [ "x$TORGROUP" != "x" ]; then + TORARGS="$TORARGS --group $TORGROUP" +fi +RETVAL=0 + +case "$1" in + + start) + if [ -f $TORPID ]; then + echo "tor appears to be already running (pid file exists)" + echo "Maybe you should run: $0 restart ?" + RETVAL=1 + else + echo -n "Starting tor..." + if [ "x$TORUSER" = "x" ]; then + $TORBIN -f $TORCONF $TORARGS + else + /bin/su -c "$TORBIN -f $TORCONF $TORARGS" $TORUSER + fi + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + echo " ok" + else + echo " ERROR!" + fi + fi + ;; + + stop) + if [ -f $TORPID ]; then + echo -n "Killing tor..." + kill `cat $TORPID` + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + echo " ok" + else + echo " ERROR!" + fi + else + echo "Unable to kill tor: $TORPID does not exist" + RETVAL=1 + fi + ;; + + reload) + if [ -f $TORPID ]; then + echo -n "Sending HUP to tor..." + kill -HUP `cat $TORPID` + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + echo " ok" + else + echo " ERROR!" + fi + else + echo "Unable to kill tor: $TORPID does not exist" + RETVAL=1 + fi + ;; + + restart) + $0 stop + if [ -f $TORPID ]; then + rm -f $TORPID + fi + $0 start + ;; + + + status) + PID=`cat $TORPID 2>/dev/null` + if [ "$PID" != "" ]; then + torstat=`ps -p $PID | grep -c "^$PID"` + if [ $torstat ]; then + echo "tor is running ($PID)" + else + echo "tor is not running (looks like it crashed, look for core? $PID)" + fi + else + echo "tor is not running (exited gracefully)" + fi + ;; + + log) + cat $TORLOG + ;; + + *) + echo "Usage: $0 (start|stop|restart|status|log)" + exit 1 +esac + +exit $RETVAL diff --git a/tags/tor-0_0_8pre1/contrib/tor.spec b/tags/tor-0_0_8pre1/contrib/tor.spec new file mode 100644 index 0000000000..9a48cecf72 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/tor.spec @@ -0,0 +1,150 @@ +# TODO: +# Add /etc/logrotate.d/tor +# + +%define blddate %(date -u +"%Y%m%d%H%M") + +%define version 0.0.7 +%define version_extra rc2 +%define vepoch 0.1 +%define tor_version %{version}%{version_extra} +# not quite right XXXXX +%define release 0.std.%{vepoch}.%{version_extra} + +Name: tor +Version: %{version} +Release: %{release} +Summary: Anonymizing overlay network for TCP +Vendor: R. Dingledine +Packager: Nick Mathewson +License: BSD-like +Group: Applications/Internet +URL: http://freehaven.net/tor/ + +Source0: http://freehaven.net/tor/dist/tor-%{tor_version}.tar.gz + +Requires: openssl >= 0.9.6 +BuildRequires: openssl-devel >= 0.9.6 +Requires(pre): shadow-utils, /usr/bin/id, /bin/date, /bin/sh +Requires(pre): %{_sbindir}/useradd, %{_sbindir}/groupadd + +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +%description +Tor is a connection-based low-latency anonymous communication system which +addresses many flaws in the original onion routing design. + +In brief, Onion Routing is a connection-oriented anonymizing communication +service. Users choose a source-routed path through a set of nodes, and +negotiate a "virtual circuit" through the network, in which each node +knows its predecessor and successor, but no others. Traffic flowing down +the circuit is unwrapped by a symmetric key at each node, which reveals +the downstream node. + +Basically Tor provides a distributed network of servers ("onion +routers"). Users bounce their tcp streams (web traffic, ftp, ssh, etc) +around the routers, and recipients, observers, and even the routers +themselves have difficulty tracking the source of the stream. + +Note that Tor does no protocol cleaning. That means there is a danger that +application protocols and associated programs can be induced to reveal +information about the initiator. Tor depends on Privoxy and similar protocol +cleaners to solve this problem. + +Client applications can use the Tor network by connecting to the local +onion proxy. If the application itself does not come with socks support +you can use a socks client such as tsocks. Some web browsers like mozilla +and web proxies like privoxy come with socks support, so you don't need an +extra socks client if you want to use Tor with them. + +Remember that this is alpha code, and the network is very small -- Tor will +not provide anonymity currently. + +This package provides the "tor" program, which serves as both a client +and a relay node. Scripts will automatically create a "tor" user and +group, set tor up to run as a daemon, and automatically start it at +installation time. + +%prep +%setup -q -n tor-%{tor_version} + +# Patch the startup script to use the right user and group IDs. Force +# the use of /bin/sh as the shell for the "tor" account. +ed -s contrib/tor.sh.in << '/EOF/' > /dev/null +,s/^TORUSER=$/TORUSER=tor/ +,s/^TORGROUP=$/TORGROUP=tor/ +,s:/bin/su:/bin/su -s /bin/sh: +# +# Save and exit ed +w +q +/EOF/ + +%build +%configure +%__make + +%install +%makeinstall + +# Install init script. +%__mkdir_p ${RPM_BUILD_ROOT}%{_initrddir} +%__install -p -m 755 contrib/tor.sh ${RPM_BUILD_ROOT}%{_initrddir}/tor + +# Directories that don't have any preinstalled files +%__mkdir_p -m 700 ${RPM_BUILD_ROOT}%{_localstatedir}/lib/tor +%__mkdir_p -m 755 ${RPM_BUILD_ROOT}%{_localstatedir}/run/tor +%__mkdir_p -m 755 ${RPM_BUILD_ROOT}%{_localstatedir}/log/tor + +%clean +[ "${RPM_BUILD_ROOT}" != "/" ] && rm -rf ${RPM_BUILD_ROOT} + +%pre +[ -f %{_initrddir}/tor ] && /sbin/service tor stop +if [ ! -n "`/usr/bin/id -g tor 2>/dev/null`" ]; then + # One would like to default the GID, but doing that properly would + # require thought. + %{_sbindir}/groupadd tor 2> /dev/null +fi +if [ ! -n "`/usr/bin/id -u tor 2>/dev/null`" ]; then + # One would also like to default the UID, but doing that properly would + # also require thought. + if [ -x /sbin/nologin ]; then + %{_sbindir}/useradd -r -g tor -d / -s /sbin/nologin tor 2> /dev/null + else + %{_sbindir}/useradd -r -g tor -d / -s /bin/false tor 2> /dev/null + fi +fi + +%post +/sbin/chkconfig --add tor +/sbin/chkconfig tor && /sbin/service tor start + +%preun +/sbin/chkconfig tor && /sbin/service tor stop +/sbin/chkconfig --del tor + +%files +%defattr(-,root,root) +%doc AUTHORS INSTALL LICENSE README ChangeLog doc/HACKING doc/TODO doc/FAQ +#%{_mandir}/man1/tor.1.gz +#%{_mandir}/man1/torify.1.gz +%{_mandir}/man*/* +%{_bindir}/tor +%{_bindir}/torify +%config %{_initrddir}/tor +%dir %{_sysconfdir}/tor/ +%config(noreplace) %{_sysconfdir}/tor/torrc.sample +%config(noreplace) %{_sysconfdir}/tor/dirservers +%config(noreplace) %{_sysconfdir}/tor/tor-tsocks.conf +%attr(0700,tor,tor) %dir %{_localstatedir}/lib/tor +%attr(0755,tor,tor) %dir %{_localstatedir}/run/tor +%attr(0755,tor,tor) %dir %{_localstatedir}/log/tor + +%changelog +* Mon Jun 06 2004 Nick Mathewson 0.0.7-0.std.0.1.rc2 +- Make spec file more happy with fc2 packaging + +* Sat Jan 17 2004 John Bashinski +- Basic spec file; tested with Red Hat 9. + diff --git a/tags/tor-0_0_8pre1/contrib/torify.1 b/tags/tor-0_0_8pre1/contrib/torify.1 new file mode 100644 index 0000000000..0bb6c1b4e8 --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/torify.1 @@ -0,0 +1,23 @@ +.TH torify 1 "" Feb-2004 "" +.\" manual page by Peter Palfrader +.SH NAME +.LP +torify \- wrapper for tsocks and tor + +.SH SYNOPSIS +\fBtorify\fP\ \fIapplication\fP\ [\fIapplication's\ arguments\fP] + +.SH DESCRIPTION +\fBtorify\fR is a simple wrapper that calls tsocks with a tor specific +configuration file. + +Tsocks itself is wrapper between the tsocks library and the application what +you would like to run socksified. + +Please note that since tsocks uses LD_PRELOAD this cannot be applied to suid +binaries. + +.SH SEE ALSO +.BR tor (1), +.BR tsocks (1), +.BR tsocks.conf (5). diff --git a/tags/tor-0_0_8pre1/contrib/torify.in b/tags/tor-0_0_8pre1/contrib/torify.in new file mode 100755 index 0000000000..f9162a19fe --- /dev/null +++ b/tags/tor-0_0_8pre1/contrib/torify.in @@ -0,0 +1,7 @@ +#! /bin/sh +# Wrapper script for use of the tsocks(8) transparent socksification library +# See the tsocks(1) and torify(1) manpages. + +TSOCKS_CONF_FILE=@CONFDIR@/tor-tsocks.conf +export TSOCKS_CONF_FILE +exec tsocks "$@" diff --git a/tags/tor-0_0_8pre1/debian/README.privoxy b/tags/tor-0_0_8pre1/debian/README.privoxy new file mode 100644 index 0000000000..0f4b4f9d95 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/README.privoxy @@ -0,0 +1,18 @@ +Tor only provides TCP layer anonymity. It does not do any protocol +cleaning, so if you are going to browse the web you still give away a +lot of information to servers. + +The privoxy package provides a privacy enhancing HTTP proxy, which +is good at filtering headers, cookies, and much more. To view the +description of the Debian privoxy package just run "apt-cache show +privoxy". Please refer to the privoxy documentation for more details. + +In order to use privoxy over tor, add the following line to your +privoxy configuration file: + forward-socks4a / localhost:9050 . +(the dot is important) + +Then configure your browser to use privoxy as its HTTP proxy. + +-- +Peter Palfrader , Tue, 17 Feb 2004 02:15:36 +0100 diff --git a/tags/tor-0_0_8pre1/debian/TODO b/tags/tor-0_0_8pre1/debian/TODO new file mode 100644 index 0000000000..7a4f99ed51 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/TODO @@ -0,0 +1,9 @@ +Legend: + - Not done + * Top priority + . Partially done + o Done + D Deferred + X Abandoned + +- strip debugging symbols when tor is more mature diff --git a/tags/tor-0_0_8pre1/debian/changelog b/tags/tor-0_0_8pre1/debian/changelog new file mode 100644 index 0000000000..a8366658a8 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/changelog @@ -0,0 +1,196 @@ +tor (0.0.7.2-1) unstable; urgency=medium + + * New upstream release. + Fixes another instance of that remote crash bug. + * Mention another reason why stop/reload may fail in the init script. + + -- Peter Palfrader Thu, 8 Jul 2004 03:21:32 +0200 + +tor (0.0.7.1-1) unstable; urgency=medium + + * New upstream release. + Fixes a bug that allows a remote crash on exit nodes. + * Logrotate var/log/tor/*log instead of just var/log/tor/log, in + case the admin wants several logs. + + -- Peter Palfrader Mon, 5 Jul 2004 19:18:12 +0200 + +tor (0.0.7-1) unstable; urgency=low + + * New upstream version + closes: #249893: FTBFS on ia64 + + -- Peter Palfrader Mon, 7 Jun 2004 21:46:08 +0200 + +tor (0.0.6.2-1) unstable; urgency=medium + + * New upstream release (breaks backwards compatibility yet again). + * Recommend socat. + * Since tor is in /usr/sbin, the manpage should be in section 8, not + in section 1. Move it there, including updating the section in + the manpage itself and the reference in torify(1). + * Update debian/copyright file. + + -- Peter Palfrader Sun, 16 May 2004 10:47:20 +0200 + +tor (0.0.6.1-1) unstable; urgency=medium + + * New upstream release (breaks backwards compatibility). + + -- Peter Palfrader Fri, 7 May 2004 00:24:49 +0200 + +tor (0.0.6-1) unstable; urgency=low + + * New upstream release (breaks backwards compatibility). + + -- Peter Palfrader Sun, 2 May 2004 23:58:36 +0200 + +tor (0.0.5+0.0.6rc4-1) unstable; urgency=low + + * New upstream release candidate. + + -- Peter Palfrader Sun, 2 May 2004 14:36:59 +0200 + +tor (0.0.5+0.0.6rc3-1) unstable; urgency=low + + * New upstream release candidate. + + -- Peter Palfrader Thu, 29 Apr 2004 11:52:07 +0200 + +tor (0.0.5+0.0.6rc2-1) unstable; urgency=low + + * New upstream release candidate. + * Mention upstream website and mailinglist archives in long + description. + + -- Peter Palfrader Mon, 26 Apr 2004 12:23:20 +0200 + +tor (0.0.5-1) unstable; urgency=low + + * New upstream release. + * Upstream installs a torrc.sample file now, rather than torrc. + Keep using torrc as dpkg handles conffile upgrades. + + -- Peter Palfrader Tue, 30 Mar 2004 20:54:00 +0200 + +tor (0.0.4-1) unstable; urgency=low + + * New upstream release (how the version numbers fly by :). + + -- Peter Palfrader Fri, 26 Mar 2004 23:46:09 +0100 + +tor (0.0.3-1) unstable; urgency=low + + * New upstream release. + * Also mention that tree.h is by Niels Provos in debian/copyright. + + -- Peter Palfrader Fri, 26 Mar 2004 20:36:08 +0100 + +tor (0.0.2-1) unstable; urgency=low + + * New upstream release. + * Uses strlcpy and strlcat by Todd C. Miller, mention him in + debian/copyright. + + -- Peter Palfrader Fri, 19 Mar 2004 12:37:17 +0100 + +tor (0.0.1+0.0.2pre27-1) unstable; urgency=low + + * New upstream release. + + -- Peter Palfrader Mon, 15 Mar 2004 05:19:16 +0100 + +tor (0.0.1+0.0.2pre26-1) unstable; urgency=low + + * New upstream release. + * Mention log and pidfile location in tor.1. + + -- Peter Palfrader Mon, 15 Mar 2004 02:21:29 +0100 + +tor (0.0.1+0.0.2pre25-1) unstable; urgency=low + + * New upstream release. + + -- Peter Palfrader Thu, 4 Mar 2004 23:05:38 +0100 + +tor (0.0.1+0.0.2pre24-1) unstable; urgency=low + + * New upstream release. + * Do not strip binaries for now. + * Add "# ulimit -c unlimited" to tor.default + * Always enable DataDirectory. + * Actually use dpatch now (to modify upstream torrc.in) + * Wait for tor to die in init stop. Let the user know if it doesn't. + + -- Peter Palfrader Wed, 3 Mar 2004 14:10:25 +0100 + +tor (0.0.1+0.0.2pre23-1) unstable; urgency=low + + * New upstream release. + * The one test that always failed has been fixed: removed comment from + rules file. + + -- Peter Palfrader Sun, 29 Feb 2004 12:36:33 +0100 + +tor (0.0.1+0.0.2pre22-1) unstable; urgency=low + + * New upstream release. + * Upstream has moved tor back to usr/bin, but we will keep it in + usr/sbin. That's the right place and it doesn't break my tab + completion there. + + -- Peter Palfrader Fri, 27 Feb 2004 01:59:09 +0100 + +tor (0.0.1+0.0.2pre21-1) unstable; urgency=low + + * New upstream release. + * 0.0.2pre20-2 removed the Recommends: on privoxy rather + than tsocks (which is now required) by mistake. Fix that. + * package description: Mention that the package starts the OP by default and + that OR can be enabled in the config. + * tor moved to sbin, updating init script. + + -- Peter Palfrader Wed, 18 Feb 2004 10:08:12 +0100 + +tor (0.0.1+0.0.2pre20-2) unstable; urgency=low + + * Add torify script, documentation, and config file. Means we also + depend on tsocks now rather than just recommending it. Right now + we install it in debian/rules, but upcoming versions might install + it in upstream's make install target. + * There's an upstream ChangeLog file now. Enjoy! + * Add a README.privoxy file that explains how to setup privoxy to + go over tor. + * As is the case too often, the INSTALL file not only covers + installation, but also basic usage and configuration. Therefore + include it in the docs dir. + * Add a lintian override for the INSTALL file. + + -- Peter Palfrader Tue, 17 Feb 2004 02:32:00 +0100 + +tor (0.0.1+0.0.2pre20-1) unstable; urgency=low + + * New upstream version. + - various design paper updates + - resolve cygwin warnings + - split the token bucket into "rate" and "burst" params + - try to resolve discrepency between bytes transmitted over TLS and actual + bandwidth use + - setuid to user _before_ complaining about running as root + - fix several memleaks and double frees + - minor logging fixes + - add more debugging for logs. + - various documentation fixes and improvements + - for perforcmance testing, paths are always 3 hops, not "3 or more" + (this will go away at a later date again) + * Add dependency on adduser which was previously missing. + * Change short description to a nicer one. + + -- Peter Palfrader Sat, 31 Jan 2004 10:10:45 +0100 + +tor (0.0.1+0.0.2pre19-1) unstable; urgency=low + + * Initial Release (closes: #216611). + + -- Peter Palfrader Sat, 10 Jan 2004 11:20:06 +0100 + diff --git a/tags/tor-0_0_8pre1/debian/compat b/tags/tor-0_0_8pre1/debian/compat new file mode 100644 index 0000000000..b8626c4cff --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/compat @@ -0,0 +1 @@ +4 diff --git a/tags/tor-0_0_8pre1/debian/control b/tags/tor-0_0_8pre1/debian/control new file mode 100644 index 0000000000..10a50e284a --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/control @@ -0,0 +1,48 @@ +Source: tor +Section: comm +Priority: optional +Maintainer: Peter Palfrader +Build-Depends: debhelper (>= 4.1.65), libssl-dev, dpatch +Standards-Version: 3.6.1 + +Package: tor +Architecture: any +Depends: ${shlibs:Depends}, adduser, tsocks +Recommends: privoxy, socat +Suggests: mixmaster, mixminion, anon-proxy +Description: anonymizing overlay network for TCP + Tor is a connection-based low-latency anonymous communication system which + addresses many flaws in the original onion routing design. + . + In brief, Onion Routing is a connection-oriented anonymizing communication + service. Users choose a source-routed path through a set of nodes, and + negotiate a "virtual circuit" through the network, in which each node + knows its predecessor and successor, but no others. Traffic flowing down + the circuit is unwrapped by a symmetric key at each node, which reveals + the downstream node. + . + Basically Tor provides a distributed network of servers ("onion + routers"). Users bounce their tcp streams (web traffic, ftp, ssh, etc) + around the routers, and recipients, observers, and even the routers + themselves have difficulty tracking the source of the stream. + . + Note that Tor does no protocol cleaning. That means there is a danger that + application protocols and associated programs can be induced to reveal + information about the initiator. Tor depends on Privoxy and similar protocol + cleaners to solve this problem. + . + Client applications can use the Tor network by connecting to the local + onion proxy. If the application itself does not come with socks support + you can use a socks client such as tsocks. Some web browsers like mozilla + and web proxies like privoxy come with socks support, so you don't need an + extra socks client if you want to use Tor with them. + . + This package enables only the onion proxy by default, but it can be configured + as a relay (server) node. + . + Remember that this is alpha code, and the network is very small -- Tor will + not provide anonymity currently. + . + The latest information can be found at http://freehaven.net/tor/, or on the + mailing lists, archived at http://archives.seul.org/or/dev/ or + http://archives.seul.org/or/announce/. diff --git a/tags/tor-0_0_8pre1/debian/copyright b/tags/tor-0_0_8pre1/debian/copyright new file mode 100644 index 0000000000..de828d1739 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/copyright @@ -0,0 +1,102 @@ +This package was debianized by Peter Palfrader on +Sat, 10 Jan 2004 11:20:06 +0100. + +It was downloaded from http://freehaven.net/tor/ + +Upstream Authors: Roger Dingledine + Nick Mathewson + Matej Pfajfar + + +Copyright (c) 2001-2004 Roger Dingledine +Copyright (c) 2002,2003 Nick Mathewson +Copyright (c) 2001-2003 Matej Pfajfar +strlcat, strlcpy: Copyright (c) 1998 Todd C. Miller +tree.h: Copyright (c) 2002 Niels Provos +Modifications for Debian: Copyright (c) 2004 Peter Palfrader + +The tor code is under this license: +=============================================================================== +Copyright (c) 2001-2004, Roger Dingledine + +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 names of the copyright owners 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. + +=============================================================================== +tree.h by Niels Provos is licensed as follows: + + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + +=============================================================================== +strlcat and strlcpy by Todd C. Miller are licensed under the following license: + + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. diff --git a/tags/tor-0_0_8pre1/debian/patches/00list b/tags/tor-0_0_8pre1/debian/patches/00list new file mode 100644 index 0000000000..13c7d1b391 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/patches/00list @@ -0,0 +1,3 @@ +01_uncomment_datadirectory.dpatch +02_add_debian_files_in_manpage.dpatch +03_tor_manpage_in_section_8.dpatch diff --git a/tags/tor-0_0_8pre1/debian/patches/01_uncomment_datadirectory.dpatch b/tags/tor-0_0_8pre1/debian/patches/01_uncomment_datadirectory.dpatch new file mode 100755 index 0000000000..112399b64b --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/patches/01_uncomment_datadirectory.dpatch @@ -0,0 +1,37 @@ +#! /bin/sh -e +## 01_uncomment_datadirectory.dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +if [ $# -lt 1 ]; then + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1 +fi + +[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts +patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}" + +case "$1" in + -patch) patch -p1 ${patch_opts} < $0;; + -unpatch) patch -R -p1 ${patch_opts} < $0;; + *) + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1;; +esac + +exit 0 + +@DPATCH@ +diff -urNad /home/weasel/projects/tor/cvs/src/config/torrc.sample.in cvs/src/config/torrc.sample.in +--- /home/weasel/projects/tor/cvs/src/config/torrc.sample.in 2004-03-01 23:41:26.000000000 +0100 ++++ cvs/src/config/torrc.sample.in 2004-03-03 13:47:25.000000000 +0100 +@@ -11,7 +11,7 @@ + ##################### Below is just for servers ##################### + + ## The directory for keeping all the keys/etc for this server +-#DataDirectory @LOCALSTATEDIR@/lib/tor ++DataDirectory @LOCALSTATEDIR@/lib/tor + + #Nickname moria # A unique handle for this server + #Address moria.seul.org # The fqdn for this server diff --git a/tags/tor-0_0_8pre1/debian/patches/02_add_debian_files_in_manpage.dpatch b/tags/tor-0_0_8pre1/debian/patches/02_add_debian_files_in_manpage.dpatch new file mode 100755 index 0000000000..ca757c6602 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/patches/02_add_debian_files_in_manpage.dpatch @@ -0,0 +1,41 @@ +#! /bin/sh -e +## 02_add_debian_files_in_manpage.dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +if [ $# -lt 1 ]; then + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1 +fi + +[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts +patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}" + +case "$1" in + -patch) patch -p1 ${patch_opts} < $0;; + -unpatch) patch -R -p1 ${patch_opts} < $0;; + *) + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1;; +esac + +exit 0 + +@DPATCH@ +diff -urNad /home/weasel/projects/tor/cvs/doc/tor.1.in cvs/doc/tor.1.in +--- /home/weasel/projects/tor/cvs/doc/tor.1.in 2004-03-01 23:41:25.000000000 +0100 ++++ cvs/doc/tor.1.in 2004-03-10 16:30:49.000000000 +0100 +@@ -137,6 +137,12 @@ + .TP + .I @LOCALSTATEDIR@/lib/tor/ + The tor server stores keys/etc here. ++.TP ++.I /var/log/tor/log ++The tor server logs to this file. ++.TP ++.I /var/run/tor/tor.pid ++The PID of the tor (master) process is stored in this file. + + .SH SEE ALSO + .BR privoxy (1), diff --git a/tags/tor-0_0_8pre1/debian/patches/03_tor_manpage_in_section_8.dpatch b/tags/tor-0_0_8pre1/debian/patches/03_tor_manpage_in_section_8.dpatch new file mode 100755 index 0000000000..e2a365b749 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/patches/03_tor_manpage_in_section_8.dpatch @@ -0,0 +1,45 @@ +#! /bin/sh -e +## 02_tor_manpage_in_section_8.dpatch by +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +if [ $# -lt 1 ]; then + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1 +fi + +[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts +patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}" + +case "$1" in + -patch) patch -p1 ${patch_opts} < $0;; + -unpatch) patch -R -p1 ${patch_opts} < $0;; + *) + echo "`basename $0`: script expects -patch|-unpatch as argument" >&2 + exit 1;; +esac + +exit 0 + +@DPATCH@ +diff -urNad /home/weasel/projects/tor/cvs/contrib/torify.1 cvs/contrib/torify.1 +--- /home/weasel/projects/tor/cvs/contrib/torify.1 2004-02-17 01:45:25.000000000 +0100 ++++ cvs/contrib/torify.1 2004-05-07 01:12:57.000000000 +0200 +@@ -18,6 +18,6 @@ + binaries. + + .SH SEE ALSO +-.BR tor (1), ++.BR tor (8), + .BR tsocks (1), + .BR tsocks.conf (5). +diff -urNad /home/weasel/projects/tor/cvs/doc/tor.1.in cvs/doc/tor.1.in +--- /home/weasel/projects/tor/cvs/doc/tor.1.in 2004-04-18 20:40:42.000000000 +0200 ++++ cvs/doc/tor.1.in 2004-05-07 01:12:12.000000000 +0200 +@@ -1,4 +1,4 @@ +-.TH TOR 1 "November 2003" "TOR" ++.TH TOR 8 "November 2003" "TOR" + .SH NAME + tor \- The second-generation onion router + .SH SYNOPSIS diff --git a/tags/tor-0_0_8pre1/debian/rules b/tags/tor-0_0_8pre1/debian/rules new file mode 100755 index 0000000000..646b68f36e --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/rules @@ -0,0 +1,113 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +CFLAGS = -Wall -g + +export PACKAGE=tor +include /usr/share/dpatch/dpatch.make + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +#ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) +# INSTALL_PROGRAM += -s +#endif + +configure: patch-stamp +config.status: configure + dh_testdir + CFLAGS="$(CFLAGS)" ./configure \ + --host=$(DEB_HOST_GNU_TYPE) \ + --build=$(DEB_BUILD_GNU_TYPE) \ + --prefix=/usr \ + --mandir=\$${prefix}/share/man \ + --infodir=\$${prefix}/share/info \ + --localstatedir=/var \ + --sysconfdir=/etc + + +build: build-stamp + +build-stamp: config.status + dh_testdir + + $(MAKE) + @echo + @echo + src/or/test + @echo + + touch build-stamp + +clean: unpatch + dh_testdir + dh_testroot + rm -f build-stamp + + -$(MAKE) distclean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + $(MAKE) install DESTDIR=$(CURDIR)/debian/tor + # move tor to where it belongs + mv $(CURDIR)/debian/tor/etc/tor/torrc.sample $(CURDIR)/debian/tor/etc/tor/torrc + mv $(CURDIR)/debian/tor/usr/bin/tor $(CURDIR)/debian/tor/usr/sbin/tor + install -d $(CURDIR)/debian/tor/usr/share/man/man8 + mv $(CURDIR)/debian/tor/usr/share/man/man1/tor.1 $(CURDIR)/debian/tor/usr/share/man/man8/tor.8 + + install -m 755 contrib/torify $(CURDIR)/debian/tor/usr/bin + install -m 644 contrib/torify.1 $(CURDIR)/debian/tor/usr/share/man/man1 + install -m 644 contrib/tor-tsocks.conf $(CURDIR)/debian/tor/etc/tor + + install -m 644 debian/tor.lintian-override $(CURDIR)/debian/tor/usr/share/lintian/overrides/tor + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_installexamples +# dh_install +# dh_installdebconf + dh_installlogrotate +# dh_installemacsen +# dh_installmime + dh_installinit +# dh_installcron + dh_installman + dh_link + #dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/tags/tor-0_0_8pre1/debian/tor.default b/tags/tor-0_0_8pre1/debian/tor.default new file mode 100644 index 0000000000..8acd2e84ff --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.default @@ -0,0 +1,13 @@ +# Defaults for tor initscript +# sourced by /etc/init.d/tor +# installed at /etc/default/tor by the maintainer scripts + +# +# This is a POSIX shell fragment +# +RUN_DAEMON="yes" + +# +# Uncomment this if you want to get coredumps +# +# ulimit -c unlimited diff --git a/tags/tor-0_0_8pre1/debian/tor.dirs b/tags/tor-0_0_8pre1/debian/tor.dirs new file mode 100644 index 0000000000..ca9f98af2d --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.dirs @@ -0,0 +1,7 @@ +etc/tor +var/lib/tor +var/log/tor +var/run/tor +usr/share/lintian/overrides +usr/bin +usr/sbin diff --git a/tags/tor-0_0_8pre1/debian/tor.docs b/tags/tor-0_0_8pre1/debian/tor.docs new file mode 100644 index 0000000000..c29884600c --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.docs @@ -0,0 +1,3 @@ +README +INSTALL +debian/README.privoxy diff --git a/tags/tor-0_0_8pre1/debian/tor.init b/tags/tor-0_0_8pre1/debian/tor.init new file mode 100644 index 0000000000..640a5e88c8 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.init @@ -0,0 +1,104 @@ +#! /bin/sh + +set -e + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/tor +NAME=tor +DESC="tor daemon" +TORLOG=/var/log/tor/log +TORPID=/var/run/tor/tor.pid +DEFAULTSFILE=/etc/default/$NAME +WAITFORDAEMON=10 +ARGS="--pidfile $TORPID --logfile $TORLOG --runasdaemon 1" + +test -x $DAEMON || exit 0 + +# Include tor defaults if available +if [ -f $DEFAULTSFILE ] ; then + . $DEFAULTSFILE +fi + +wait_for_deaddaemon () { + pid=$1 + sleep 1 + if test -n "$pid" + then + if kill -0 $pid 2>/dev/null + then + echo -n "." + cnt=0 + while kill -0 $pid 2>/dev/null + do + cnt=`expr $cnt + 1` + if [ $cnt -gt $WAITFORDAEMON ] + then + echo " FAILED." + return 1 + fi + sleep 1 + echo -n "." + done + fi + fi + return 0 +} + +case "$1" in + start) + if [ "$RUN_DAEMON" != "yes" ]; then + echo "Not starting $DESC (Disabled in $DEFAULTSFILE)." + else + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --oknodo \ + --chuid debian-tor:debian-tor \ + --pidfile $TORPID \ + --exec $DAEMON -- $ARGS + echo "$NAME." + fi + ;; + stop) + echo -n "Stopping $DESC: " + pid=`cat $TORPID 2>/dev/null` || true + if test ! -f $TORPID -o -z "$pid" + then + echo "not running (there is no $TORPID)." + elif start-stop-daemon --stop --quiet --pidfile $TORPID --exec $DAEMON + then + wait_for_deaddaemon $pid + echo "$NAME." + elif kill -0 $pid 2>/dev/null + then + echo "FAILED (Is $pid not $NAME? Is $DAEMON a different binary now?)." + else + echo "FAILED ($DAEMON died: process $pid not running; or permission denied)." + fi + ;; + reload|force-reload) + echo -n "Reloading $DESC configuration: " + pid=`cat $TORPID 2>/dev/null` || true + if test ! -f $TORPID -o -z "$pid" + then + echo "not running (there is no $TORPID)." + elif start-stop-daemon --stop --signal 1 --quiet --pidfile $TORPID --exec $DAEMON + then + echo "$NAME." + elif kill -0 $pid 2>/dev/null + then + echo "FAILED (Is $pid not $NAME? Is $DAEMON a different binary now?)." + else + echo "FAILED ($DAEMON died: process $pid not running; or permission denied)." + fi + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/tags/tor-0_0_8pre1/debian/tor.lintian-override b/tags/tor-0_0_8pre1/debian/tor.lintian-override new file mode 100644 index 0000000000..192b63b66f --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.lintian-override @@ -0,0 +1 @@ +tor: package-contains-upstream-install-documentation diff --git a/tags/tor-0_0_8pre1/debian/tor.logrotate b/tags/tor-0_0_8pre1/debian/tor.logrotate new file mode 100644 index 0000000000..676ce89894 --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.logrotate @@ -0,0 +1,11 @@ +/var/log/tor/*log { + daily + rotate 5 + compress + delaycompress + missingok + notifempty + postrotate + /etc/init.d/tor reload > /dev/null + endscript +} diff --git a/tags/tor-0_0_8pre1/debian/tor.postinst b/tags/tor-0_0_8pre1/debian/tor.postinst new file mode 100644 index 0000000000..cd2e91beeb --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.postinst @@ -0,0 +1,54 @@ +#!/bin/sh -e + +# checking debian-tor account + +uid=`getent passwd debian-tor | cut -d ":" -f 3` +home=`getent passwd debian-tor | cut -d ":" -f 6` + +# if there is the uid the account is there and we can do +# the sanit(ar)y checks otherwise we can safely create it. + +if [ "$uid" ]; then + # guess??? the checks!!! + if [ $uid -ge 100 ] && [ $uid -le 999 ]; then + echo "debian-tor uid check: ok" + else + echo "ERROR: debian-tor account has a non-system uid!" + echo "Please check /usr/share/doc/tor/README.Debian on how to" + echo "correct this problem" + exit 1 + fi + if [ "$home" = "/var/lib/tor" ]; then + echo "debian-tor homedir check: ok" + else + echo "ERROR: debian-tor account has an invalid home directory!" + echo "Please check /usr/share/doc/tor/README.Debian on how to" + echo "correct this problem" + exit 1 + fi +else + # what this might mean?? oh creating a system l^Huser! + adduser --quiet \ + --system \ + --disabled-password \ + --home /var/lib/tor \ + --no-create-home \ + --shell /bin/bash \ + --group \ + debian-tor +fi + +# ch{owning,moding} things around +# We will do nothing across upgrades. + +if [ "$2" = "" ]; then + for i in lib log run; do + chown -R debian-tor:debian-tor /var/$i/tor + chmod -R 700 /var/$i/tor + find /var/$i/tor -type f -exec chmod 600 '{}' ';' + done +fi + +#DEBHELPER# + +exit 0 diff --git a/tags/tor-0_0_8pre1/debian/tor.postrm b/tags/tor-0_0_8pre1/debian/tor.postrm new file mode 100644 index 0000000000..cb3707e27f --- /dev/null +++ b/tags/tor-0_0_8pre1/debian/tor.postrm @@ -0,0 +1,11 @@ +#!/bin/sh -e + +if [ "$1" = "purge" ]; then + # logs have to be removed according to policy. + rm -rf /var/log/tor/ + rm -rf /var/lib/tor/ +fi + +#DEBHELPER# + +exit 0 diff --git a/tags/tor-0_0_8pre1/doc/.cvsignore b/tags/tor-0_0_8pre1/doc/.cvsignore new file mode 100644 index 0000000000..5e15c4bc3d --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/.cvsignore @@ -0,0 +1,8 @@ +Makefile +Makefile.in +tor.1 +*.aux +*.dvi +*.blg +*.bbl +*.log \ No newline at end of file diff --git a/tags/tor-0_0_8pre1/doc/CLIENTS b/tags/tor-0_0_8pre1/doc/CLIENTS new file mode 100644 index 0000000000..78a06ed757 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/CLIENTS @@ -0,0 +1,66 @@ + + Part one: Overview and explanation + +Because tor is an application-level proxy, it needs client-side support +from every client program that wants to use it. (This is different from +systems like Freedom, which used a single client-side program to capture +all packets and redirect them to the Freedom network.) Client applications +need two general classes of modifications to be compatible with tor: + +1) Whenever they call connect(), they instead should connect() to the +local onion proxy and tell it "address and port". The onion proxy will +itself make a connection to "address and port", and then the client +application can talk through that socket as if it's directly connected. To +support as many applications as possible, tor uses the common "socks" +protocol which does exactly the above. So applications with socks support +will support tor without needing any modifications. + +2) Applications must not call gethostbyname() to resolve an address +they intend to later connect() to via onion routing. gethostbyname() +contacts the dns server of the target machine -- thus giving away the +fact that you intend to make an anonymous connection to it. + +To clarify, I need to explain more about the socks protocol. Socks +comes in three flavors: 4, 4a, and 5. The socks4 protocol basically +uses IP and port -- so it is unsuitable because of the gethostbyname() +issue above. Socks4a is a slight modification to the socks4 protocol, +whereby you can specify an IP of 0.0.0.x to signal the socks server +that you will instead be sending a hostname (fqdn). So applications with +socks4a support are all set. Socks5, on the other hand, allows the client +to specify "address type" and then an address -- so some applications +choose to supply an IP and others choose to supply a hostname. If the +application uses socks5 you must investigate further to decide whether +it's leaking anonymity. + + + Part two: using tsocks to transparently replace library calls + +tsocks (available from http://tsocks.sourceforge.net/ or from your +favorite apt-get equivalent) allows you to run a program as normal, +but it replaces the system calls for connect() to connect to the socks +server first and then pass it your destination info. In our case the +socks server is a tor process (running either locally or elsewhere). +In general this works quite well for command-line processes like finger, +ssh, etc. But there are a couple of catches: A) tsocks doesn't intercept +calls to gethostbyname. So unless you specify an IP rather than hostname, +you'll be giving yourself away. B) Programs which are suid don't let you +intercept the system calls -- ssh falls into this category. But you can +make a local copy of ssh and use that. C) Probably tsocks doesn't behave +well for behemoths like Mozilla. + + + Part three: applications which support tor correctly + +[this section is outdated and wrong. we should tie it into the main +tor-doc.html one day.] + +http: Mozilla: set your socks4 proxy to be the onion proxy (but see above) + privoxy: set your socks4a proxy to be the onion proxy + wget: run privoxy, and then add the line + "http_proxy=http://localhost:8118" to your ~/.wgetrc. +ssh: tsocks ssh arma@18.244.0.188 +ftp: tsocks wget ftp://18.244.0.188/quux.tar --passive + Mozilla: set your socks4 proxy to be the onion proxy + + + diff --git a/tags/tor-0_0_8pre1/doc/FAQ b/tags/tor-0_0_8pre1/doc/FAQ new file mode 100644 index 0000000000..28e37e37a7 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/FAQ @@ -0,0 +1,121 @@ +The Onion Routing (TOR) Frequently Asked Questions +-------------------------------------------------- + +1. General. + +1.1. What is Tor? + +Tor is an implementation of version 2 of Onion Routing. +Go read the tor-design.pdf for the details. + +In brief, Onion Routing is a connection-oriented anonymizing communication +service. Users choose a source-routed path through a set of nodes, and +negotiate a "virtual circuit" through the network, in which each node +knows its predecessor and successor, but no others. Traffic flowing down +the circuit is unwrapped by a symmetric key at each node, which reveals +the downstream node. + +Basically Tor provides a distributed network of servers ("onion +routers"). Users bounce their tcp streams (web traffic, ftp, ssh, etc) +around the routers, and recipients, observers, and even the routers +themselves have difficulty tracking the source of the stream. + +1.2. Why's it called Tor? + +Because Tor is the onion routing system. I kept telling people I was +working on onion routing, and they said "Neat. Which one?" Even if onion +routing has become a standard household term, this is the actual onion +routing project, started out of the Naval Research Lab. + +(Theories about recursive acronyms are ok too. It's also got a fine +translation into German.) + +1.3 Is there a backdoor in Tor? + +Not right now, but if this answer changes we probably won't be allowed +to tell you. You should always check the source (or at least the diffs +since the last release) for suspicious things; and if we don't give you +source, that's a sure sign something funny could be going on. + +2. Compiling and installing. + +[Read the README file for now; check back here once we've got packages/etc +for you.] + + +3. Running Tor. + +3.1. What kind of server should I run? + +The same executable functions as both client and server, depending on +which ports are specified in the configuration file. You can specify: +* SocksPort: client applications (eg privoxy, Mozilla) can speak socks to + this port. +* ORPort: other onion routers connect to this port +* DirPort: onion proxies and onion routers speak http to this port, to + pull down a directory of which nodes are currently available. + +3.2. So I can just run a full onion router and join the network? + +No. Users should run just an onion proxy. If you start up a full onion +router, the rest of the routers in the system won't recognize you, +so they will reject your handshake attempts. + +3.3. How do I join the network then? + +If you just want to use the onion routing network, you can run a proxy +and you're all set. If you want to run a router, you must convince +the directory server operators (currently arma@mit.edu) that you're a +trustworthy and reliable person. From there, the operators add you to +the directory, which propagates out to the rest of the network. All +nodes will know about you within a half hour. + +3.4. I want to run a directory server too. + +If you run a very reliable node, you plan to be around for a long time, +and you want to spend some time ensuring that router operators are +people we know and like, we may want you to run a directory server +too. We must manually add you to the 'dirservers' file that's part of +the distribution; users will only know about you when they upgrade to +a new version. Of course, you can always just start up your router as a +directory server too --- but users won't know to ask you for directories, +and more importantly, you'll never learn from the real directory servers +about recently joined routers. + + +4. Development. + +4.1. Who's doing this? + +4.2. Can I help? + +4.3. I've got a bug. + + +5. Anonymity. + +5.1. So I'm totally anonymous if I use Tor? + + + +5.2. Where can I learn more about anonymity? + +5.3. What attacks remain against onion routing? + + + +6. Comparison to related projects. + +6.1. Onion Routing. + +Tor *is* onion routing. + +6.2. Freedom. + + +7. Protocol and application support. + +7.1. http? ftp? udp? socks? mozilla? + + + diff --git a/tags/tor-0_0_8pre1/doc/HACKING b/tags/tor-0_0_8pre1/doc/HACKING new file mode 100644 index 0000000000..0ea9147834 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/HACKING @@ -0,0 +1,580 @@ + Guide to Hacking Tor + +(As of 8 October 2003, this was all accurate. If you're reading this in +the distant future, stuff may have changed.) + +0. Intro and required reading + + Onion Routing is still very much in development stages. This document + aims to get you started in the right direction if you want to understand + the code, add features, fix bugs, etc. + + Read the README file first, so you can get familiar with the basics of + installing and running an onion router. + + Then, skim some of the introductory materials in tor-design.pdf, + tor-spec.txt, and the Tor FAQ to learn more about how the Tor protocol + is supposed to work. This document will assume you know about Cells, + Circuits, Streams, Connections, Onion Routers, and Onion Proxies. + +1. Code organization + +1.1. The modules + + The code is divided into two directories: ./src/common and ./src/or. + The "common" directory contains general purpose utility functions not + specific to onion routing. The "or" directory implements all + onion-routing and onion-proxy specific functionality. + + Files in ./src/common: + + aes.[ch] -- Implements the AES cipher (with 128-bit keys and blocks), + and a counter-mode stream cipher on top of AES. This code is + taken from the main Rijndael distribution. (We include this + because many people are running older versions of OpenSSL without + AES support.) + + crypto.[ch] -- Wrapper functions to present a consistent interface to + public-key and symmetric cryptography operations from OpenSSL. + + fakepoll.[ch] -- Used on systems that don't have a poll() system call; + reimplements() poll using the select() system call. + + log.[ch] -- Tor's logging subsystem. + + test.h -- Macros used by unit tests. + + torint.h -- Provides missing [u]int*_t types for environments that + don't have stdint.h. + + tortls.[ch] -- Wrapper functions to present a consistent interface to + TLS, SSL, and X.509 functions from OpenSSL. + + util.[ch] -- Miscellaneous portability and convenience functions. + + Files in ./src/or: + + [General-purpose modules] + + or.h -- Common header file: include everything, define everything. + + buffers.c -- Implements a generic buffer interface. Buffers are + fairly opaque string holders that can read to or flush from: + memory, file descriptors, or TLS connections. + + Also implements parsing functions to read HTTP and SOCKS commands + from buffers. + + tree.h -- A splay tree implementation by Niels Provos. Used by + dns.c for dns caching at exits, and by connection_edge.c for dns + caching at clients. + + config.c -- Code to parse and validate the configuration file. + + [Background processing modules] + + cpuworker.c -- Implements a farm of 'CPU worker' processes to perform + CPU-intensive tasks in the background, so as not interrupt the + onion router. (OR only) + + dns.c -- Implements a farm of 'DNS worker' processes to perform DNS + lookups for onion routers and cache the results. [This needs to + be done in the background because of the lack of a good, + ubiquitous asynchronous DNS implementation.] (OR only) + + [Directory-related functionality.] + + directory.c -- 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. + + dirserv.c -- Code to manage directory contents and generate + directories. [Directory server only] + + routers.c -- Code to parse directories and router descriptors; and to + generate a router descriptor corresponding to this OR's + capabilities. Also presents some high-level interfaces for + managing an OR or OP's view of the directory. + + [Circuit-related modules.] + + circuit.c -- Code to create circuits, manage circuits, and route + relay cells along circuits. + + onion.c -- Code to generate and respond to "onion skins". + + [Core protocol implementation.] + + connection.c -- Code used in common by all connection types. See + 1.2. below for more general information about connections. + + connection_edge.c -- Code used only by edge connections. + + command.c -- Code to handle specific cell types. + + connection_or.c -- Code to implement cell-speaking connections. + + [Toplevel modules.] + + main.c -- Toplevel module. Initializes keys, handles signals, + multiplexes between connections, implements main loop, and drives + scheduled events. + + tor_main.c -- Stub module containing a main() function. Allows unit + test binary to link against main.c + + [Unit tests] + + test.c -- Contains unit tests for many pieces of the lower level Tor + modules. + +1.2. All about connections + + All sockets in Tor are handled as different types of nonblocking + 'connections'. (What the Tor spec calls a "Connection", the code refers + to as a "Cell-speaking" or "OR" connection.) + + Connections are implemented by the connection_t struct, defined in or.h. + Not every kind of connection uses all the fields in connection_t; see + the comments in or.h and the assertions in assert_connection_ok() for + more information. + + Every connection has a type and a state. Connections never change their + type, but can go through many state changes in their lifetime. + + The connection types break down as follows: + + [Cell-speaking connections] + CONN_TYPE_OR -- A bidirectional TLS connection transmitting a + sequence of cells. May be from an OR to an OR, or from an OP to + an OR. + + [Edge connections] + CONN_TYPE_EXIT -- A TCP connection from an onion router to a + Stream's destination. [OR only] + CONN_TYPE_AP -- A SOCKS proxy connection from the end user + application to the onion proxy. [OP only] + + [Listeners] + CONN_TYPE_OR_LISTENER [OR only] + CONN_TYPE_AP_LISTENER [OP only] + CONN_TYPE_DIR_LISTENER [Directory server only] + -- Bound network sockets, waiting for incoming connections. + + [Internal] + CONN_TYPE_DNSWORKER -- Connection from the main process to a DNS + worker process. [OR only] + + CONN_TYPE_CPUWORKER -- Connection from the main process to a CPU + worker process. [OR only] + + Connection states are documented in or.h. + + 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 the + front of conn->outbuf. Connections differ primarily in the functions + called to fill and drain these buffers. + +1.3. All about circuits. + + A circuit_t structure fills two roles. First, a circuit_t links two + connections together: either an edge connection and an OR connection, + or two OR connections. (When joined to an OR connection, a circuit_t + affects only cells sent to a particular circID on that connection. When + joined to an edge connection, a circuit_t affects all data.) + + Second, a circuit_t holds the cipher keys and state for sending data + along a given circuit. At the OP, it has a sequence of ciphers, each + of which is shared with a single OR along the circuit. Separate + ciphers are used for data going "forward" (away from the OP) and + "backward" (towards the OP). At the OR, a circuit has only two stream + ciphers: one for data going forward, and one for data going backward. + +1.4. Asynchronous IO and the main loop. + + Tor uses the poll(2) system call (or it wraps select(2) to act like + poll, if poll is not available) to handle nonblocking (asynchronous) + IO. If you're not familiar with nonblocking IO, check out the links + at the end of this document. + + All asynchronous logic is handled in main.c. The functions + 'connection_add', 'connection_set_poll_socket', and 'connection_remove' + manage an array of connection_t*, and keep in synch with the array of + struct pollfd required by poll(2). (This array of connection_t* is + accessible via get_connection_array, but users should generally call + one of the 'connection_get_by_*' functions in connection.c to look up + individual connections.) + + To trap read and write events, connections call the functions + 'connection_{is|stop|start}_{reading|writing}'. If you want + to completely reset the events you're watching for, use + 'connection_watch_events'. + + Every time poll() finishes, main.c calls conn_read and conn_write on + every connection. These functions dispatch events that have something + to read to connection_handle_read, and events that have something to + write to connection_handle_write, respectively. + + When connections need to be closed, they can respond in two ways. Most + simply, they can make connection_handle_* return an error (-1), + which will make conn_{read|write} close them. But if it's not + convenient to return -1 (for example, processing one connection causes + you to realize that a second one should close), then you can also + mark a connection to close by setting conn->marked_for_close. Marked + connections will be closed at the end of the current iteration of + the main loop. + + The main loop handles several other operations: First, it checks + whether any signals have been received that require a response (HUP, + KILL, USR1, CHLD). Second, it calls prepare_for_poll to handle recurring + tasks and compute the necessary poll timeout. These recurring tasks + include periodically fetching the directory, timing out unused + circuits, incrementing flow control windows and re-enabling connections + that were blocking for more bandwidth, and maintaining statistics. + + A word about TLS: 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 main.c to detect TLS objects with non-empty internal buffers. + 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. + +1.5. How data flows (An illustration.) + + Suppose an OR receives 256 bytes along an OR connection. These 256 + bytes turn out to be a data relay cell, which gets decrypted and + delivered to an edge connection. Here we give a possible call sequence + for the delivery of this data. + + (This may be outdated quickly.) + + do_main_loop -- Calls poll(2), receives a POLLIN event on a struct + pollfd, then calls: + conn_read -- Looks up the corresponding connection_t, and calls: + connection_handle_read -- Calls: + connection_read_to_buf -- Notices that it has an OR connection so: + read_to_buf_tls -- Pulls data from the TLS stream onto conn->inbuf. + connection_process_inbuf -- Notices that it has an OR connection so: + connection_or_process_inbuf -- Checks whether conn is open, and calls: + connection_process_cell_from_inbuf -- Notices it has enough data for + a cell, then calls: + connection_fetch_from_buf -- Pulls the cell from the buffer. + cell_unpack -- Decodes the raw cell into a cell_t + command_process_cell -- Notices it is a relay cell, so calls: + command_process_relay_cell -- Looks up the circuit for the cell, + makes sure the circuit is live, then passes the cell to: + circuit_deliver_relay_cell -- Passes the cell to each of: + relay_crypt -- Strips a layer of encryption from the cell and + notices that the cell is for local delivery. + connection_edge_process_relay_cell -- extracts the cell's + relay command, and makes sure the edge connection is + open. Since it has a DATA cell and an open connection, + calls: + circuit_consider_sending_sendme -- check if the total number + of cells received by all streams on this circuit is + enough that we should send back an acknowledgement + (requesting that more cells be sent to any stream). + connection_write_to_buf -- To place the data on the outgoing + buffer of the correct edge connection, by calling: + connection_start_writing -- To tell the main poll loop about + the pending data. + write_to_buf -- To actually place the outgoing data on the + edge connection. + connection_consider_sending_sendme -- if the outbuf waiting + to flush to the exit connection is not too full, check + if the total number of cells received on this stream + is enough that we should send back an acknowledgement + (requesting that more cells be sent to this stream). + + In a subsequent iteration, main notices that the edge connection is + ready for writing: + + do_main_loop -- Calls poll(2), receives a POLLOUT event on a struct + pollfd, then calls: + conn_write -- Looks up the corresponding connection_t, and calls: + connection_handle_write -- This isn't a TLS connection, so calls: + flush_buf -- Delivers data from the edge connection's outbuf to the + network. + connection_wants_to_flush -- Reports that all data has been flushed. + connection_finished_flushing -- Notices the connection is an exit, + and calls: + connection_edge_finished_flushing -- The connection is open, so it + calls: + connection_stop_writing -- Tells the main poll loop that this + connection has no more data to write. + connection_consider_sending_sendme -- now that the outbuf + is empty, check again if the total number of cells + received on this stream is enough that we should send + back an acknowledgement (requesting that more cells be + sent to this stream). + + +1.6. Routers, descriptors, and directories + + All Tor processes need to keep track of a list of onion routers, for + several reasons: + - OPs need to establish connections and circuits to ORs. + - ORs need to establish connections to other ORs. + - OPs and ORs need to fetch directories from a directory server. + - ORs need to upload their descriptors to directory servers. + - Directory servers need to know which ORs are allowed onto the + network, what the descriptors are for those ORs, and which of + those ORs are currently live. + + Thus, every Tor process keeps track of a list of all the ORs it knows + in a static variable 'directory' in the routers.c module. This + variable contains a routerinfo_t object for each known OR. On startup, + the directory is initialized to a list of known directory servers (via + router_get_list_from_file()). Later, the directory is updated via + router_get_dir_from_string(). (OPs and ORs retrieve fresh directories + from directory servers; directory servers generate their own.) + + Every OR must periodically regenerate a router descriptor for itself. + The descriptor and the corresponding routerinfo_t are stored in the + 'desc_routerinfo' and 'descriptor' static variables in routers.c. + + Additionally, a directory server keeps track of a list of the + router descriptors it knows in a separate list in dirserv.c. It + uses this list, checking which OR connections are open, to build + directories. + +1.7. Data model + + [XXX] + +1.8. Flow control + + [XXX] + +2. Coding conventions + +2.1. Details + + Use tor_malloc, tor_strdup, and tor_gettimeofday instead of their + generic equivalents. (They always succeed or exit.) + + Use INLINE instead of 'inline', so that we work properly on windows. + +2.2. Calling and naming conventions + + Whenever possible, functions should return -1 on error and and 0 on + success. + + For multi-word identifiers, use lowercase words combined with + underscores. (e.g., "multi_word_identifier"). Use ALL_CAPS for macros and + constants. + + Typenames should end with "_t". + + Function names should be prefixed with a module name or object name. (In + general, code to manipulate an object should be a module with the same + name as the object, so it's hard to tell which convention is used.) + + Functions that do things should have imperative-verb names + (e.g. buffer_clear, buffer_resize); functions that return booleans should + have predicate names (e.g. buffer_is_empty, buffer_needs_resizing). + +2.3. What To Optimize + + Don't optimize anything if it's not in the critical path. Right now, + the critical path seems to be AES, logging, and the network itself. + Feel free to do your own profiling to determine otherwise. + +2.4. Log conventions + + Log convention: use only these four log severities. + + ERR is if something fatal just happened. + WARN if something bad happened, but we're still running. The + bad thing is either a bug in the code, an attack or buggy + protocol/implementation of the remote peer, etc. The operator should + examine the bad thing and try to correct it. + NOTICE if it's something the operator will want to know about. + (No error or warning messages should be expected during normal OR or OP + operation. I expect most people to run on -l notice eventually. If a + library function is currently called such that failure always means + ERR, then the library function should log WARN and let the caller + log ERR.) + INFO means something happened (maybe bad, maybe ok), but there's nothing + you need to (or can) do about it. + DEBUG is for everything louder than INFO. + + [XXX Proposed convention: every messages of severity INFO or higher should + either (A) be intelligible to end-users who don't know the Tor source; or + (B) somehow inform the end-users that they aren't expected to understand + the message (perhaps with a string like "internal error"). Option (A) is + to be preferred to option (B). -NM] + +2.5. Doxygen + + We use the 'doxygen' utility to generate documentation from our source code. + Here's how to use it: + + 1. Begin every file that should be documented with + /** + * \file filename.c + * \brief Short desccription of the file + */ + + (Doxygen will recognize any comment beginning with /** as special.) + + 2. Before any function, structure, #define, or variable you want to + document, add a comment of the form: + + /** Describe the function's actions in imperative sentences. + * + * Use blank lines for paragraph breaks + * - and + * - hyphens + * - for + * - lists. + * + * Write argument_names in boldface. + * + * \code + * place_example_code(); + * between_code_and_endcode_commands(); + * \endcode + */ + + 3. Make sure to escape the characters "<", ">", "\", "%" and "#" as "\<", + "\>", "\\", "\%", and "\#". + + 4. To document structure members, you can use two forms: + + struct foo { + /** You can put the comment before an element; */ + int a; + int b; /**< Or use the less-than symbol to put the comment after the element. */ + }; + + 5. See the Doxygen manual for more information; this summary just scratches + the surface. + +3. References + + About Tor + + See http://freehaven.net/tor/ + http://freehaven.net/tor/cvs/doc/tor-spec.txt + http://freehaven.net/tor/cvs/doc/tor-design.tex + http://freehaven.net/tor/cvs/doc/FAQ + + About anonymity + + See http://freehaven.net/anonbib/ + + About nonblocking IO + + [XXX insert references] + + +# ====================================================================== +# Old HACKING document; merge into the above, move into tor-design.tex, +# or delete. +# ====================================================================== +The pieces. + + Routers. Onion routers, as far as the 'tor' program is concerned, + are a bunch of data items that are loaded into the router_array when + the program starts. Periodically it downloads a new set of routers + from a directory server, and updates the router_array. When a new OR + connection is started (see below), the relevant information is copied + from the router struct to the connection struct. + + Connections. A connection is a long-standing tcp socket between + nodes. A connection is named based on what it's connected to -- an "OR + connection" has an onion router on the other end, an "OP connection" has + an onion proxy 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. + + Circuits. A circuit is a path over the onion routing + network. Applications can connect to one end of the circuit, and can + create exit connections at the other end of the circuit. AP and exit + connections have only one circuit associated with them (and thus these + connection types are closed when the circuit is closed), whereas OP and + OR connections multiplex many circuits at once, and stay standing even + when there are no circuits running over them. + + Streams. Streams are specific conversations between an AP and an exit. + Streams are multiplexed over circuits. + + Cells. Some connections, specifically OR and OP connections, speak + "cells". This means that data over that connection is bundled into 256 + byte packets (8 bytes of header and 248 bytes of payload). Each cell has + a type, or "command", which indicates what it's for. + +Robustness features. + +[XXX no longer up to date] + Bandwidth throttling. Each cell-speaking connection has a maximum + bandwidth it can use, as specified in the routers.or file. Bandwidth + throttling can occur on both the sender side and the receiving side. If + the LinkPadding option is on, the sending side sends cells at regularly + spaced intervals (e.g., a connection with a bandwidth of 25600B/s would + queue a cell every 10ms). The receiving side protects against misbehaving + servers that send cells more frequently, by using a simple token bucket: + + Each connection has a token bucket with a specified capacity. Tokens are + added to the bucket each second (when the bucket is full, new tokens + are discarded.) Each token represents permission to receive one byte + from the network --- to receive a byte, the connection must remove a + token from the bucket. Thus if the bucket is empty, that connection must + wait until more tokens arrive. The number of tokens we add enforces a + longterm average rate of incoming bytes, yet we still permit short-term + bursts above the allowed bandwidth. Currently bucket sizes are set to + ten seconds worth of traffic. + + The bandwidth throttling uses TCP to push back when we stop reading. + We extend it with token buckets to allow more flexibility for traffic + bursts. + + Data congestion control. Even with the above bandwidth throttling, + we still need to worry about congestion, either accidental or intentional. + If a lot of people make circuits into same node, and they all come out + through the same connection, then that connection may become saturated + (be unable to send out data cells as quickly as it wants to). An adversary + can make a 'put' request through the onion routing network to a webserver + he owns, and then refuse to read any of the bytes at the webserver end + of the circuit. These bottlenecks can propagate back through the entire + network, mucking up everything. + + (See the tor-spec.txt document for details of how congestion control + works.) + + In practice, all the nodes in the circuit maintain a receive window + close to maximum except the exit node, which stays around 0, periodically + receiving a sendme and reading more data cells from the webserver. + In this way we can use pretty much all of the available bandwidth for + data, but gracefully back off when faced with multiple circuits (a new + sendme arrives only after some cells have traversed the entire network), + stalled network connections, or attacks. + + We don't need to reimplement full tcp windows, with sequence numbers, + the ability to drop cells when we're full etc, because the tcp streams + already guarantee in-order delivery of each cell. Rather than trying + to build some sort of tcp-on-tcp scheme, we implement this minimal data + congestion control; so far it's enough. + + Router twins. In many cases when we ask for a router with a given + address and port, we really mean a router who knows a given key. Router + twins are two or more routers that share the same private key. We thus + give routers extra flexibility in choosing the next hop in the circuit: if + some of the twins are down or slow, it can choose the more available ones. + + Currently the code tries for the primary router first, and if it's down, + chooses the first available twin. + + + + diff --git a/tags/tor-0_0_8pre1/doc/Makefile.am b/tags/tor-0_0_8pre1/doc/Makefile.am new file mode 100644 index 0000000000..434b04b7b2 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/Makefile.am @@ -0,0 +1,4 @@ + +EXTRA_DIST = tor-spec.txt CLIENTS FAQ HACKING rend-spec.txt tor-design.tex tor-doc.html tor-doc.css + +man_MANS = tor.1 diff --git a/tags/tor-0_0_8pre1/doc/TODO b/tags/tor-0_0_8pre1/doc/TODO new file mode 100644 index 0000000000..18e9d44b5e --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/TODO @@ -0,0 +1,300 @@ + +Legend: +SPEC!! - Not specified +SPEC - Spec not finalized +NICK - nick claims +ARMA - arma claims + - Not done + * Top priority + . Partially done + o Done + D Deferred + X Abandoned + +For scalability: + - Slightly smarter bandwidth management: use link capacity + intelligently. + - Handle full buffers without totally borking + +For 0.0.8: + + milestone 1: + o make all ORs serve the directory too. + o "AuthoritativeDir 1" for dirservers + o non-authorative servers with dirport publish opt dircacheport + o make clients read that and use it. + o make clients able to read a normal dirport from non-trusted OR too + o make ORs parse-and-keep-and-serve the directory they pull down + o authoritativedirservers should pull down directories from + other authdirservers, to merge descriptors. + . allow dirservers to serve running-router list separately. + o "get /running-routers" will fetch just this. + o actually make the clients use this sometimes. + o distinguish directory-is-dirty from runninglist-is-dirty + - ORs keep this too, and serve it + o Design: do we need running and non-running lists? + o tor remembers descriptor-lists across reboots. + . Packages define datadir as /var/lib/tor/. If no datadir is defined, + then choose, make, and secure ~/.tor as datadir. + o Adjust tor + o Change torrc.sample + D Change packages (not till 0.0.8 packages!) + o Look in ~/.torrc if no */etc/torrc is found. +NICK - Possible to get autoconf to easily install things into ~/.tor? + o Contact info, pgp fingerprint, comments in router desc. + o Add a ContactInfo line to torrc, which gets published in + descriptor (as opt) + o write tor version at the top of each log file + + milestone 2: + . refer to things by key: + o extend cells need ip:port:identitykeyhash. + o Lookup routers and connections by key digest; accept hex + key digest in place of nicknames. + o Audit all uses of lookup-by-hostname and lookup-by-addr-port + to search by digest when appropriate. + o make sure to use addr/port in cpuworker tasks, because + OPs don't have keys. + o and fix the function comments in rephist + o Rep-hist functions need to store info by keyid + - also use this in intro points and rendezvous points, and + hidserv descs. [XXXX This isn't enough.] + - figure out what to do about ip:port:differentkey + o ORs connect on demand. attach circuits to new connections, keep + create cells around somewhere, send destroy if fail. + o nickname defaults to first piece of hostname + o running-routers list refers to nickname if verified, else + hash-base64'ed. + o Mark routers as verified or unverified based on whether + running-routers list includes nickname or id hash. + o put OR uptime in descriptor + o name the secret-key directory something to discourage people + from mailing their identity key to tor-ops + + milestone 3: + - users can set their bandwidth, or we auto-detect it: + - advertised bandwidth defaults to 10KB + o advertised bandwidth is the min of max seen in each direction + in the past N seconds. + o calculate this + o not counting "local" connections + - round detected bandwidth up to nearest 10KB + - client software not upload descriptor until: + - you've been running for an hour + - it's sufficiently satisfied with its bandwidth + - it decides it is reachable + - start counting again if your IP ever changes. + - never regenerate identity keys, for now. + - you can set a bit for not-being-an-OR. +NICK - Reputation info needs to give better weight to recent events than + very old ones. + - Have clients and dirservers preserve reputation info over + reboots. + - clients choose nodes proportional to advertised bandwidth + o authdirserver includes descriptor. + - and lists as running iff: + - he can connect to you + - he has successfully extended to you + - he has sufficient mean-time-between-failures + - Don't accept ORs with nicknames same as verified ORs' nicknames. + - add new "Middleman 1" config variable? + o if torrc not found, exitpolicy reject *:* + o change if(options.ORPort) to what we really mean. + o same with socksport. + o get contrib/tor_resolve into the tarball and installed + - and working + + for pre1: + o 0.0.8 ORs should use identity key for 0.0.7 ORs sometimes but + not always? + o we should publish advertised_bandwidth in descriptor + o bug: 0.0.8 OPs can't extend from an 0.0.7 OR to an 0.0.8 OR + + post pre1: + - when we sigint tor, the dns/cpuworkers don't intercept sigint? + - "AcceptOnlyVerifiedRouters" config option? + - why does common/util.c build-depend on or/or.h ? + - ORs use uniquer default nicknames + - Tors deal appropriately when a newly-verified router has the + same nickname as another router they know about + X 007 can't extend to unverified 008. they will never be able to. + - if a begin failed due to exit policy, but we believe the IP + should have been allowed, switch that router to exitpolicy + reject *:* until we get our next directory. + - make advertised_server_mode() ORs fetch dirs more often. + - should the running-routers list put unverified routers at the + end? + - tor-resolve needs a man page + - tor-resolve should make use of cached answers? + + + ongoing: + . rename/rearrange functions for what file they're in + - generalize our transport: add transport.c in preparation for + http, airhook, etc transport. + +For September: +NICK . Windows port + o works as client + - deal with pollhup / reached_eof on all platforms + . robust as a client + . works as server + - can be configured + - robust as a server + . Usable as NT service + - docs for building in win + - installer + + - Docs + - FAQ + o overview of tor. how does it work, what's it do, pros and + cons of using it, why should I use it, etc. + - a howto tutorial with examples + o tutorial: how to set up your own tor network + - (need to not hardcode dirservers file in config.c) + . correct, update, polish spec + - document the exposed function api? + - document what we mean by socks. + +NICK . packages + . rpm + - find a long-term rpm maintainer + + - code + - better warn/info messages + o let tor do resolves. + o extend socks4 to do resolves? + o make script to ask tor for resolves + - tsocks + - gather patches, submit to maintainer + - intercept gethostbyname and others, do resolve via tor + - redesign and thorough code revamp, with particular eye toward: + - support half-open tcp connections + - conn key rotation + - other transports -- http, airhook + - modular introduction mechanism + - allow non-clique topology + +Other details and small and hard things: + - tor should be able to have a pool of outgoing IP addresses + that it is able to rotate through. (maybe) + - tie into squid + - buffer size pool, to let a few buffers grow huge or many buffers + grow a bit + - hidserv offerers shouldn't need to define a SocksPort + - when the client fails to pick an intro point for a hidserv, + it should refetch the hidserv desc. + . should maybe make clients exit(1) when bad things happen? + e.g. clock skew. + - should retry exitpolicy end streams even if the end cell didn't + resolve the address for you + - Add '[...truncated]' or similar to truncated log entries (like the directory + in connection_dir_process_inbuf()). + . Make logs handle it better when writing to them fails. + o Dirserver shouldn't put you in running-routers list if you haven't + uploaded a descriptor recently + . Refactor: add own routerinfo to routerlist. Right now, only + router_get_by_nickname knows about 'this router', as a hack to + get circuit_launch_new to do the right thing. + . Scrubbing proxies + - Find an smtp proxy? + . Get socks4a support into Mozilla + - Extend by hostname, not by IP. + - Need a relay teardown cell, separate from one-way ends. + - Make it harder to circumvent bandwidth caps: look at number of bytes + sent across sockets, not number sent inside TLS stream. + - fix router_get_by_* functions so they can get ourselves too, + and audit everything to make sure rend and intro points are + just as likely to be us as not. + + +***************************Future tasks:**************************** + +Rendezvous and hidden services: + make it fast: + - preemptively build and start rendezvous circs. + - preemptively build n-1 hops of intro circs? + - cannibalize general circs? + make it reliable: + - standby/hotswap/redundant services. + - store stuff to disk? dirservers forget service descriptors when + they restart; nodes offering hidden services forget their chosen + intro points when they restart. + make it robust: + - auth mechanisms to let midpoint and bob selectively choose + connection requests. + make it scalable: + - right now the hidserv store/lookup system is run by the dirservers; + this won't scale. + +Tor scalability: + Relax clique assumptions. + Redesign how directories are handled. + - Separate running-routers lookup from descriptor list lookup. + - Resolve directory agreement somehow. + - Cache directory on all servers. + Find and remove bottlenecks + - Address linear searches on e.g. circuit and connection lists. + Reputation/memory system, so dirservers can measure people, + and so other people can verify their measurements. + - Need to measure via relay, so it's not distinguishable. + Bandwidth-aware path selection. So people with T3's are picked + more often than people with DSL. + Reliability-aware node selection. So people who are stable are + preferred for long-term circuits such as intro and rend circs, + and general circs for irc, aim, ssh, etc. + Let dissidents get to Tor servers via Tor users. ("Backbone model") + +Anonymity improvements: + Is abandoning the circuit the only option when an extend fails, or + can we do something without impacting anonymity too much? + Is exiting from the middle of the circuit always a bad idea? + Helper nodes. Decide how to use them to improve safety. + DNS resolution: need to make tor support resolve requests. Need to write + a script and an interface (including an extension to the socks + protocol) so we can ask it to do resolve requests. Need to patch + tsocks to intercept gethostbyname, else we'll continue leaking it. + Improve path selection algorithms based on routing-zones paper. Be sure + to start and end circuits in different ASs. Ideally, consider AS of + source and destination -- maybe even enter and exit via nearby AS. + Intermediate model, with some delays and mixing. + Add defensive dropping regime? + +Make it more correct: + Handle half-open connections: right now we don't support all TCP + streams, at least according to the protocol. But we handle all that + we've seen in the wild. + Support IPv6. + +Efficiency/speed/robustness: + Congestion control. Is our current design sufficient once we have heavy + use? Need to measure and tweak, or maybe overhaul. + Allow small cells and large cells on the same network? + Cell buffering and resending. This will allow us to handle broken + circuits as long as the endpoints don't break, plus will allow + connection (tls session key) rotation. + Implement Morphmix, so we can compare its behavior, complexity, etc. + Use cpuworker for more heavy lifting. + - Signing (and verifying) hidserv descriptors + - Signing (and verifying) intro/rend requests + - Signing (and verifying) router descriptors + - Signing (and verifying) directories + - Doing TLS handshake (this is very hard to separate out, though) + Buffer size pool: allocate a maximum size for all buffers, not + a maximum size for each buffer. So we don't have to give up as + quickly (and kill the thickpipe!) when there's congestion. + Exit node caching: tie into squid or other caching web proxy. + Other transport. HTTP, udp, rdp, airhook, etc. May have to do our own + link crypto, unless we can bully openssl into it. + +P2P Tor: + Do all the scalability stuff above, first. + Incentives to relay. Not so hard. + Incentives to allow exit. Possibly quite hard. + Sybil defenses without having a human bottleneck. + How to gather random sample of nodes. + How to handle nodelist recommendations. + Consider incremental switches: a p2p tor with only 50 users has + different anonymity properties than one with 10k users, and should + be treated differently. + diff --git a/tags/tor-0_0_8pre1/doc/cell-struct.eps b/tags/tor-0_0_8pre1/doc/cell-struct.eps new file mode 100644 index 0000000000..eb9fcb8643 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/cell-struct.eps @@ -0,0 +1,189 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: cell-struct.fig +%%Creator: fig2dev Version 3.2 Patchlevel 4 +%%CreationDate: Mon May 17 00:04:58 2004 +%%For: root@localhost.localdomain (root) +%%BoundingBox: 0 0 254 73 +%%Magnification: 1.0000 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +newpath 0 73 moveto 0 0 lineto 254 0 lineto 254 73 lineto closepath clip newpath +-35.3 77.2 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def + +$F2psBegin +10 setmiterlimit +0 slj 0 slc + 0.06000 0.06000 sc +% +% Fig objects follow +% +% +% here starts figure with depth 50 +% Polyline +7.500 slw +n 1200 975 m + 1200 1275 l gs col0 s gr +% Polyline +n 1725 975 m + 1725 1275 l gs col0 s gr +% Polyline +n 600 975 m 4800 975 l 4800 1275 l 600 1275 l + cp gs col0 s gr +% Polyline +n 1200 300 m + 1200 600 l gs col0 s gr +% Polyline +n 1725 300 m + 1725 600 l gs col0 s gr +% Polyline +n 600 300 m 4800 300 l 4800 600 l 600 600 l + cp gs col0 s gr +% Polyline +n 2550 975 m + 2550 1275 l gs col0 s gr +% Polyline +n 3150 975 m + 3150 1275 l gs col0 s gr +% Polyline +n 3450 975 m + 3450 1275 l gs col0 s gr +% Polyline +n 3900 975 m + 3900 1275 l gs col0 s gr +/Times-Roman ff 180.00 scf sf +675 1200 m +gs 1 -1 sc (CircID) col0 sh gr +/Times-Roman ff 180.00 scf sf +900 900 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +1275 1200 m +gs 1 -1 sc (Relay) col0 sh gr +/Times-Roman ff 180.00 scf sf +1800 1200 m +gs 1 -1 sc (StreamID) col0 sh gr +/Times-Roman ff 180.00 scf sf +2625 1200 m +gs 1 -1 sc (Digest) col0 sh gr +/Times-Roman ff 180.00 scf sf +3150 1200 m +gs 1 -1 sc (Len) col0 sh gr +/Times-Roman ff 180.00 scf sf +4200 1200 m +gs 1 -1 sc (DATA) col0 sh gr +/Times-Roman ff 180.00 scf sf +675 525 m +gs 1 -1 sc (CircID) col0 sh gr +/Times-Roman ff 180.00 scf sf +1275 525 m +gs 1 -1 sc (CMD) col0 sh gr +/Times-Roman ff 180.00 scf sf +900 225 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +1425 225 m +gs 1 -1 sc (1) col0 sh gr +/Times-Roman ff 180.00 scf sf +3225 525 m +gs 1 -1 sc (DATA) col0 sh gr +/Times-Roman ff 180.00 scf sf +3225 900 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +3450 1200 m +gs 1 -1 sc (CMD) col0 sh gr +/Times-Roman ff 180.00 scf sf +3600 900 m +gs 1 -1 sc (1) col0 sh gr +/Times-Roman ff 180.00 scf sf +3300 225 m +gs 1 -1 sc (509 bytes) col0 sh gr +/Times-Roman ff 180.00 scf sf +1425 900 m +gs 1 -1 sc (1) col0 sh gr +/Times-Roman ff 180.00 scf sf +2100 900 m +gs 1 -1 sc (2) col0 sh gr +/Times-Roman ff 180.00 scf sf +2850 900 m +gs 1 -1 sc (6) col0 sh gr +/Times-Roman ff 180.00 scf sf +4350 900 m +gs 1 -1 sc (498) col0 sh gr +% here ends figure; +$F2psEnd +rs +showpage diff --git a/tags/tor-0_0_8pre1/doc/cell-struct.fig b/tags/tor-0_0_8pre1/doc/cell-struct.fig new file mode 100644 index 0000000000..3490673ca6 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/cell-struct.fig @@ -0,0 +1,49 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1200 975 1200 1275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1725 975 1725 1275 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 600 975 4800 975 4800 1275 600 1275 600 975 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1200 300 1200 600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1725 300 1725 600 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 600 300 4800 300 4800 600 600 600 600 300 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2550 975 2550 1275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3150 975 3150 1275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3450 975 3450 1275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3900 975 3900 1275 +4 0 0 50 -1 0 12 0.0000 4 135 510 675 1200 CircID\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 900 900 2\001 +4 0 0 50 -1 0 12 0.0000 4 180 435 1275 1200 Relay\001 +4 0 0 50 -1 0 12 0.0000 4 135 735 1800 1200 StreamID\001 +4 0 0 50 -1 0 12 0.0000 4 180 510 2625 1200 Digest\001 +4 0 0 50 -1 0 12 0.0000 4 135 285 3150 1200 Len\001 +4 0 0 50 -1 0 12 0.0000 4 135 510 4200 1200 DATA\001 +4 0 0 50 -1 0 12 0.0000 4 135 510 675 525 CircID\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 1275 525 CMD\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 900 225 2\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 1425 225 1\001 +4 0 0 50 -1 0 12 0.0000 4 135 510 3225 525 DATA\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 3225 900 2\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 3450 1200 CMD\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 3600 900 1\001 +4 0 0 50 -1 0 12 0.0000 4 180 735 3300 225 509 bytes\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 1425 900 1\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 2100 900 2\001 +4 0 0 50 -1 0 12 0.0000 4 135 90 2850 900 6\001 +4 0 0 50 -1 0 12 0.0000 4 135 270 4350 900 498\001 diff --git a/tags/tor-0_0_8pre1/doc/cell-struct.pdf b/tags/tor-0_0_8pre1/doc/cell-struct.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1a95b8777a0cf4d20a56da229c950999939c88ce GIT binary patch literal 6175 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 "rm *" + +Even an external adversary could do this! + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Rendezvous points + +allow hidden services + +don't need (brittle) reply onions + + Access-controlled: Bob can control who he talks to + Robust: Bob's service is available even when some Tor nodes go down + Smear-resistant: Evil service can't frame a rendezvous router + Application-transparent: Don't need to modify Bob's apache + +%size 6 +(Not implemented yet) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +How do we compare security? + +Assume adversary owns c of n nodes + can choose which +%size 6 +What's the chance for a random Alice and Bob that he wins? + +Freedom, Tor: (c/n)^2 +Peekabooty, six-four, etc: c/n +Jap (if no padding): 1 if c>1 +Anonymizer: 1 if c>0 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Future work + +Threshold directory agreement + +Scalability: Morphmix/p2p extensions? +Restricted-route (non-clique topology) + +Non-TCP transport + +Implement rendezvous points + +Make it work better + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +We have working code + +Plus a design document, +and a byte-level specification +and a Debian package (in Unstable) + +%size 9 +http://freehaven.net/tor/ + +%size 6 +Privacy Enhancing Technologies workshop + +%size 9 +http://petworkshop.org/ + diff --git a/tags/tor-0_0_8pre1/doc/interaction.eps b/tags/tor-0_0_8pre1/doc/interaction.eps new file mode 100644 index 0000000000..9b4e3db619 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/interaction.eps @@ -0,0 +1,463 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: interaction.fig +%%Creator: fig2dev Version 3.2 Patchlevel 3d +%%CreationDate: Sat Jan 31 05:25:23 2004 +%%For: nickm@totoro.wangafu.net () +%%BoundingBox: 0 0 449 235 +%%Magnification: 1.0000 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +newpath 0 235 moveto 0 0 lineto 449 0 lineto 449 235 lineto closepath clip newpath +-62.3 239.8 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/reencdict 12 dict def /ReEncode { reencdict begin +/newcodesandnames exch def /newfontname exch def /basefontname exch def +/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def +basefontdict { exch dup /FID ne { dup /Encoding eq +{ exch dup length array copy newfont 3 1 roll put } +{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall +newfont /FontName newfontname put newcodesandnames aload pop +128 1 255 { newfont /Encoding get exch /.notdef put } for +newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat +newfontname newfont definefont pop end } def +/isovec [ +8#055 /minus 8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde +8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis +8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron +8#220 /dotlessi 8#230 /oe 8#231 /OE +8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling +8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis +8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot +8#255 /hyphen 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus +8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph +8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine +8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf +8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute +8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring +8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute +8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute +8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve +8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply +8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex +8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave +8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring +8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute +8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute +8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve +8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide +8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex +8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def +/Times-Bold /Times-Bold-iso isovec ReEncode +/Times-Roman /Times-Roman-iso isovec ReEncode +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def + +$F2psBegin +10 setmiterlimit + 0.06000 0.06000 sc +% +% Fig objects follow +% +% Polyline +15.000 slw +n 6000 300 m + 6000 3975 l gs col0 s gr +% Polyline +7.500 slw +gs clippath +3615 555 m 3615 495 l 3464 495 l 3584 525 l 3464 555 l cp +eoclip +n 1200 525 m + 3600 525 l gs col0 s gr gr + +% arrowhead +n 3464 555 m 3584 525 l 3464 495 l 3464 555 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 795 m 1185 855 l 1336 855 l 1216 825 l 1336 795 l cp +eoclip +n 3600 825 m + 1200 825 l gs col0 s gr gr + +% arrowhead +n 1336 795 m 1216 825 l 1336 855 l 1336 795 l cp gs 0.00 setgray ef gr col0 s +% Polyline +15.000 slw +n 1200 300 m + 1200 3975 l gs col0 s gr +% Polyline +7.500 slw +gs clippath +3615 1155 m 3615 1095 l 3464 1095 l 3584 1125 l 3464 1155 l cp +eoclip +n 1200 1125 m + 3600 1125 l gs col0 s gr gr + +% arrowhead +n 3464 1155 m 3584 1125 l 3464 1095 l 3464 1155 l cp gs 0.00 setgray ef gr col0 s +% Polyline +15.000 slw +n 3600 300 m + 3600 3975 l gs col0 s gr +% Polyline +7.500 slw +gs clippath +6015 1230 m 6015 1170 l 5864 1170 l 5984 1200 l 5864 1230 l cp +eoclip +n 3600 1200 m + 6000 1200 l gs col0 s gr gr + +% arrowhead +n 5864 1230 m 5984 1200 l 5864 1170 l 5864 1230 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3585 1470 m 3585 1530 l 3736 1530 l 3616 1500 l 3736 1470 l cp +eoclip +n 6000 1500 m + 3600 1500 l gs col0 s gr gr + +% arrowhead +n 3736 1470 m 3616 1500 l 3736 1530 l 3736 1470 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 1545 m 1185 1605 l 1336 1605 l 1216 1575 l 1336 1545 l cp +eoclip +n 3600 1575 m + 1200 1575 l gs col0 s gr gr + +% arrowhead +n 1336 1545 m 1216 1575 l 1336 1605 l 1336 1545 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 45] 45 sd +n 1050 1800 m + 8325 1800 l gs col0 s gr [] 0 sd +% Polyline +gs clippath +3615 2130 m 3615 2070 l 3464 2070 l 3584 2100 l 3464 2130 l cp +eoclip +n 1200 2100 m + 3600 2100 l gs col0 s gr gr + +% arrowhead +n 3464 2130 m 3584 2100 l 3464 2070 l 3464 2130 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +6015 2205 m 6015 2145 l 5864 2145 l 5984 2175 l 5864 2205 l cp +eoclip +n 3600 2175 m + 6000 2175 l gs col0 s gr gr + +% arrowhead +n 5864 2205 m 5984 2175 l 5864 2145 l 5864 2205 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [60] 0 sd +gs clippath +8190 2430 m 8190 2370 l 8039 2370 l 8159 2400 l 8039 2430 l cp +5985 2370 m 5985 2430 l 6136 2430 l 6016 2400 l 6136 2370 l cp +eoclip +n 6000 2400 m + 8175 2400 l gs col0 s gr gr + [] 0 sd +% arrowhead +n 6136 2370 m 6016 2400 l 6136 2430 l 6136 2370 l cp gs 0.00 setgray ef gr col0 s +% arrowhead +n 8039 2430 m 8159 2400 l 8039 2370 l 8039 2430 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3585 2520 m 3585 2580 l 3736 2580 l 3616 2550 l 3736 2520 l cp +eoclip +n 6000 2550 m + 3600 2550 l gs col0 s gr gr + +% arrowhead +n 3736 2520 m 3616 2550 l 3736 2580 l 3736 2520 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 2595 m 1185 2655 l 1336 2655 l 1216 2625 l 1336 2595 l cp +eoclip +n 3600 2625 m + 1200 2625 l gs col0 s gr gr + +% arrowhead +n 1336 2595 m 1216 2625 l 1336 2655 l 1336 2595 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3615 3030 m 3615 2970 l 3464 2970 l 3584 3000 l 3464 3030 l cp +eoclip +n 1200 3000 m + 3600 3000 l gs col0 s gr gr + +% arrowhead +n 3464 3030 m 3584 3000 l 3464 2970 l 3464 3030 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +6015 3105 m 6015 3045 l 5864 3045 l 5984 3075 l 5864 3105 l cp +eoclip +n 3600 3075 m + 6000 3075 l gs col0 s gr gr + +% arrowhead +n 5864 3105 m 5984 3075 l 5864 3045 l 5864 3105 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +8190 3180 m 8190 3120 l 8039 3120 l 8159 3150 l 8039 3180 l cp +eoclip +n 6000 3150 m + 8175 3150 l gs col0 s gr gr + +% arrowhead +n 8039 3180 m 8159 3150 l 8039 3120 l 8039 3180 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5985 3420 m 5985 3480 l 6136 3480 l 6016 3450 l 6136 3420 l cp +eoclip +n 8175 3450 m + 6000 3450 l gs col0 s gr gr + +% arrowhead +n 6136 3420 m 6016 3450 l 6136 3480 l 6136 3420 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5985 3495 m 5985 3555 l 6136 3555 l 6016 3525 l 6136 3495 l cp +eoclip +n 8175 3525 m + 6000 3525 l gs col0 s gr gr + +% arrowhead +n 6136 3495 m 6016 3525 l 6136 3555 l 6136 3495 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5985 3570 m 5985 3630 l 6136 3630 l 6016 3600 l 6136 3570 l cp +eoclip +n 8175 3600 m + 6000 3600 l gs col0 s gr gr + +% arrowhead +n 6136 3570 m 6016 3600 l 6136 3630 l 6136 3570 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3585 3495 m 3585 3555 l 3736 3555 l 3616 3525 l 3736 3495 l cp +eoclip +n 6000 3525 m + 3600 3525 l gs col0 s gr gr + +% arrowhead +n 3736 3495 m 3616 3525 l 3736 3555 l 3736 3495 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3585 3645 m 3585 3705 l 3736 3705 l 3616 3675 l 3736 3645 l cp +eoclip +n 6000 3675 m + 3600 3675 l gs col0 s gr gr + +% arrowhead +n 3736 3645 m 3616 3675 l 3736 3705 l 3736 3645 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3585 3570 m 3585 3630 l 3736 3630 l 3616 3600 l 3736 3570 l cp +eoclip +n 6000 3600 m + 3600 3600 l gs col0 s gr gr + +% arrowhead +n 3736 3570 m 3616 3600 l 3736 3630 l 3736 3570 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 3645 m 1185 3705 l 1336 3705 l 1216 3675 l 1336 3645 l cp +eoclip +n 3600 3675 m + 1200 3675 l gs col0 s gr gr + +% arrowhead +n 1336 3645 m 1216 3675 l 1336 3705 l 1336 3645 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 3720 m 1185 3780 l 1336 3780 l 1216 3750 l 1336 3720 l cp +eoclip +n 3600 3750 m + 1200 3750 l gs col0 s gr gr + +% arrowhead +n 1336 3720 m 1216 3750 l 1336 3780 l 1336 3720 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1185 3795 m 1185 3855 l 1336 3855 l 1216 3825 l 1336 3795 l cp +eoclip +n 3600 3825 m + 1200 3825 l gs col0 s gr gr + +% arrowhead +n 1336 3795 m 1216 3825 l 1336 3855 l 1336 3795 l cp gs 0.00 setgray ef gr col0 s +% Polyline +15.000 slw +n 8175 300 m + 8175 3975 l gs col0 s gr +% Polyline +7.500 slw +n 6300 825 m 7950 825 l 7950 1725 l 6300 1725 l + cp gs col7 1.00 shd ef gr gs col0 s gr +/Times-Bold-iso ff 180.00 scf sf +3375 225 m +gs 1 -1 sc (OR 1) col0 sh gr +/Times-Bold-iso ff 180.00 scf sf +1050 225 m +gs 1 -1 sc (Alice) col0 sh gr +/Times-Bold-iso ff 180.00 scf sf +5775 225 m +gs 1 -1 sc (OR 2) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +6075 3075 m +gs 1 -1 sc ("HTTP GET...") col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +4800 3975 m +gs 1 -1 sc (. . .) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 3975 m +gs 1 -1 sc (. . .) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +2400 3975 m +gs 1 -1 sc (. . .) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 2325 m +gs 1 -1 sc (\(TCP handshake\)) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Bold-iso ff 180.00 scf sf +7875 225 m +gs 1 -1 sc (website) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 1425 m +gs 1 -1 sc ({X}--AES encryption) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 1200 m +gs 1 -1 sc (E\(x\)--RSA encryption) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 975 m +gs 1 -1 sc (Legend:) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +2400 225 m +gs 1 -1 sc (\(link is TLS-encrypted\)) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +1275 1050 m +gs 1 -1 sc (Relay c1{Extend, OR2, E\(g^x2\)}) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +1275 2025 m +gs 1 -1 sc (Relay c1{{Begin :80}}) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3525 1500 m +gs 1 -1 sc (Relay c1{Extended, g^y2, H\(K2\)}) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3675 2100 m +gs 1 -1 sc (Relay c2{Begin :80}) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3525 2550 m +gs 1 -1 sc (Relay c1{{Connected}}) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +5925 2475 m +gs 1 -1 sc (Relay c2{Connected}) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +1275 2925 m +gs 1 -1 sc (Relay c1{{Data, "HTTP GET..."}}) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3675 3000 m +gs 1 -1 sc (Relay c2{Data, "HTTP GET..."}) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +4800 225 m +gs 1 -1 sc (\(link is TLS-encryped\)) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7050 225 m +gs 1 -1 sc (\(unencrypted\)) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +7125 1650 m +gs 1 -1 sc (cN--a circID) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3525 3600 m +gs 1 -1 sc (Relay c1{{Data, \(response\)}}) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +8100 3375 m +gs 1 -1 sc (\(response\)) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +5925 3450 m +gs 1 -1 sc (Relay c2{Data, \(response\)}) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +5925 1425 m +gs 1 -1 sc (Created c2, g^y2, H\(K2\)) dup sw pop neg 0 rm col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3675 1125 m +gs 1 -1 sc (Create c2, E\(g^x2\)) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +1275 450 m +gs 1 -1 sc (Create c1, E\(g^x1\)) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +3525 750 m +gs 1 -1 sc (Created c1, g^y1, H\(K1\)) dup sw pop neg 0 rm col0 sh gr +$F2psEnd +rs diff --git a/tags/tor-0_0_8pre1/doc/interaction.fig b/tags/tor-0_0_8pre1/doc/interaction.fig new file mode 100644 index 0000000000..a7b49e0a52 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/interaction.fig @@ -0,0 +1,122 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +2 1 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 6000 300 6000 3975 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1200 525 3600 525 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 825 1200 825 +2 1 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 1200 300 1200 3975 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1200 1125 3600 1125 +2 1 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 3600 300 3600 3975 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 1200 6000 1200 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 1500 3600 1500 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 1575 1200 1575 +2 1 2 1 0 7 50 0 -1 3.000 0 0 -1 0 0 2 + 1050 1800 8325 1800 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1200 2100 3600 2100 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 2175 6000 2175 +2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 1 2 + 1 1 1.00 60.00 120.00 + 1 1 1.00 60.00 120.00 + 6000 2400 8175 2400 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 2550 3600 2550 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 2625 1200 2625 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1200 3000 3600 3000 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 3075 6000 3075 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 3150 8175 3150 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 8175 3450 6000 3450 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 8175 3525 6000 3525 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 8175 3600 6000 3600 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 3525 3600 3525 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 3675 3600 3675 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6000 3600 3600 3600 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 3675 1200 3675 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 3750 1200 3750 +2 1 0 1 0 7 50 0 -1 3.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3600 3825 1200 3825 +2 1 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 8175 300 8175 3975 +2 2 0 1 0 7 50 0 20 3.000 0 0 -1 0 0 5 + 6300 825 7950 825 7950 1725 6300 1725 6300 825 +4 0 0 50 0 2 12 0.0000 4 135 450 3375 225 OR 1\001 +4 0 0 50 0 2 12 0.0000 4 135 420 1050 225 Alice\001 +4 0 0 50 0 2 12 0.0000 4 135 450 5775 225 OR 2\001 +4 0 0 50 0 0 10 0.0000 4 105 960 6075 3075 "HTTP GET..."\001 +4 1 0 50 0 0 10 0.0000 4 15 135 4800 3975 . . .\001 +4 1 0 50 0 0 10 0.0000 4 15 135 7125 3975 . . .\001 +4 1 0 50 0 0 10 0.0000 4 15 135 2400 3975 . . .\001 +4 1 0 50 0 0 10 0.0000 4 135 1050 7125 2325 (TCP handshake)\001 +4 0 0 50 0 2 12 0.0000 4 135 630 7875 225 website\001 +4 1 0 50 0 0 10 0.0000 4 135 1335 7125 1425 {X}--AES encryption\001 +4 1 0 50 0 0 10 0.0000 4 135 1410 7125 1200 E(x)--RSA encryption\001 +4 1 0 50 0 0 10 0.0000 4 135 480 7125 975 Legend:\001 +4 1 0 50 0 0 10 0.0000 4 135 1455 2400 225 (link is TLS-encrypted)\001 +4 0 0 50 0 0 10 0.0000 4 135 2085 1275 1050 Relay c1{Extend, OR2, E(g^x2)}\001 +4 0 0 50 0 0 10 0.0000 4 135 1965 1275 2025 Relay c1{{Begin :80}}\001 +4 2 0 50 0 0 10 0.0000 4 135 2190 3525 1500 Relay c1{Extended, g^y2, H(K2)}\001 +4 0 0 50 0 0 10 0.0000 4 135 1845 3675 2100 Relay c2{Begin :80}\001 +4 2 0 50 0 0 10 0.0000 4 135 1410 3525 2550 Relay c1{{Connected}}\001 +4 2 0 50 0 0 10 0.0000 4 135 1290 5925 2475 Relay c2{Connected}\001 +4 0 0 50 0 0 10 0.0000 4 135 2085 1275 2925 Relay c1{{Data, "HTTP GET..."}}\001 +4 0 0 50 0 0 10 0.0000 4 135 1965 3675 3000 Relay c2{Data, "HTTP GET..."}\001 +4 1 0 50 0 0 10 0.0000 4 135 1365 4800 225 (link is TLS-encryped)\001 +4 1 0 50 0 0 10 0.0000 4 135 870 7050 225 (unencrypted)\001 +4 1 0 50 0 0 10 0.0000 4 105 780 7125 1650 cN--a circID\001 +4 2 0 50 0 0 10 0.0000 4 135 1860 3525 3600 Relay c1{{Data, (response)}}\001 +4 2 0 50 0 0 10 0.0000 4 135 645 8100 3375 (response)\001 +4 2 0 50 0 0 10 0.0000 4 135 1650 5925 3450 Relay c2{Data, (response)}\001 +4 2 0 50 0 0 10 0.0000 4 135 1545 5925 1425 Created c2, g^y2, H(K2)\001 +4 0 0 50 0 0 10 0.0000 4 135 1170 3675 1125 Create c2, E(g^x2)\001 +4 0 0 50 0 0 10 0.0000 4 135 1170 1275 450 Create c1, E(g^x1)\001 +4 2 0 50 0 0 10 0.0000 4 135 1545 3525 750 Created c1, g^y1, H(K1)\001 diff --git a/tags/tor-0_0_8pre1/doc/interaction.pdf b/tags/tor-0_0_8pre1/doc/interaction.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8def0add59c1316e878827d5b48cce21bb5b9f11 GIT binary patch literal 35540 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 Qg@f } + { s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't := + nameptr #1 > + { namesleft #1 > + { ", " * t * } + { numnames #2 > + { "," * } + 'skip$ + if$ + t "others" = + { " et~al." * } + { " and " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {format.authors} +{ author empty$ + { "" } + { author format.names } + if$ +} + +FUNCTION {format.editors} +{ editor empty$ + { "" } + { editor format.names + editor num.names$ #1 > + { ", editors" * } + { ", editor" * } + if$ + } + if$ +} + +FUNCTION {format.title} +{ title empty$ + { "" } + { title "t" change.case$ } + if$ +} + +FUNCTION {n.dashify} +{ 't := + "" + { t empty$ not } + { t #1 #1 substring$ "-" = + { t #1 #2 substring$ "--" = not + { "--" * + t #2 global.max$ substring$ 't := + } + { { t #1 #1 substring$ "-" = } + { "-" * + t #2 global.max$ substring$ 't := + } + while$ + } + if$ + } + { t #1 #1 substring$ * + t #2 global.max$ substring$ 't := + } + if$ + } + while$ +} + +FUNCTION {format.date} +{ year empty$ + { month empty$ + { "" } + { "there's a month but no year in " cite$ * warning$ + month + } + if$ + } + { month empty$ + 'year + { month " " * year * } + if$ + } + if$ +} + +FUNCTION {format.btitle} +{ title emphasize +} + +FUNCTION {tie.or.space.connect} +{ duplicate$ text.length$ #3 < + { "~" } + { " " } + if$ + swap$ * * +} + +FUNCTION {either.or.check} +{ empty$ + 'pop$ + { "can't use both " swap$ * " fields in " * cite$ * warning$ } + if$ +} + +FUNCTION {format.bvolume} +{ volume empty$ + { "" } + { "volume" volume tie.or.space.connect + series empty$ + 'skip$ + { " of " * series emphasize * } + if$ + "volume and number" number either.or.check + } + if$ +} + +FUNCTION {format.number.series} +{ volume empty$ + { number empty$ + { series field.or.null } + { output.state mid.sentence = + { "number" } + { "Number" } + if$ + number tie.or.space.connect + series empty$ + { "there's a number but no series in " cite$ * warning$ } + { " in " * series * } + if$ + } + if$ + } + { "" } + if$ +} + +FUNCTION {format.edition} +{ edition empty$ + { "" } + { output.state mid.sentence = + { edition "l" change.case$ " edition" * } + { edition "t" change.case$ " edition" * } + if$ + } + if$ +} + +INTEGERS { multiresult } + +FUNCTION {multi.page.check} +{ 't := + #0 'multiresult := + { multiresult not + t empty$ not + and + } + { t #1 #1 substring$ + duplicate$ "-" = + swap$ duplicate$ "," = + swap$ "+" = + or or + { #1 'multiresult := } + { t #2 global.max$ substring$ 't := } + if$ + } + while$ + multiresult +} + +FUNCTION {format.pages} +{ pages empty$ + { "" } + { pages multi.page.check + { "pages" pages n.dashify tie.or.space.connect } + { "page" pages tie.or.space.connect } + if$ + } + if$ +} + +FUNCTION {format.vol.num.pages} +{ volume field.or.null + number empty$ + 'skip$ + { "(" number * ")" * * + volume empty$ + { "there's a number but no volume in " cite$ * warning$ } + 'skip$ + if$ + } + if$ + pages empty$ + 'skip$ + { duplicate$ empty$ + { pop$ format.pages } + { ":" * pages n.dashify * } + if$ + } + if$ +} + +FUNCTION {format.chapter.pages} +{ chapter empty$ + 'format.pages + { type empty$ + { "chapter" } + { type "l" change.case$ } + if$ + chapter tie.or.space.connect + pages empty$ + 'skip$ + { ", " * format.pages * } + if$ + } + if$ +} + +FUNCTION {format.in.ed.booktitle} +{ booktitle empty$ + { "" } + { editor empty$ + { "In " booktitle emphasize * } + { "In " format.editors * ", " * booktitle emphasize * } + if$ + } + if$ +} + +FUNCTION {empty.misc.check} + +{ author empty$ title empty$ howpublished empty$ + month empty$ year empty$ note empty$ + and and and and and + key empty$ not and + { "all relevant fields are empty in " cite$ * warning$ } + 'skip$ + if$ +} + +FUNCTION {format.thesis.type} +{ type empty$ + 'skip$ + { pop$ + type "t" change.case$ + } + if$ +} + +FUNCTION {format.tr.number} +{ type empty$ + { "Technical Report" } + 'type + if$ + number empty$ + { "t" change.case$ } + { number tie.or.space.connect } + if$ +} + +FUNCTION {format.article.crossref} +{ key empty$ + { journal empty$ + { "need key or journal for " cite$ * " to crossref " * crossref * + warning$ + "" + } + { "In {\em " journal * "\/}" * } + if$ + } + { "In " key * } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {format.crossref.editor} +{ editor #1 "{vv~}{ll}" format.name$ + editor num.names$ duplicate$ + #2 > + { pop$ " et~al." * } + { #2 < + 'skip$ + { editor #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { " et~al." * } + { " and " * editor #2 "{vv~}{ll}" format.name$ * } + if$ + } + if$ + } + if$ +} + +FUNCTION {format.book.crossref} +{ volume empty$ + { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ + "In " + } + { "Volume" volume tie.or.space.connect + " of " * + } + if$ + editor empty$ + editor field.or.null author field.or.null = + or + { key empty$ + { series empty$ + { "need editor, key, or series for " cite$ * " to crossref " * + crossref * warning$ + "" * + } + { "{\em " * series * "\/}" * } + if$ + } + { key * } + if$ + } + { format.crossref.editor * } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {format.incoll.inproc.crossref} +{ editor empty$ + editor field.or.null author field.or.null = + or + { key empty$ + { booktitle empty$ + { "need editor, key, or booktitle for " cite$ * " to crossref " * + crossref * warning$ + "" + } + { "In {\em " booktitle * "\/}" * } + if$ + } + { "In " key * } + if$ + } + { "In " format.crossref.editor * } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {article} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + crossref missing$ + { journal emphasize "journal" output.check + format.vol.num.pages output + format.date "year" output.check + } + { format.article.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {book} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + new.block + format.btitle "title" output.check + crossref missing$ + { format.bvolume output + new.block + format.number.series output + new.sentence + publisher "publisher" output.check + address output + } + { new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {booklet} +{ output.bibitem + format.authors output + new.block + format.title "title" output.check + howpublished address new.block.checkb + howpublished output + address output + format.date output + new.block + note output + fin.entry +} + +FUNCTION {inbook} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check } + { format.authors output.nonnull + + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + new.block + format.btitle "title" output.check + crossref missing$ + { format.bvolume output + format.chapter.pages "chapter and pages" output.check + new.block + format.number.series output + new.sentence + publisher "publisher" output.check + address output + } + { format.chapter.pages "chapter and pages" output.check + new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {incollection} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + format.chapter.pages output + new.sentence + publisher "publisher" output.check + address output + format.edition output + format.date "year" output.check + } + { format.incoll.inproc.crossref output.nonnull + format.chapter.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {inproceedings} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + format.pages output + address empty$ + { organization publisher new.sentence.checkb + organization output + publisher output + format.date "year" output.check + } + { address output.nonnull + format.date "year" output.check + new.sentence + organization output + publisher output + } + if$ + } + { format.incoll.inproc.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {conference} { inproceedings } + +FUNCTION {manual} +{ output.bibitem + author empty$ + { organization empty$ + 'skip$ + { organization output.nonnull + address output + } + if$ + } + { format.authors output.nonnull } + if$ + new.block + format.btitle "title" output.check + author empty$ + { organization empty$ + { address new.block.checka + address output + } + 'skip$ + if$ + } + { organization address new.block.checkb + organization output + address output + } + if$ + format.edition output + format.date output + new.block + note output + fin.entry +} + +FUNCTION {mastersthesis} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + "Master's thesis" format.thesis.type output.nonnull + school "school" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {misc} +{ output.bibitem + format.authors output + title howpublished new.block.checkb + format.title output + howpublished new.block.checka + howpublished output + format.date output + new.block + note output + fin.entry + empty.misc.check +} + +FUNCTION {phdthesis} +{ output.bibitem + format.authors "author" output.check + new.block + format.btitle "title" output.check + new.block + "PhD thesis" format.thesis.type output.nonnull + school "school" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {proceedings} +{ output.bibitem + editor empty$ + { organization output } + { format.editors output.nonnull } + + if$ + new.block + format.btitle "title" output.check + format.bvolume output + format.number.series output + address empty$ + { editor empty$ + { publisher new.sentence.checka } + { organization publisher new.sentence.checkb + organization output + } + if$ + publisher output + format.date "year" output.check + } + { address output.nonnull + format.date "year" output.check + new.sentence + editor empty$ + 'skip$ + { organization output } + if$ + publisher output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {techreport} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + format.tr.number output.nonnull + institution "institution" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {unpublished} +{ output.bibitem + format.authors "author" output.check + new.block + format.title "title" output.check + new.block + note "note" output.check + format.date output + fin.entry +} + +FUNCTION {default.type} { misc } + +MACRO {jan} {"Jan."} + +MACRO {feb} {"Feb."} + +MACRO {mar} {"Mar."} + +MACRO {apr} {"Apr."} + +MACRO {may} {"May"} + +MACRO {jun} {"June"} + +MACRO {jul} {"July"} + +MACRO {aug} {"Aug."} + +MACRO {sep} {"Sept."} + +MACRO {oct} {"Oct."} + +MACRO {nov} {"Nov."} + +MACRO {dec} {"Dec."} + +MACRO {acmcs} {"ACM Comput. Surv."} + +MACRO {acta} {"Acta Inf."} + +MACRO {cacm} {"Commun. ACM"} + +MACRO {ibmjrd} {"IBM J. Res. Dev."} + +MACRO {ibmsj} {"IBM Syst.~J."} + +MACRO {ieeese} {"IEEE Trans. Softw. Eng."} + +MACRO {ieeetc} {"IEEE Trans. Comput."} + +MACRO {ieeetcad} + {"IEEE Trans. Comput.-Aided Design Integrated Circuits"} + +MACRO {ipl} {"Inf. Process. Lett."} + +MACRO {jacm} {"J.~ACM"} + +MACRO {jcss} {"J.~Comput. Syst. Sci."} + +MACRO {scp} {"Sci. Comput. Programming"} + +MACRO {sicomp} {"SIAM J. Comput."} + +MACRO {tocs} {"ACM Trans. Comput. Syst."} + +MACRO {tods} {"ACM Trans. Database Syst."} + +MACRO {tog} {"ACM Trans. Gr."} + +MACRO {toms} {"ACM Trans. Math. Softw."} + +MACRO {toois} {"ACM Trans. Office Inf. Syst."} + +MACRO {toplas} {"ACM Trans. Prog. Lang. Syst."} + +MACRO {tcs} {"Theoretical Comput. Sci."} + +READ + +FUNCTION {sortify} +{ purify$ + "l" change.case$ +} + +INTEGERS { len } + +FUNCTION {chop.word} +{ 's := + 'len := + s #1 len substring$ = + { s len #1 + global.max$ substring$ } + 's + if$ +} + +FUNCTION {sort.format.names} +{ 's := + #1 'nameptr := + "" + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { nameptr #1 > + { " " * } + 'skip$ + if$ + s nameptr "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" format.name$ 't := + nameptr numnames = t "others" = and + { "et al" * } + { t sortify * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {sort.format.title} +{ 't := + "A " #2 + "An " #3 + "The " #4 t chop.word + chop.word + chop.word + sortify + #1 global.max$ substring$ +} + +FUNCTION {author.sort} +{ author empty$ + { key empty$ + { "to sort, need author or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.editor.sort} +{ author empty$ + { editor empty$ + { key empty$ + { "to sort, need author, editor, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.organization.sort} +{ author empty$ + + { organization empty$ + { key empty$ + { "to sort, need author, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {editor.organization.sort} +{ editor empty$ + { organization empty$ + { key empty$ + { "to sort, need editor, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { editor sort.format.names } + if$ +} + +FUNCTION {presort} +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.sort + { type$ "proceedings" = + 'editor.organization.sort + { type$ "manual" = + 'author.organization.sort + 'author.sort + if$ + } + if$ + } + if$ + " " + * + year field.or.null sortify + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {presort} + +SORT + +STRINGS { longest.label } + +INTEGERS { number.label longest.label.width } + +FUNCTION {initialize.longest.label} +{ "" 'longest.label := + #1 'number.label := + #0 'longest.label.width := +} + +FUNCTION {longest.label.pass} +{ number.label int.to.str$ 'label := + number.label #1 + 'number.label := + label width$ longest.label.width > + { label 'longest.label := + label width$ 'longest.label.width := + } + 'skip$ + if$ +} + +EXECUTE {initialize.longest.label} + +ITERATE {longest.label.pass} + +FUNCTION {begin.bib} +{ preamble$ empty$ + 'skip$ + { preamble$ write$ newline$ } + if$ + "\begin{thebibliography}{" longest.label * + "}\setlength{\itemsep}{-1ex}\small" * write$ newline$ +} + +EXECUTE {begin.bib} + +EXECUTE {init.state.consts} + +ITERATE {call.type$} + +FUNCTION {end.bib} +{ newline$ + "\end{thebibliography}" write$ newline$ +} + +EXECUTE {end.bib} + +% end of file latex8.bst +% --------------------------------------------------------------- + + + diff --git a/tags/tor-0_0_8pre1/doc/latex8.sty b/tags/tor-0_0_8pre1/doc/latex8.sty new file mode 100644 index 0000000000..2028cee9c7 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/latex8.sty @@ -0,0 +1,170 @@ + + +% --------------------------------------------------------------- +% +% $Id$ +% +% by Paolo.Ienne@di.epfl.ch +% +% --------------------------------------------------------------- +% +% no guarantee is given that the format corresponds perfectly to +% IEEE 8.5" x 11" Proceedings, but most features should be ok. +% +% --------------------------------------------------------------- +% with LaTeX2e: +% ============= +% +% use as +% \documentclass[times,10pt,twocolumn]{article} +% \usepackage{latex8} +% \usepackage{times} +% +% --------------------------------------------------------------- +% with LaTeX 2.09: +% ================ +% +% use as +% \documentstyle[times,art10,twocolumn,latex8]{article} +% +% --------------------------------------------------------------- +% with both versions: +% =================== +% +% specify \pagestyle{empty} to omit page numbers in the final +% version +% +% specify references as +% \bibliographystyle{latex8} +% \bibliography{...your files...} +% +% use Section{} and SubSection{} instead of standard section{} +% and subsection{} to obtain headings in the form +% "1.3. My heading" +% +% --------------------------------------------------------------- + +\typeout{IEEE 8.5 x 11-Inch Proceedings Style `latex8.sty'.} + +% ten point helvetica bold required for captions +% in some sites the name of the helvetica bold font may differ, +% change the name here: +\font\tenhv = phvb at 10pt +%\font\tenhv = phvb7t at 10pt + +% eleven point times bold required for second-order headings +% \font\elvbf = cmbx10 scaled 1100 +\font\elvbf = ptmb scaled 1100 + +% set dimensions of columns, gap between columns, and paragraph indent +\setlength{\textheight}{8.875in} +\setlength{\textwidth}{6.875in} +\setlength{\columnsep}{0.3125in} +%\setlength{\columnsep}{0.26in} +\setlength{\topmargin}{0in} +\setlength{\headheight}{0in} +\setlength{\headsep}{.5in} +\setlength{\parindent}{1pc} +\setlength{\oddsidemargin}{-.304in} +\setlength{\evensidemargin}{-.304in} + +% memento from size10.clo +% \normalsize{\@setfontsize\normalsize\@xpt\@xiipt} +% \small{\@setfontsize\small\@ixpt{11}} +% \footnotesize{\@setfontsize\footnotesize\@viiipt{9.5}} +% \scriptsize{\@setfontsize\scriptsize\@viipt\@viiipt} +% \tiny{\@setfontsize\tiny\@vpt\@vipt} +% \large{\@setfontsize\large\@xiipt{14}} +% \Large{\@setfontsize\Large\@xivpt{18}} +% \LARGE{\@setfontsize\LARGE\@xviipt{22}} +% \huge{\@setfontsize\huge\@xxpt{25}} +% \Huge{\@setfontsize\Huge\@xxvpt{30}} + +\def\@maketitle + { + \newpage + \null + \vskip .375in + \begin{center} + {\Large \bf \@title \par} + % additional two empty lines at the end of the title + \vspace*{24pt} + { + \large + \lineskip .5em + \begin{tabular}[t]{c} + \@author + \end{tabular} + \par + } + % additional small space at the end of the author name + \vskip .5em + { + \large + \begin{tabular}[t]{c} + \@affiliation + \end{tabular} + \par + \ifx \@empty \@email + \else + \begin{tabular}{r@{~}l} + E-mail: & {\tt \@email} + \end{tabular} + \par + \fi + } + % additional empty line at the end of the title block + \vspace*{12pt} + \end{center} + } + +\def\abstract + {% + \centerline{\large\bf Abstract}% + \vspace*{12pt}% + \it% + } + +\def\endabstract + { + % additional empty line at the end of the abstract + \vspace*{12pt} + } + +\def\affiliation#1{\gdef\@affiliation{#1}} \gdef\@affiliation{} + +\def\email#1{\gdef\@email{#1}} +\gdef\@email{} + +\newlength{\@ctmp} +\newlength{\@figindent} +\setlength{\@figindent}{1pc} + +\long\def\@makecaption#1#2{ + \vskip 10pt + \setbox\@tempboxa\hbox{\tenhv\noindent #1.~#2} + \setlength{\@ctmp}{\hsize} + \addtolength{\@ctmp}{-\@figindent}\addtolength{\@ctmp}{-\@figindent} + % IF longer than one indented paragraph line + \ifdim \wd\@tempboxa >\@ctmp + % THEN set as an indented paragraph + \begin{list}{}{\leftmargin\@figindent \rightmargin\leftmargin} + \item[]\tenhv #1.~#2\par + \end{list} + \else + % ELSE center + \hbox to\hsize{\hfil\box\@tempboxa\hfil} + \fi} + +% correct heading spacing and type +\def\section{\@startsection {section}{1}{\z@} + {14pt plus 2pt minus 2pt}{14pt plus 2pt minus 2pt} {\large\bf}} +\def\subsection{\@startsection {subsection}{2}{\z@} + {13pt plus 2pt minus 2pt}{13pt plus 2pt minus 2pt} {\elvbf}} + +% add the period after section numbers +\newcommand{\Section}[1]{\section{\hskip -1em.~#1}} +\newcommand{\SubSection}[1]{\subsection{\hskip -1em.~#1}} + +% end of file latex8.sty +% --------------------------------------------------------------- diff --git a/tags/tor-0_0_8pre1/doc/rend-spec.txt b/tags/tor-0_0_8pre1/doc/rend-spec.txt new file mode 100644 index 0000000000..ac2b0aca8f --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/rend-spec.txt @@ -0,0 +1,333 @@ +$Id$ + +[XXX We need to make nicknames in intro* cells padded to 20 + bytes, and maybe have a fixed length for nicknames in the + descriptor too -RD] + +Tor Rendezvous Spec + +0. Overview and preliminaries + + Rendezvous points provide location-hidden services (server + anonymity) for the onion routing network. With rendezvous points, + Bob can offer a TCP service (say, a webserver) via the onion + routing network, without revealing the IP of that service. + + Bob does this by anonymously advertising a public key for his + service, along with a list of onion routers to act as "Introduction + Points" for his service. He creates forward OR circuits to those + introduction points, and tells them about his public key. To + connect to Bob, Alice first builds an OR circuit to an OR to act as + her "Rendezvous Point", then connects to one of Bob's chosen + introduction points, and asks it to tell him about her Rendezvous + Point (RP). If Bob chooses to answer, he builds an OR circuit to her + RP, and tells it to connect him to Alice. The RP joints their + circuits together, and begins relaying cells. Alice's 'BEGIN' + cells are received directly by Bob's OP, which responds by + communication with the local server implementing Bob's service. + + Below, we describe a network-level specification of this service, + along with interfaces to make this process transparent to Alice + (so long as she is using an OP). + +0.1. Notation, conventions and prerequisites + + In the specifications below, we use the same notation as in + "tor-spec.txt". The service specified here also requires the existence of + an onion routing network as specified in "tor-spec.txt". + + H(x) is a SHA1 digest of x. + PKSign(SK,x) is a PKCS.1-padded RSA signature of x with SK. + PKEncrypt(SK,x) is a PKCS.1-padded RSA encryption of x with SK. + Public keys are all RSA, and encoded in ASN.1. + All integers are stored in network (big-endian) order. + All symmetric encryption uses AES in counter mode, except where + otherwise noted. + + In all discussions, "Alice" will refer to a user connecting to a + location-hidden service, and "Bob" will refer to a user running a + location-hidden service. + +0.2. Protocol outline + + 1. Bob->Bob's OP: "Offer IP:Port as public-key-name:Port". [configuration] + (We do not specify this step; it is left to the implementor of + Bob's OP.) + + 2. Bob's OP generates keypair and rendezvous service descriptor: + "Meet public-key X at introduction point A, B, or C." (signed) + + 3. Bob's OP->Introduction point via Tor: [introduction setup] + "This pk is me." + + 4. Bob's OP->directory service via Tor: publishes Bob's service descriptor + [advertisement] + + 5. Out of band, Alice receives a y.onion:port address. She opens a + SOCKS connection to her OP, and requests y.onion:port. + + 6. Alice's OP retrieves Bob's descriptor via Tor: [descriptor lookup.] + + 7. Alice's OP chooses a rendezvous point, opens a circuit to that + rendezvous point, and establishes a rendezvous circuit. [rendezvous + setup.] + + 8. Alice connects to the Introduction point via Tor, and tells it about + her rendezvous point. (Encrypted to Bob.) [Introduction 1] + + 9. The Introduction point passes this on to Bob's OP via Tor, along the + introduction circuit. [Introduction 2] + + 10. Bob's OP decides whether to connect to Alice, and if so, creates a + circuit to Alice's RP via Tor. Establishes a shared circuit. + [Rendezvous.] + + 11. Alice's OP sends begin cells to Bob's OP. [Connection] + +0.3. Constants and new cell types + + Relay cell types + 32 -- RELAY_ESTABLISH_INTRO + 33 -- RELAY_ESTABLISH_RENDEZVOUS + 34 -- RELAY_INTRODUCE1 + 35 -- RELAY_INTRODUCE2 + 36 -- RELAY_RENDEZVOUS1 + 37 -- RELAY_RENDEZVOUS2 + 38 -- RELAY_INTRO_ESTABLISHED + 39 -- RELAY_RENDEZVOUS_ESTABLISHED + 40 -- RELAY_COMMAND_INTRODUCE_ACK + +1. The Protocol + +1.1. Bob configures his local OP. + + We do not specify a format for the OP configuration file. However, + OPs SHOULD allow Bob to provide more than one advertised service + per OP, and MUST allow Bob to specify one or more virtual ports per + service. Bob provides a mapping from each of these virtual ports + to a local IP:Port pair. + +1.2. Bob's OP generates service descriptors. + + The first time the OP provides an advertised service, it generates + a public/private keypair (stored locally). Periodically, the OP + generates service descriptor, containing: + + KL Key length [2 octets] + PK Bob's public key [KL octets] + TS A timestamp [4 octets] + NI Number of introduction points [2 octets] + Ipt A list of NUL-terminated OR nicknames [variable] + SIG Signature of above fields [variable] + + KL is the length of PK, in octets. (Currently, KL must be 128.) + TS is the number of seconds elapsed since Jan 1, 1970. + + [It's ok for Bob to advertise 0 introduction points. He might want + to do that if he previously advertised some introduction points, + and now he doesn't have any. -RD] + + [Shouldn't the nicknames be hostname:port's instead? That way, Alice's + directory servers don't need to know Bob's chosen introduction points. + Not important now, but essential if we ever have a non-total-knowledge + design. -NM] + +1.3. Bob's OP establishes his introduction points. + + The OP establishes a new introduction circuit to each introduction + point. These circuits MUST NOT be used for anything but rendezvous + introduction. To establish the introduction, Bob sends a + RELAY_ESTABLISH_INTRO cell, containing: + + KL Key length [2 octets] + PK Bob's public key [KL octets] + HS Hash of session info [20 octets] + SIG Signature of above information [variable] + + To prevent replay attacks, the HS field contains a SHA-1 hash based on the + shared secret KH between Bob's OP and the introduction point, as + follows: + HS = H(KH | "INTRODUCE") + That is: + HS = H(KH | [49 4E 54 52 4F 44 55 43 45]) + (KH, as specified in tor-spec.txt, is H(g^xy | [00]) .) + + Upon receiving such a cell, the OR first checks that the signature is + correct with the included public key. If so, it checks whether HS is + correct given the shared state between Bob's OP and the OR. If either + check fails, the OP discards the cell; otherwise, it associates the + circuit with Bob's public key, and dissociates any other circuits + currently associated with PK. On success, the OR sends Bob a + RELAY_INTRO_ESTABLISHED cell with an empty payload. + +1.4. Bob's OP advertises his server descriptor + + Bob's OP opens a stream to each directory server's directory port via Tor. + (He may re-use old circuits for this.) + Over this stream, Bob's OP makes an HTTP 'POST' request, to the URL + '/rendezvous/publish' (relative to the directory server's root), + containing as its body Bob's service descriptor. Upon receiving a + descriptor, the directory server checks the signature, and discards the + descriptor if the signature does not match the enclosed public key. Next, + the directory server checks the timestamp. If the timestamp is more than + 24 hours in the past or more than 1 hour in the future, or the directory + server already has a newer descriptor with the same public key, the server + discards the descriptor. Otherwise, the server discards any older + descriptors with the same public key, and associates the new descriptor + with the public key. The directory server remembers this descriptor for + at least 24 hours after its timestamp. At least every 24 hours, Bob's OP + uploads a fresh descriptor. + +1.5. Alice receives a y.onion address + + When Alice receives a pointer to a location-hidden service, it is as a + hostname of the form "y.onion", where y is a base-32 encoding of a + 10-octet hash of Bob's service's public key, computed as follows: + + 1. Let H = H(PK). + 2. Let H' = the first 80 bits of H, considering each octet from + most significant bit to least significant bit. + 2. Generate a 16-character encoding of H', using base32 as defined + in RFC 3548. + + (We only use 80 bits instead of the 160 bits from SHA1 because we don't + need to worry about man-in-the-middle attacks, and because it will make + handling the url's more convenient.) + + [Yes, numbers are allowed at the beginning. See RFC1123. -NM] + +1.6. Alice's OP retrieves a service descriptor + + Alice opens a stream to a directory server via Tor, and makes an HTTP GET + request for the document '/rendezvous/', where ' is replaced with the + encoding of Bob's public key as described above. (She may re-use old + circuits for this.) The directory replies with a 404 HTTP response if + it does not recognize , and otherwise returns Bob's most recently + uploaded service descriptor. + + If Alice's OP receives a 404 response, it tries the other directory + servers, and only fails the lookup if none recognizes the public key hash. + + Upon receiving a service descriptor, Alice verifies with the same process + as the directory server uses, described above in section 1.4. + + The directory server gives a 400 response if it cannot understand Alice's + request. + + Alice should cache the descriptor locally, but should not use + descriptors that are more than 24 hours older than their timestamp. + [Caching may make her partitionable, but she fetched it anonymously, + and we can't very well *not* cache it. -RD] + +1.7. Alice's OP establishes a rendezvous point. + + When Alice requests a connection to a given location-hidden service, + and Alice's OP does not have an established circuit to that service, + the OP builds a rendezvous circuit. It does this by establishing + a circuit to a randomly chosen OR, and sending a + RELAY_ESTABLISH_RENDEZVOUS cell to that OR. The body of that cell + contains: + + RC Rendezvous cookie [20 octets] + + The rendezvous cookie is an arbitrary 20-byte value, chosen randomly by + Alice's OP. + + Upon receiving a RELAY_ESTABLISH_RENDEZVOUS cell, the OR associates the + RC with the circuit that sent it. It replies to Alice with an empty + RELAY_RENDEZVOUS_ESTABLISHED cell to indicate success. + + Alice's OP MUST NOT use the circuit which sent the cell for any purpose + other than rendezvous with the given location-hidden service. + +1.8. Introduction: from Alice's OP to Introduction Point + + Alice builds a separate circuit to one of Bob's chosen introduction + points, and sends it a RELAY_INTRODUCE1 cell containing: + + Cleartext + PK_ID Identifier for Bob's PK [20 octets] + + Encrypted to Bob's PK: + RP Rendezvous point's nickname [20 octets] + RC Rendezvous cookie [20 octets] + g^x Diffie-Hellman data, part 1 [128 octetes] + + PK_ID is the hash of Bob's public key. RP is NUL-padded. + + The hybrid encryption to Bob's PK works just like the hybrid + encryption in CREATE cells (see main spec). Thus the payload of the + RELAY_INTRODUCE1 cell on the wire will contain 20+42+16+20+20+128=246 + bytes. + +1.9. Introduction: From the Introduction Point to Bob's OP + + If the Introduction Point recognizes PK_ID as a public key which has + established a circuit for introductions as in 1.3 above, it sends the body + of the cell in a new RELAY_INTRODUCE2 cell down the corresponding circuit. + (If the PK_ID is unrecognized, the RELAY_INTRODUCE1 cell is discarded.) + + After sending the RELAY_INTRODUCE2 cell, the OR replies to Alice with an + empty RELAY_COMMAND_INTRODUCE_ACK cell. If no RELAY_INTRODUCE2 cell can + be sent, the OR replies to Alice with a non-empty cell to indicate an + error. (The semantics of the cell body may be determined later; the + current implementation sends a single '1' byte on failure.) + + When Bob's OP receives the RELAY_INTRODUCE2 cell, it decrypts it with + the private key for the corresponding hidden service, and extracts the + rendezvous point's nickname, the rendezvous cookie, and the value of g^x + chosen by Alice. + +1.10. Rendezvous + + Bob's OP build a new Tor circuit ending at Alice's chosen rendezvous + point, and sends a RELAY_RENDEZVOUS1 cell along this circuit, containing: + RC Rendezvous cookie [20 octets] + g^y Diffie-Hellman [128 octets] + KH Handshake digest [20 octets] + + (Bob's OP MUST NOT use this circuit for any other purpose.) + + If the RP recognizes RC, it relays the rest of the cell down the + corresponding circuit in a RELAY_RENDEZVOUS2 cell, containing: + + g^y Diffie-Hellman [128 octets] + KH Handshake digest [20 octets] + + (If the RP does not recognize the RC, it discards the cell and + tears down the circuit.) + + When Alice's OP receives a RELAY_RENDEZVOUS2 cell on a circuit which + has sent a RELAY_ESTABLISH_RENDEZVOUS cell but which has not yet received + a reply, it uses g^y and H(g^xy) to complete the handshake as in the Tor + circuit extend process: they establish a 60-octet string as + K = SHA1(g^xy | [00]) | SHA1(g^xy | [01]) | SHA1(g^xy | [02]) + and generate + KH = K[0..15] + Kf = K[16..31] + Kb = K[32..47] + + Subsequently, the rendezvous point passes relay cells, unchanged, from + each of the two circuits to the other. When Alice's OP sends + RELAY cells along the circuit, it first encrypts them with the + Kf, then with all of the keys for the ORs in Alice's side of the circuit; + and when Alice's OP receives RELAY cells from the circuit, it decrypts + them with the keys for the ORs in Alice's side of the circuit, then + decrypts them with Kb. Bob's OP does the same, with Kf and Kb + interchanged. + +1.11. Creating streams + + To open TCP connections to Bob's location-hidden service, Alice's OP sends + a RELAY_BEGIN cell along the established circuit, using the special + address "", and a chosen port. Bob's OP chooses a destination IP and + port, based on the configuration of the service connected to the circuit, + and opens a TCP stream. From then on, Bob's OP treats the stream as an + ordinary exit connection. + [ Except he doesn't include addr in the connected cell or the end + cell. -RD] + + Alice MAY send multiple RELAY_BEGIN cells along the circuit, to open + multiple streams to Bob. Alice SHOULD NOT send RELAY_BEGIN cells for any + other address along her circuit to Bob; if she does, Bob MUST reject them. + diff --git a/tags/tor-0_0_8pre1/doc/rump-fc04.mgp b/tags/tor-0_0_8pre1/doc/rump-fc04.mgp new file mode 100644 index 0000000000..efbf6c840c --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/rump-fc04.mgp @@ -0,0 +1,175 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%deffont "standard" xfont "comic sans ms-medium-r" +%%deffont "thick" xfont "arial black-medium-r" +%%deffont "typewriter" xfont "courier new-bold-r" +%%deffont "type2writer" xfont "arial narrow-bold-r" +%%deffont "standard" tfont "standard.ttf", tmfont "kochi-mincho.ttf" +%%deffont "thick" tfont "thick.ttf", tmfont "goth.ttf" +%%deffont "typewriter" tfont "typewriter.ttf", tmfont "goth.ttf" +%deffont "standard" xfont "helvetica-medium-r", tfont "arial.ttf", tmfont "times.ttf" +%deffont "thick" xfont "helvetica-bold-r", tfont "arialbd.ttf", tmfont "hoso6.ttf" +%deffont "italic" xfont "helvetica-italic-r", tfont "ariali.ttf", tmfont "hoso6.ttf" +%deffont "typewriter" xfont "courier-medium-r", tfont "typewriter.ttf", tmfont "hoso6.ttf" +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Default settings per each line numbers. +%% +%default 1 leftfill, size 8, fore "black", back "white", font "thick", hgap 1 +%default 2 size 8, vgap 10, prefix " ", ccolor "black" +%default 3 size 6, bar "gray70", vgap 0 +%default 4 size 6, fore "black", vgap 0, prefix " ", font "standard" +%% +%%default 1 area 90 90, leftfill, size 9, fore "yellow", back "blue", font "thick" +%%default 2 size 9, vgap 10, prefix " " +%%default 3 size 7, bar "gray70", vgap 10 +%%default 4 size 7, vgap 30, prefix " ", font "standard" +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Default settings that are applied to TAB-indented lines. +%% +%tab 1 size 5, vgap 40, prefix " ", icon arc "red" 50 +%tab 2 size 4, vgap 35, prefix " ", icon delta3 "blue" 40 +%tab 3 size 3, vgap 35, prefix " ", icon dia "DarkViolet" 40 +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page +%nodefault +%center, size 9, font "thick", back "white", fore "black" + + + +Tor: +%size 8 +Next-generation Onion Routing + + +%size 7 +Roger Dingledine +Nick Mathewson +Paul Syverson + +%%The Free Haven Project +%%%font "typewriter", fore "blue" +%%http://freehaven.net/ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Low-latency anonymity system + +%leftfill +Deployed: 19 nodes, hundreds of users (?) + +Many improvements on earlier design + +Free software -- available source code + +Design is not covered by earlier onion routing +patent + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Perfect forward secrecy + + +Telescoping circuit + + negotiates keys at each hop + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%page +%% +%%Separation from "protocol cleaning" +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +No mixing, padding, traffic shaping (yet) + + +Please show us they're worth the usability tradeoff + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%page +%% +%%Many TCP streams can share one circuit +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Congestion control + + +Simple rate limiting + +Plus have to keep internal nodes from overflowing + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Directory servers + + +Approve new servers + +Tell clients who's up right now + + plus their keys, location, etc + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Variable exit policies + + +Each server allows different outgoing connections + +E.g. no servers allow outgoing mail currently + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +End-to-end integrity checking + + +In previous onion routing, an insider could change +the text being transmitted: + +"dir" => "rm *" + +Even an external adversary could do this! + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Rendezvous points + + +allow hidden services + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Differences / limitations + + +We're TCP-only, not all IP (but we're user-space and very portable) + +Not peer-to-peer + +No protocol normalization + +%%Not unobservable + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +We have working code + + +Plus a design document, +and a byte-level specification + +%size 9 +http://freehaven.net/tor/ + diff --git a/tags/tor-0_0_8pre1/doc/socks-extensions.txt b/tags/tor-0_0_8pre1/doc/socks-extensions.txt new file mode 100644 index 0000000000..b2988ef5c3 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/socks-extensions.txt @@ -0,0 +1,60 @@ +$Id$ +Tor's extensions to the SOCKS protocol + +1. Overview + + The SOCKS protocol provides a generic interface for TCP proxies. Client + software connects to a SOCKS server via TCP, and requests a TCP connection + to another address and port. The SOCKS server establishes the connection, + and reports success or failure to the client. After the connection has + been established, the client application uses the TCP stream as usual. + + Tor supports SOCKS4 as defined in [1], SOCKS4A as defined in [2], and + SOCKS5 as defined in [3]. + + The stickiest issue for Tor in supporting clients, in practice, is forcing + DNS lookups to occur at the OR side: if clients do their own DNS lookup, + the DNS server can learn which addresses the client wants to reach. + SOCKS4 supports addressing by IPv4 address; SOCKS4A is a kludge on top of + SOCKS4 to allow addressing by hostname; SOCKS5 supports IPv4, IPv6, and + hostnames. + +1.1. Extent of support + + Tor supports the SOCKS4, SOCKS4A, and SOCKS5 standards, except as follows: + + BOTH: + - The BIND command is not supported. + + SOCKS4,4A: + - SOCKS4 usernames are ignored. + + SOCKS5: + - The (SOCKS5) "UDP ASSOCIATE" command is not supported. + - IPv6 is not supported in CONNECT commands. + - Only the "NO AUTHENTICATION" (SOCKS5) authentication method [00] is + supported. + +2. Name lookup + + As an extension to SOCKS4A and SOCKS5, Tor implements a new command value, + "RESOLVE" [F0]. When Tor receives a "RESOLVE" SOCKS command, it initiates + a remote lookup of the hostname provided as the target address in the SOCKS + request. The reply is either an error (if the address couldn't be + resolved) or a success response. In the case of success, the address is + stored in the portion of the SOCKS response reserved for remote IP address. + + (We support RESOLVE in SOCKS4 too, even though it is unnecessary.) + +3. HTTP-resistance + + Tor checks the first byte of each socks request to see whether it looks + more like an HTTP request (that is, it starts with a "G", "H", or "P"). If + so, Tor returns a small webpage, telling the user that his/her browser is + misconfigured. This is helpful for the many users who mistakenly try to + use Tor as an HTTP proxy instead of a SOCKS proxy. + +References: + [1] http://archive.socks.permeo.com/protocol/socks4.protocol + [2] http://archive.socks.permeo.com/protocol/socks4a.protocol + [3] SOCKS5: RFC1928 diff --git a/tags/tor-0_0_8pre1/doc/tor-design.bib b/tags/tor-0_0_8pre1/doc/tor-design.bib new file mode 100644 index 0000000000..cf60f2cd22 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-design.bib @@ -0,0 +1,1093 @@ + +% fix me +@misc{tannenbaum96, + author = "Andrew Tannenbaum", + title = "Computer Networks", + year = "1996", + publisher = "Prentice Hall, 3rd edition", +} + +@article{ meadows96, + author = "Catherine Meadows", + title = "The {NRL} Protocol Analyzer: An Overview", + journal = "Journal of Logic Programming", + volume = "26", + number = "2", + pages = "113--131", + year = "1996", +} +@inproceedings{kesdogan:pet2002, + title = {Unobservable Surfing on the World Wide Web: Is Private Information Retrieval an + alternative to the MIX based Approach?}, + author = {Dogan Kesdogan and Mark Borning and Michael Schmeink}, + booktitle = {Privacy Enhancing Technologies (PET 2002)}, + year = {2002}, + month = {April}, + editor = {Roger Dingledine and Paul Syverson}, + publisher = {Springer-Verlag, LNCS 2482}, +} + +@inproceedings{statistical-disclosure, + title = {Statistical Disclosure Attacks}, + author = {George Danezis}, + booktitle = {Security and Privacy in the Age of Uncertainty ({SEC2003})}, + organization = {{IFIP TC11}}, + year = {2003}, + month = {May}, + address = {Athens}, + pages = {421--426}, + publisher = {Kluwer}, +} + +@inproceedings{limits-open, + title = {Limits of Anonymity in Open Environments}, + author = {Dogan Kesdogan and Dakshi Agrawal and Stefan Penz}, + booktitle = {Information Hiding Workshop (IH 2002)}, + year = {2002}, + month = {October}, + editor = {Fabien Petitcolas}, + publisher = {Springer-Verlag, LNCS 2578}, +} + +@inproceedings{isdn-mixes, + title = {{ISDN-mixes: Untraceable communication with very small bandwidth overhead}}, + author = {Andreas Pfitzmann and Birgit Pfitzmann and Michael Waidner}, + booktitle = {GI/ITG Conference on Communication in Distributed Systems}, + year = {1991}, + month = {February}, + pages = {451-463}, +} + + +@Article{jerichow-jsac98, + author = {Anja Jerichow and Jan M\"{u}ller and Andreas + Pfitzmann and Birgit Pfitzmann and Michael Waidner}, + title = {Real-Time Mixes: A Bandwidth-Efficient Anonymity Protocol}, + journal = {IEEE Journal on Selected Areas in Communications}, + year = 1998, + volume = 16, + number = 4, + pages = {495--509}, + month = {May} +} + +@inproceedings{tarzan:ccs02, + title = {Tarzan: A Peer-to-Peer Anonymizing Network Layer}, + author = {Michael J. Freedman and Robert Morris}, + booktitle = {9th {ACM} {C}onference on {C}omputer and {C}ommunications + {S}ecurity ({CCS 2002})}, + year = {2002}, + month = {November}, + address = {Washington, DC}, +} + +@inproceedings{cebolla, + title = {{Cebolla: Pragmatic IP Anonymity}}, + author = {Zach Brown}, + booktitle = {Ottawa Linux Symposium}, + year = {2002}, + month = {June}, +} + +@inproceedings{eax, + author = "M. Bellare and P. Rogaway and D. Wagner", + title = {The {EAX} Mode of Operation: A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and Efficiency}, + booktitle = {Fast Software Encryption 2004}, + month = {February}, + year = {2004}, +} + +@misc{darkside, + title = {{The Dark Side of the Web: An Open Proxy's View}}, + author = {Vivek S. Pai and Limin Wang and KyoungSoo Park and Ruoming Pang and Larry Peterson}, + note = {\newline \url{http://codeen.cs.princeton.edu/}}, +} +% note = {Submitted to HotNets-II. \url{http://codeen.cs.princeton.edu/}}, + +@Misc{anonymizer, + key = {anonymizer}, + title = {The {Anonymizer}}, + note = {\url{http://anonymizer.com/}} +} + +@Misc{privoxy, + key = {privoxy}, + title = {{Privoxy}}, + note = {\url{http://www.privoxy.org/}} +} + +@inproceedings{anonnet, + title = {{Analysis of an Anonymity Network for Web Browsing}}, + author = {Marc Rennhard and Sandro Rafaeli and Laurent Mathy and Bernhard Plattner and + David Hutchison}, + booktitle = {{IEEE 7th Intl. Workshop on Enterprise Security (WET ICE + 2002)}}, + year = {2002}, + month = {June}, + address = {Pittsburgh, USA}, +} +% pages = {49--54}, + +@inproceedings{econymics, + title = {On the Economics of Anonymity}, + author = {Alessandro Acquisti and Roger Dingledine and Paul Syverson}, + booktitle = {Financial Cryptography}, + year = {2003}, + editor = {Rebecca N. Wright}, + publisher = {Springer-Verlag, LNCS 2742}, +} + +@inproceedings{defensive-dropping, + title = {Timing Analysis in Low-Latency Mix-Based Systems}, + author = {Brian N. Levine and Michael K. Reiter and Chenxi Wang and Matthew Wright}, + booktitle = {Financial Cryptography}, + year = {2004}, + editor = {Ari Juels}, + publisher = {Springer-Verlag, LNCS (forthcoming)}, +} + +@inproceedings{morphmix:fc04, + title = {Practical Anonymity for the Masses with MorphMix}, + author = {Marc Rennhard and Bernhard Plattner}, + booktitle = {Financial Cryptography}, + year = {2004}, + editor = {Ari Juels}, + publisher = {Springer-Verlag, LNCS (forthcoming)}, +} + +@inproceedings{eternity, + title = {The Eternity Service}, + author = {Ross Anderson}, + booktitle = {Pragocrypt '96}, + year = {1996}, +} + %note = {\url{http://www.cl.cam.ac.uk/users/rja14/eternity/eternity.html}}, + + +@inproceedings{minion-design, + title = {Mixminion: Design of a Type {III} Anonymous Remailer Protocol}, + author = {George Danezis and Roger Dingledine and Nick Mathewson}, + booktitle = {2003 IEEE Symposium on Security and Privacy}, + year = {2003}, + month = {May}, + publisher = {IEEE CS}, + pages = {2--15}, +} + %note = {\url{http://mixminion.net/minion-design.pdf}}, + +@inproceedings{ rao-pseudonymity, + author = "Josyula R. Rao and Pankaj Rohatgi", + title = "Can Pseudonymity Really Guarantee Privacy?", + booktitle = "Proceedings of the Ninth USENIX Security Symposium", + year = {2000}, + month = Aug, + publisher = {USENIX}, + pages = "85--96", +} + %note = {\url{http://www.usenix.org/publications/library/proceedings/sec2000/ +%full_papers/rao/rao.pdf}}, + +@InProceedings{pfitzmann90how, + author = "Birgit Pfitzmann and Andreas Pfitzmann", + title = "How to Break the Direct {RSA}-Implementation of {MIXes}", + booktitle = {Eurocrypt 89}, + publisher = {Springer-Verlag, LNCS 434}, + year = {1990}, + note = {\url{http://citeseer.nj.nec.com/pfitzmann90how.html}}, +} + +@Misc{tor-spec, + author = {Roger Dingledine and Nick Mathewson}, + title = {Tor Protocol Specifications}, + note = {\url{http://freehaven.net/tor/tor-spec.txt}}, +} + +@InProceedings{BM:mixencrypt, + author = {M{\"o}ller, Bodo}, + title = {Provably Secure Public-Key Encryption for Length-Preserving Chaumian Mixes}, + booktitle = {{CT-RSA} 2003}, + publisher = {Springer-Verlag, LNCS 2612}, + year = 2003, +} + +@InProceedings{back01, + author = {Adam Back and Ulf M\"oller and Anton Stiglic}, + title = {Traffic Analysis Attacks and Trade-Offs in Anonymity Providing Systems}, + booktitle = {Information Hiding (IH 2001)}, + pages = {245--257}, + year = 2001, + editor = {Ira S. Moskowitz}, + publisher = {Springer-Verlag, LNCS 2137}, +} + %note = {\newline \url{http://www.cypherspace.org/adam/pubs/traffic.pdf}}, + +@InProceedings{rackoff93cryptographic, + author = {Charles Rackoff and Daniel R. Simon}, + title = {Cryptographic Defense Against Traffic Analysis}, + booktitle = {{ACM} Symposium on Theory of Computing}, + pages = {672--681}, + year = {1993}, +} + %note = {\url{http://research.microsoft.com/crypto/dansimon/me.htm}}, + +@InProceedings{freehaven-berk, + author = {Roger Dingledine and Michael J. Freedman and David Molnar}, + title = {The Free Haven Project: Distributed Anonymous Storage Service}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + year = {2000}, + month = {July}, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, +} + %note = {\url{http://freehaven.net/papers.html}}, + +@InProceedings{raymond00, + author = {J. F. Raymond}, + title = {{Traffic Analysis: Protocols, Attacks, Design Issues, + and Open Problems}}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + year = 2000, + month = {July}, + pages = {10-29}, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, +} + +@InProceedings{sybil, + author = "John Douceur", + title = {{The Sybil Attack}}, + booktitle = "Proceedings of the 1st International Peer To Peer Systems Workshop (IPTPS)", + month = Mar, + year = 2002, +} + +@InProceedings{trickle02, + author = {Andrei Serjantov and Roger Dingledine and Paul Syverson}, + title = {From a Trickle to a Flood: Active Attacks on Several + Mix Types}, + booktitle = {Information Hiding (IH 2002)}, + year = {2002}, + editor = {Fabien Petitcolas}, + publisher = {Springer-Verlag, LNCS 2578}, +} + +@InProceedings{langos02, + author = {Oliver Berthold and Heinrich Langos}, + title = {Dummy Traffic Against Long Term Intersection Attacks}, + booktitle = {Privacy Enhancing Technologies (PET 2002)}, + year = {2002}, + editor = {Roger Dingledine and Paul Syverson}, + publisher = {Springer-Verlag, LNCS 2482} +} + + +@InProceedings{hintz-pet02, + author = {Andrew Hintz}, + title = {Fingerprinting Websites Using Traffic Analysis}, + booktitle = {Privacy Enhancing Technologies (PET 2002)}, + pages = {171--178}, + year = 2002, + editor = {Roger Dingledine and Paul Syverson}, + publisher = {Springer-Verlag, LNCS 2482} +} + +@InProceedings{or-discex00, + author = {Paul Syverson and Michael Reed and David Goldschlag}, + title = {{O}nion {R}outing Access Configurations}, + booktitle = {DARPA Information Survivability Conference and + Exposition (DISCEX 2000)}, + year = {2000}, + publisher = {IEEE CS Press}, + pages = {34--40}, + volume = {1}, +} + %note = {\newline \url{http://www.onion-router.net/Publications.html}}, + +@Inproceedings{or-pet00, + title = {{Towards an Analysis of Onion Routing Security}}, + author = {Paul Syverson and Gene Tsudik and Michael Reed and + Carl Landwehr}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + year = 2000, + month = {July}, + pages = {96--114}, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, +} + %note = {\url{http://www.onion-router.net/Publications/WDIAU-2000.ps.gz}}, + +@Inproceedings{freenet-pets00, + title = {Freenet: A Distributed Anonymous Information Storage + and Retrieval System}, + author = {Ian Clarke and Oskar Sandberg and Brandon Wiley and + Theodore W. Hong}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + year = 2000, + month = {July}, + pages = {46--66}, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, +} + %note = {\url{http://citeseer.nj.nec.com/clarke00freenet.html}}, + +@InProceedings{or-ih96, + author = {David M. Goldschlag and Michael G. Reed and Paul + F. Syverson}, + title = {Hiding Routing Information}, + booktitle = {Information Hiding, First International Workshop}, + pages = {137--150}, + year = 1996, + editor = {R. Anderson}, + month = {May}, + publisher = {Springer-Verlag, LNCS 1174}, +} + +@InProceedings{federrath-ih96, + author = {Hannes Federrath and Anja Jerichow and Andreas Pfitzmann}, + title = {{MIXes} in Mobile Communication Systems: Location + Management with Privacy}, + booktitle = {Information Hiding, First International Workshop}, + pages = {121--135}, + year = 1996, + editor = {R. Anderson}, + month = {May}, + publisher = {Springer-Verlag, LNCS 1174}, +} + + +@InProceedings{reed-protocols97, + author = {Michael G. Reed and Paul F. Syverson and David + M. Goldschlag}, + title = {Protocols Using Anonymous Connections: Mobile Applications}, + booktitle = {Security Protocols: 5th International Workshop}, + pages = {13--23}, + year = 1997, + editor = {Bruce Christianson and Bruno Crispo and Mark Lomas + and Michael Roe}, + month = {April}, + publisher = {Springer-Verlag, LNCS 1361} +} + + + +@Article{or-jsac98, + author = {Michael G. Reed and Paul F. Syverson and David + M. Goldschlag}, + title = {Anonymous Connections and Onion Routing}, + journal = {IEEE Journal on Selected Areas in Communications}, + year = 1998, + volume = 16, + number = 4, + pages = {482--494}, + month = {May}, +} + %note = {\url{http://www.onion-router.net/Publications/JSAC-1998.ps.gz}} + +@Misc{TLS, + author = {T. Dierks and C. Allen}, + title = {The {TLS} {P}rotocol --- {V}ersion 1.0}, + howpublished = {IETF RFC 2246}, + month = {January}, + year = {1999}, +} +%note = {\url{http://www.rfc-editor.org/rfc/rfc2246.txt}}, + +@Misc{SMTP, + author = {J. Postel}, + title = {Simple {M}ail {T}ransfer {P}rotocol}, + howpublished = {IETF RFC 2821 (also STD0010)}, + month = {April}, + year = {2001}, + note = {\url{http://www.rfc-editor.org/rfc/rfc2821.txt}}, +} + +@Misc{IMAP, + author = {M. Crispin}, + title = {Internet {M}essage {A}ccess {P}rotocol --- {V}ersion 4rev1}, + howpublished = {IETF RFC 2060}, + month = {December}, + year = {1996}, + note = {\url{http://www.rfc-editor.org/rfc/rfc2060.txt}}, +} + +@misc{pipenet, + title = {PipeNet 1.1}, + author = {Wei Dai}, + year = 1996, + month = {August}, + howpublished = {Usenet post}, + note = {\url{http://www.eskimo.com/~weidai/pipenet.txt} First mentioned + in a post to the cypherpunks list, Feb.\ 1995.}, +} + + +@Misc{POP3, + author = {J. Myers and M. Rose}, + title = {Post {O}ffice {P}rotocol --- {V}ersion 3}, + howpublished = {IETF RFC 1939 (also STD0053)}, + month = {May}, + year = {1996}, + note = {\url{http://www.rfc-editor.org/rfc/rfc1939.txt}}, +} + + +@InProceedings{shuffle, + author = {C. Andrew Neff}, + title = {A Verifiable Secret Shuffle and its Application to E-Voting}, + booktitle = {8th ACM Conference on Computer and Communications + Security (CCS-8)}, + pages = {116--125}, + year = 2001, + editor = {P. Samarati}, + month = {November}, + publisher = {ACM Press}, +} + %note = {\url{http://www.votehere.net/ada_compliant/ourtechnology/ + % technicaldocs/shuffle.pdf}}, + +@InProceedings{dolev91, + author = {Danny Dolev and Cynthia Dwork and Moni Naor}, + title = {Non-Malleable Cryptography}, + booktitle = {23rd ACM Symposium on the Theory of Computing (STOC)}, + pages = {542--552}, + year = 1991, + note = {Updated version at + \url{http://citeseer.nj.nec.com/dolev00nonmalleable.html}}, +} + +@TechReport{rsw96, + author = {Ronald L. Rivest and Adi Shamir and David A. Wagner}, + title = {Time-lock puzzles and timed-release Crypto}, + year = 1996, + type = {MIT LCS technical memo}, + number = {MIT/LCS/TR-684}, + month = {February}, + note = {\newline \url{http://citeseer.nj.nec.com/rivest96timelock.html}}, +} + +@InProceedings{web-mix, + author = {Oliver Berthold and Hannes Federrath and Stefan K\"opsell}, + title = {Web {MIX}es: A system for anonymous and unobservable + {I}nternet access}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, + year = {2000}, +} +% pages = {115--129}, + +@InProceedings{disad-free-routes, + author = {Oliver Berthold and Andreas Pfitzmann and Ronny Standtke}, + title = {The disadvantages of free {MIX} routes and how to overcome + them}, + booktitle = {Designing Privacy Enhancing Technologies: Workshop + on Design Issue in Anonymity and Unobservability}, + pages = {30--45}, + year = 2000, + editor = {H. Federrath}, + publisher = {Springer-Verlag, LNCS 2009}, +} + %note = {\url{http://www.tik.ee.ethz.ch/~weiler/lehre/netsec/Unterlagen/anon/ + % disadvantages_berthold.pdf}}, + +@InProceedings{boneh00, + author = {Dan Boneh and Moni Naor}, + title = {Timed Commitments}, + booktitle = {Advances in Cryptology -- {CRYPTO} 2000}, + pages = {236--254}, + year = 2000, + publisher = {Springer-Verlag, LNCS 1880}, + note = {\newline \url{http://crypto.stanford.edu/~dabo/abstracts/timedcommit.html}}, +} + +@InProceedings{goldschlag98, + author = {David M. Goldschlag and Stuart G. Stubblebine}, + title = {Publicly Verifiable Lotteries: Applications of + Delaying Functions}, + booktitle = {Financial Cryptography}, + pages = {214--226}, + year = 1998, + publisher = {Springer-Verlag, LNCS 1465}, + note = {\newline \url{http://citeseer.nj.nec.com/goldschlag98publicly.html}}, +} + +@InProceedings{syverson98, + author = {Paul Syverson}, + title = {Weakly Secret Bit Commitment: Applications to + Lotteries and Fair Exchange}, + booktitle = {Computer Security Foundations Workshop (CSFW11)}, + pages = {2--13}, + year = 1998, + address = {Rockport Massachusetts}, + month = {June}, + publisher = {IEEE CS Press}, + note = {\newline \url{http://chacs.nrl.navy.mil/publications/CHACS/1998/}}, +} + +@Misc{shoup-iso, + author = {Victor Shoup}, + title = {A Proposal for an {ISO} {S}tandard for Public Key Encryption (version 2.1)}, + note = {Revised December 20, 2001. \url{http://www.shoup.net/papers/}}, +} + +@Misc{shoup-oaep, + author = {Victor Shoup}, + title = {{OAEP} Reconsidered}, + howpublished = {{IACR} e-print 2000/060}, + note = {\newline \url{http://eprint.iacr.org/2000/060/}}, +} + +@Misc{oaep-still-alive, + author = {E. Fujisaki and D. Pointcheval and T. Okamoto and J. Stern}, + title = {{RSA}-{OAEP} is Still Alive!}, + howpublished = {{IACR} e-print 2000/061}, + note = {\newline \url{http://eprint.iacr.org/2000/061/}}, +} + +@misc{echolot, + author = {Peter Palfrader}, + title = {Echolot: a pinger for anonymous remailers}, + note = {\url{http://www.palfrader.org/echolot/}}, +} + +@Misc{mixmaster-attacks, + author = {Lance Cottrell}, + title = {Mixmaster and Remailer Attacks}, + note = {\url{http://www.obscura.com/~loki/remailer/remailer-essay.html}}, +} + +@Misc{mixmaster-spec, + author = {Ulf M{\"o}ller and Lance Cottrell and Peter + Palfrader and Len Sassaman}, + title = {Mixmaster {P}rotocol --- {V}ersion 2}, + year = {2003}, + month = {July}, + howpublished = {Draft}, + note = {\url{http://www.abditum.com/mixmaster-spec.txt}}, +} + +@InProceedings{puzzles-tls, + author = "Drew Dean and Adam Stubblefield", + title = {{Using Client Puzzles to Protect TLS}}, + booktitle = "Proceedings of the 10th USENIX Security Symposium", + year = {2001}, + month = Aug, + publisher = {USENIX}, +} + +@InProceedings{breadpudding, + author = {Markus Jakobsson and Ari Juels}, + title = {Proofs of Work and Bread Pudding Protocols}, + booktitle = {Proceedings of the IFIP TC6 and TC11 Joint Working + Conference on Communications and Multimedia Security + (CMS '99)}, + year = 1999, + month = {September}, + publisher = {Kluwer} +} + +@Misc{hashcash, + author = {Adam Back}, + title = {Hash cash}, + note = {\newline \url{http://www.cypherspace.org/~adam/hashcash/}}, +} + +@InProceedings{oreilly-acc, + author = {Roger Dingledine and Michael J. Freedman and David Molnar}, + title = {Accountability}, + booktitle = {Peer-to-peer: Harnessing the Benefits of a Disruptive + Technology}, + year = {2001}, + publisher = {O'Reilly and Associates}, +} + + +@InProceedings{han, + author = {Yongfei Han}, + title = {Investigation of non-repudiation protocols}, + booktitle = {ACISP '96}, + year = 1996, + publisher = {Springer-Verlag}, +} + + +@Misc{socks5, + key = {socks5}, + title = {{SOCKS} {P}rotocol {V}ersion 5}, + howpublished= {IETF RFC 1928}, + month = {March}, + year = 1996, + note = {\url{http://www.ietf.org/rfc/rfc1928.txt}} +} + +@InProceedings{abe, + author = {Masayuki Abe}, + title = {Universally Verifiable {MIX} With Verification Work Independent of + The Number of {MIX} Servers}, + booktitle = {{EUROCRYPT} 1998}, + year = {1998}, + publisher = {Springer-Verlag, LNCS 1403}, +} + +@InProceedings{desmedt, + author = {Yvo Desmedt and Kaoru Kurosawa}, + title = {How To Break a Practical {MIX} and Design a New One}, + booktitle = {{EUROCRYPT} 2000}, + year = {2000}, + publisher = {Springer-Verlag, LNCS 1803}, + note = {\url{http://citeseer.nj.nec.com/447709.html}}, +} + +@InProceedings{mitkuro, + author = {M. Mitomo and K. Kurosawa}, + title = {{Attack for Flash MIX}}, + booktitle = {{ASIACRYPT} 2000}, + year = {2000}, + publisher = {Springer-Verlag, LNCS 1976}, + note = {\newline \url{http://citeseer.nj.nec.com/450148.html}}, +} + +@InProceedings{hybrid-mix, + author = {M. Ohkubo and M. Abe}, + title = {A {L}ength-{I}nvariant {H}ybrid {MIX}}, + booktitle = {Advances in Cryptology - {ASIACRYPT} 2000}, + year = {2000}, + publisher = {Springer-Verlag, LNCS 1976}, +} + +@InProceedings{PShuffle, + author = {Jun Furukawa and Kazue Sako}, + title = {An Efficient Scheme for Proving a Shuffle}, + editor = {Joe Kilian}, + booktitle = {CRYPTO 2001}, + year = {2001}, + publisher = {Springer-Verlag, LNCS 2139}, +} + + +@InProceedings{jakobsson-optimally, + author = "Markus Jakobsson and Ari Juels", + title = "An Optimally Robust Hybrid Mix Network (Extended Abstract)", + booktitle = {Principles of Distributed Computing - {PODC} '01}, + year = "2001", + publisher = {ACM Press}, + note = {\url{http://citeseer.nj.nec.com/492015.html}}, +} + +@InProceedings{kesdogan, + author = {D. Kesdogan and M. Egner and T. B\"uschkes}, + title = {Stop-and-Go {MIX}es Providing Probabilistic Anonymity in an Open + System}, + booktitle = {Information Hiding (IH 1998)}, + year = {1998}, + publisher = {Springer-Verlag, LNCS 1525}, +} + %note = {\url{http://www.cl.cam.ac.uk/~fapp2/ihw98/ihw98-sgmix.pdf}}, + +@InProceedings{socks4, + author = {David Koblas and Michelle R. Koblas}, + title = {{SOCKS}}, + booktitle = {UNIX Security III Symposium (1992 USENIX Security + Symposium)}, + pages = {77--83}, + year = 1992, + publisher = {USENIX}, +} + +@InProceedings{flash-mix, + author = {Markus Jakobsson}, + title = {Flash {M}ixing}, + booktitle = {Principles of Distributed Computing - {PODC} '99}, + year = {1999}, + publisher = {ACM Press}, + note = {\newline \url{http://citeseer.nj.nec.com/jakobsson99flash.html}}, +} + +@InProceedings{SK, + author = {Joe Kilian and Kazue Sako}, + title = {Receipt-Free {MIX}-Type Voting Scheme - A Practical Solution to + the Implementation of a Voting Booth}, + booktitle = {EUROCRYPT '95}, + year = {1995}, + publisher = {Springer-Verlag}, +} + +@InProceedings{OAEP, + author = {M. Bellare and P. Rogaway}, + year = {1994}, + booktitle = {EUROCRYPT '94}, + title = {Optimal {A}symmetric {E}ncryption {P}adding : How To Encrypt With + {RSA}}, + publisher = {Springer-Verlag}, + note = {\newline \url{http://www-cse.ucsd.edu/users/mihir/papers/oaep.html}}, +} +@inproceedings{babel, + title = {Mixing {E}-mail With {B}abel}, + author = {Ceki G\"ulc\"u and Gene Tsudik}, + booktitle = {{Network and Distributed Security Symposium (NDSS 96)}}, + year = 1996, + month = {February}, + pages = {2--16}, + publisher = {IEEE}, +} + %note = {\url{http://citeseer.nj.nec.com/2254.html}}, + +@Misc{rprocess, + author = {RProcess}, + title = {Selective Denial of Service Attacks}, + note = {\newline \url{http://www.eff.org/pub/Privacy/Anonymity/1999\_09\_DoS\_remail\_vuln.html}}, +} + +@Article{remailer-history, + author = {Sameer Parekh}, + title = {Prospects for Remailers}, + journal = {First Monday}, + volume = {1}, + number = {2}, + month = {August}, + year = {1996}, + note = {\url{http://www.firstmonday.dk/issues/issue2/remailers/}}, +} + +@Article{chaum-mix, + author = {David Chaum}, + title = {Untraceable electronic mail, return addresses, and digital pseudo-nyms}, + journal = {Communications of the ACM}, + year = {1981}, + volume = {4}, + number = {2}, + month = {February}, +} + %note = {\url{http://www.eskimo.com/~weidai/mix-net.txt}}, + +@InProceedings{nym-alias-net, + author = {David Mazi\`{e}res and M. Frans Kaashoek}, + title = {{The Design, Implementation and Operation of an Email + Pseudonym Server}}, + booktitle = {$5^{th}$ ACM Conference on Computer and + Communications Security (CCS'98)}, + year = 1998, + publisher = {ACM Press}, +} + %note = {\newline \url{http://www.scs.cs.nyu.edu/~dm/}}, + +@InProceedings{tangler, + author = {Marc Waldman and David Mazi\`{e}res}, + title = {Tangler: A Censorship-Resistant Publishing System + Based on Document Entanglements}, + booktitle = {$8^{th}$ ACM Conference on Computer and + Communications Security (CCS-8)}, + pages = {86--135}, + year = 2001, + publisher = {ACM Press}, +} + %note = {\url{http://www.scs.cs.nyu.edu/~dm/}} + +@misc{neochaum, + author = {Tim May}, + title = {Payment mixes for anonymity}, + howpublished = {E-mail archived at + \url{http://\newline www.inet-one.com/cypherpunks/dir.2000.02.28-2000.03.05/msg00334.html}}, +} + +@misc{helsingius, + author = {J. Helsingius}, + title = {{\tt anon.penet.fi} press release}, + note = {\newline \url{http://www.penet.fi/press-english.html}}, +} + +@InProceedings{garay97secure, + author = {J. Garay and R. Gennaro and C. Jutla and T. Rabin}, + title = {Secure distributed storage and retrieval}, + booktitle = {11th International Workshop, WDAG '97}, + pages = {275--289}, + year = {1997}, + publisher = {Springer-Verlag, LNCS 1320}, + note = {\newline \url{http://citeseer.nj.nec.com/garay97secure.html}}, +} + +@InProceedings{PIK, + author = {C. Park and K. Itoh and K. Kurosawa}, + title = {Efficient anonymous channel and all/nothing election scheme}, + booktitle = {Advances in Cryptology -- {EUROCRYPT} '93}, + pages = {248--259}, + publisher = {Springer-Verlag, LNCS 765}, +} + +@Misc{pgpfaq, + key = {PGP}, + title = {{PGP} {FAQ}}, + note = {\newline \url{http://www.faqs.org/faqs/pgp-faq/}}, +} + +@Article{riordan-schneier, + author = {James Riordan and Bruce Schneier}, + title = {A Certified E-mail Protocol with No Trusted Third Party}, + journal = {13th Annual Computer Security Applications Conference}, + month = {December}, + year = {1998}, + note = {\newline \url{http://www.counterpane.com/certified-email.html}}, +} + + +@Article{crowds-tissec, + author = {Michael K. Reiter and Aviel D. Rubin}, + title = {Crowds: Anonymity for Web Transactions}, + journal = {ACM TISSEC}, + year = 1998, + volume = 1, + number = 1, + pages = {66--92}, + month = {June}, +} + %note = {\url{http://citeseer.nj.nec.com/284739.html}} + +@Article{crowds-dimacs, + author = {Michael K. Reiter and Aviel D. Rubin}, + title = {Crowds: Anonymity for Web Transactions}, + journal = {{DIMACS} Technical Report (Revised)}, + volume = {97}, + number = {15}, + month = {August}, + year = {1997}, +} + +@Misc{advogato, + author = {Raph Levien}, + title = {Advogato's Trust Metric}, + note = {\newline \url{http://www.advogato.org/trust-metric.html}}, +} + +@InProceedings{publius, + author = {Marc Waldman and Aviel Rubin and Lorrie Cranor}, + title = {Publius: {A} robust, tamper-evident, censorship-resistant and + source-anonymous web publishing system}, + booktitle = {Proc. 9th USENIX Security Symposium}, + pages = {59--72}, + year = {2000}, + month = {August}, +} + %note = {\newline \url{http://citeseer.nj.nec.com/waldman00publius.html}}, + +@Misc{freedom-nyms, + author = {Russell Samuels}, + title = {Untraceable Nym Creation on the {F}reedom {N}etwork}, + year = {1999}, + month = {November}, + day = {21}, + note = {\newline \url{http://www.freedom.net/products/whitepapers/white11.html}}, +} + +@techreport{freedom2-arch, + title = {Freedom Systems 2.0 Architecture}, + author = {Philippe Boucher and Adam Shostack and Ian Goldberg}, + institution = {Zero Knowledge Systems, {Inc.}}, + year = {2000}, + month = {December}, + type = {White Paper}, + day = {18}, +} + +@techreport{freedom21-security, + title = {Freedom Systems 2.1 Security Issues and Analysis}, + author = {Adam Back and Ian Goldberg and Adam Shostack}, + institution = {Zero Knowledge Systems, {Inc.}}, + year = {2001}, + month = {May}, + type = {White Paper}, +} + +@inproceedings{cfs:sosp01, + title = {Wide-area cooperative storage with {CFS}}, + author = {Frank Dabek and M. Frans Kaashoek and David Karger and Robert Morris and Ion Stoica}, + booktitle = {18th {ACM} {S}ymposium on {O}perating {S}ystems {P}rinciples ({SOSP} '01)}, + year = {2001}, + month = {October}, + address = {Chateau Lake Louise, Banff, Canada}, +} + +@inproceedings{SS03, + title = {Passive Attack Analysis for Connection-Based Anonymity Systems}, + author = {Andrei Serjantov and Peter Sewell}, + booktitle = {Computer Security -- ESORICS 2003}, + publisher = {Springer-Verlag, LNCS 2808}, + year = {2003}, + month = {October}, +} + %note = {\url{http://www.cl.cam.ac.uk/users/aas23/papers_aas/conn_sys.ps}}, + +@Misc{pk-relations, + author = {M. Bellare and A. Desai and D. Pointcheval and P. Rogaway}, + title = {Relations Among Notions of Security for Public-Key Encryption + Schemes}, + howpublished = { + Extended abstract in {\em Advances in Cryptology - CRYPTO '98}, LNCS Vol. 1462. + Springer-Verlag, 1998. + Full version available from \newline \url{http://www-cse.ucsd.edu/users/mihir/}}, +} + + +@InProceedings{mix-acc, + author = {Roger Dingledine and Michael J. Freedman and David + Hopwood and David Molnar}, + title = {{A Reputation System to Increase MIX-net + Reliability}}, + booktitle = {Information Hiding (IH 2001)}, + pages = {126--141}, + year = 2001, + editor = {Ira S. Moskowitz}, + publisher = {Springer-Verlag, LNCS 2137}, +} + %note = {\url{http://www.freehaven.net/papers.html}}, + +@InProceedings{casc-rep, + author = {Roger Dingledine and Paul Syverson}, + title = {{Reliable MIX Cascade Networks through Reputation}}, + booktitle = {Financial Cryptography}, + year = 2002, + editor = {Matt Blaze}, + publisher = {Springer-Verlag, LNCS 2357}, +} + %note = {\newline \url{http://www.freehaven.net/papers.html}}, + +@InProceedings{zhou96certified, + author = {Zhou and Gollmann}, + title = {Certified Electronic Mail}, + booktitle = {{ESORICS: European Symposium on Research in Computer + Security}}, + publisher = {Springer-Verlag, LNCS 1146}, + year = {1996}, + note = {\newline \url{http://citeseer.nj.nec.com/zhou96certified.html}}, +} + +@Misc{realtime-mix, + author = {Anja Jerichow and Jan M\"uller and Andreas Pfitzmann and + Birgit Pfitzmann and Michael Waidner}, + title = {{Real-Time MIXes: A Bandwidth-Efficient Anonymity Protocol}}, + howpublished = {IEEE Journal on Selected Areas in Communications, 1998.}, + note = {\url{http://www.zurich.ibm.com/security/publications/1998.html}}, +} + +@InProceedings{danezis-pets03, + author = {George Danezis}, + title = {Mix-networks with Restricted Routes}, + booktitle = {Privacy Enhancing Technologies (PET 2003)}, + year = 2003, + editor = {Roger Dingledine}, + publisher = {Springer-Verlag LNCS 2760} +} + +@InProceedings{gap-pets03, + author = {Krista Bennett and Christian Grothoff}, + title = {{GAP} -- practical anonymous networking}, + booktitle = {Privacy Enhancing Technologies (PET 2003)}, + year = 2003, + editor = {Roger Dingledine}, + publisher = {Springer-Verlag LNCS 2760} +} + +@Article{hordes-jcs, + author = {Brian Neal Levine and Clay Shields}, + title = {Hordes: A Multicast-Based Protocol for Anonymity}, + journal = {Journal of Computer Security}, + year = 2002, + volume = 10, + number = 3, + pages = {213--240} +} + +@TechReport{herbivore, + author = {Sharad Goel and Mark Robson and Milo Polte and Emin G\"{u}n Sirer}, + title = {Herbivore: A Scalable and Efficient Protocol for Anonymous Communication}, + institution = {Cornell University Computing and Information Science}, + year = 2003, + type = {Technical Report}, + number = {TR2003-1890}, + month = {February} +} + +@InProceedings{p5, + author = {Rob Sherwood and Bobby Bhattacharjee and Aravind Srinivasan}, + title = {$P^5$: A Protocol for Scalable Anonymous Communication}, + booktitle = {IEEE Symposium on Security and Privacy}, + pages = {58--70}, + year = 2002, + publisher = {IEEE CS} +} + +@phdthesis{ian-thesis, + title = {A Pseudonymous Communications Infrastructure for the Internet}, + author = {Ian Goldberg}, + school = {UC Berkeley}, + year = {2000}, + month = {Dec}, +} + +@Article{taz, + author = {Ian Goldberg and David Wagner}, + title = {TAZ Servers and the Rewebber Network: Enabling + Anonymous Publishing on the World Wide Web}, + journal = {First Monday}, + year = 1998, + volume = 3, + number = 4, + month = {August}, + note = {\url{http://www.firstmonday.dk/issues/issue3_4/goldberg/}} +} + +@Misc{tcp-over-tcp-is-bad, + key = {tcp-over-tcp-is-bad}, + title = {Why {TCP} Over {TCP} Is A Bad Idea}, + author = {Olaf Titz}, + note = {\url{http://sites.inka.de/sites/bigred/devel/tcp-tcp.html}} +} + +@inproceedings{wright02, + title = {An Analysis of the Degradation of Anonymous Protocols}, + author = {Matthew Wright and Micah Adler and Brian Neil Levine and Clay Shields}, + booktitle = {{Network and Distributed Security Symposium (NDSS 02)}}, + year = {2002}, + month = {February}, + publisher = {IEEE}, +} + +@inproceedings{wright03, + title = {Defending Anonymous Communication Against Passive Logging Attacks}, + author = {Matthew Wright and Micah Adler and Brian Neil Levine and Clay Shields}, + booktitle = {IEEE Symposium on Security and Privacy}, + pages= {28--41}, + year = {2003}, + month = {May}, + publisher = {IEEE CS}, +} + +@Misc{jap-backdoor, + author={{The AN.ON Project}}, + howpublished={Press release}, + year={2003}, + month={September}, + title={German Police proceeds against anonymity service}, + note={\url{http://www.datenschutzzentrum.de/material/themen/presse/anon-bka_e.htm}} +} + +@article{shsm03, + title = {Using Caching for Browsing Anonymity}, + author = {Anna Shubina and Sean Smith}, + journal = {ACM SIGEcom Exchanges}, + volume = {4}, + number = {2}, + year = {2003}, + month = {Sept}, + www_pdf_url = {http://www.acm.org/sigs/sigecom/exchanges/volume_4_(03)/4.2-Shubina.pdf}, + www_section = {Anonymous communication}, +} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "tor-design" +%%% End: diff --git a/tags/tor-0_0_8pre1/doc/tor-design.html b/tags/tor-0_0_8pre1/doc/tor-design.html new file mode 100644 index 0000000000..a02731f174 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-design.html @@ -0,0 +1,2486 @@ + + + + + + + + + Tor: The Second-Generation Onion Router + + + +

Tor: The Second-Generation Onion Router

+
+ +

+Roger Dingledine, The Free Haven Project, arma@freehaven.net
+Nick Mathewson, The Free Haven Project, nickm@freehaven.net
+Paul Syverson, Naval Research Lab, syverson@itd.nrl.navy.mil

+ +
+ +
+ +

Abstract

+We present Tor, a circuit-based low-latency anonymous communication +service. This second-generation Onion Routing system addresses limitations +in the original design by adding perfect forward secrecy, congestion +control, directory servers, integrity checking, configurable exit policies, +and a practical design for location-hidden services via rendezvous +points. Tor works on the real-world +Internet, requires no special privileges or kernel modifications, requires +little synchronization or coordination between nodes, and provides a +reasonable tradeoff between anonymity, usability, and efficiency. +We briefly describe our experiences with an international network of +more than 30 nodes. We close with a list of open problems in anonymous communication. + +
+ +
+ +
+

+1  Overview

+ + + +
+Onion Routing is a distributed overlay network designed to anonymize +TCP-based applications like web browsing, secure shell, +and instant messaging. Clients choose a path through the network and +build a circuit, in which each node (or "onion router" or "OR") +in the path knows its predecessor and successor, but no other nodes in +the circuit. Traffic flows down the circuit in fixed-size +cells, which are unwrapped by a symmetric key at each node +(like the layers of an onion) and relayed downstream. The +Onion Routing project published several design and analysis +papers [27,41,48,49]. While a wide area Onion +Routing network was deployed briefly, the only long-running +public implementation was a fragile +proof-of-concept that ran on a single machine. Even this simple deployment +processed connections from over sixty thousand distinct IP addresses from +all over the world at a rate of about fifty thousand per day. +But many critical design and deployment issues were never +resolved, and the design has not been updated in years. Here +we describe Tor, a protocol for asynchronous, loosely federated onion +routers that provides the following improvements over the old Onion +Routing design: + +
+Perfect forward secrecy: In the original Onion Routing design, +a single hostile node could record traffic and +later compromise successive nodes in the circuit and force them +to decrypt it. Rather than using a single multiply encrypted data +structure (an onion) to lay each circuit, +Tor now uses an incremental or telescoping path-building design, +where the initiator negotiates session keys with each successive hop in +the circuit. Once these keys are deleted, subsequently compromised nodes +cannot decrypt old traffic. As a side benefit, onion replay detection +is no longer necessary, and the process of building circuits is more +reliable, since the initiator knows when a hop fails and can then try +extending to a new node. + +
+Separation of "protocol cleaning" from anonymity: +Onion Routing originally required a separate "application +proxy" for each supported application protocol-most of which were +never written, so many applications were never supported. Tor uses the +standard and near-ubiquitous SOCKS [32] proxy interface, allowing +us to support most TCP-based programs without modification. Tor now +relies on the filtering features of privacy-enhancing +application-level proxies such as Privoxy [39], without trying +to duplicate those features itself. + +
+No mixing, padding, or traffic shaping (yet): Onion +Routing originally called for batching and reordering cells as they arrived, +assumed padding between ORs, and in +later designs added padding between onion proxies (users) and +ORs [27,41]. Tradeoffs between padding protection +and cost were discussed, and traffic shaping algorithms were +theorized [49] to provide good security without expensive +padding, but no concrete padding scheme was suggested. +Recent research [1] +and deployment experience [4] suggest that this +level of resource use is not practical or economical; and even full +link padding is still vulnerable [33]. Thus, +until we have a proven and convenient design for traffic shaping or +low-latency mixing that improves anonymity against a realistic +adversary, we leave these strategies out. + +
+Many TCP streams can share one circuit: Onion Routing originally +built a separate circuit for each +application-level request, but this required +multiple public key operations for every request, and also presented +a threat to anonymity from building so many circuits; see +Section 9. Tor multiplexes multiple TCP +streams along each circuit to improve efficiency and anonymity. + +
+Leaky-pipe circuit topology: Through in-band signaling +within the circuit, Tor initiators can direct traffic to nodes partway +down the circuit. This novel approach +allows traffic to exit the circuit from the middle-possibly +frustrating traffic shape and volume attacks based on observing the end +of the circuit. (It also allows for long-range padding if +future research shows this to be worthwhile.) + +
+Congestion control: Earlier anonymity designs do not +address traffic bottlenecks. Unfortunately, typical approaches to +load balancing and flow control in overlay networks involve inter-node +control communication and global views of traffic. Tor's decentralized +congestion control uses end-to-end acks to maintain anonymity +while allowing nodes at the edges of the network to detect congestion +or flooding and send less data until the congestion subsides. + +
+Directory servers: The earlier Onion Routing design +planned to flood state information through the network-an approach +that can be unreliable and complex. Tor takes a simplified view toward distributing this +information. Certain more trusted nodes act as directory +servers: they provide signed directories describing known +routers and their current state. Users periodically download them +via HTTP. + +
+Variable exit policies: Tor provides a consistent mechanism +for each node to advertise a policy describing the hosts +and ports to which it will connect. These exit policies are critical +in a volunteer-based distributed infrastructure, because each operator +is comfortable with allowing different types of traffic to exit +from his node. + +
+End-to-end integrity checking: The original Onion Routing +design did no integrity checking on data. Any node on the +circuit could change the contents of data cells as they passed by-for +example, to alter a connection request so it would connect +to a different webserver, or to `tag' encrypted traffic and look for +corresponding corrupted traffic at the network edges [15]. +Tor hampers these attacks by verifying data integrity before it leaves +the network. + +
+ +
+Rendezvous points and hidden services: +Tor provides an integrated mechanism for responder anonymity via +location-protected servers. Previous Onion Routing designs included +long-lived "reply onions" that could be used to build circuits +to a hidden server, but these reply onions did not provide forward +security, and became useless if any node in the path went down +or rotated its keys. In Tor, clients negotiate rendezvous points +to connect with hidden servers; reply onions are no longer required. + +
+Unlike Freedom [8], Tor does not require OS kernel +patches or network stack support. This prevents us from anonymizing +non-TCP protocols, but has greatly helped our portability and +deployability. + +
+ +
+We have implemented all of the above features, including rendezvous +points. Our source code is +available under a free license, and Tor +is not covered by the patent that affected distribution and use of +earlier versions of Onion Routing. +We have deployed a wide-area alpha network +to test the design, to get more experience with usability +and users, and to provide a research platform for experimentation. +As of this writing, the network stands at 32 nodes spread over two continents. + +
+We review previous work in Section 2, describe +our goals and assumptions in Section 3, +and then address the above list of improvements in +Sections 45, and 6. +We summarize +in Section 7 how our design stands up to +known attacks, and talk about our early deployment experiences in +Section 8. We conclude with a list of open problems in +Section 9 and future work for the Onion +Routing project in Section 10. + +
+ +
+

+2  Related work

+ + + +
+Modern anonymity systems date to Chaum's Mix-Net +design [10]. Chaum +proposed hiding the correspondence between sender and recipient by +wrapping messages in layers of public-key cryptography, and relaying them +through a path composed of "mixes." Each mix in turn +decrypts, delays, and re-orders messages before relaying them +onward. + +
+Subsequent relay-based anonymity designs have diverged in two +main directions. Systems like Babel [28], +Mixmaster [36], +and Mixminion [15] have tried +to maximize anonymity at the cost of introducing comparatively large and +variable latencies. Because of this decision, these high-latency +networks resist strong global adversaries, +but introduce too much lag for interactive tasks like web browsing, +Internet chat, or SSH connections. + +
+Tor belongs to the second category: low-latency designs that +try to anonymize interactive network traffic. These systems handle +a variety of bidirectional protocols. They also provide more convenient +mail delivery than the high-latency anonymous email +networks, because the remote mail server provides explicit and timely +delivery confirmation. But because these designs typically +involve many packets that must be delivered quickly, it is +difficult for them to prevent an attacker who can eavesdrop both ends of the +communication from correlating the timing and volume +of traffic entering the anonymity network with traffic leaving it [45]. +These +protocols are similarly vulnerable to an active adversary who introduces +timing patterns into traffic entering the network and looks +for correlated patterns among exiting traffic. +Although some work has been done to frustrate these attacks, most designs +protect primarily against traffic analysis rather than traffic +confirmation (see Section 3.1). + +
+The simplest low-latency designs are single-hop proxies such as the +Anonymizer [3]: a single trusted server strips the +data's origin before relaying it. These designs are easy to +analyze, but users must trust the anonymizing proxy. +Concentrating the traffic to this single point increases the anonymity set +(the people a given user is hiding among), but it is vulnerable if the +adversary can observe all traffic entering and leaving the proxy. + +
+More complex are distributed-trust, circuit-based anonymizing systems. +In these designs, a user establishes one or more medium-term bidirectional +end-to-end circuits, and tunnels data in fixed-size cells. +Establishing circuits is computationally expensive and typically +requires public-key +cryptography, whereas relaying cells is comparatively inexpensive and +typically requires only symmetric encryption. +Because a circuit crosses several servers, and each server only knows +the adjacent servers in the circuit, no single server can link a +user to her communication partners. + +
+The Java Anon Proxy (also known as JAP or Web MIXes) uses fixed shared +routes known as cascades. As with a single-hop proxy, this +approach aggregates users into larger anonymity sets, but again an +attacker only needs to observe both ends of the cascade to bridge all +the system's traffic. The Java Anon Proxy's design +calls for padding between end users and the head of the +cascade [7]. However, it is not demonstrated whether the current +implementation's padding policy improves anonymity. + +
+PipeNet [5,12], another low-latency design proposed +around the same time as Onion Routing, gave +stronger anonymity but allowed a single user to shut +down the network by not sending. Systems like ISDN +mixes [38] were designed for other environments with +different assumptions. + +
+In P2P designs like Tarzan [24] and +MorphMix [43], all participants both generate +traffic and relay traffic for others. These systems aim to conceal +whether a given peer originated a request +or just relayed it from another peer. While Tarzan and MorphMix use +layered encryption as above, Crowds [42] simply assumes +an adversary who cannot observe the initiator: it uses no public-key +encryption, so any node on a circuit can read users' traffic. + +
+Hordes [34] is based on Crowds but also uses multicast +responses to hide the initiator. Herbivore [25] and +P5 [46] go even further, requiring broadcast. +These systems are designed primarily for communication among peers, +although Herbivore users can make external connections by +requesting a peer to serve as a proxy. + +
+Systems like Freedom and the original Onion Routing build circuits +all at once, using a layered "onion" of public-key encrypted messages, +each layer of which provides session keys and the address of the +next server in the circuit. Tor as described herein, Tarzan, MorphMix, +Cebolla [9], and Rennhard's Anonymity Network [44] +build circuits +in stages, extending them one hop at a time. +Section 4.2 describes how this +approach enables perfect forward secrecy. + +
+Circuit-based designs must choose which protocol layer +to anonymize. They may intercept IP packets directly, and +relay them whole (stripping the source address) along the +circuit [8,24]. Like +Tor, they may accept TCP streams and relay the data in those streams, +ignoring the breakdown of that data into TCP +segments [43,44]. Finally, like Crowds, they may accept +application-level protocols such as HTTP and relay the application +requests themselves. +Making this protocol-layer decision requires a compromise between flexibility +and anonymity. For example, a system that understands HTTP +can strip +identifying information from requests, can take advantage of caching +to limit the number of requests that leave the network, and can batch +or encode requests to minimize the number of connections. +On the other hand, an IP-level anonymizer can handle nearly any protocol, +even ones unforeseen by its designers (though these systems require +kernel-level modifications to some operating systems, and so are more +complex and less portable). TCP-level anonymity networks like Tor present +a middle approach: they are application neutral (so long as the +application supports, or can be tunneled across, TCP), but by treating +application connections as data streams rather than raw TCP packets, +they avoid the inefficiencies of tunneling TCP over +TCP. + +
+Distributed-trust anonymizing systems need to prevent attackers from +adding too many servers and thus compromising user paths. +Tor relies on a small set of well-known directory servers, run by +independent parties, to decide which nodes can +join. Tarzan and MorphMix allow unknown users to run servers, and use +a limited resource (like IP addresses) to prevent an attacker from +controlling too much of the network. Crowds suggests requiring +written, notarized requests from potential crowd members. + +
+Anonymous communication is essential for censorship-resistant +systems like Eternity [2], Free Haven [19], +Publius [53], and Tangler [52]. Tor's rendezvous +points enable connections between mutually anonymous entities; they +are a building block for location-hidden servers, which are needed by +Eternity and Free Haven. + +
+ +
+

+3  Design goals and assumptions

+ + + +
+Goals
+Like other low-latency anonymity designs, Tor seeks to frustrate +attackers from linking communication partners, or from linking +multiple communications to or from a single user. Within this +main goal, however, several considerations have directed +Tor's evolution. + +
+Deployability: The design must be deployed and used in the +real world. Thus it +must not be expensive to run (for example, by requiring more bandwidth +than volunteers are willing to provide); must not place a heavy +liability burden on operators (for example, by allowing attackers to +implicate onion routers in illegal activities); and must not be +difficult or expensive to implement (for example, by requiring kernel +patches, or separate proxies for every protocol). We also cannot +require non-anonymous parties (such as websites) +to run our software. (Our rendezvous point design does not meet +this goal for non-anonymous users talking to hidden servers, +however; see Section 5.) + +
+Usability: A hard-to-use system has fewer users-and because +anonymity systems hide users among users, a system with fewer users +provides less anonymity. Usability is thus not only a convenience: +it is a security requirement [1,5]. Tor should +therefore not +require modifying familiar applications; should not introduce prohibitive +delays; +and should require as few configuration decisions +as possible. Finally, Tor should be easily implementable on all common +platforms; we cannot require users to change their operating system +to be anonymous. (Tor currently runs on Win32, Linux, +Solaris, BSD-style Unix, MacOS X, and probably others.) + +
+Flexibility: The protocol must be flexible and well-specified, +so Tor can serve as a test-bed for future research. +Many of the open problems in low-latency anonymity +networks, such as generating dummy traffic or preventing Sybil +attacks [22], may be solvable independently from the issues +solved by +Tor. Hopefully future systems will not need to reinvent Tor's design. + +
+Simple design: The protocol's design and security +parameters must be well-understood. Additional features impose implementation +and complexity costs; adding unproven techniques to the design threatens +deployability, readability, and ease of security analysis. Tor aims to +deploy a simple and stable system that integrates the best accepted +approaches to protecting anonymity.
+ +
+Non-goals +
+In favoring simple, deployable designs, we have explicitly deferred +several possible goals, either because they are solved elsewhere, or because +they are not yet solved. + +
+Not peer-to-peer: Tarzan and MorphMix aim to scale to completely +decentralized peer-to-peer environments with thousands of short-lived +servers, many of which may be controlled by an adversary. This approach +is appealing, but still has many open +problems [24,43]. + +
+Not secure against end-to-end attacks: Tor does not claim +to completely solve end-to-end timing or intersection +attacks. Some approaches, such as having users run their own onion routers, +may help; +see Section 9 for more discussion. + +
+No protocol normalization: Tor does not provide protocol +normalization like Privoxy or the Anonymizer. If senders want anonymity from +responders while using complex and variable +protocols like HTTP, Tor must be layered with a filtering proxy such +as Privoxy to hide differences between clients, and expunge protocol +features that leak identity. +Note that by this separation Tor can also provide services that +are anonymous to the network yet authenticated to the responder, like +SSH. Similarly, Tor does not integrate +tunneling for non-stream-based protocols like UDP; this must be +provided by an external service if appropriate. + +
+Not steganographic: Tor does not try to conceal who is connected +to the network. + +
+

+3.1  Threat Model

+ + + +
+A global passive adversary is the most commonly assumed threat when +analyzing theoretical anonymity designs. But like all practical +low-latency systems, Tor does not protect against such a strong +adversary. Instead, we assume an adversary who can observe some fraction +of network traffic; who can generate, modify, delete, or delay +traffic; who can operate onion routers of his own; and who can +compromise some fraction of the onion routers. + +
+In low-latency anonymity systems that use layered encryption, the +adversary's typical goal is to observe both the initiator and the +responder. By observing both ends, passive attackers can confirm a +suspicion that Alice is +talking to Bob if the timing and volume patterns of the traffic on the +connection are distinct enough; active attackers can induce timing +signatures on the traffic to force distinct patterns. Rather +than focusing on these traffic confirmation attacks, +we aim to prevent traffic +analysis attacks, where the adversary uses traffic patterns to learn +which points in the network he should attack. + +
+Our adversary might try to link an initiator Alice with her +communication partners, or try to build a profile of Alice's +behavior. He might mount passive attacks by observing the network edges +and correlating traffic entering and leaving the network-by +relationships in packet timing, volume, or externally visible +user-selected +options. The adversary can also mount active attacks by compromising +routers or keys; by replaying traffic; by selectively denying service +to trustworthy routers to move users to +compromised routers, or denying service to users to see if traffic +elsewhere in the +network stops; or by introducing patterns into traffic that can later be +detected. The adversary might subvert the directory servers to give users +differing views of network state. Additionally, he can try to decrease +the network's reliability by attacking nodes or by performing antisocial +activities from reliable nodes and trying to get them taken down-making +the network unreliable flushes users to other less anonymous +systems, where they may be easier to attack. We summarize +in Section 7 how well the Tor design defends against +each of these attacks. + +
+ +
+

+4  The Tor Design

+ + + +
+The Tor network is an overlay network; each onion router (OR) +runs as a normal +user-level process without any special privileges. +Each onion router maintains a TLS [17] +connection to every other onion router. +Each user +runs local software called an onion proxy (OP) to fetch directories, +establish circuits across the network, +and handle connections from user applications. These onion proxies accept +TCP streams and multiplex them across the circuits. The onion +router on the other side +of the circuit connects to the requested destinations +and relays data. + +
+Each onion router maintains a long-term identity key and a short-term +onion key. The identity +key is used to sign TLS certificates, to sign the OR's router +descriptor (a summary of its keys, address, bandwidth, exit policy, +and so on), and (by directory servers) to sign directories. The onion key is used to decrypt requests +from users to set up a circuit and negotiate ephemeral keys. +The TLS protocol also establishes a short-term link key when communicating +between ORs. Short-term keys are rotated periodically and +independently, to limit the impact of key compromise. + +
+Section 4.1 presents the fixed-size +cells that are the unit of communication in Tor. We describe +in Section 4.2 how circuits are +built, extended, truncated, and destroyed. Section 4.3 +describes how TCP streams are routed through the network. We address +integrity checking in Section 4.4, +and resource limiting in Section 4.5. +Finally, +Section 4.6 talks about congestion control and +fairness issues. + +
+

+4.1  Cells

+ + + +
+Onion routers communicate with one another, and with users' OPs, via +TLS connections with ephemeral keys. Using TLS conceals the data on +the connection with perfect forward secrecy, and prevents an attacker +from modifying data on the wire or impersonating an OR. + +
+Traffic passes along these connections in fixed-size cells. Each cell +is 512 bytes, and consists of a header and a payload. The header includes a circuit +identifier (circID) that specifies which circuit the cell refers to +(many circuits can be multiplexed over the single TLS connection), and +a command to describe what to do with the cell's payload. (Circuit +identifiers are connection-specific: each circuit has a different +circID on each OP/OR or OR/OR connection it traverses.) +Based on their command, cells are either control cells, which are +always interpreted by the node that receives them, or relay cells, +which carry end-to-end stream data. The control cell commands are: +padding (currently used for keepalive, but also usable for link +padding); create or created (used to set up a new circuit); +and destroy (to tear down a circuit). + +
+Relay cells have an additional header (the relay header) at the front +of the payload, containing a streamID (stream identifier: many streams can +be multiplexed over a circuit); an end-to-end checksum for integrity +checking; the length of the relay payload; and a relay command. +The entire contents of the relay header and the relay cell payload +are encrypted or decrypted together as the relay cell moves along the +circuit, using the 128-bit AES cipher in counter mode to generate a +cipher stream. The relay commands are: relay +data (for data flowing down the stream), relay begin (to open a +stream), relay end (to close a stream cleanly), relay +teardown (to close a broken stream), relay connected +(to notify the OP that a relay begin has succeeded), relay +extend and relay extended (to extend the circuit by a hop, +and to acknowledge), relay truncate and relay truncated +(to tear down only part of the circuit, and to acknowledge), relay +sendme (used for congestion control), and relay drop (used to +implement long-range dummies). +We give a visual overview of cell structure plus the details of relay +cell structure, and then describe each of these cell types and commands +in more detail below. + +
+ +
+ +
+ +
cell-struct.png +
+
+

+4.2  Circuits and streams

+ + + +
+Onion Routing originally built one circuit for each +TCP stream. Because building a circuit can take several tenths of a +second (due to public-key cryptography and network latency), +this design imposed high costs on applications like web browsing that +open many TCP streams. + +
+In Tor, each circuit can be shared by many TCP streams. To avoid +delays, users construct circuits preemptively. To limit linkability +among their streams, users' OPs build a new circuit +periodically if the previous ones have been used, +and expire old used circuits that no longer have any open streams. +OPs consider rotating to a new circuit once a minute: thus +even heavy users spend negligible time +building circuits, but a limited number of requests can be linked +to each other through a given exit node. Also, because circuits are built +in the background, OPs can recover from failed circuit creation +without harming user experience.
+ +
+ +
+ +
interaction.png + +
Figure 1: Alice builds a two-hop circuit and begins fetching a web page.
+ + +
+
+Constructing a circuit +
+A user's OP constructs circuits incrementally, negotiating a +symmetric key with each OR on the circuit, one hop at a time. To begin +creating a new circuit, the OP (call her Alice) sends a +create cell to the first node in her chosen path (call him Bob). +(She chooses a new +circID CAB not currently used on the connection from her to Bob.) +The create cell's +payload contains the first half of the Diffie-Hellman handshake +(gx), encrypted to the onion key of the OR (call him Bob). Bob +responds with a created cell containing gy +along with a hash of the negotiated key K=gxy. + +
+Once the circuit has been established, Alice and Bob can send one +another relay cells encrypted with the negotiated +key.1 More detail is given in +the next section. + +
+To extend the circuit further, Alice sends a relay extend cell +to Bob, specifying the address of the next OR (call her Carol), and +an encrypted gx2 for her. Bob copies the half-handshake into a +create cell, and passes it to Carol to extend the circuit. +(Bob chooses a new circID CBC not currently used on the connection +between him and Carol. Alice never needs to know this circID; only Bob +associates CAB on his connection with Alice to CBC on +his connection with Carol.) +When Carol responds with a created cell, Bob wraps the payload +into a relay extended cell and passes it back to Alice. Now +the circuit is extended to Carol, and Alice and Carol share a common key +K2 = gx2 y2. + +
+To extend the circuit to a third node or beyond, Alice +proceeds as above, always telling the last node in the circuit to +extend one hop further. + +
+This circuit-level handshake protocol achieves unilateral entity +authentication (Alice knows she's handshaking with the OR, but +the OR doesn't care who is opening the circuit-Alice uses no public key +and remains anonymous) and unilateral key authentication +(Alice and the OR agree on a key, and Alice knows only the OR learns +it). It also achieves forward +secrecy and key freshness. More formally, the protocol is as follows +(where EPKBob(·) is encryption with Bob's public key, +H is a secure hash function, and | is concatenation): + +
+ + + + +
Alice -> Bob : EPKBob(gx)
Bob -> Alice : gy, H(K | "handshake") +
+ + +
+ In the second step, Bob proves that it was he who received gx, +and who chose y. We use PK encryption in the first step +(rather than, say, using the first two steps of STS, which has a +signature in the second step) because a single cell is too small to +hold both a public key and a signature. Preliminary analysis with the +NRL protocol analyzer [35] shows this protocol to be +secure (including perfect forward secrecy) under the +traditional Dolev-Yao model.
+ +
+Relay cells
+Once Alice has established the circuit (so she shares keys with each +OR on the circuit), she can send relay cells. +Upon receiving a relay +cell, an OR looks up the corresponding circuit, and decrypts the relay +header and payload with the session key for that circuit. +If the cell is headed away from Alice the OR then checks whether the +decrypted cell has a valid digest (as an optimization, the first +two bytes of the integrity check are zero, so in most cases we can avoid +computing the hash). +If valid, it accepts the relay cell and processes it as described +below. Otherwise, +the OR looks up the circID and OR for the +next step in the circuit, replaces the circID as appropriate, and +sends the decrypted relay cell to the next OR. (If the OR at the end +of the circuit receives an unrecognized relay cell, an error has +occurred, and the circuit is torn down.) + +
+OPs treat incoming relay cells similarly: they iteratively unwrap the +relay header and payload with the session keys shared with each +OR on the circuit, from the closest to farthest. +If at any stage the digest is valid, the cell must have +originated at the OR whose encryption has just been removed. + +
+To construct a relay cell addressed to a given OR, Alice assigns the +digest, and then iteratively +encrypts the cell payload (that is, the relay header and payload) with +the symmetric key of each hop up to that OR. Because the digest is +encrypted to a different value at each step, only at the targeted OR +will it have a meaningful value.2 +This leaky pipe circuit topology +allows Alice's streams to exit at different ORs on a single circuit. +Alice may choose different exit points because of their exit policies, +or to keep the ORs from knowing that two streams +originate from the same person. + +
+When an OR later replies to Alice with a relay cell, it +encrypts the cell's relay header and payload with the single key it +shares with Alice, and sends the cell back toward Alice along the +circuit. Subsequent ORs add further layers of encryption as they +relay the cell back to Alice. + +
+To tear down a circuit, Alice sends a destroy control +cell. Each OR in the circuit receives the destroy cell, closes +all streams on that circuit, and passes a new destroy cell +forward. But just as circuits are built incrementally, they can also +be torn down incrementally: Alice can send a relay +truncate cell to a single OR on a circuit. That OR then sends a +destroy cell forward, and acknowledges with a +relay truncated cell. Alice can then extend the circuit to +different nodes, without signaling to the intermediate nodes (or +a limited observer) that she has changed her circuit. +Similarly, if a node on the circuit goes down, the adjacent +node can send a relay truncated cell back to Alice. Thus the +"break a node and see which circuits go down" +attack [4] is weakened. + +
+

+4.3  Opening and closing streams

+ + + +
+When Alice's application wants a TCP connection to a given +address and port, it asks the OP (via SOCKS) to make the +connection. The OP chooses the newest open circuit (or creates one if +needed), and chooses a suitable OR on that circuit to be the +exit node (usually the last node, but maybe others due to exit policy +conflicts; see Section 6.2.) The OP then opens +the stream by sending a relay begin cell to the exit node, +using a new random streamID. Once the +exit node connects to the remote host, it responds +with a relay connected cell. Upon receipt, the OP sends a +SOCKS reply to notify the application of its success. The OP +now accepts data from the application's TCP stream, packaging it into +relay data cells and sending those cells along the circuit to +the chosen OR. + +
+There's a catch to using SOCKS, however-some applications pass the +alphanumeric hostname to the Tor client, while others resolve it into +an IP address first and then pass the IP address to the Tor client. If +the application does DNS resolution first, Alice thereby reveals her +destination to the remote DNS server, rather than sending the hostname +through the Tor network to be resolved at the far end. Common applications +like Mozilla and SSH have this flaw. + +
+With Mozilla, the flaw is easy to address: the filtering HTTP +proxy called Privoxy gives a hostname to the Tor client, so Alice's +computer never does DNS resolution. +But a portable general solution, such as is needed for +SSH, is +an open problem. Modifying or replacing the local nameserver +can be invasive, brittle, and unportable. Forcing the resolver +library to prefer TCP rather than UDP is hard, and also has +portability problems. Dynamically intercepting system calls to the +resolver library seems a promising direction. We could also provide +a tool similar to dig to perform a private lookup through the +Tor network. Currently, we encourage the use of privacy-aware proxies +like Privoxy wherever possible. + +
+Closing a Tor stream is analogous to closing a TCP stream: it uses a +two-step handshake for normal operation, or a one-step handshake for +errors. If the stream closes abnormally, the adjacent node simply sends a +relay teardown cell. If the stream closes normally, the node sends +a relay end cell down the circuit, and the other side responds with +its own relay end cell. Because +all relay cells use layered encryption, only the destination OR knows +that a given relay cell is a request to close a stream. This two-step +handshake allows Tor to support TCP-based applications that use half-closed +connections. + +
+

+4.4  Integrity checking on streams

+ + + +
+Because the old Onion Routing design used a stream cipher without integrity +checking, traffic was +vulnerable to a malleability attack: though the attacker could not +decrypt cells, any changes to encrypted data +would create corresponding changes to the data leaving the network. +This weakness allowed an adversary who could guess the encrypted content +to change a padding cell to a destroy +cell; change the destination address in a relay begin cell to the +adversary's webserver; or change an FTP command from +dir to rm *. (Even an external +adversary could do this, because the link encryption similarly used a +stream cipher.) + +
+Because Tor uses TLS on its links, external adversaries cannot modify +data. Addressing the insider malleability attack, however, is +more complex. + +
+We could do integrity checking of the relay cells at each hop, either +by including hashes or by using an authenticating cipher mode like +EAX [6], but there are some problems. First, these approaches +impose a message-expansion overhead at each hop, and so we would have to +either leak the path length or waste bytes by padding to a maximum +path length. Second, these solutions can only verify traffic coming +from Alice: ORs would not be able to produce suitable hashes for +the intermediate hops, since the ORs on a circuit do not know the +other ORs' session keys. Third, we have already accepted that our design +is vulnerable to end-to-end timing attacks; so tagging attacks performed +within the circuit provide no additional information to the attacker. + +
+Thus, we check integrity only at the edges of each stream. (Remember that +in our leaky-pipe circuit topology, a stream's edge could be any hop +in the circuit.) When Alice +negotiates a key with a new hop, they each initialize a SHA-1 +digest with a derivative of that key, +thus beginning with randomness that only the two of them know. +Then they each incrementally add to the SHA-1 digest the contents of +all relay cells they create, and include with each relay cell the +first four bytes of the current digest. Each also keeps a SHA-1 +digest of data received, to verify that the received hashes are correct. + +
+To be sure of removing or modifying a cell, the attacker must be able +to deduce the current digest state (which depends on all +traffic between Alice and Bob, starting with their negotiated key). +Attacks on SHA-1 where the adversary can incrementally add to a hash +to produce a new valid hash don't work, because all hashes are +end-to-end encrypted across the circuit. The computational overhead +of computing the digests is minimal compared to doing the AES +encryption performed at each hop of the circuit. We use only four +bytes per cell to minimize overhead; the chance that an adversary will +correctly guess a valid hash +is +acceptably low, given that the OP or OR tear down the circuit if they +receive a bad hash. + +
+

+4.5  Rate limiting and fairness

+ + + +
+Volunteers are more willing to run services that can limit +their bandwidth usage. To accommodate them, Tor servers use a +token bucket approach [50] to +enforce a long-term average rate of incoming bytes, while still +permitting short-term bursts above the allowed bandwidth. + +
+ +
+Because the Tor protocol outputs about the same number of bytes as it +takes in, it is sufficient in practice to limit only incoming bytes. +With TCP streams, however, the correspondence is not one-to-one: +relaying a single incoming byte can require an entire 512-byte cell. +(We can't just wait for more bytes, because the local application may +be awaiting a reply.) Therefore, we treat this case as if the entire +cell size had been read, regardless of the cell's fullness. + +
+Further, inspired by Rennhard et al's design in [44], a +circuit's edges can heuristically distinguish interactive streams from bulk +streams by comparing the frequency with which they supply cells. We can +provide good latency for interactive streams by giving them preferential +service, while still giving good overall throughput to the bulk +streams. Such preferential treatment presents a possible end-to-end +attack, but an adversary observing both +ends of the stream can already learn this information through timing +attacks. + +
+

+4.6  Congestion control

+ + + +
+Even with bandwidth rate limiting, we still need to worry about +congestion, either accidental or intentional. If enough users choose the +same OR-to-OR connection for their circuits, that connection can become +saturated. For example, an attacker could send a large file +through the Tor network to a webserver he runs, and then +refuse to read any of the bytes at the webserver end of the +circuit. Without some congestion control mechanism, these bottlenecks +can propagate back through the entire network. We don't need to +reimplement full TCP windows (with sequence numbers, +the ability to drop cells when we're full and retransmit later, and so +on), +because TCP already guarantees in-order delivery of each +cell. +We describe our response below. + +
+Circuit-level throttling: +To control a circuit's bandwidth usage, each OR keeps track of two +windows. The packaging window tracks how many relay data cells the OR is +allowed to package (from incoming TCP streams) for transmission back to the OP, +and the delivery window tracks how many relay data cells it is willing +to deliver to TCP streams outside the network. Each window is initialized +(say, to 1000 data cells). When a data cell is packaged or delivered, +the appropriate window is decremented. When an OR has received enough +data cells (currently 100), it sends a relay sendme cell towards the OP, +with streamID zero. When an OR receives a relay sendme cell with +streamID zero, it increments its packaging window. Either of these cells +increments the corresponding window by 100. If the packaging window +reaches 0, the OR stops reading from TCP connections for all streams +on the corresponding circuit, and sends no more relay data cells until +receiving a relay sendme cell. + +
+The OP behaves identically, except that it must track a packaging window +and a delivery window for every OR in the circuit. If a packaging window +reaches 0, it stops reading from streams destined for that OR. + +
+Stream-level throttling: +The stream-level congestion control mechanism is similar to the +circuit-level mechanism. ORs and OPs use relay sendme cells +to implement end-to-end flow control for individual streams across +circuits. Each stream begins with a packaging window (currently 500 cells), +and increments the window by a fixed value (50) upon receiving a relay +sendme cell. Rather than always returning a relay sendme cell as soon +as enough cells have arrived, the stream-level congestion control also +has to check whether data has been successfully flushed onto the TCP +stream; it sends the relay sendme cell only when the number of bytes pending +to be flushed is under some threshold (currently 10 cells' worth). + +
+ +
+These arbitrarily chosen parameters seem to give tolerable throughput +and delay; see Section 8. + +
+

+5  Rendezvous Points and hidden services

+ + + +
+Rendezvous points are a building block for location-hidden +services (also known as responder anonymity) in the Tor +network. Location-hidden services allow Bob to offer a TCP +service, such as a webserver, without revealing his IP address. +This type of anonymity protects against distributed DoS attacks: +attackers are forced to attack the onion routing network +because they do not know Bob's IP address. + +
+Our design for location-hidden servers has the following goals. +Access-control: Bob needs a way to filter incoming requests, +so an attacker cannot flood Bob simply by making many connections to him. +Robustness: Bob should be able to maintain a long-term pseudonymous +identity even in the presence of router failure. Bob's service must +not be tied to a single OR, and Bob must be able to migrate his service +across ORs. Smear-resistance: +A social attacker +should not be able to "frame" a rendezvous router by +offering an illegal or disreputable location-hidden service and +making observers believe the router created that service. +Application-transparency: Although we require users +to run special software to access location-hidden servers, we must not +require them to modify their applications. + +
+We provide location-hiding for Bob by allowing him to advertise +several onion routers (his introduction points) as contact +points. He may do this on any robust efficient +key-value lookup system with authenticated updates, such as a +distributed hash table (DHT) like CFS [11].3 Alice, the client, chooses an OR as her +rendezvous point. She connects to one of Bob's introduction +points, informs him of her rendezvous point, and then waits for him +to connect to the rendezvous point. This extra level of indirection +helps Bob's introduction points avoid problems associated with serving +unpopular files directly (for example, if Bob serves +material that the introduction point's community finds objectionable, +or if Bob's service tends to get attacked by network vandals). +The extra level of indirection also allows Bob to respond to some requests +and ignore others. + +
+

+5.1  Rendezvous points in Tor

+ +
+The following steps are +performed on behalf of Alice and Bob by their local OPs; +application integration is described more fully below. + +
+ +
+ +
+
  • Bob generates a long-term public key pair to identify his service.
  • +
    +
  • Bob chooses some introduction points, and advertises them on + the lookup service, signing the advertisement with his public key. He + can add more later.
  • +
    +
  • Bob builds a circuit to each of his introduction points, and tells + them to wait for requests.
  • +
    +
  • Alice learns about Bob's service out of band (perhaps Bob told her, + or she found it on a website). She retrieves the details of Bob's + service from the lookup service. If Alice wants to access Bob's + service anonymously, she must connect to the lookup service via Tor.
  • +
    +
  • Alice chooses an OR as the rendezvous point (RP) for her connection to + Bob's service. She builds a circuit to the RP, and gives it a + randomly chosen "rendezvous cookie" to recognize Bob.
  • +
    +
  • Alice opens an anonymous stream to one of Bob's introduction + points, and gives it a message (encrypted with Bob's public key) + telling it about herself, + her RP and rendezvous cookie, and the + start of a DH + handshake. The introduction point sends the message to Bob.
  • +
    +
  • If Bob wants to talk to Alice, he builds a circuit to Alice's + RP and sends the rendezvous cookie, the second half of the DH + handshake, and a hash of the session + key they now share. By the same argument as in + Section 4.2, Alice knows she + shares the key only with Bob.
  • +
    +
  • The RP connects Alice's circuit to Bob's. Note that RP can't + recognize Alice, Bob, or the data they transmit.
  • +
    +
  • Alice sends a relay begin cell along the circuit. It + arrives at Bob's OP, which connects to Bob's + webserver.
  • +
    +
  • An anonymous stream has been established, and Alice and Bob + communicate as normal. +
  • +
    + +
    +When establishing an introduction point, Bob provides the onion router +with the public key identifying his service. Bob signs his +messages, so others cannot usurp his introduction point +in the future. He uses the same public key to establish the other +introduction points for his service, and periodically refreshes his +entry in the lookup service. + +
    +The message that Alice gives +the introduction point includes a hash of Bob's public key and an optional initial authorization token (the +introduction point can do prescreening, for example to block replays). Her +message to Bob may include an end-to-end authorization token so Bob +can choose whether to respond. +The authorization tokens can be used to provide selective access: +important users can get uninterrupted access. +During normal situations, Bob's service might simply be offered +directly from mirrors, while Bob gives out tokens to high-priority users. If +the mirrors are knocked down, +those users can switch to accessing Bob's service via +the Tor rendezvous system. + +
    +Bob's introduction points are themselves subject to DoS-he must +open many introduction points or risk such an attack. +He can provide selected users with a current list or future schedule of +unadvertised introduction points; +this is most practical +if there is a stable and large group of introduction points +available. Bob could also give secret public keys +for consulting the lookup service. All of these approaches +limit exposure even when +some selected users collude in the DoS. + +
    +

    +5.2  Integration with user applications

    + +
    +Bob configures his onion proxy to know the local IP address and port of his +service, a strategy for authorizing clients, and his public key. The onion +proxy anonymously publishes a signed statement of Bob's +public key, an expiration time, and +the current introduction points for his service onto the lookup service, +indexed +by the hash of his public key. Bob's webserver is unmodified, +and doesn't even know that it's hidden behind the Tor network. + +
    +Alice's applications also work unchanged-her client interface +remains a SOCKS proxy. We encode all of the necessary information +into the fully qualified domain name (FQDN) Alice uses when establishing her +connection. Location-hidden services use a virtual top level domain +called .onion: thus hostnames take the form x.y.onion where +x is the authorization cookie and y encodes the hash of +the public key. Alice's onion proxy +examines addresses; if they're destined for a hidden server, it decodes +the key and starts the rendezvous as described above. + +
    +

    +5.3  Previous rendezvous work

    + +
    +Rendezvous points in low-latency anonymity systems were first +described for use in ISDN telephony [30,38]. +Later low-latency designs used rendezvous points for hiding location +of mobile phones and low-power location +trackers [23,40]. Rendezvous for +anonymizing low-latency +Internet connections was suggested in early Onion Routing +work [27], but the first published design was by Ian +Goldberg [26]. His design differs from +ours in three ways. First, Goldberg suggests that Alice should manually +hunt down a current location of the service via Gnutella; our approach +makes lookup transparent to the user, as well as faster and more robust. +Second, in Tor the client and server negotiate session keys +with Diffie-Hellman, so plaintext is not exposed even at the rendezvous +point. Third, +our design minimizes the exposure from running the +service, to encourage volunteers to offer introduction and rendezvous +services. Tor's introduction points do not output any bytes to the +clients; the rendezvous points don't know the client or the server, +and can't read the data being transmitted. The indirection scheme is +also designed to include authentication/authorization-if Alice doesn't +include the right cookie with her request for service, Bob need not even +acknowledge his existence. + +
    +

    +6  Other design decisions

    + + + +
    +

    +6.1  Denial of service

    + + + +
    +Providing Tor as a public service creates many opportunities for +denial-of-service attacks against the network. While +flow control and rate limiting (discussed in +Section 4.6) prevent users from consuming more +bandwidth than routers are willing to provide, opportunities remain for +users to +consume more network resources than their fair share, or to render the +network unusable for others. + +
    +First of all, there are several CPU-consuming denial-of-service +attacks wherein an attacker can force an OR to perform expensive +cryptographic operations. For example, an attacker can +fake the start of a TLS handshake, forcing the OR to carry out its +(comparatively expensive) half of the handshake at no real computational +cost to the attacker. + +
    +We have not yet implemented any defenses for these attacks, but several +approaches are possible. First, ORs can +require clients to solve a puzzle [16] while beginning new +TLS handshakes or accepting create cells. So long as these +tokens are easy to verify and computationally expensive to produce, this +approach limits the attack multiplier. Additionally, ORs can limit +the rate at which they accept create cells and TLS connections, +so that +the computational work of processing them does not drown out the +symmetric cryptography operations that keep cells +flowing. This rate limiting could, however, allow an attacker +to slow down other users when they build new circuits. + +
    + +
    +Adversaries can also attack the Tor network's hosts and network +links. Disrupting a single circuit or link breaks all streams passing +along that part of the circuit. Users similarly lose service +when a router crashes or its operator restarts it. The current +Tor design treats such attacks as intermittent network failures, and +depends on users and applications to respond or recover as appropriate. A +future design could use an end-to-end TCP-like acknowledgment protocol, +so no streams are lost unless the entry or exit point is +disrupted. This solution would require more buffering at the network +edges, however, and the performance and anonymity implications from this +extra complexity still require investigation. + +
    +

    +6.2  Exit policies and abuse

    + + + +
    + +
    +Exit abuse is a serious barrier to wide-scale Tor deployment. Anonymity +presents would-be vandals and abusers with an opportunity to hide +the origins of their activities. Attackers can harm the Tor network by +implicating exit servers for their abuse. Also, applications that commonly +use IP-based authentication (such as institutional mail or webservers) +can be fooled by the fact that anonymous connections appear to originate +at the exit OR. + +
    +We stress that Tor does not enable any new class of abuse. Spammers +and other attackers already have access to thousands of misconfigured +systems worldwide, and the Tor network is far from the easiest way +to launch attacks. +But because the +onion routers can be mistaken for the originators of the abuse, +and the volunteers who run them may not want to deal with the hassle of +explaining anonymity networks to irate administrators, we must block or limit +abuse through the Tor network. + +
    +To mitigate abuse issues, each onion router's exit policy +describes to which external addresses and ports the router will +connect. On one end of the spectrum are open exit +nodes that will connect anywhere. On the other end are middleman +nodes that only relay traffic to other Tor nodes, and private exit +nodes that only connect to a local host or network. A private +exit can allow a client to connect to a given host or +network more securely-an external adversary cannot eavesdrop traffic +between the private exit and the final destination, and so is less sure of +Alice's destination and activities. Most onion routers in the current +network function as +restricted exits that permit connections to the world at large, +but prevent access to certain abuse-prone addresses and services such +as SMTP. +The OR might also be able to authenticate clients to +prevent exit abuse without harming anonymity [48]. + +
    + +
    +Many administrators use port restrictions to support only a +limited set of services, such as HTTP, SSH, or AIM. +This is not a complete solution, of course, since abuse opportunities for these +protocols are still well known. + +
    +We have not yet encountered any abuse in the deployed network, but if +we do we should consider using proxies to clean traffic for certain +protocols as it leaves the network. For example, much abusive HTTP +behavior (such as exploiting buffer overflows or well-known script +vulnerabilities) can be detected in a straightforward manner. +Similarly, one could run automatic spam filtering software (such as +SpamAssassin) on email exiting the OR network. + +
    +ORs may also rewrite exiting traffic to append +headers or other information indicating that the traffic has passed +through an anonymity service. This approach is commonly used +by email-only anonymity systems. ORs can also +run on servers with hostnames like anonymous to further +alert abuse targets to the nature of the anonymous traffic. + +
    +A mixture of open and restricted exit nodes allows the most +flexibility for volunteers running servers. But while having many +middleman nodes provides a large and robust network, +having only a few exit nodes reduces the number of points +an adversary needs to monitor for traffic analysis, and places a +greater burden on the exit nodes. This tension can be seen in the +Java Anon Proxy +cascade model, wherein only one node in each cascade needs to handle +abuse complaints-but an adversary only needs to observe the entry +and exit of a cascade to perform traffic analysis on all that +cascade's users. The hydra model (many entries, few exits) presents a +different compromise: only a few exit nodes are needed, but an +adversary needs to work harder to watch all the clients; see +Section 10. + +
    +Finally, we note that exit abuse must not be dismissed as a peripheral +issue: when a system's public image suffers, it can reduce the number +and diversity of that system's users, and thereby reduce the anonymity +of the system itself. Like usability, public perception is a +security parameter. Sadly, preventing abuse of open exit nodes is an +unsolved problem, and will probably remain an arms race for the +foreseeable future. The abuse problems faced by Princeton's CoDeeN +project [37] give us a glimpse of likely issues. + +
    +

    +6.3  Directory Servers

    + + + +
    +First-generation Onion Routing designs [8,41] used +in-band network status updates: each router flooded a signed statement +to its neighbors, which propagated it onward. But anonymizing networks +have different security goals than typical link-state routing protocols. +For example, delays (accidental or intentional) +that can cause different parts of the network to have different views +of link-state and topology are not only inconvenient: they give +attackers an opportunity to exploit differences in client knowledge. +We also worry about attacks to deceive a +client about the router membership list, topology, or current network +state. Such partitioning attacks on client knowledge help an +adversary to efficiently deploy resources +against a target [15]. + +
    +Tor uses a small group of redundant, well-known onion routers to +track changes in network topology and node state, including keys and +exit policies. Each such directory server acts as an HTTP +server, so clients can fetch current network state +and router lists, and so other ORs can upload +state information. Onion routers periodically publish signed +statements of their state to each directory server. The directory servers +combine this information with their own views of network liveness, +and generate a signed description (a directory) of the entire +network state. Client software is +pre-loaded with a list of the directory servers and their keys, +to bootstrap each client's view of the network. + +
    +When a directory server receives a signed statement for an OR, it +checks whether the OR's identity key is recognized. Directory +servers do not advertise unrecognized ORs-if they did, +an adversary could take over the network by creating many +servers [22]. Instead, new nodes must be approved by the +directory +server administrator before they are included. Mechanisms for automated +node approval are an area of active research, and are discussed more +in Section 9. + +
    +Of course, a variety of attacks remain. An adversary who controls +a directory server can track clients by providing them different +information-perhaps by listing only nodes under its control, or by +informing only certain clients about a given node. Even an external +adversary can exploit differences in client knowledge: clients who use +a node listed on one directory server but not the others are vulnerable. + +
    +Thus these directory servers must be synchronized and redundant, so +that they can agree on a common directory. Clients should only trust +this directory if it is signed by a threshold of the directory +servers. + +
    +The directory servers in Tor are modeled after those in +Mixminion [15], but our situation is easier. First, +we make the +simplifying assumption that all participants agree on the set of +directory servers. Second, while Mixminion needs to predict node +behavior, Tor only needs a threshold consensus of the current +state of the network. Third, we assume that we can fall back to the +human administrators to discover and resolve problems when a consensus +directory cannot be reached. Since there are relatively few directory +servers (currently 3, but we expect as many as 9 as the network scales), +we can afford operations like broadcast to simplify the consensus-building +protocol. + +
    +To avoid attacks where a router connects to all the directory servers +but refuses to relay traffic from other routers, the directory servers +must also build circuits and use them to anonymously test router +reliability [18]. Unfortunately, this defense is not yet +designed or +implemented. + +
    +Using directory servers is simpler and more flexible than flooding. +Flooding is expensive, and complicates the analysis when we +start experimenting with non-clique network topologies. Signed +directories can be cached by other +onion routers, +so directory servers are not a performance +bottleneck when we have many users, and do not aid traffic analysis by +forcing clients to announce their existence to any +central point. + +
    +

    +7  Attacks and Defenses

    + + + +
    +Below we summarize a variety of attacks, and discuss how well our +design withstands them.
    + +
    +Passive attacks
    +Observing user traffic patterns. Observing a user's connection +will not reveal her destination or data, but it will +reveal traffic patterns (both sent and received). Profiling via user +connection patterns requires further processing, because multiple +application streams may be operating simultaneously or in series over +a single circuit. + +
    +Observing user content. While content at the user end is encrypted, +connections to responders may not be (indeed, the responding website +itself may be hostile). While filtering content is not a primary goal +of Onion Routing, Tor can directly use Privoxy and related +filtering services to anonymize application data streams. + +
    +Option distinguishability. We allow clients to choose +configuration options. For example, clients concerned about request +linkability should rotate circuits more often than those concerned +about traceability. Allowing choice may attract users with different +needs; but clients who are +in the minority may lose more anonymity by appearing distinct than they +gain by optimizing their behavior [1]. + +
    +End-to-end timing correlation. Tor only minimally hides +such correlations. An attacker watching patterns of +traffic at the initiator and the responder will be +able to confirm the correspondence with high probability. The +greatest protection currently available against such confirmation is to hide +the connection between the onion proxy and the first Tor node, +by running the OP on the Tor node or behind a firewall. This approach +requires an observer to separate traffic originating at the onion +router from traffic passing through it: a global observer can do this, +but it might be beyond a limited observer's capabilities. + +
    +End-to-end size correlation. Simple packet counting +will also be effective in confirming +endpoints of a stream. However, even without padding, we may have some +limited protection: the leaky pipe topology means different numbers +of packets may enter one end of a circuit than exit at the other. + +
    +Website fingerprinting. All the effective passive +attacks above are traffic confirmation attacks, +which puts them outside our design goals. There is also +a passive traffic analysis attack that is potentially effective. +Rather than searching exit connections for timing and volume +correlations, the adversary may build up a database of +"fingerprints" containing file sizes and access patterns for +targeted websites. He can later confirm a user's connection to a given +site simply by consulting the database. This attack has +been shown to be effective against SafeWeb [29]. +It may be less effective against Tor, since +streams are multiplexed within the same circuit, and +fingerprinting will be limited to +the granularity of cells (currently 512 bytes). Additional +defenses could include +larger cell sizes, padding schemes to group websites +into large sets, and link +padding or long-range dummies.4
    + +
    +Active attacks
    +Compromise keys. An attacker who learns the TLS session key can +see control cells and encrypted relay cells on every circuit on that +connection; learning a circuit +session key lets him unwrap one layer of the encryption. An attacker +who learns an OR's TLS private key can impersonate that OR for the TLS +key's lifetime, but he must +also learn the onion key to decrypt create cells (and because of +perfect forward secrecy, he cannot hijack already established circuits +without also compromising their session keys). Periodic key rotation +limits the window of opportunity for these attacks. On the other hand, +an attacker who learns a node's identity key can replace that node +indefinitely by sending new forged descriptors to the directory servers. + +
    +Iterated compromise. A roving adversary who can +compromise ORs (by system intrusion, legal coercion, or extralegal +coercion) could march down the circuit compromising the +nodes until he reaches the end. Unless the adversary can complete +this attack within the lifetime of the circuit, however, the ORs +will have discarded the necessary information before the attack can +be completed. (Thanks to the perfect forward secrecy of session +keys, the attacker cannot force nodes to decrypt recorded +traffic once the circuits have been closed.) Additionally, building +circuits that cross jurisdictions can make legal coercion +harder-this phenomenon is commonly called "jurisdictional +arbitrage." The Java Anon Proxy project recently experienced the +need for this approach, when +a German court forced them to add a backdoor to +their nodes [51]. + +
    +Run a recipient. An adversary running a webserver +trivially learns the timing patterns of users connecting to it, and +can introduce arbitrary patterns in its responses. +End-to-end attacks become easier: if the adversary can induce +users to connect to his webserver (perhaps by advertising +content targeted to those users), he now holds one end of their +connection. There is also a danger that application +protocols and associated programs can be induced to reveal information +about the initiator. Tor depends on Privoxy and similar protocol cleaners +to solve this latter problem. + +
    +Run an onion proxy. It is expected that end users will +nearly always run their own local onion proxy. However, in some +settings, it may be necessary for the proxy to run +remotely-typically, in institutions that want +to monitor the activity of those connecting to the proxy. +Compromising an onion proxy compromises all future connections +through it. + +
    +DoS non-observed nodes. An observer who can only watch some +of the Tor network can increase the value of this traffic +by attacking non-observed nodes to shut them down, reduce +their reliability, or persuade users that they are not trustworthy. +The best defense here is robustness. + +
    +Run a hostile OR. In addition to being a local observer, +an isolated hostile node can create circuits through itself, or alter +traffic patterns to affect traffic at other nodes. Nonetheless, a hostile +node must be immediately adjacent to both endpoints to compromise the +anonymity of a circuit. If an adversary can +run multiple ORs, and can persuade the directory servers +that those ORs are trustworthy and independent, then occasionally +some user will choose one of those ORs for the start and another +as the end of a circuit. If an adversary +controls m > 1 of N nodes, he can correlate at most +([m/N])2 of the traffic-although an +adversary +could still attract a disproportionately large amount of traffic +by running an OR with a permissive exit policy, or by +degrading the reliability of other routers. + +
    +Introduce timing into messages. This is simply a stronger +version of passive timing attacks already discussed earlier. + +
    +Tagging attacks. A hostile node could "tag" a +cell by altering it. If the +stream were, for example, an unencrypted request to a Web site, +the garbled content coming out at the appropriate time would confirm +the association. However, integrity checks on cells prevent +this attack. + +
    +Replace contents of unauthenticated protocols. When +relaying an unauthenticated protocol like HTTP, a hostile exit node +can impersonate the target server. Clients +should prefer protocols with end-to-end authentication. + +
    +Replay attacks. Some anonymity protocols are vulnerable +to replay attacks. Tor is not; replaying one side of a handshake +will result in a different negotiated session key, and so the rest +of the recorded session can't be used. + +
    +Smear attacks. An attacker could use the Tor network for +socially disapproved acts, to bring the +network into disrepute and get its operators to shut it down. +Exit policies reduce the possibilities for abuse, but +ultimately the network requires volunteers who can tolerate +some political heat. + +
    +Distribute hostile code. An attacker could trick users +into running subverted Tor software that did not, in fact, anonymize +their connections-or worse, could trick ORs into running weakened +software that provided users with less anonymity. We address this +problem (but do not solve it completely) by signing all Tor releases +with an official public key, and including an entry in the directory +that lists which versions are currently believed to be secure. To +prevent an attacker from subverting the official release itself +(through threats, bribery, or insider attacks), we provide all +releases in source code form, encourage source audits, and +frequently warn our users never to trust any software (even from +us) that comes without source.
    + +
    +Directory attacks
    +Destroy directory servers. If a few directory +servers disappear, the others still decide on a valid +directory. So long as any directory servers remain in operation, +they will still broadcast their views of the network and generate a +consensus directory. (If more than half are destroyed, this +directory will not, however, have enough signatures for clients to +use it automatically; human intervention will be necessary for +clients to decide whether to trust the resulting directory.) + +
    +Subvert a directory server. By taking over a directory server, +an attacker can partially influence the final directory. Since ORs +are included or excluded by majority vote, the corrupt directory can +at worst cast a tie-breaking vote to decide whether to include +marginal ORs. It remains to be seen how often such marginal cases +occur in practice. + +
    +Subvert a majority of directory servers. An adversary who controls +more than half the directory servers can include as many compromised +ORs in the final directory as he wishes. We must ensure that directory +server operators are independent and attack-resistant. + +
    +Encourage directory server dissent. The directory +agreement protocol assumes that directory server operators agree on +the set of directory servers. An adversary who can persuade some +of the directory server operators to distrust one another could +split the quorum into mutually hostile camps, thus partitioning +users based on which directory they use. Tor does not address +this attack. + +
    +Trick the directory servers into listing a hostile OR. +Our threat model explicitly assumes directory server operators will +be able to filter out most hostile ORs. + +
    +Convince the directories that a malfunctioning OR is +working. In the current Tor implementation, directory servers +assume that an OR is running correctly if they can start a TLS +connection to it. A hostile OR could easily subvert this test by +accepting TLS connections from ORs but ignoring all cells. Directory +servers must actively test ORs by building circuits and streams as +appropriate. The tradeoffs of a similar approach are discussed +in [18].
    + +
    +Attacks against rendezvous points
    +Make many introduction requests. An attacker could +try to deny Bob service by flooding his introduction points with +requests. Because the introduction points can block requests that +lack authorization tokens, however, Bob can restrict the volume of +requests he receives, or require a certain amount of computation for +every request he receives. + +
    +Attack an introduction point. An attacker could +disrupt a location-hidden service by disabling its introduction +points. But because a service's identity is attached to its public +key, the service can simply re-advertise +itself at a different introduction point. Advertisements can also be +done secretly so that only high-priority clients know the address of +Bob's introduction points or so that different clients know of different +introduction points. This forces the attacker to disable all possible +introduction points. + +
    +Compromise an introduction point. An attacker who controls +Bob's introduction point can flood Bob with +introduction requests, or prevent valid introduction requests from +reaching him. Bob can notice a flood, and close the circuit. To notice +blocking of valid requests, however, he should periodically test the +introduction point by sending rendezvous requests and making +sure he receives them. + +
    +Compromise a rendezvous point. A rendezvous +point is no more sensitive than any other OR on +a circuit, since all data passing through the rendezvous is encrypted +with a session key shared by Alice and Bob. + +
    +

    +8  Early experiences: Tor in the Wild

    + + + +
    +As of mid-May 2004, the Tor network consists of 32 nodes +(24 in the US, 8 in Europe), and more are joining each week as the code +matures. (For comparison, the current remailer network +has about 40 nodes.) Each node has at least a 768Kb/768Kb connection, and +many have 10Mb. The number of users varies (and of course, it's hard to +tell for sure), but we sometimes have several hundred users-administrators at +several companies have begun sending their entire departments' web +traffic through Tor, to block other divisions of +their company from reading their traffic. Tor users have reported using +the network for web browsing, FTP, IRC, AIM, Kazaa, SSH, and +recipient-anonymous email via rendezvous points. One user has anonymously +set up a Wiki as a hidden service, where other users anonymously publish +the addresses of their hidden services. + +
    +Each Tor node currently processes roughly 800,000 relay +cells (a bit under half a gigabyte) per week. On average, about 80% +of each 498-byte payload is full for cells going back to the client, +whereas about 40% is full for cells coming from the client. (The difference +arises because most of the network's traffic is web browsing.) Interactive +traffic like SSH brings down the average a lot-once we have more +experience, and assuming we can resolve the anonymity issues, we may +partition traffic into two relay cell sizes: one to handle +bulk traffic and one for interactive traffic. + +
    +Based in part on our restrictive default exit policy (we +reject SMTP requests) and our low profile, we have had no abuse +issues since the network was deployed in October +2003. Our slow growth rate gives us time to add features, +resolve bugs, and get a feel for what users actually want from an +anonymity system. Even though having more users would bolster our +anonymity sets, we are not eager to attract the Kazaa or warez +communities-we feel that we must build a reputation for privacy, human +rights, research, and other socially laudable activities. + +
    +As for performance, profiling shows that Tor spends almost +all its CPU time in AES, which is fast. Current latency is attributable +to two factors. First, network latency is critical: we are +intentionally bouncing traffic around the world several times. Second, +our end-to-end congestion control algorithm focuses on protecting +volunteer servers from accidental DoS rather than on optimizing +performance. To quantify these effects, we did some informal tests using a network of 4 +nodes on the same machine (a heavily loaded 1GHz Athlon). We downloaded a 60 +megabyte file from debian.org every 30 minutes for 54 hours (108 sample +points). It arrived in about 300 seconds on average, compared to 210s for a +direct download. We ran a similar test on the production Tor network, +fetching the front page of cnn.com (55 kilobytes): +while a direct +download consistently took about 0.3s, the performance through Tor varied. +Some downloads were as fast as 0.4s, with a median at 2.8s, and +90% finishing within 5.3s. It seems that as the network expands, the chance +of building a slow circuit (one that includes a slow or heavily loaded node +or link) is increasing. On the other hand, as our users remain satisfied +with this increased latency, we can address our performance incrementally as we +proceed with development. +
    + +
    + +
    +Although Tor's clique topology and full-visibility directories present +scaling problems, we still expect the network to support a few hundred +nodes and maybe 10,000 users before we're forced to become +more distributed. With luck, the experience we gain running the current +topology will help us choose among alternatives when the time comes. + +
    +

    +9  Open Questions in Low-latency Anonymity

    + + + +
    +In addition to the non-goals in +Section 3, many questions must be solved +before we can be confident of Tor's security. + +
    +Many of these open issues are questions of balance. For example, +how often should users rotate to fresh circuits? Frequent rotation +is inefficient, expensive, and may lead to intersection attacks and +predecessor attacks [54], but infrequent rotation makes the +user's traffic linkable. Besides opening fresh circuits, clients can +also exit from the middle of the circuit, +or truncate and re-extend the circuit. More analysis is +needed to determine the proper tradeoff. + +
    + +
    +How should we choose path lengths? If Alice always uses two hops, +then both ORs can be certain that by colluding they will learn about +Alice and Bob. In our current approach, Alice always chooses at least +three nodes unrelated to herself and her destination. +Should Alice choose a random path length (e.g. from a geometric +distribution) to foil an attacker who +uses timing to learn that he is the fifth hop and thus concludes that +both Alice and the responder are running ORs? + +
    +Throughout this paper, we have assumed that end-to-end traffic +confirmation will immediately and automatically defeat a low-latency +anonymity system. Even high-latency anonymity systems can be +vulnerable to end-to-end traffic confirmation, if the traffic volumes +are high enough, and if users' habits are sufficiently +distinct [14,31]. Can anything be +done to +make low-latency systems resist these attacks as well as high-latency +systems? Tor already makes some effort to conceal the starts and ends of +streams by wrapping long-range control commands in identical-looking +relay cells. Link padding could frustrate passive observers who count +packets; long-range padding could work against observers who own the +first hop in a circuit. But more research remains to find an efficient +and practical approach. Volunteers prefer not to run constant-bandwidth +padding; but no convincing traffic shaping approach has been +specified. Recent work on long-range padding [33] +shows promise. One could also try to reduce correlation in packet timing +by batching and re-ordering packets, but it is unclear whether this could +improve anonymity without introducing so much latency as to render the +network unusable. + +
    +A cascade topology may better defend against traffic confirmation by +aggregating users, and making padding and +mixing more affordable. Does the hydra topology (many input nodes, +few output nodes) work better against some adversaries? Are we going +to get a hydra anyway because most nodes will be middleman nodes? + +
    +Common wisdom suggests that Alice should run her own OR for best +anonymity, because traffic coming from her node could plausibly have +come from elsewhere. How much mixing does this approach need? Is it +immediately beneficial because of real-world adversaries that can't +observe Alice's router, but can run routers of their own? + +
    +To scale to many users, and to prevent an attacker from observing the +whole network, it may be necessary +to support far more servers than Tor currently anticipates. +This introduces several issues. First, if approval by a central set +of directory servers is no longer feasible, what mechanism should be used +to prevent adversaries from signing up many colluding servers? Second, +if clients can no longer have a complete picture of the network, +how can they perform discovery while preventing attackers from +manipulating or exploiting gaps in their knowledge? Third, if there +are too many servers for every server to constantly communicate with +every other, which non-clique topology should the network use? +(Restricted-route topologies promise comparable anonymity with better +scalability [13], but whatever topology we choose, we +need some way to keep attackers from manipulating their position within +it [21].) Fourth, if no central authority is tracking +server reliability, how do we stop unreliable servers from making +the network unusable? Fifth, do clients receive so much anonymity +from running their own ORs that we should expect them all to do +so [1], or do we need another incentive structure to +motivate them? Tarzan and MorphMix present possible solutions. + +
    + +
    +When a Tor node goes down, all its circuits (and thus streams) must break. +Will users abandon the system because of this brittleness? How well +does the method in Section 6.1 allow streams to survive +node failure? If affected users rebuild circuits immediately, how much +anonymity is lost? It seems the problem is even worse in a peer-to-peer +environment-such systems don't yet provide an incentive for peers to +stay connected when they're done retrieving content, so we would expect +a higher churn rate. + +
    + +
    +

    +10  Future Directions

    + + + +
    +Tor brings together many innovations into a unified deployable system. The +next immediate steps include: + +
    +Scalability: Tor's emphasis on deployability and design simplicity +has led us to adopt a clique topology, semi-centralized +directories, and a full-network-visibility model for client +knowledge. These properties will not scale past a few hundred servers. +Section 9 describes some promising +approaches, but more deployment experience will be helpful in learning +the relative importance of these bottlenecks. + +
    +Bandwidth classes: This paper assumes that all ORs have +good bandwidth and latency. We should instead adopt the MorphMix model, +where nodes advertise their bandwidth level (DSL, T1, T3), and +Alice avoids bottlenecks by choosing nodes that match or +exceed her bandwidth. In this way DSL users can usefully join the Tor +network. + +
    +Incentives: Volunteers who run nodes are rewarded with publicity +and possibly better anonymity [1]. More nodes means increased +scalability, and more users can mean more anonymity. We need to continue +examining the incentive structures for participating in Tor. Further, +we need to explore more approaches to limiting abuse, and understand +why most people don't bother using privacy systems. + +
    +Cover traffic: Currently Tor omits cover traffic-its costs +in performance and bandwidth are clear but its security benefits are +not well understood. We must pursue more research on link-level cover +traffic and long-range cover traffic to determine whether some simple padding +method offers provable protection against our chosen adversary. + +
    + +
    +Caching at exit nodes: Perhaps each exit node should run a +caching web proxy [47], to improve anonymity for cached pages +(Alice's request never +leaves the Tor network), to improve speed, and to reduce bandwidth cost. +On the other hand, forward security is weakened because caches +constitute a record of retrieved files. We must find the right +balance between usability and security. + +
    +Better directory distribution: +Clients currently download a description of +the entire network every 15 minutes. As the state grows larger +and clients more numerous, we may need a solution in which +clients receive incremental updates to directory state. +More generally, we must find more +scalable yet practical ways to distribute up-to-date snapshots of +network status without introducing new attacks. + +
    +Further specification review: Our public +byte-level specification [20] needs +external review. We hope that as Tor +is deployed, more people will examine its +specification. + +
    +Multisystem interoperability: We are currently working with the +designer of MorphMix to unify the specification and implementation of +the common elements of our two systems. So far, this seems +to be relatively straightforward. Interoperability will allow testing +and direct comparison of the two designs for trust and scalability. + +
    +Wider-scale deployment: The original goal of Tor was to +gain experience in deploying an anonymizing overlay network, and +learn from having actual users. We are now at a point in design +and development where we can start deploying a wider network. Once +we have many actual users, we will doubtlessly be better +able to evaluate some of our design decisions, including our +robustness/latency tradeoffs, our performance tradeoffs (including +cell size), our abuse-prevention mechanisms, and +our overall usability. + +
    + +
    + +

    Acknowledgments

    + We thank Peter Palfrader, Geoff Goodell, Adam Shostack, Joseph Sokol-Margolis, + John Bashinski, and Zack Brown + for editing and comments; + Matej Pfajfar, Andrei Serjantov, Marc Rennhard for design discussions; + Bram Cohen for congestion control discussions; + Adam Back for suggesting telescoping circuits; and + Cathy Meadows for formal analysis of the extend protocol. + This work has been supported by ONR and DARPA. + +
    + +
    + +
    +

    References

    + +
    +
    [1]
    +A. Acquisti, R. Dingledine, and P. Syverson. + On the economics of anonymity. + In R. N. Wright, editor, Financial Cryptography. + Springer-Verlag, LNCS 2742, 2003. + +
    +
    +
    [2]
    +R. Anderson. + The eternity service. + In Pragocrypt '96, 1996. + +
    +
    +
    [3]
    +The Anonymizer. + <http://anonymizer.com/>. + +
    +
    +
    [4]
    +A. Back, I. Goldberg, and A. Shostack. + Freedom systems 2.1 security issues and analysis. + White paper, Zero Knowledge Systems, Inc., May 2001. + +
    +
    +
    [5]
    +A. Back, U. Möller, and A. Stiglic. + Traffic analysis attacks and trade-offs in anonymity providing + systems. + In I. S. Moskowitz, editor, Information Hiding (IH 2001), pages + 245-257. Springer-Verlag, LNCS 2137, 2001. + +
    +
    +
    [6]
    +M. Bellare, P. Rogaway, and D. Wagner. + The EAX mode of operation: A two-pass authenticated-encryption + scheme optimized for simplicity and efficiency. + In Fast Software Encryption 2004, February 2004. + +
    +
    +
    [7]
    +O. Berthold, H. Federrath, and S. Köpsell. + Web MIXes: A system for anonymous and unobservable Internet + access. + In H. Federrath, editor, Designing Privacy Enhancing + Technologies: Workshop on Design Issue in Anonymity and Unobservability. + Springer-Verlag, LNCS 2009, 2000. + +
    +
    +
    [8]
    +P. Boucher, A. Shostack, and I. Goldberg. + Freedom systems 2.0 architecture. + White paper, Zero Knowledge Systems, Inc., December 2000. + +
    +
    +
    [9]
    +Z. Brown. + Cebolla: Pragmatic IP Anonymity. + In Ottawa Linux Symposium, June 2002. + +
    +
    +
    [10]
    +D. Chaum. + Untraceable electronic mail, return addresses, and digital + pseudo-nyms. + Communications of the ACM, 4(2), February 1981. + +
    +
    +
    [11]
    +F. Dabek, M. F. Kaashoek, D. Karger, R. Morris, and I. Stoica. + Wide-area cooperative storage with CFS. + In 18th ACM Symposium on Operating Systems Principles + (SOSP '01), Chateau Lake Louise, Banff, Canada, October 2001. + +
    +
    +
    [12]
    +W. Dai. + Pipenet 1.1. + Usenet post, August 1996. + <http://www.eskimo.com/ weidai/pipenet.txt> First mentioned in a + post to the cypherpunks list, Feb. 1995. + +
    +
    +
    [13]
    +G. Danezis. + Mix-networks with restricted routes. + In R. Dingledine, editor, Privacy Enhancing Technologies (PET + 2003). Springer-Verlag LNCS 2760, 2003. + +
    +
    +
    [14]
    +G. Danezis. + Statistical disclosure attacks. + In Security and Privacy in the Age of Uncertainty (SEC2003), + pages 421-426, Athens, May 2003. IFIP TC11, Kluwer. + +
    +
    +
    [15]
    +G. Danezis, R. Dingledine, and N. Mathewson. + Mixminion: Design of a type III anonymous remailer protocol. + In 2003 IEEE Symposium on Security and Privacy, pages 2-15. + IEEE CS, May 2003. + +
    +
    +
    [16]
    +D. Dean and A. Stubblefield. + Using Client Puzzles to Protect TLS. + In Proceedings of the 10th USENIX Security Symposium. USENIX, + Aug. 2001. + +
    +
    +
    [17]
    +T. Dierks and C. Allen. + The TLS Protocol - Version 1.0. + IETF RFC 2246, January 1999. + +
    +
    +
    [18]
    +R. Dingledine, M. J. Freedman, D. Hopwood, and D. Molnar. + A Reputation System to Increase MIX-net Reliability. + In I. S. Moskowitz, editor, Information Hiding (IH 2001), pages + 126-141. Springer-Verlag, LNCS 2137, 2001. + +
    +
    +
    [19]
    +R. Dingledine, M. J. Freedman, and D. Molnar. + The free haven project: Distributed anonymous storage service. + In H. Federrath, editor, Designing Privacy Enhancing + Technologies: Workshop on Design Issue in Anonymity and Unobservability. + Springer-Verlag, LNCS 2009, July 2000. + +
    +
    +
    [20]
    +R. Dingledine and N. Mathewson. + Tor protocol specifications. + <http://freehaven.net/tor/tor-spec.txt>. + +
    +
    +
    [21]
    +R. Dingledine and P. Syverson. + Reliable MIX Cascade Networks through Reputation. + In M. Blaze, editor, Financial Cryptography. Springer-Verlag, + LNCS 2357, 2002. + +
    +
    +
    [22]
    +J. Douceur. + The Sybil Attack. + In Proceedings of the 1st International Peer To Peer Systems + Workshop (IPTPS), Mar. 2002. + +
    +
    +
    [23]
    +H. Federrath, A. Jerichow, and A. Pfitzmann. + MIXes in mobile communication systems: Location management with + privacy. + In R. Anderson, editor, Information Hiding, First International + Workshop, pages 121-135. Springer-Verlag, LNCS 1174, May 1996. + +
    +
    +
    [24]
    +M. J. Freedman and R. Morris. + Tarzan: A peer-to-peer anonymizing network layer. + In 9th ACM Conference on Computer and Communications + Security (CCS 2002), Washington, DC, November 2002. + +
    +
    +
    [25]
    +S. Goel, M. Robson, M. Polte, and E. G. Sirer. + Herbivore: A scalable and efficient protocol for anonymous + communication. + Technical Report TR2003-1890, Cornell University Computing and + Information Science, February 2003. + +
    +
    +
    [26]
    +I. Goldberg. + A Pseudonymous Communications Infrastructure for the Internet. + PhD thesis, UC Berkeley, Dec 2000. + +
    +
    +
    [27]
    +D. M. Goldschlag, M. G. Reed, and P. F. Syverson. + Hiding routing information. + In R. Anderson, editor, Information Hiding, First International + Workshop, pages 137-150. Springer-Verlag, LNCS 1174, May 1996. + +
    +
    +
    [28]
    +C. Gülcü and G. Tsudik. + Mixing E-mail with Babel. + In Network and Distributed Security Symposium (NDSS 96), + pages 2-16. IEEE, February 1996. + +
    +
    +
    [29]
    +A. Hintz. + Fingerprinting websites using traffic analysis. + In R. Dingledine and P. Syverson, editors, Privacy Enhancing + Technologies (PET 2002), pages 171-178. Springer-Verlag, LNCS 2482, 2002. + +
    +
    +
    [30]
    +A. Jerichow, J. Müller, A. Pfitzmann, B. Pfitzmann, and M. Waidner. + Real-time mixes: A bandwidth-efficient anonymity protocol. + IEEE Journal on Selected Areas in Communications, + 16(4):495-509, May 1998. + +
    +
    +
    [31]
    +D. Kesdogan, D. Agrawal, and S. Penz. + Limits of anonymity in open environments. + In F. Petitcolas, editor, Information Hiding Workshop (IH + 2002). Springer-Verlag, LNCS 2578, October 2002. + +
    +
    +
    [32]
    +D. Koblas and M. R. Koblas. + SOCKS. + In UNIX Security III Symposium (1992 USENIX Security + Symposium), pages 77-83. USENIX, 1992. + +
    +
    +
    [33]
    +B. N. Levine, M. K. Reiter, C. Wang, and M. Wright. + Timing analysis in low-latency mix-based systems. + In A. Juels, editor, Financial Cryptography. Springer-Verlag, + LNCS (forthcoming), 2004. + +
    +
    +
    [34]
    +B. N. Levine and C. Shields. + Hordes: A multicast-based protocol for anonymity. + Journal of Computer Security, 10(3):213-240, 2002. + +
    +
    +
    [35]
    +C. Meadows. + The NRL protocol analyzer: An overview. + Journal of Logic Programming, 26(2):113-131, 1996. + +
    +
    +
    [36]
    +U. Möller, L. Cottrell, P. Palfrader, and L. Sassaman. + Mixmaster Protocol - Version 2. + Draft, July 2003. + <http://www.abditum.com/mixmaster-spec.txt>. + +
    +
    +
    [37]
    +V. S. Pai, L. Wang, K. Park, R. Pang, and L. Peterson. + The Dark Side of the Web: An Open Proxy's View. + <http://codeen.cs.princeton.edu/>. + +
    +
    +
    [38]
    +A. Pfitzmann, B. Pfitzmann, and M. Waidner. + ISDN-mixes: Untraceable communication with very small bandwidth + overhead. + In GI/ITG Conference on Communication in Distributed Systems, + pages 451-463, February 1991. + +
    +
    +
    [39]
    +Privoxy. + <http://www.privoxy.org/>. + +
    +
    +
    [40]
    +M. G. Reed, P. F. Syverson, and D. M. Goldschlag. + Protocols using anonymous connections: Mobile applications. + In B. Christianson, B. Crispo, M. Lomas, and M. Roe, editors, + Security Protocols: 5th International Workshop, pages 13-23. + Springer-Verlag, LNCS 1361, April 1997. + +
    +
    +
    [41]
    +M. G. Reed, P. F. Syverson, and D. M. Goldschlag. + Anonymous connections and onion routing. + IEEE Journal on Selected Areas in Communications, + 16(4):482-494, May 1998. + +
    +
    +
    [42]
    +M. K. Reiter and A. D. Rubin. + Crowds: Anonymity for web transactions. + ACM TISSEC, 1(1):66-92, June 1998. + +
    +
    +
    [43]
    +M. Rennhard and B. Plattner. + Practical anonymity for the masses with morphmix. + In A. Juels, editor, Financial Cryptography. Springer-Verlag, + LNCS (forthcoming), 2004. + +
    +
    +
    [44]
    +M. Rennhard, S. Rafaeli, L. Mathy, B. Plattner, and D. Hutchison. + Analysis of an Anonymity Network for Web Browsing. + In IEEE 7th Intl. Workshop on Enterprise Security (WET ICE + 2002), Pittsburgh, USA, June 2002. + +
    +
    +
    [45]
    +A. Serjantov and P. Sewell. + Passive attack analysis for connection-based anonymity systems. + In Computer Security - ESORICS 2003. Springer-Verlag, LNCS + 2808, October 2003. + +
    +
    +
    [46]
    +R. Sherwood, B. Bhattacharjee, and A. Srinivasan. + p5: A protocol for scalable anonymous communication. + In IEEE Symposium on Security and Privacy, pages 58-70. IEEE + CS, 2002. + +
    +
    +
    [47]
    +A. Shubina and S. Smith. + Using caching for browsing anonymity. + ACM SIGEcom Exchanges, 4(2), Sept 2003. + +
    +
    +
    [48]
    +P. Syverson, M. Reed, and D. Goldschlag. + Onion Routing access configurations. + In DARPA Information Survivability Conference and Exposition + (DISCEX 2000), volume 1, pages 34-40. IEEE CS Press, 2000. + +
    +
    +
    [49]
    +P. Syverson, G. Tsudik, M. Reed, and C. Landwehr. + Towards an Analysis of Onion Routing Security. + In H. Federrath, editor, Designing Privacy Enhancing + Technologies: Workshop on Design Issue in Anonymity and Unobservability, + pages 96-114. Springer-Verlag, LNCS 2009, July 2000. + +
    +
    +
    [50]
    +A. Tannenbaum. + Computer networks, 1996. + +
    +
    +
    [51]
    +The AN.ON Project. + German police proceeds against anonymity service. + Press release, September 2003. + + <http://www.datenschutzzentrum.de/material/themen/presse/anon-bka_e.htm>. + +
    +
    +
    [52]
    +M. Waldman and D. Mazières. + Tangler: A censorship-resistant publishing system based on document + entanglements. + In 8th ACM Conference on Computer and Communications + Security (CCS-8), pages 86-135. ACM Press, 2001. + +
    +
    +
    [53]
    +M. Waldman, A. Rubin, and L. Cranor. + Publius: A robust, tamper-evident, censorship-resistant and + source-anonymous web publishing system. + In Proc. 9th USENIX Security Symposium, pages 59-72, August + 2000. + +
    +
    +
    [54]
    +M. Wright, M. Adler, B. N. Levine, and C. Shields. + Defending anonymous communication against passive logging attacks. + In IEEE Symposium on Security and Privacy, pages 28-41. IEEE + CS, May 2003.
    +
    + + +
    +

    Footnotes:

    + +
    +1Actually, the negotiated key is used to derive two + symmetric keys: one for each direction. +
    +2 + With 48 bits of digest per cell, the probability of an accidental +collision is far lower than the chance of hardware failure. +
    +3 +Rather than rely on an external infrastructure, the Onion Routing network +can run the lookup service itself. Our current implementation provides a +simple lookup system on the +directory servers. +
    +4Note that this fingerprinting +attack should not be confused with the much more complicated latency +attacks of [5], which require a fingerprint of the latencies +of all circuits through the network, combined with those from the +network edges to the target user and the responder website. +


    File translated from +TEX +by +TTH, +version 3.59.
    On 18 May 2004, 10:45.
    + diff --git a/tags/tor-0_0_8pre1/doc/tor-design.pdf b/tags/tor-0_0_8pre1/doc/tor-design.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76a226515397791ced23deeff72df459b0a4578e GIT binary patch literal 175246 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

    o4by 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�gl80d^#%TiKO~5h#~UJD~?u;Q2D%5Rgk7>33~gNWQGj7Vg*#e02P^aRnjYz)qdo0cZDR(5_+ zxA)&(13AbeO1%ME{gtwNm3%@{VGbXjvm7RJ&NtH)GP1Sg(Rb~#rxBxNMD+ix_q_+T zT>-H1D>@|?G?*3>FDAKq&o_bFi!r6IjE?)2!3lExH2Q{uVyFJ^4s#88;*svNReoh6 zEgzY}Vv?3PeU5op-c?qh?QHRlY(kY4q|i;iWoPi^o0XP?^sTJ@A9$UTL{w3gwJh1# z{a}&VGs-_Mz@RSN1^OsNJ1YVbjKV<|>32$mUqDV8)C^13cYvAgH!gw`E;~m{+Td*! zW2GNouQp+rOFmI=>a6)4Jm~%rV$z?eQvD;1JrbX!M**9otC2xx!P+Ro+|B@-^-kgd zH!ZKRr8CUD?FLPzc^G(?qv5(&$`>%Vp%?D2DU!3x=J>LarKN`SAwSGc%a=mozb>+w zkx;mEu&x#%Dkk#bk(BH(O!*W>YqgZPDpQt0(|UV`!Hr&%f5Jz?lytSy!p(lLRtLv~ zcMlZSGC+Ux+m)IFhP9{Y5&uEmPKT%xhR_KJ?a*L6swWHo^0V%JeO*O;Lyqpka*J7g zq@3Baq6S*lNuKiiLpgDEQRrcTTCL(|vC}o#;(9fYUbieH*r!l+OnV^zhiSg4zJo0< z4l~zbmi)Zr*KNScN0*9GBcV?^74{oo!A31u*IXt;S z!pnkO(%jeySu9nDNaqXuqJ8>}5o6s8H7wI`I|&h<_#kpCr4qJoX|5Ohv}})aBz<>w zak(G9`Z-=`{))1UZZG0F5lZH(Bo@~xo7Up1d3{Lv9>W5lIT&>y&%P7+6@Sp!*!1nF z4W|&H!vmQ0NWL6lX|a6b_1fQ@{W=7`q@{aR-!4$Y+%kXuOHFax(+`3ULdh}mDqVmd zEjaR5z+^^4cGz*XQ~UX7W)A)!FFbKKW#iA~>1tNx1|`Mg%giC;rVhsy+|&7UjpE2K z&ex2HDRWVumZ}0H3!MnKEKKv&)4CFg$Jz^?t?L6D^Vtii$)T>mxq z@4fy@(Dnb@R+%L^H~_5w2ccKC|H{Sx{+XDUi|v2vtSP#>_Ir{I|KY5~f%Rxt$y|!* zNvMeuRQLl1=a5u))8x81tXSpdNjH{$AB&pm3JEyiOlYC9&eze)4j=V}*v;U3T<}N| zw?x-Qu*^*fzwh1UP3GShvX$-?klXyV$2Kr-VL0z1wDXN}>SqmzNrmm1HmJeoYL?}B zk7*QYZLx*%!tm;{kps<|ScUJzs_4{~_wgaG@&3`!bYm?E)ciDjT#ePBnAxcq=USkSfJY;2i=RhJ{*ytxBW0_f#gD-Ir&t~_ zGPuF~qRt!;N~*ppau(_oaDg!6@mM zV3S}rVTZx01RtJ`XjvEOsmz#k{yC6Zk)QiVo(=y^`+(=QE$-Q2mz|Z4@hC^`hMf*f zVI%nK>oj6{(L!8_?<~mxwJWngN8`l=z(<(zau@E@Msc-1$-ukwcBiEHW42TexN&^E zeQS&RPV!?XMa&W1bnB~)clyb&MhN5<(x?y_ziZ~e_NzY0yI$)aE|W#C*wU|VH+xMS z|EKE@Fd>sqmNwpdVKpnl;8V%_gGl7xQp&o3It>Ki`W&ZT(V~didT0x=@t4GpbGkQ` zwzypd%+U%Vjz{ErN3q#A%?qJ9Z0+s{w|cthVG0oA+K?6o(Q=ADMev-o#dxuRd&Kd4 z-@q}-M<}MlJ0EXKInBe4N+I!rH zVZTrH%!2Cd+P2nY!{PB2LLBkDiDz^z6bwn0T;{wfP(%b;t)_{uGXvL>9a^fB9S|w8 zfZyk3LEmw9Rlgx>N#$$SSRrr&}<6PP{|D1XAUbuMu0s6>J$rZLAjIq-F$I#L(nn4l9z6)&C)KqLNM^@2G@1~ zvAm(i&zjp`5{^_Wf0kudjw6~&l0vhHb&6|54`(L#ITqGY=lg_6ZkKkES|jRgzWFUN6Bq z

    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\urlstyle{tt}\Url} +\newcommand\emailaddr{\begingroup \def\UrlLeft{<}\def\UrlRight{>}\urlstyle{tt}\Url} + +\newcommand{\workingnote}[1]{} % The version that hides the note. +%\newcommand{\workingnote}[1]{(**#1)} % The version that makes the note visible. + +% If an URL ends up with '%'s in it, that's because the line *in the .bib/.tex +% file* is too long, so break it there (it doesn't matter if the next line is +% indented with spaces). -DH + +%\newif\ifpdf +%\ifx\pdfoutput\undefined +% \pdffalse +%\else +% \pdfoutput=1 +% \pdftrue +%\fi + +\newenvironment{tightlist}{\begin{list}{$\bullet$}{ + \setlength{\itemsep}{0mm} + \setlength{\parsep}{0mm} + % \setlength{\labelsep}{0mm} + % \setlength{\labelwidth}{0mm} + % \setlength{\topsep}{0mm} + }}{\end{list}} + +% Cut down on whitespace above and below figures displayed at head/foot of +% page. +\setlength{\textfloatsep}{3mm} +% Cut down on whitespace above and below figures displayed in middle of page +\setlength{\intextsep}{3mm} + +\begin{document} + +%% Use dvipdfm instead. --DH +%\ifpdf +% \pdfcompresslevel=9 +% \pdfpagewidth=\the\paperwidth +% \pdfpageheight=\the\paperheight +%\fi + +\title{Tor: The Second-Generation Onion Router} %\\DRAFT VERSION} +% Putting the 'Private' back in 'Virtual Private Network' + +\author{Roger Dingledine \\ The Free Haven Project \\ arma@freehaven.net \and +Nick Mathewson \\ The Free Haven Project \\ nickm@freehaven.net \and +Paul Syverson \\ Naval Research Lab \\ syverson@itd.nrl.navy.mil} + +\maketitle +\thispagestyle{empty} + +\begin{abstract} +We present Tor, a circuit-based low-latency anonymous communication +service. This second-generation Onion Routing system addresses limitations +in the original design by adding perfect forward secrecy, congestion +control, directory servers, integrity checking, configurable exit policies, +and a practical design for location-hidden services via rendezvous +points. Tor works on the real-world +Internet, requires no special privileges or kernel modifications, requires +little synchronization or coordination between nodes, and provides a +reasonable tradeoff between anonymity, usability, and efficiency. +We briefly describe our experiences with an international network of +more than 30 nodes. % that has been running for several months. +We close with a list of open problems in anonymous communication. +\end{abstract} + +%\begin{center} +%\textbf{Keywords:} anonymity, peer-to-peer, remailer, nymserver, reply block +%\end{center} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Overview} +\label{sec:intro} + +Onion Routing is a distributed overlay network designed to anonymize +TCP-based applications like web browsing, secure shell, +and instant messaging. Clients choose a path through the network and +build a \emph{circuit}, in which each node (or ``onion router'' or ``OR'') +in the path knows its predecessor and successor, but no other nodes in +the circuit. Traffic flows down the circuit in fixed-size +\emph{cells}, which are unwrapped by a symmetric key at each node +(like the layers of an onion) and relayed downstream. The +Onion Routing project published several design and analysis +papers \cite{or-ih96,or-jsac98,or-discex00,or-pet00}. While a wide area Onion +Routing network was deployed briefly, the only long-running +public implementation was a fragile +proof-of-concept that ran on a single machine. Even this simple deployment +processed connections from over sixty thousand distinct IP addresses from +all over the world at a rate of about fifty thousand per day. +But many critical design and deployment issues were never +resolved, and the design has not been updated in years. Here +we describe Tor, a protocol for asynchronous, loosely federated onion +routers that provides the following improvements over the old Onion +Routing design: + +\textbf{Perfect forward secrecy:} In the original Onion Routing design, +a single hostile node could record traffic and +later compromise successive nodes in the circuit and force them +to decrypt it. Rather than using a single multiply encrypted data +structure (an \emph{onion}) to lay each circuit, +Tor now uses an incremental or \emph{telescoping} path-building design, +where the initiator negotiates session keys with each successive hop in +the circuit. Once these keys are deleted, subsequently compromised nodes +cannot decrypt old traffic. As a side benefit, onion replay detection +is no longer necessary, and the process of building circuits is more +reliable, since the initiator knows when a hop fails and can then try +extending to a new node. + +\textbf{Separation of ``protocol cleaning'' from anonymity:} +Onion Routing originally required a separate ``application +proxy'' for each supported application protocol---most of which were +never written, so many applications were never supported. Tor uses the +standard and near-ubiquitous SOCKS~\cite{socks4} proxy interface, allowing +us to support most TCP-based programs without modification. Tor now +relies on the filtering features of privacy-enhancing +application-level proxies such as Privoxy~\cite{privoxy}, without trying +to duplicate those features itself. + +\textbf{No mixing, padding, or traffic shaping (yet):} Onion +Routing originally called for batching and reordering cells as they arrived, +assumed padding between ORs, and in +later designs added padding between onion proxies (users) and +ORs~\cite{or-ih96,or-jsac98}. Tradeoffs between padding protection +and cost were discussed, and \emph{traffic shaping} algorithms were +theorized~\cite{or-pet00} to provide good security without expensive +padding, but no concrete padding scheme was suggested. +Recent research~\cite{econymics} +and deployment experience~\cite{freedom21-security} suggest that this +level of resource use is not practical or economical; and even full +link padding is still vulnerable~\cite{defensive-dropping}. Thus, +until we have a proven and convenient design for traffic shaping or +low-latency mixing that improves anonymity against a realistic +adversary, we leave these strategies out. + +\textbf{Many TCP streams can share one circuit:} Onion Routing originally +built a separate circuit for each +application-level request, but this required +multiple public key operations for every request, and also presented +a threat to anonymity from building so many circuits; see +Section~\ref{sec:maintaining-anonymity}. Tor multiplexes multiple TCP +streams along each circuit to improve efficiency and anonymity. + +\textbf{Leaky-pipe circuit topology:} Through in-band signaling +within the circuit, Tor initiators can direct traffic to nodes partway +down the circuit. This novel approach +allows traffic to exit the circuit from the middle---possibly +frustrating traffic shape and volume attacks based on observing the end +of the circuit. (It also allows for long-range padding if +future research shows this to be worthwhile.) + +\textbf{Congestion control:} Earlier anonymity designs do not +address traffic bottlenecks. Unfortunately, typical approaches to +load balancing and flow control in overlay networks involve inter-node +control communication and global views of traffic. Tor's decentralized +congestion control uses end-to-end acks to maintain anonymity +while allowing nodes at the edges of the network to detect congestion +or flooding and send less data until the congestion subsides. + +\textbf{Directory servers:} The earlier Onion Routing design +planned to flood state information through the network---an approach +that can be unreliable and complex. % open to partitioning attacks. +Tor takes a simplified view toward distributing this +information. Certain more trusted nodes act as \emph{directory +servers}: they provide signed directories describing known +routers and their current state. Users periodically download them +via HTTP. + +\textbf{Variable exit policies:} Tor provides a consistent mechanism +for each node to advertise a policy describing the hosts +and ports to which it will connect. These exit policies are critical +in a volunteer-based distributed infrastructure, because each operator +is comfortable with allowing different types of traffic to exit +from his node. + +\textbf{End-to-end integrity checking:} The original Onion Routing +design did no integrity checking on data. Any node on the +circuit could change the contents of data cells as they passed by---for +example, to alter a connection request so it would connect +to a different webserver, or to `tag' encrypted traffic and look for +corresponding corrupted traffic at the network edges~\cite{minion-design}. +Tor hampers these attacks by verifying data integrity before it leaves +the network. + +%\textbf{Improved robustness to failed nodes:} A failed node +%in the old design meant that circuit building failed, but thanks to +%Tor's step-by-step circuit building, users notice failed nodes +%while building circuits and route around them. Additionally, liveness +%information from directories allows users to avoid unreliable nodes in +%the first place. +%% Can't really claim this, now that we've found so many variants of +%% attack on partial-circuit-building. -RD + +\textbf{Rendezvous points and hidden services:} +Tor provides an integrated mechanism for responder anonymity via +location-protected servers. Previous Onion Routing designs included +long-lived ``reply onions'' that could be used to build circuits +to a hidden server, but these reply onions did not provide forward +security, and became useless if any node in the path went down +or rotated its keys. In Tor, clients negotiate {\it rendezvous points} +to connect with hidden servers; reply onions are no longer required. + +Unlike Freedom~\cite{freedom2-arch}, Tor does not require OS kernel +patches or network stack support. This prevents us from anonymizing +non-TCP protocols, but has greatly helped our portability and +deployability. + +%Unlike Freedom~\cite{freedom2-arch}, Tor only anonymizes +%TCP-based protocols---not requiring patches (or built-in support) in an +%operating system's network stack has been valuable to Tor's +%portability and deployability. + +We have implemented all of the above features, including rendezvous +points. Our source code is +available under a free license, and Tor +%, as far as we know, is unencumbered by patents. +is not covered by the patent that affected distribution and use of +earlier versions of Onion Routing. +We have deployed a wide-area alpha network +to test the design, to get more experience with usability +and users, and to provide a research platform for experimentation. +As of this writing, the network stands at 32 nodes %in thirteen +%distinct administrative domains +spread over two continents. + +We review previous work in Section~\ref{sec:related-work}, describe +our goals and assumptions in Section~\ref{sec:assumptions}, +and then address the above list of improvements in +Sections~\ref{sec:design},~\ref{sec:rendezvous}, and~\ref{sec:other-design}. +We summarize +in Section~\ref{sec:attacks} how our design stands up to +known attacks, and talk about our early deployment experiences in +Section~\ref{sec:in-the-wild}. We conclude with a list of open problems in +Section~\ref{sec:maintaining-anonymity} and future work for the Onion +Routing project in Section~\ref{sec:conclusion}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Related work} +\label{sec:related-work} + +Modern anonymity systems date to Chaum's {\bf Mix-Net} +design~\cite{chaum-mix}. Chaum +proposed hiding the correspondence between sender and recipient by +wrapping messages in layers of public-key cryptography, and relaying them +through a path composed of ``mixes.'' Each mix in turn +decrypts, delays, and re-orders messages before relaying them +onward. +%toward their destinations. + +Subsequent relay-based anonymity designs have diverged in two +main directions. Systems like {\bf Babel}~\cite{babel}, +{\bf Mixmaster}~\cite{mixmaster-spec}, +and {\bf Mixminion}~\cite{minion-design} have tried +to maximize anonymity at the cost of introducing comparatively large and +variable latencies. Because of this decision, these \emph{high-latency} +networks resist strong global adversaries, +but introduce too much lag for interactive tasks like web browsing, +Internet chat, or SSH connections. + +Tor belongs to the second category: \emph{low-latency} designs that +try to anonymize interactive network traffic. These systems handle +a variety of bidirectional protocols. They also provide more convenient +mail delivery than the high-latency anonymous email +networks, because the remote mail server provides explicit and timely +delivery confirmation. But because these designs typically +involve many packets that must be delivered quickly, it is +difficult for them to prevent an attacker who can eavesdrop both ends of the +communication from correlating the timing and volume +of traffic entering the anonymity network with traffic leaving it~\cite{SS03}. +These +protocols are similarly vulnerable to an active adversary who introduces +timing patterns into traffic entering the network and looks +for correlated patterns among exiting traffic. +Although some work has been done to frustrate these attacks, most designs +protect primarily against traffic analysis rather than traffic +confirmation (see Section~\ref{subsec:threat-model}). + +The simplest low-latency designs are single-hop proxies such as the +{\bf Anonymizer}~\cite{anonymizer}: a single trusted server strips the +data's origin before relaying it. These designs are easy to +analyze, but users must trust the anonymizing proxy. +Concentrating the traffic to this single point increases the anonymity set +(the people a given user is hiding among), but it is vulnerable if the +adversary can observe all traffic entering and leaving the proxy. + +More complex are distributed-trust, circuit-based anonymizing systems. +In these designs, a user establishes one or more medium-term bidirectional +end-to-end circuits, and tunnels data in fixed-size cells. +Establishing circuits is computationally expensive and typically +requires public-key +cryptography, whereas relaying cells is comparatively inexpensive and +typically requires only symmetric encryption. +Because a circuit crosses several servers, and each server only knows +the adjacent servers in the circuit, no single server can link a +user to her communication partners. + +The {\bf Java Anon Proxy} (also known as JAP or Web MIXes) uses fixed shared +routes known as \emph{cascades}. As with a single-hop proxy, this +approach aggregates users into larger anonymity sets, but again an +attacker only needs to observe both ends of the cascade to bridge all +the system's traffic. The Java Anon Proxy's design +calls for padding between end users and the head of the +cascade~\cite{web-mix}. However, it is not demonstrated whether the current +implementation's padding policy improves anonymity. + +{\bf PipeNet}~\cite{back01, pipenet}, another low-latency design proposed +around the same time as Onion Routing, gave +stronger anonymity but allowed a single user to shut +down the network by not sending. Systems like {\bf ISDN +mixes}~\cite{isdn-mixes} were designed for other environments with +different assumptions. +%XXX please can we fix this sentence to something less demeaning + +In P2P designs like {\bf Tarzan}~\cite{tarzan:ccs02} and +{\bf MorphMix}~\cite{morphmix:fc04}, all participants both generate +traffic and relay traffic for others. These systems aim to conceal +whether a given peer originated a request +or just relayed it from another peer. While Tarzan and MorphMix use +layered encryption as above, {\bf Crowds}~\cite{crowds-tissec} simply assumes +an adversary who cannot observe the initiator: it uses no public-key +encryption, so any node on a circuit can read users' traffic. + +{\bf Hordes}~\cite{hordes-jcs} is based on Crowds but also uses multicast +responses to hide the initiator. {\bf Herbivore}~\cite{herbivore} and +$\mbox{\bf P}^{\mathbf 5}$~\cite{p5} go even further, requiring broadcast. +These systems are designed primarily for communication among peers, +although Herbivore users can make external connections by +requesting a peer to serve as a proxy. + +Systems like {\bf Freedom} and the original Onion Routing build circuits +all at once, using a layered ``onion'' of public-key encrypted messages, +each layer of which provides session keys and the address of the +next server in the circuit. Tor as described herein, Tarzan, MorphMix, +{\bf Cebolla}~\cite{cebolla}, and Rennhard's {\bf Anonymity Network}~\cite{anonnet} +build circuits +in stages, extending them one hop at a time. +Section~\ref{subsubsec:constructing-a-circuit} describes how this +approach enables perfect forward secrecy. + +Circuit-based designs must choose which protocol layer +to anonymize. They may intercept IP packets directly, and +relay them whole (stripping the source address) along the +circuit~\cite{freedom2-arch,tarzan:ccs02}. Like +Tor, they may accept TCP streams and relay the data in those streams, +ignoring the breakdown of that data into TCP +segments~\cite{morphmix:fc04,anonnet}. Finally, like Crowds, they may accept +application-level protocols such as HTTP and relay the application +requests themselves. +Making this protocol-layer decision requires a compromise between flexibility +and anonymity. For example, a system that understands HTTP +can strip +identifying information from requests, can take advantage of caching +to limit the number of requests that leave the network, and can batch +or encode requests to minimize the number of connections. +On the other hand, an IP-level anonymizer can handle nearly any protocol, +even ones unforeseen by its designers (though these systems require +kernel-level modifications to some operating systems, and so are more +complex and less portable). TCP-level anonymity networks like Tor present +a middle approach: they are application neutral (so long as the +application supports, or can be tunneled across, TCP), but by treating +application connections as data streams rather than raw TCP packets, +they avoid the inefficiencies of tunneling TCP over +TCP. + +Distributed-trust anonymizing systems need to prevent attackers from +adding too many servers and thus compromising user paths. +Tor relies on a small set of well-known directory servers, run by +independent parties, to decide which nodes can +join. Tarzan and MorphMix allow unknown users to run servers, and use +a limited resource (like IP addresses) to prevent an attacker from +controlling too much of the network. Crowds suggests requiring +written, notarized requests from potential crowd members. + +Anonymous communication is essential for censorship-resistant +systems like Eternity~\cite{eternity}, Free~Haven~\cite{freehaven-berk}, +Publius~\cite{publius}, and Tangler~\cite{tangler}. Tor's rendezvous +points enable connections between mutually anonymous entities; they +are a building block for location-hidden servers, which are needed by +Eternity and Free~Haven. + +% didn't include rewebbers. No clear place to put them, so I'll leave +% them out for now. -RD + +\section{Design goals and assumptions} +\label{sec:assumptions} + +\noindent{\large\bf Goals}\\ +Like other low-latency anonymity designs, Tor seeks to frustrate +attackers from linking communication partners, or from linking +multiple communications to or from a single user. Within this +main goal, however, several considerations have directed +Tor's evolution. + +\textbf{Deployability:} The design must be deployed and used in the +real world. Thus it +must not be expensive to run (for example, by requiring more bandwidth +than volunteers are willing to provide); must not place a heavy +liability burden on operators (for example, by allowing attackers to +implicate onion routers in illegal activities); and must not be +difficult or expensive to implement (for example, by requiring kernel +patches, or separate proxies for every protocol). We also cannot +require non-anonymous parties (such as websites) +to run our software. (Our rendezvous point design does not meet +this goal for non-anonymous users talking to hidden servers, +however; see Section~\ref{sec:rendezvous}.) + +\textbf{Usability:} A hard-to-use system has fewer users---and because +anonymity systems hide users among users, a system with fewer users +provides less anonymity. Usability is thus not only a convenience: +it is a security requirement~\cite{econymics,back01}. Tor should +therefore not +require modifying familiar applications; should not introduce prohibitive +delays; +and should require as few configuration decisions +as possible. Finally, Tor should be easily implementable on all common +platforms; we cannot require users to change their operating system +to be anonymous. (Tor currently runs on Win32, Linux, +Solaris, BSD-style Unix, MacOS X, and probably others.) + +\textbf{Flexibility:} The protocol must be flexible and well-specified, +so Tor can serve as a test-bed for future research. +Many of the open problems in low-latency anonymity +networks, such as generating dummy traffic or preventing Sybil +attacks~\cite{sybil}, may be solvable independently from the issues +solved by +Tor. Hopefully future systems will not need to reinvent Tor's design. +%(But note that while a flexible design benefits researchers, +%there is a danger that differing choices of extensions will make users +%distinguishable. Experiments should be run on a separate network.) + +\textbf{Simple design:} The protocol's design and security +parameters must be well-understood. Additional features impose implementation +and complexity costs; adding unproven techniques to the design threatens +deployability, readability, and ease of security analysis. Tor aims to +deploy a simple and stable system that integrates the best accepted +approaches to protecting anonymity.\\ + +\noindent{\large\bf Non-goals}\label{subsec:non-goals}\\ +In favoring simple, deployable designs, we have explicitly deferred +several possible goals, either because they are solved elsewhere, or because +they are not yet solved. + +\textbf{Not peer-to-peer:} Tarzan and MorphMix aim to scale to completely +decentralized peer-to-peer environments with thousands of short-lived +servers, many of which may be controlled by an adversary. This approach +is appealing, but still has many open +problems~\cite{tarzan:ccs02,morphmix:fc04}. + +\textbf{Not secure against end-to-end attacks:} Tor does not claim +to completely solve end-to-end timing or intersection +attacks. Some approaches, such as having users run their own onion routers, +may help; +see Section~\ref{sec:maintaining-anonymity} for more discussion. + +\textbf{No protocol normalization:} Tor does not provide \emph{protocol +normalization} like Privoxy or the Anonymizer. If senders want anonymity from +responders while using complex and variable +protocols like HTTP, Tor must be layered with a filtering proxy such +as Privoxy to hide differences between clients, and expunge protocol +features that leak identity. +Note that by this separation Tor can also provide services that +are anonymous to the network yet authenticated to the responder, like +SSH. Similarly, Tor does not integrate +tunneling for non-stream-based protocols like UDP; this must be +provided by an external service if appropriate. + +\textbf{Not steganographic:} Tor does not try to conceal who is connected +to the network. + +\subsection{Threat Model} +\label{subsec:threat-model} + +A global passive adversary is the most commonly assumed threat when +analyzing theoretical anonymity designs. But like all practical +low-latency systems, Tor does not protect against such a strong +adversary. Instead, we assume an adversary who can observe some fraction +of network traffic; who can generate, modify, delete, or delay +traffic; who can operate onion routers of his own; and who can +compromise some fraction of the onion routers. + +In low-latency anonymity systems that use layered encryption, the +adversary's typical goal is to observe both the initiator and the +responder. By observing both ends, passive attackers can confirm a +suspicion that Alice is +talking to Bob if the timing and volume patterns of the traffic on the +connection are distinct enough; active attackers can induce timing +signatures on the traffic to force distinct patterns. Rather +than focusing on these \emph{traffic confirmation} attacks, +we aim to prevent \emph{traffic +analysis} attacks, where the adversary uses traffic patterns to learn +which points in the network he should attack. + +Our adversary might try to link an initiator Alice with her +communication partners, or try to build a profile of Alice's +behavior. He might mount passive attacks by observing the network edges +and correlating traffic entering and leaving the network---by +relationships in packet timing, volume, or externally visible +user-selected +options. The adversary can also mount active attacks by compromising +routers or keys; by replaying traffic; by selectively denying service +to trustworthy routers to move users to +compromised routers, or denying service to users to see if traffic +elsewhere in the +network stops; or by introducing patterns into traffic that can later be +detected. The adversary might subvert the directory servers to give users +differing views of network state. Additionally, he can try to decrease +the network's reliability by attacking nodes or by performing antisocial +activities from reliable nodes and trying to get them taken down---making +the network unreliable flushes users to other less anonymous +systems, where they may be easier to attack. We summarize +in Section~\ref{sec:attacks} how well the Tor design defends against +each of these attacks. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{The Tor Design} +\label{sec:design} + +The Tor network is an overlay network; each onion router (OR) +runs as a normal +user-level process without any special privileges. +Each onion router maintains a TLS~\cite{TLS} +connection to every other onion router. +%(We discuss alternatives to this clique-topology assumption in +%Section~\ref{sec:maintaining-anonymity}.) +% A subset of the ORs also act as +%directory servers, tracking which routers are in the network; +%see Section~\ref{subsec:dirservers} for directory server details. +Each user +runs local software called an onion proxy (OP) to fetch directories, +establish circuits across the network, +and handle connections from user applications. These onion proxies accept +TCP streams and multiplex them across the circuits. The onion +router on the other side +of the circuit connects to the requested destinations +and relays data. + +Each onion router maintains a long-term identity key and a short-term +onion key. The identity +key is used to sign TLS certificates, to sign the OR's \emph{router +descriptor} (a summary of its keys, address, bandwidth, exit policy, +and so on), and (by directory servers) to sign directories. %Changing +%the identity key of a router is considered equivalent to creating a +%new router. +The onion key is used to decrypt requests +from users to set up a circuit and negotiate ephemeral keys. +The TLS protocol also establishes a short-term link key when communicating +between ORs. Short-term keys are rotated periodically and +independently, to limit the impact of key compromise. + +Section~\ref{subsec:cells} presents the fixed-size +\emph{cells} that are the unit of communication in Tor. We describe +in Section~\ref{subsec:circuits} how circuits are +built, extended, truncated, and destroyed. Section~\ref{subsec:tcp} +describes how TCP streams are routed through the network. We address +integrity checking in Section~\ref{subsec:integrity-checking}, +and resource limiting in Section~\ref{subsec:rate-limit}. +Finally, +Section~\ref{subsec:congestion} talks about congestion control and +fairness issues. + +\subsection{Cells} +\label{subsec:cells} + +Onion routers communicate with one another, and with users' OPs, via +TLS connections with ephemeral keys. Using TLS conceals the data on +the connection with perfect forward secrecy, and prevents an attacker +from modifying data on the wire or impersonating an OR. + +Traffic passes along these connections in fixed-size cells. Each cell +is 512 bytes, %(but see Section~\ref{sec:conclusion} for a discussion of +%allowing large cells and small cells on the same network), +and consists of a header and a payload. The header includes a circuit +identifier (circID) that specifies which circuit the cell refers to +(many circuits can be multiplexed over the single TLS connection), and +a command to describe what to do with the cell's payload. (Circuit +identifiers are connection-specific: each circuit has a different +circID on each OP/OR or OR/OR connection it traverses.) +Based on their command, cells are either \emph{control} cells, which are +always interpreted by the node that receives them, or \emph{relay} cells, +which carry end-to-end stream data. The control cell commands are: +\emph{padding} (currently used for keepalive, but also usable for link +padding); \emph{create} or \emph{created} (used to set up a new circuit); +and \emph{destroy} (to tear down a circuit). + +Relay cells have an additional header (the relay header) at the front +of the payload, containing a streamID (stream identifier: many streams can +be multiplexed over a circuit); an end-to-end checksum for integrity +checking; the length of the relay payload; and a relay command. +The entire contents of the relay header and the relay cell payload +are encrypted or decrypted together as the relay cell moves along the +circuit, using the 128-bit AES cipher in counter mode to generate a +cipher stream. The relay commands are: \emph{relay +data} (for data flowing down the stream), \emph{relay begin} (to open a +stream), \emph{relay end} (to close a stream cleanly), \emph{relay +teardown} (to close a broken stream), \emph{relay connected} +(to notify the OP that a relay begin has succeeded), \emph{relay +extend} and \emph{relay extended} (to extend the circuit by a hop, +and to acknowledge), \emph{relay truncate} and \emph{relay truncated} +(to tear down only part of the circuit, and to acknowledge), \emph{relay +sendme} (used for congestion control), and \emph{relay drop} (used to +implement long-range dummies). +We give a visual overview of cell structure plus the details of relay +cell structure, and then describe each of these cell types and commands +in more detail below. + +%\begin{figure}[h] +%\unitlength=1cm +%\centering +%\begin{picture}(8.0,1.5) +%\put(4,.5){\makebox(0,0)[c]{\epsfig{file=cell-struct,width=7cm}}} +%\end{picture} +%\end{figure} + +\begin{figure}[h] +\centering +\mbox{\epsfig{figure=cell-struct,width=7cm}} +\end{figure} + +\subsection{Circuits and streams} +\label{subsec:circuits} + +Onion Routing originally built one circuit for each +TCP stream. Because building a circuit can take several tenths of a +second (due to public-key cryptography and network latency), +this design imposed high costs on applications like web browsing that +open many TCP streams. + +In Tor, each circuit can be shared by many TCP streams. To avoid +delays, users construct circuits preemptively. To limit linkability +among their streams, users' OPs build a new circuit +periodically if the previous ones have been used, +and expire old used circuits that no longer have any open streams. +OPs consider rotating to a new circuit once a minute: thus +even heavy users spend negligible time +building circuits, but a limited number of requests can be linked +to each other through a given exit node. Also, because circuits are built +in the background, OPs can recover from failed circuit creation +without harming user experience.\\ + +\begin{figure}[h] +\centering +\mbox{\epsfig{figure=interaction,width=8.75cm}} +\caption{Alice builds a two-hop circuit and begins fetching a web page.} +\label{fig:interaction} +\end{figure} + +\noindent{\large\bf Constructing a circuit}\label{subsubsec:constructing-a-circuit}\\ +%\subsubsection{Constructing a circuit} +A user's OP constructs circuits incrementally, negotiating a +symmetric key with each OR on the circuit, one hop at a time. To begin +creating a new circuit, the OP (call her Alice) sends a +\emph{create} cell to the first node in her chosen path (call him Bob). +(She chooses a new +circID $C_{AB}$ not currently used on the connection from her to Bob.) +The \emph{create} cell's +payload contains the first half of the Diffie-Hellman handshake +($g^x$), encrypted to the onion key of the OR (call him Bob). Bob +responds with a \emph{created} cell containing $g^y$ +along with a hash of the negotiated key $K=g^{xy}$. + +Once the circuit has been established, Alice and Bob can send one +another relay cells encrypted with the negotiated +key.\footnote{Actually, the negotiated key is used to derive two + symmetric keys: one for each direction.} More detail is given in +the next section. + +To extend the circuit further, Alice sends a \emph{relay extend} cell +to Bob, specifying the address of the next OR (call her Carol), and +an encrypted $g^{x_2}$ for her. Bob copies the half-handshake into a +\emph{create} cell, and passes it to Carol to extend the circuit. +(Bob chooses a new circID $C_{BC}$ not currently used on the connection +between him and Carol. Alice never needs to know this circID; only Bob +associates $C_{AB}$ on his connection with Alice to $C_{BC}$ on +his connection with Carol.) +When Carol responds with a \emph{created} cell, Bob wraps the payload +into a \emph{relay extended} cell and passes it back to Alice. Now +the circuit is extended to Carol, and Alice and Carol share a common key +$K_2 = g^{x_2 y_2}$. + +To extend the circuit to a third node or beyond, Alice +proceeds as above, always telling the last node in the circuit to +extend one hop further. + +This circuit-level handshake protocol achieves unilateral entity +authentication (Alice knows she's handshaking with the OR, but +the OR doesn't care who is opening the circuit---Alice uses no public key +and remains anonymous) and unilateral key authentication +(Alice and the OR agree on a key, and Alice knows only the OR learns +it). It also achieves forward +secrecy and key freshness. More formally, the protocol is as follows +(where $E_{PK_{Bob}}(\cdot)$ is encryption with Bob's public key, +$H$ is a secure hash function, and $|$ is concatenation): +\begin{equation*} +\begin{aligned} +\mathrm{Alice} \rightarrow \mathrm{Bob}&: E_{PK_{Bob}}(g^x) \\ +\mathrm{Bob} \rightarrow \mathrm{Alice}&: g^y, H(K | \mathrm{``handshake"}) \\ +\end{aligned} +\end{equation*} + +\noindent In the second step, Bob proves that it was he who received $g^x$, +and who chose $y$. We use PK encryption in the first step +(rather than, say, using the first two steps of STS, which has a +signature in the second step) because a single cell is too small to +hold both a public key and a signature. Preliminary analysis with the +NRL protocol analyzer~\cite{meadows96} shows this protocol to be +secure (including perfect forward secrecy) under the +traditional Dolev-Yao model.\\ + +\noindent{\large\bf Relay cells}\\ +%\subsubsection{Relay cells} +% +Once Alice has established the circuit (so she shares keys with each +OR on the circuit), she can send relay cells. +%Recall that every relay cell has a streamID that indicates to which +%stream the cell belongs. %This streamID allows a relay cell to be +%addressed to any OR on the circuit. +Upon receiving a relay +cell, an OR looks up the corresponding circuit, and decrypts the relay +header and payload with the session key for that circuit. +If the cell is headed away from Alice the OR then checks whether the +decrypted cell has a valid digest (as an optimization, the first +two bytes of the integrity check are zero, so in most cases we can avoid +computing the hash). +%is recognized---either because it +%corresponds to an open stream at this OR for the given circuit, or because +%it is the control streamID (zero). +If valid, it accepts the relay cell and processes it as described +below. Otherwise, +the OR looks up the circID and OR for the +next step in the circuit, replaces the circID as appropriate, and +sends the decrypted relay cell to the next OR. (If the OR at the end +of the circuit receives an unrecognized relay cell, an error has +occurred, and the circuit is torn down.) + +OPs treat incoming relay cells similarly: they iteratively unwrap the +relay header and payload with the session keys shared with each +OR on the circuit, from the closest to farthest. +If at any stage the digest is valid, the cell must have +originated at the OR whose encryption has just been removed. + +To construct a relay cell addressed to a given OR, Alice assigns the +digest, and then iteratively +encrypts the cell payload (that is, the relay header and payload) with +the symmetric key of each hop up to that OR. Because the digest is +encrypted to a different value at each step, only at the targeted OR +will it have a meaningful value.\footnote{ + % Should we just say that 2^56 is itself negligible? + % Assuming 4-hop circuits with 10 streams per hop, there are 33 + % possible bad streamIDs before the last circuit. This still + % gives an error only once every 2 million terabytes (approx). +With 48 bits of digest per cell, the probability of an accidental +collision is far lower than the chance of hardware failure.} +This \emph{leaky pipe} circuit topology +allows Alice's streams to exit at different ORs on a single circuit. +Alice may choose different exit points because of their exit policies, +or to keep the ORs from knowing that two streams +originate from the same person. + +When an OR later replies to Alice with a relay cell, it +encrypts the cell's relay header and payload with the single key it +shares with Alice, and sends the cell back toward Alice along the +circuit. Subsequent ORs add further layers of encryption as they +relay the cell back to Alice. + +To tear down a circuit, Alice sends a \emph{destroy} control +cell. Each OR in the circuit receives the \emph{destroy} cell, closes +all streams on that circuit, and passes a new \emph{destroy} cell +forward. But just as circuits are built incrementally, they can also +be torn down incrementally: Alice can send a \emph{relay +truncate} cell to a single OR on a circuit. That OR then sends a +\emph{destroy} cell forward, and acknowledges with a +\emph{relay truncated} cell. Alice can then extend the circuit to +different nodes, without signaling to the intermediate nodes (or +a limited observer) that she has changed her circuit. +Similarly, if a node on the circuit goes down, the adjacent +node can send a \emph{relay truncated} cell back to Alice. Thus the +``break a node and see which circuits go down'' +attack~\cite{freedom21-security} is weakened. + +\subsection{Opening and closing streams} +\label{subsec:tcp} + +When Alice's application wants a TCP connection to a given +address and port, it asks the OP (via SOCKS) to make the +connection. The OP chooses the newest open circuit (or creates one if +needed), and chooses a suitable OR on that circuit to be the +exit node (usually the last node, but maybe others due to exit policy +conflicts; see Section~\ref{subsec:exitpolicies}.) The OP then opens +the stream by sending a \emph{relay begin} cell to the exit node, +using a new random streamID. Once the +exit node connects to the remote host, it responds +with a \emph{relay connected} cell. Upon receipt, the OP sends a +SOCKS reply to notify the application of its success. The OP +now accepts data from the application's TCP stream, packaging it into +\emph{relay data} cells and sending those cells along the circuit to +the chosen OR. + +There's a catch to using SOCKS, however---some applications pass the +alphanumeric hostname to the Tor client, while others resolve it into +an IP address first and then pass the IP address to the Tor client. If +the application does DNS resolution first, Alice thereby reveals her +destination to the remote DNS server, rather than sending the hostname +through the Tor network to be resolved at the far end. Common applications +like Mozilla and SSH have this flaw. + +With Mozilla, the flaw is easy to address: the filtering HTTP +proxy called Privoxy gives a hostname to the Tor client, so Alice's +computer never does DNS resolution. +But a portable general solution, such as is needed for +SSH, is +an open problem. Modifying or replacing the local nameserver +can be invasive, brittle, and unportable. Forcing the resolver +library to prefer TCP rather than UDP is hard, and also has +portability problems. Dynamically intercepting system calls to the +resolver library seems a promising direction. We could also provide +a tool similar to \emph{dig} to perform a private lookup through the +Tor network. Currently, we encourage the use of privacy-aware proxies +like Privoxy wherever possible. + +Closing a Tor stream is analogous to closing a TCP stream: it uses a +two-step handshake for normal operation, or a one-step handshake for +errors. If the stream closes abnormally, the adjacent node simply sends a +\emph{relay teardown} cell. If the stream closes normally, the node sends +a \emph{relay end} cell down the circuit, and the other side responds with +its own \emph{relay end} cell. Because +all relay cells use layered encryption, only the destination OR knows +that a given relay cell is a request to close a stream. This two-step +handshake allows Tor to support TCP-based applications that use half-closed +connections. +% such as broken HTTP clients that close their side of the +%stream after writing but are still willing to read. + +\subsection{Integrity checking on streams} +\label{subsec:integrity-checking} + +Because the old Onion Routing design used a stream cipher without integrity +checking, traffic was +vulnerable to a malleability attack: though the attacker could not +decrypt cells, any changes to encrypted data +would create corresponding changes to the data leaving the network. +This weakness allowed an adversary who could guess the encrypted content +to change a padding cell to a destroy +cell; change the destination address in a \emph{relay begin} cell to the +adversary's webserver; or change an FTP command from +{\tt dir} to {\tt rm~*}. (Even an external +adversary could do this, because the link encryption similarly used a +stream cipher.) + +Because Tor uses TLS on its links, external adversaries cannot modify +data. Addressing the insider malleability attack, however, is +more complex. + +We could do integrity checking of the relay cells at each hop, either +by including hashes or by using an authenticating cipher mode like +EAX~\cite{eax}, but there are some problems. First, these approaches +impose a message-expansion overhead at each hop, and so we would have to +either leak the path length or waste bytes by padding to a maximum +path length. Second, these solutions can only verify traffic coming +from Alice: ORs would not be able to produce suitable hashes for +the intermediate hops, since the ORs on a circuit do not know the +other ORs' session keys. Third, we have already accepted that our design +is vulnerable to end-to-end timing attacks; so tagging attacks performed +within the circuit provide no additional information to the attacker. + +Thus, we check integrity only at the edges of each stream. (Remember that +in our leaky-pipe circuit topology, a stream's edge could be any hop +in the circuit.) When Alice +negotiates a key with a new hop, they each initialize a SHA-1 +digest with a derivative of that key, +thus beginning with randomness that only the two of them know. +Then they each incrementally add to the SHA-1 digest the contents of +all relay cells they create, and include with each relay cell the +first four bytes of the current digest. Each also keeps a SHA-1 +digest of data received, to verify that the received hashes are correct. + +To be sure of removing or modifying a cell, the attacker must be able +to deduce the current digest state (which depends on all +traffic between Alice and Bob, starting with their negotiated key). +Attacks on SHA-1 where the adversary can incrementally add to a hash +to produce a new valid hash don't work, because all hashes are +end-to-end encrypted across the circuit. The computational overhead +of computing the digests is minimal compared to doing the AES +encryption performed at each hop of the circuit. We use only four +bytes per cell to minimize overhead; the chance that an adversary will +correctly guess a valid hash +%, plus the payload the current cell, +is +acceptably low, given that the OP or OR tear down the circuit if they +receive a bad hash. + +\subsection{Rate limiting and fairness} +\label{subsec:rate-limit} + +Volunteers are more willing to run services that can limit +their bandwidth usage. To accommodate them, Tor servers use a +token bucket approach~\cite{tannenbaum96} to +enforce a long-term average rate of incoming bytes, while still +permitting short-term bursts above the allowed bandwidth. +% Current bucket sizes are set to ten seconds' worth of traffic. + +%Further, we want to avoid starving any Tor streams. Entire circuits +%could starve if we read greedily from connections and one connection +%uses all the remaining bandwidth. We solve this by dividing the number +%of tokens in the bucket by the number of connections that want to read, +%and reading at most that number of bytes from each connection. We iterate +%this procedure until the number of tokens in the bucket is under some +%threshold (currently 10KB), at which point we greedily read from connections. + +Because the Tor protocol outputs about the same number of bytes as it +takes in, it is sufficient in practice to limit only incoming bytes. +With TCP streams, however, the correspondence is not one-to-one: +relaying a single incoming byte can require an entire 512-byte cell. +(We can't just wait for more bytes, because the local application may +be awaiting a reply.) Therefore, we treat this case as if the entire +cell size had been read, regardless of the cell's fullness. + +Further, inspired by Rennhard et al's design in~\cite{anonnet}, a +circuit's edges can heuristically distinguish interactive streams from bulk +streams by comparing the frequency with which they supply cells. We can +provide good latency for interactive streams by giving them preferential +service, while still giving good overall throughput to the bulk +streams. Such preferential treatment presents a possible end-to-end +attack, but an adversary observing both +ends of the stream can already learn this information through timing +attacks. + +\subsection{Congestion control} +\label{subsec:congestion} + +Even with bandwidth rate limiting, we still need to worry about +congestion, either accidental or intentional. If enough users choose the +same OR-to-OR connection for their circuits, that connection can become +saturated. For example, an attacker could send a large file +through the Tor network to a webserver he runs, and then +refuse to read any of the bytes at the webserver end of the +circuit. Without some congestion control mechanism, these bottlenecks +can propagate back through the entire network. We don't need to +reimplement full TCP windows (with sequence numbers, +the ability to drop cells when we're full and retransmit later, and so +on), +because TCP already guarantees in-order delivery of each +cell. +%But we need to investigate further the effects of the current +%parameters on throughput and latency, while also keeping privacy in mind; +%see Section~\ref{sec:maintaining-anonymity} for more discussion. +We describe our response below. + +\textbf{Circuit-level throttling:} +To control a circuit's bandwidth usage, each OR keeps track of two +windows. The \emph{packaging window} tracks how many relay data cells the OR is +allowed to package (from incoming TCP streams) for transmission back to the OP, +and the \emph{delivery window} tracks how many relay data cells it is willing +to deliver to TCP streams outside the network. Each window is initialized +(say, to 1000 data cells). When a data cell is packaged or delivered, +the appropriate window is decremented. When an OR has received enough +data cells (currently 100), it sends a \emph{relay sendme} cell towards the OP, +with streamID zero. When an OR receives a \emph{relay sendme} cell with +streamID zero, it increments its packaging window. Either of these cells +increments the corresponding window by 100. If the packaging window +reaches 0, the OR stops reading from TCP connections for all streams +on the corresponding circuit, and sends no more relay data cells until +receiving a \emph{relay sendme} cell. + +The OP behaves identically, except that it must track a packaging window +and a delivery window for every OR in the circuit. If a packaging window +reaches 0, it stops reading from streams destined for that OR. + +\textbf{Stream-level throttling}: +The stream-level congestion control mechanism is similar to the +circuit-level mechanism. ORs and OPs use \emph{relay sendme} cells +to implement end-to-end flow control for individual streams across +circuits. Each stream begins with a packaging window (currently 500 cells), +and increments the window by a fixed value (50) upon receiving a \emph{relay +sendme} cell. Rather than always returning a \emph{relay sendme} cell as soon +as enough cells have arrived, the stream-level congestion control also +has to check whether data has been successfully flushed onto the TCP +stream; it sends the \emph{relay sendme} cell only when the number of bytes pending +to be flushed is under some threshold (currently 10 cells' worth). + +%% Maybe omit this next paragraph. -NM +%Currently, non-data relay cells do not affect the windows. Thus we +%avoid potential deadlock issues, for example, arising because a stream +%can't send a \emph{relay sendme} cell when its packaging window is empty. + +These arbitrarily chosen parameters seem to give tolerable throughput +and delay; see Section~\ref{sec:in-the-wild}. + +\section{Rendezvous Points and hidden services} +\label{sec:rendezvous} + +Rendezvous points are a building block for \emph{location-hidden +services} (also known as \emph{responder anonymity}) in the Tor +network. Location-hidden services allow Bob to offer a TCP +service, such as a webserver, without revealing his IP address. +This type of anonymity protects against distributed DoS attacks: +attackers are forced to attack the onion routing network +because they do not know Bob's IP address. + +Our design for location-hidden servers has the following goals. +\textbf{Access-control:} Bob needs a way to filter incoming requests, +so an attacker cannot flood Bob simply by making many connections to him. +\textbf{Robustness:} Bob should be able to maintain a long-term pseudonymous +identity even in the presence of router failure. Bob's service must +not be tied to a single OR, and Bob must be able to migrate his service +across ORs. \textbf{Smear-resistance:} +A social attacker +should not be able to ``frame'' a rendezvous router by +offering an illegal or disreputable location-hidden service and +making observers believe the router created that service. +\textbf{Application-transparency:} Although we require users +to run special software to access location-hidden servers, we must not +require them to modify their applications. + +We provide location-hiding for Bob by allowing him to advertise +several onion routers (his \emph{introduction points}) as contact +points. He may do this on any robust efficient +key-value lookup system with authenticated updates, such as a +distributed hash table (DHT) like CFS~\cite{cfs:sosp01}.\footnote{ +Rather than rely on an external infrastructure, the Onion Routing network +can run the lookup service itself. Our current implementation provides a +simple lookup system on the +directory servers.} Alice, the client, chooses an OR as her +\emph{rendezvous point}. She connects to one of Bob's introduction +points, informs him of her rendezvous point, and then waits for him +to connect to the rendezvous point. This extra level of indirection +helps Bob's introduction points avoid problems associated with serving +unpopular files directly (for example, if Bob serves +material that the introduction point's community finds objectionable, +or if Bob's service tends to get attacked by network vandals). +The extra level of indirection also allows Bob to respond to some requests +and ignore others. + +\subsection{Rendezvous points in Tor} + +The following steps are +%We give an overview of the steps of a rendezvous. These are +performed on behalf of Alice and Bob by their local OPs; +application integration is described more fully below. + +\begin{tightlist} +\item Bob generates a long-term public key pair to identify his service. +\item Bob chooses some introduction points, and advertises them on + the lookup service, signing the advertisement with his public key. He + can add more later. +\item Bob builds a circuit to each of his introduction points, and tells + them to wait for requests. +\item Alice learns about Bob's service out of band (perhaps Bob told her, + or she found it on a website). She retrieves the details of Bob's + service from the lookup service. If Alice wants to access Bob's + service anonymously, she must connect to the lookup service via Tor. +\item Alice chooses an OR as the rendezvous point (RP) for her connection to + Bob's service. She builds a circuit to the RP, and gives it a + randomly chosen ``rendezvous cookie'' to recognize Bob. +\item Alice opens an anonymous stream to one of Bob's introduction + points, and gives it a message (encrypted with Bob's public key) + telling it about herself, + her RP and rendezvous cookie, and the + start of a DH + handshake. The introduction point sends the message to Bob. +\item If Bob wants to talk to Alice, he builds a circuit to Alice's + RP and sends the rendezvous cookie, the second half of the DH + handshake, and a hash of the session + key they now share. By the same argument as in + Section~\ref{subsubsec:constructing-a-circuit}, Alice knows she + shares the key only with Bob. +\item The RP connects Alice's circuit to Bob's. Note that RP can't + recognize Alice, Bob, or the data they transmit. +\item Alice sends a \emph{relay begin} cell along the circuit. It + arrives at Bob's OP, which connects to Bob's + webserver. +\item An anonymous stream has been established, and Alice and Bob + communicate as normal. +\end{tightlist} + +When establishing an introduction point, Bob provides the onion router +with the public key identifying his service. Bob signs his +messages, so others cannot usurp his introduction point +in the future. He uses the same public key to establish the other +introduction points for his service, and periodically refreshes his +entry in the lookup service. + +The message that Alice gives +the introduction point includes a hash of Bob's public key % to identify +%the service, along with +and an optional initial authorization token (the +introduction point can do prescreening, for example to block replays). Her +message to Bob may include an end-to-end authorization token so Bob +can choose whether to respond. +The authorization tokens can be used to provide selective access: +important users can get uninterrupted access. +%important users get tokens to ensure uninterrupted access. %to the +%service. +During normal situations, Bob's service might simply be offered +directly from mirrors, while Bob gives out tokens to high-priority users. If +the mirrors are knocked down, +%by distributed DoS attacks or even +%physical attack, +those users can switch to accessing Bob's service via +the Tor rendezvous system. + +Bob's introduction points are themselves subject to DoS---he must +open many introduction points or risk such an attack. +He can provide selected users with a current list or future schedule of +unadvertised introduction points; +this is most practical +if there is a stable and large group of introduction points +available. Bob could also give secret public keys +for consulting the lookup service. All of these approaches +limit exposure even when +some selected users collude in the DoS\@. + +\subsection{Integration with user applications} + +Bob configures his onion proxy to know the local IP address and port of his +service, a strategy for authorizing clients, and his public key. The onion +proxy anonymously publishes a signed statement of Bob's +public key, an expiration time, and +the current introduction points for his service onto the lookup service, +indexed +by the hash of his public key. Bob's webserver is unmodified, +and doesn't even know that it's hidden behind the Tor network. + +Alice's applications also work unchanged---her client interface +remains a SOCKS proxy. We encode all of the necessary information +into the fully qualified domain name (FQDN) Alice uses when establishing her +connection. Location-hidden services use a virtual top level domain +called {\tt .onion}: thus hostnames take the form {\tt x.y.onion} where +{\tt x} is the authorization cookie and {\tt y} encodes the hash of +the public key. Alice's onion proxy +examines addresses; if they're destined for a hidden server, it decodes +the key and starts the rendezvous as described above. + +\subsection{Previous rendezvous work} +%XXXX Should this get integrated into the earlier related work section? -NM + +Rendezvous points in low-latency anonymity systems were first +described for use in ISDN telephony~\cite{jerichow-jsac98,isdn-mixes}. +Later low-latency designs used rendezvous points for hiding location +of mobile phones and low-power location +trackers~\cite{federrath-ih96,reed-protocols97}. Rendezvous for +anonymizing low-latency +Internet connections was suggested in early Onion Routing +work~\cite{or-ih96}, but the first published design was by Ian +Goldberg~\cite{ian-thesis}. His design differs from +ours in three ways. First, Goldberg suggests that Alice should manually +hunt down a current location of the service via Gnutella; our approach +makes lookup transparent to the user, as well as faster and more robust. +Second, in Tor the client and server negotiate session keys +with Diffie-Hellman, so plaintext is not exposed even at the rendezvous +point. Third, +our design minimizes the exposure from running the +service, to encourage volunteers to offer introduction and rendezvous +services. Tor's introduction points do not output any bytes to the +clients; the rendezvous points don't know the client or the server, +and can't read the data being transmitted. The indirection scheme is +also designed to include authentication/authorization---if Alice doesn't +include the right cookie with her request for service, Bob need not even +acknowledge his existence. + +\section{Other design decisions} +\label{sec:other-design} + +\subsection{Denial of service} +\label{subsec:dos} + +Providing Tor as a public service creates many opportunities for +denial-of-service attacks against the network. While +flow control and rate limiting (discussed in +Section~\ref{subsec:congestion}) prevent users from consuming more +bandwidth than routers are willing to provide, opportunities remain for +users to +consume more network resources than their fair share, or to render the +network unusable for others. + +First of all, there are several CPU-consuming denial-of-service +attacks wherein an attacker can force an OR to perform expensive +cryptographic operations. For example, an attacker can +%\emph{create} cell full of junk bytes can force an OR to perform an RSA +%decrypt. +%Similarly, an attacker can +fake the start of a TLS handshake, forcing the OR to carry out its +(comparatively expensive) half of the handshake at no real computational +cost to the attacker. + +We have not yet implemented any defenses for these attacks, but several +approaches are possible. First, ORs can +require clients to solve a puzzle~\cite{puzzles-tls} while beginning new +TLS handshakes or accepting \emph{create} cells. So long as these +tokens are easy to verify and computationally expensive to produce, this +approach limits the attack multiplier. Additionally, ORs can limit +the rate at which they accept \emph{create} cells and TLS connections, +so that +the computational work of processing them does not drown out the +symmetric cryptography operations that keep cells +flowing. This rate limiting could, however, allow an attacker +to slow down other users when they build new circuits. + +% What about link-to-link rate limiting? + +Adversaries can also attack the Tor network's hosts and network +links. Disrupting a single circuit or link breaks all streams passing +along that part of the circuit. Users similarly lose service +when a router crashes or its operator restarts it. The current +Tor design treats such attacks as intermittent network failures, and +depends on users and applications to respond or recover as appropriate. A +future design could use an end-to-end TCP-like acknowledgment protocol, +so no streams are lost unless the entry or exit point is +disrupted. This solution would require more buffering at the network +edges, however, and the performance and anonymity implications from this +extra complexity still require investigation. + +\subsection{Exit policies and abuse} +\label{subsec:exitpolicies} + +% originally, we planned to put the "users only know the hostname, +% not the IP, but exit policies are by IP" problem here too. Not +% worth putting in the submission, but worth thinking about putting +% in sometime somehow. -RD + +Exit abuse is a serious barrier to wide-scale Tor deployment. Anonymity +presents would-be vandals and abusers with an opportunity to hide +the origins of their activities. Attackers can harm the Tor network by +implicating exit servers for their abuse. Also, applications that commonly +use IP-based authentication (such as institutional mail or webservers) +can be fooled by the fact that anonymous connections appear to originate +at the exit OR. + +We stress that Tor does not enable any new class of abuse. Spammers +and other attackers already have access to thousands of misconfigured +systems worldwide, and the Tor network is far from the easiest way +to launch attacks. +%Indeed, because of its limited +%anonymity, Tor is probably not a good way to commit crimes. +But because the +onion routers can be mistaken for the originators of the abuse, +and the volunteers who run them may not want to deal with the hassle of +explaining anonymity networks to irate administrators, we must block or limit +abuse through the Tor network. + +To mitigate abuse issues, each onion router's \emph{exit policy} +describes to which external addresses and ports the router will +connect. On one end of the spectrum are \emph{open exit} +nodes that will connect anywhere. On the other end are \emph{middleman} +nodes that only relay traffic to other Tor nodes, and \emph{private exit} +nodes that only connect to a local host or network. A private +exit can allow a client to connect to a given host or +network more securely---an external adversary cannot eavesdrop traffic +between the private exit and the final destination, and so is less sure of +Alice's destination and activities. Most onion routers in the current +network function as +\emph{restricted exits} that permit connections to the world at large, +but prevent access to certain abuse-prone addresses and services such +as SMTP. +The OR might also be able to authenticate clients to +prevent exit abuse without harming anonymity~\cite{or-discex00}. + +%The abuse issues on closed (e.g. military) networks are different +%from the abuse on open networks like the Internet. While these IP-based +%access controls are still commonplace on the Internet, on closed networks, +%nearly all participants will be honest, and end-to-end authentication +%can be assumed for important traffic. + +Many administrators use port restrictions to support only a +limited set of services, such as HTTP, SSH, or AIM. +This is not a complete solution, of course, since abuse opportunities for these +protocols are still well known. + +We have not yet encountered any abuse in the deployed network, but if +we do we should consider using proxies to clean traffic for certain +protocols as it leaves the network. For example, much abusive HTTP +behavior (such as exploiting buffer overflows or well-known script +vulnerabilities) can be detected in a straightforward manner. +Similarly, one could run automatic spam filtering software (such as +SpamAssassin) on email exiting the OR network. + +ORs may also rewrite exiting traffic to append +headers or other information indicating that the traffic has passed +through an anonymity service. This approach is commonly used +by email-only anonymity systems. ORs can also +run on servers with hostnames like {\tt anonymous} to further +alert abuse targets to the nature of the anonymous traffic. + +A mixture of open and restricted exit nodes allows the most +flexibility for volunteers running servers. But while having many +middleman nodes provides a large and robust network, +having only a few exit nodes reduces the number of points +an adversary needs to monitor for traffic analysis, and places a +greater burden on the exit nodes. This tension can be seen in the +Java Anon Proxy +cascade model, wherein only one node in each cascade needs to handle +abuse complaints---but an adversary only needs to observe the entry +and exit of a cascade to perform traffic analysis on all that +cascade's users. The hydra model (many entries, few exits) presents a +different compromise: only a few exit nodes are needed, but an +adversary needs to work harder to watch all the clients; see +Section~\ref{sec:conclusion}. + +Finally, we note that exit abuse must not be dismissed as a peripheral +issue: when a system's public image suffers, it can reduce the number +and diversity of that system's users, and thereby reduce the anonymity +of the system itself. Like usability, public perception is a +security parameter. Sadly, preventing abuse of open exit nodes is an +unsolved problem, and will probably remain an arms race for the +foreseeable future. The abuse problems faced by Princeton's CoDeeN +project~\cite{darkside} give us a glimpse of likely issues. + +\subsection{Directory Servers} +\label{subsec:dirservers} + +First-generation Onion Routing designs~\cite{freedom2-arch,or-jsac98} used +in-band network status updates: each router flooded a signed statement +to its neighbors, which propagated it onward. But anonymizing networks +have different security goals than typical link-state routing protocols. +For example, delays (accidental or intentional) +that can cause different parts of the network to have different views +of link-state and topology are not only inconvenient: they give +attackers an opportunity to exploit differences in client knowledge. +We also worry about attacks to deceive a +client about the router membership list, topology, or current network +state. Such \emph{partitioning attacks} on client knowledge help an +adversary to efficiently deploy resources +against a target~\cite{minion-design}. + +Tor uses a small group of redundant, well-known onion routers to +track changes in network topology and node state, including keys and +exit policies. Each such \emph{directory server} acts as an HTTP +server, so clients can fetch current network state +and router lists, and so other ORs can upload +state information. Onion routers periodically publish signed +statements of their state to each directory server. The directory servers +combine this information with their own views of network liveness, +and generate a signed description (a \emph{directory}) of the entire +network state. Client software is +pre-loaded with a list of the directory servers and their keys, +to bootstrap each client's view of the network. +% XXX this means that clients will be forced to upgrade as the +% XXX dirservers change or get compromised. argue that this is ok. + +When a directory server receives a signed statement for an OR, it +checks whether the OR's identity key is recognized. Directory +servers do not advertise unrecognized ORs---if they did, +an adversary could take over the network by creating many +servers~\cite{sybil}. Instead, new nodes must be approved by the +directory +server administrator before they are included. Mechanisms for automated +node approval are an area of active research, and are discussed more +in Section~\ref{sec:maintaining-anonymity}. + +Of course, a variety of attacks remain. An adversary who controls +a directory server can track clients by providing them different +information---perhaps by listing only nodes under its control, or by +informing only certain clients about a given node. Even an external +adversary can exploit differences in client knowledge: clients who use +a node listed on one directory server but not the others are vulnerable. + +Thus these directory servers must be synchronized and redundant, so +that they can agree on a common directory. Clients should only trust +this directory if it is signed by a threshold of the directory +servers. + +The directory servers in Tor are modeled after those in +Mixminion~\cite{minion-design}, but our situation is easier. First, +we make the +simplifying assumption that all participants agree on the set of +directory servers. Second, while Mixminion needs to predict node +behavior, Tor only needs a threshold consensus of the current +state of the network. Third, we assume that we can fall back to the +human administrators to discover and resolve problems when a consensus +directory cannot be reached. Since there are relatively few directory +servers (currently 3, but we expect as many as 9 as the network scales), +we can afford operations like broadcast to simplify the consensus-building +protocol. + +To avoid attacks where a router connects to all the directory servers +but refuses to relay traffic from other routers, the directory servers +must also build circuits and use them to anonymously test router +reliability~\cite{mix-acc}. Unfortunately, this defense is not yet +designed or +implemented. + +Using directory servers is simpler and more flexible than flooding. +Flooding is expensive, and complicates the analysis when we +start experimenting with non-clique network topologies. Signed +directories can be cached by other +onion routers, +so directory servers are not a performance +bottleneck when we have many users, and do not aid traffic analysis by +forcing clients to announce their existence to any +central point. + +\section{Attacks and Defenses} +\label{sec:attacks} + +Below we summarize a variety of attacks, and discuss how well our +design withstands them.\\ + +\noindent{\large\bf Passive attacks}\\ +\emph{Observing user traffic patterns.} Observing a user's connection +will not reveal her destination or data, but it will +reveal traffic patterns (both sent and received). Profiling via user +connection patterns requires further processing, because multiple +application streams may be operating simultaneously or in series over +a single circuit. + +\emph{Observing user content.} While content at the user end is encrypted, +connections to responders may not be (indeed, the responding website +itself may be hostile). While filtering content is not a primary goal +of Onion Routing, Tor can directly use Privoxy and related +filtering services to anonymize application data streams. + +\emph{Option distinguishability.} We allow clients to choose +configuration options. For example, clients concerned about request +linkability should rotate circuits more often than those concerned +about traceability. Allowing choice may attract users with different +%There is economic incentive to attract users by +%allowing this choice; +needs; but clients who are +in the minority may lose more anonymity by appearing distinct than they +gain by optimizing their behavior~\cite{econymics}. + +\emph{End-to-end timing correlation.} Tor only minimally hides +such correlations. An attacker watching patterns of +traffic at the initiator and the responder will be +able to confirm the correspondence with high probability. The +greatest protection currently available against such confirmation is to hide +the connection between the onion proxy and the first Tor node, +by running the OP on the Tor node or behind a firewall. This approach +requires an observer to separate traffic originating at the onion +router from traffic passing through it: a global observer can do this, +but it might be beyond a limited observer's capabilities. + +\emph{End-to-end size correlation.} Simple packet counting +will also be effective in confirming +endpoints of a stream. However, even without padding, we may have some +limited protection: the leaky pipe topology means different numbers +of packets may enter one end of a circuit than exit at the other. + +\emph{Website fingerprinting.} All the effective passive +attacks above are traffic confirmation attacks, +which puts them outside our design goals. There is also +a passive traffic analysis attack that is potentially effective. +Rather than searching exit connections for timing and volume +correlations, the adversary may build up a database of +``fingerprints'' containing file sizes and access patterns for +targeted websites. He can later confirm a user's connection to a given +site simply by consulting the database. This attack has +been shown to be effective against SafeWeb~\cite{hintz-pet02}. +It may be less effective against Tor, since +streams are multiplexed within the same circuit, and +fingerprinting will be limited to +the granularity of cells (currently 512 bytes). Additional +defenses could include +larger cell sizes, padding schemes to group websites +into large sets, and link +padding or long-range dummies.\footnote{Note that this fingerprinting +attack should not be confused with the much more complicated latency +attacks of~\cite{back01}, which require a fingerprint of the latencies +of all circuits through the network, combined with those from the +network edges to the target user and the responder website.}\\ + +\noindent{\large\bf Active attacks}\\ +\emph{Compromise keys.} An attacker who learns the TLS session key can +see control cells and encrypted relay cells on every circuit on that +connection; learning a circuit +session key lets him unwrap one layer of the encryption. An attacker +who learns an OR's TLS private key can impersonate that OR for the TLS +key's lifetime, but he must +also learn the onion key to decrypt \emph{create} cells (and because of +perfect forward secrecy, he cannot hijack already established circuits +without also compromising their session keys). Periodic key rotation +limits the window of opportunity for these attacks. On the other hand, +an attacker who learns a node's identity key can replace that node +indefinitely by sending new forged descriptors to the directory servers. + +\emph{Iterated compromise.} A roving adversary who can +compromise ORs (by system intrusion, legal coercion, or extralegal +coercion) could march down the circuit compromising the +nodes until he reaches the end. Unless the adversary can complete +this attack within the lifetime of the circuit, however, the ORs +will have discarded the necessary information before the attack can +be completed. (Thanks to the perfect forward secrecy of session +keys, the attacker cannot force nodes to decrypt recorded +traffic once the circuits have been closed.) Additionally, building +circuits that cross jurisdictions can make legal coercion +harder---this phenomenon is commonly called ``jurisdictional +arbitrage.'' The Java Anon Proxy project recently experienced the +need for this approach, when +a German court forced them to add a backdoor to +their nodes~\cite{jap-backdoor}. + +\emph{Run a recipient.} An adversary running a webserver +trivially learns the timing patterns of users connecting to it, and +can introduce arbitrary patterns in its responses. +End-to-end attacks become easier: if the adversary can induce +users to connect to his webserver (perhaps by advertising +content targeted to those users), he now holds one end of their +connection. There is also a danger that application +protocols and associated programs can be induced to reveal information +about the initiator. Tor depends on Privoxy and similar protocol cleaners +to solve this latter problem. + +\emph{Run an onion proxy.} It is expected that end users will +nearly always run their own local onion proxy. However, in some +settings, it may be necessary for the proxy to run +remotely---typically, in institutions that want +to monitor the activity of those connecting to the proxy. +Compromising an onion proxy compromises all future connections +through it. + +\emph{DoS non-observed nodes.} An observer who can only watch some +of the Tor network can increase the value of this traffic +by attacking non-observed nodes to shut them down, reduce +their reliability, or persuade users that they are not trustworthy. +The best defense here is robustness. + +\emph{Run a hostile OR.} In addition to being a local observer, +an isolated hostile node can create circuits through itself, or alter +traffic patterns to affect traffic at other nodes. Nonetheless, a hostile +node must be immediately adjacent to both endpoints to compromise the +anonymity of a circuit. If an adversary can +run multiple ORs, and can persuade the directory servers +that those ORs are trustworthy and independent, then occasionally +some user will choose one of those ORs for the start and another +as the end of a circuit. If an adversary +controls $m>1$ of $N$ nodes, he can correlate at most +$\left(\frac{m}{N}\right)^2$ of the traffic---although an +adversary +could still attract a disproportionately large amount of traffic +by running an OR with a permissive exit policy, or by +degrading the reliability of other routers. + +\emph{Introduce timing into messages.} This is simply a stronger +version of passive timing attacks already discussed earlier. + +\emph{Tagging attacks.} A hostile node could ``tag'' a +cell by altering it. If the +stream were, for example, an unencrypted request to a Web site, +the garbled content coming out at the appropriate time would confirm +the association. However, integrity checks on cells prevent +this attack. + +\emph{Replace contents of unauthenticated protocols.} When +relaying an unauthenticated protocol like HTTP, a hostile exit node +can impersonate the target server. Clients +should prefer protocols with end-to-end authentication. + +\emph{Replay attacks.} Some anonymity protocols are vulnerable +to replay attacks. Tor is not; replaying one side of a handshake +will result in a different negotiated session key, and so the rest +of the recorded session can't be used. + +\emph{Smear attacks.} An attacker could use the Tor network for +socially disapproved acts, to bring the +network into disrepute and get its operators to shut it down. +Exit policies reduce the possibilities for abuse, but +ultimately the network requires volunteers who can tolerate +some political heat. + +\emph{Distribute hostile code.} An attacker could trick users +into running subverted Tor software that did not, in fact, anonymize +their connections---or worse, could trick ORs into running weakened +software that provided users with less anonymity. We address this +problem (but do not solve it completely) by signing all Tor releases +with an official public key, and including an entry in the directory +that lists which versions are currently believed to be secure. To +prevent an attacker from subverting the official release itself +(through threats, bribery, or insider attacks), we provide all +releases in source code form, encourage source audits, and +frequently warn our users never to trust any software (even from +us) that comes without source.\\ + +\noindent{\large\bf Directory attacks}\\ +\emph{Destroy directory servers.} If a few directory +servers disappear, the others still decide on a valid +directory. So long as any directory servers remain in operation, +they will still broadcast their views of the network and generate a +consensus directory. (If more than half are destroyed, this +directory will not, however, have enough signatures for clients to +use it automatically; human intervention will be necessary for +clients to decide whether to trust the resulting directory.) + +\emph{Subvert a directory server.} By taking over a directory server, +an attacker can partially influence the final directory. Since ORs +are included or excluded by majority vote, the corrupt directory can +at worst cast a tie-breaking vote to decide whether to include +marginal ORs. It remains to be seen how often such marginal cases +occur in practice. + +\emph{Subvert a majority of directory servers.} An adversary who controls +more than half the directory servers can include as many compromised +ORs in the final directory as he wishes. We must ensure that directory +server operators are independent and attack-resistant. + +\emph{Encourage directory server dissent.} The directory +agreement protocol assumes that directory server operators agree on +the set of directory servers. An adversary who can persuade some +of the directory server operators to distrust one another could +split the quorum into mutually hostile camps, thus partitioning +users based on which directory they use. Tor does not address +this attack. + +\emph{Trick the directory servers into listing a hostile OR.} +Our threat model explicitly assumes directory server operators will +be able to filter out most hostile ORs. +% If this is not true, an +% attacker can flood the directory with compromised servers. + +\emph{Convince the directories that a malfunctioning OR is +working.} In the current Tor implementation, directory servers +assume that an OR is running correctly if they can start a TLS +connection to it. A hostile OR could easily subvert this test by +accepting TLS connections from ORs but ignoring all cells. Directory +servers must actively test ORs by building circuits and streams as +appropriate. The tradeoffs of a similar approach are discussed +in~\cite{mix-acc}.\\ + +\noindent{\large\bf Attacks against rendezvous points}\\ +\emph{Make many introduction requests.} An attacker could +try to deny Bob service by flooding his introduction points with +requests. Because the introduction points can block requests that +lack authorization tokens, however, Bob can restrict the volume of +requests he receives, or require a certain amount of computation for +every request he receives. + +\emph{Attack an introduction point.} An attacker could +disrupt a location-hidden service by disabling its introduction +points. But because a service's identity is attached to its public +key, the service can simply re-advertise +itself at a different introduction point. Advertisements can also be +done secretly so that only high-priority clients know the address of +Bob's introduction points or so that different clients know of different +introduction points. This forces the attacker to disable all possible +introduction points. + +\emph{Compromise an introduction point.} An attacker who controls +Bob's introduction point can flood Bob with +introduction requests, or prevent valid introduction requests from +reaching him. Bob can notice a flood, and close the circuit. To notice +blocking of valid requests, however, he should periodically test the +introduction point by sending rendezvous requests and making +sure he receives them. + +\emph{Compromise a rendezvous point.} A rendezvous +point is no more sensitive than any other OR on +a circuit, since all data passing through the rendezvous is encrypted +with a session key shared by Alice and Bob. + +\section{Early experiences: Tor in the Wild} +\label{sec:in-the-wild} + +As of mid-May 2004, the Tor network consists of 32 nodes +(24 in the US, 8 in Europe), and more are joining each week as the code +matures. (For comparison, the current remailer network +has about 40 nodes.) % We haven't asked PlanetLab to provide +%Tor nodes, since their AUP wouldn't allow exit nodes (see +%also~\cite{darkside}) and because we aim to build a long-term community of +%node operators and developers.} +Each node has at least a 768Kb/768Kb connection, and +many have 10Mb. The number of users varies (and of course, it's hard to +tell for sure), but we sometimes have several hundred users---administrators at +several companies have begun sending their entire departments' web +traffic through Tor, to block other divisions of +their company from reading their traffic. Tor users have reported using +the network for web browsing, FTP, IRC, AIM, Kazaa, SSH, and +recipient-anonymous email via rendezvous points. One user has anonymously +set up a Wiki as a hidden service, where other users anonymously publish +the addresses of their hidden services. + +Each Tor node currently processes roughly 800,000 relay +cells (a bit under half a gigabyte) per week. On average, about 80\% +of each 498-byte payload is full for cells going back to the client, +whereas about 40\% is full for cells coming from the client. (The difference +arises because most of the network's traffic is web browsing.) Interactive +traffic like SSH brings down the average a lot---once we have more +experience, and assuming we can resolve the anonymity issues, we may +partition traffic into two relay cell sizes: one to handle +bulk traffic and one for interactive traffic. + +Based in part on our restrictive default exit policy (we +reject SMTP requests) and our low profile, we have had no abuse +issues since the network was deployed in October +2003. Our slow growth rate gives us time to add features, +resolve bugs, and get a feel for what users actually want from an +anonymity system. Even though having more users would bolster our +anonymity sets, we are not eager to attract the Kazaa or warez +communities---we feel that we must build a reputation for privacy, human +rights, research, and other socially laudable activities. + +As for performance, profiling shows that Tor spends almost +all its CPU time in AES, which is fast. Current latency is attributable +to two factors. First, network latency is critical: we are +intentionally bouncing traffic around the world several times. Second, +our end-to-end congestion control algorithm focuses on protecting +volunteer servers from accidental DoS rather than on optimizing +performance. % Right now the first $500 \times 500\mbox{B}=250\mbox{KB}$ +%of the stream arrives +%quickly, and after that throughput depends on the rate that \emph{relay +%sendme} acknowledgments arrive. +To quantify these effects, we did some informal tests using a network of 4 +nodes on the same machine (a heavily loaded 1GHz Athlon). We downloaded a 60 +megabyte file from {\tt debian.org} every 30 minutes for 54 hours (108 sample +points). It arrived in about 300 seconds on average, compared to 210s for a +direct download. We ran a similar test on the production Tor network, +fetching the front page of {\tt cnn.com} (55 kilobytes): +% every 20 seconds for 8952 data points +while a direct +download consistently took about 0.3s, the performance through Tor varied. +Some downloads were as fast as 0.4s, with a median at 2.8s, and +90\% finishing within 5.3s. It seems that as the network expands, the chance +of building a slow circuit (one that includes a slow or heavily loaded node +or link) is increasing. On the other hand, as our users remain satisfied +with this increased latency, we can address our performance incrementally as we +proceed with development. %\footnote{For example, we have just begun pushing +%a pipelining patch to the production network that seems to decrease +%latency for medium-to-large files; we will present revised benchmarks +%as they become available.} + +%With the current network's topology and load, users can typically get 1-2 +%megabits sustained transfer rate, which is good enough for now. +%Indeed, the Tor +%design aims foremost to provide a security research platform; performance +%only needs to be sufficient to retain users~\cite{econymics,back01}. +%We can tweak the congestion control +%parameters to provide faster throughput at the cost of +%larger buffers at each node; adding the heuristics mentioned in +%Section~\ref{subsec:rate-limit} to favor low-volume +%streams may also help. More research remains to find the +%right balance. +% We should say _HOW MUCH_ latency there is in these cases. -NM + +%performs badly on lossy networks. may need airhook or something else as +%transport alternative? + +Although Tor's clique topology and full-visibility directories present +scaling problems, we still expect the network to support a few hundred +nodes and maybe 10,000 users before we're forced to become +more distributed. With luck, the experience we gain running the current +topology will help us choose among alternatives when the time comes. + +\section{Open Questions in Low-latency Anonymity} +\label{sec:maintaining-anonymity} + +In addition to the non-goals in +Section~\ref{subsec:non-goals}, many questions must be solved +before we can be confident of Tor's security. + +Many of these open issues are questions of balance. For example, +how often should users rotate to fresh circuits? Frequent rotation +is inefficient, expensive, and may lead to intersection attacks and +predecessor attacks~\cite{wright03}, but infrequent rotation makes the +user's traffic linkable. Besides opening fresh circuits, clients can +also exit from the middle of the circuit, +or truncate and re-extend the circuit. More analysis is +needed to determine the proper tradeoff. + +%% Duplicated by 'Better directory distribution' in section 9. +% +%A similar question surrounds timing of directory operations: how often +%should directories be updated? Clients that update infrequently receive +%an inaccurate picture of the network, but frequent updates can overload +%the directory servers. More generally, we must find more +%decentralized yet practical ways to distribute up-to-date snapshots of +%network status without introducing new attacks. + +How should we choose path lengths? If Alice always uses two hops, +then both ORs can be certain that by colluding they will learn about +Alice and Bob. In our current approach, Alice always chooses at least +three nodes unrelated to herself and her destination. +%% This point is subtle, but not IMO necessary. Anybody who thinks +%% about it will see that it's implied by the above sentence; anybody +%% who doesn't think about it is safe in his ignorance. +% +%Thus normally she chooses +%three nodes, but if she is running an OR and her destination is on an OR, +%she uses five. +Should Alice choose a random path length (e.g.~from a geometric +distribution) to foil an attacker who +uses timing to learn that he is the fifth hop and thus concludes that +both Alice and the responder are running ORs? + +Throughout this paper, we have assumed that end-to-end traffic +confirmation will immediately and automatically defeat a low-latency +anonymity system. Even high-latency anonymity systems can be +vulnerable to end-to-end traffic confirmation, if the traffic volumes +are high enough, and if users' habits are sufficiently +distinct~\cite{statistical-disclosure,limits-open}. Can anything be +done to +make low-latency systems resist these attacks as well as high-latency +systems? Tor already makes some effort to conceal the starts and ends of +streams by wrapping long-range control commands in identical-looking +relay cells. Link padding could frustrate passive observers who count +packets; long-range padding could work against observers who own the +first hop in a circuit. But more research remains to find an efficient +and practical approach. Volunteers prefer not to run constant-bandwidth +padding; but no convincing traffic shaping approach has been +specified. Recent work on long-range padding~\cite{defensive-dropping} +shows promise. One could also try to reduce correlation in packet timing +by batching and re-ordering packets, but it is unclear whether this could +improve anonymity without introducing so much latency as to render the +network unusable. + +A cascade topology may better defend against traffic confirmation by +aggregating users, and making padding and +mixing more affordable. Does the hydra topology (many input nodes, +few output nodes) work better against some adversaries? Are we going +to get a hydra anyway because most nodes will be middleman nodes? + +Common wisdom suggests that Alice should run her own OR for best +anonymity, because traffic coming from her node could plausibly have +come from elsewhere. How much mixing does this approach need? Is it +immediately beneficial because of real-world adversaries that can't +observe Alice's router, but can run routers of their own? + +To scale to many users, and to prevent an attacker from observing the +whole network, it may be necessary +to support far more servers than Tor currently anticipates. +This introduces several issues. First, if approval by a central set +of directory servers is no longer feasible, what mechanism should be used +to prevent adversaries from signing up many colluding servers? Second, +if clients can no longer have a complete picture of the network, +how can they perform discovery while preventing attackers from +manipulating or exploiting gaps in their knowledge? Third, if there +are too many servers for every server to constantly communicate with +every other, which non-clique topology should the network use? +(Restricted-route topologies promise comparable anonymity with better +scalability~\cite{danezis-pets03}, but whatever topology we choose, we +need some way to keep attackers from manipulating their position within +it~\cite{casc-rep}.) Fourth, if no central authority is tracking +server reliability, how do we stop unreliable servers from making +the network unusable? Fifth, do clients receive so much anonymity +from running their own ORs that we should expect them all to do +so~\cite{econymics}, or do we need another incentive structure to +motivate them? Tarzan and MorphMix present possible solutions. + +% advogato, captcha + +When a Tor node goes down, all its circuits (and thus streams) must break. +Will users abandon the system because of this brittleness? How well +does the method in Section~\ref{subsec:dos} allow streams to survive +node failure? If affected users rebuild circuits immediately, how much +anonymity is lost? It seems the problem is even worse in a peer-to-peer +environment---such systems don't yet provide an incentive for peers to +stay connected when they're done retrieving content, so we would expect +a higher churn rate. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{Future Directions} +\label{sec:conclusion} + +Tor brings together many innovations into a unified deployable system. The +next immediate steps include: + +\emph{Scalability:} Tor's emphasis on deployability and design simplicity +has led us to adopt a clique topology, semi-centralized +directories, and a full-network-visibility model for client +knowledge. These properties will not scale past a few hundred servers. +Section~\ref{sec:maintaining-anonymity} describes some promising +approaches, but more deployment experience will be helpful in learning +the relative importance of these bottlenecks. + +\emph{Bandwidth classes:} This paper assumes that all ORs have +good bandwidth and latency. We should instead adopt the MorphMix model, +where nodes advertise their bandwidth level (DSL, T1, T3), and +Alice avoids bottlenecks by choosing nodes that match or +exceed her bandwidth. In this way DSL users can usefully join the Tor +network. + +\emph{Incentives:} Volunteers who run nodes are rewarded with publicity +and possibly better anonymity~\cite{econymics}. More nodes means increased +scalability, and more users can mean more anonymity. We need to continue +examining the incentive structures for participating in Tor. Further, +we need to explore more approaches to limiting abuse, and understand +why most people don't bother using privacy systems. + +\emph{Cover traffic:} Currently Tor omits cover traffic---its costs +in performance and bandwidth are clear but its security benefits are +not well understood. We must pursue more research on link-level cover +traffic and long-range cover traffic to determine whether some simple padding +method offers provable protection against our chosen adversary. + +%%\emph{Offer two relay cell sizes:} Traffic on the Internet tends to be +%%large for bulk transfers and small for interactive traffic. One cell +%%size cannot be optimal for both types of traffic. +% This should go in the spec and todo, but not the paper yet. -RD + +\emph{Caching at exit nodes:} Perhaps each exit node should run a +caching web proxy~\cite{shsm03}, to improve anonymity for cached pages +(Alice's request never +leaves the Tor network), to improve speed, and to reduce bandwidth cost. +On the other hand, forward security is weakened because caches +constitute a record of retrieved files. We must find the right +balance between usability and security. + +\emph{Better directory distribution:} +Clients currently download a description of +the entire network every 15 minutes. As the state grows larger +and clients more numerous, we may need a solution in which +clients receive incremental updates to directory state. +More generally, we must find more +scalable yet practical ways to distribute up-to-date snapshots of +network status without introducing new attacks. + +\emph{Further specification review:} Our public +byte-level specification~\cite{tor-spec} needs +external review. We hope that as Tor +is deployed, more people will examine its +specification. + +\emph{Multisystem interoperability:} We are currently working with the +designer of MorphMix to unify the specification and implementation of +the common elements of our two systems. So far, this seems +to be relatively straightforward. Interoperability will allow testing +and direct comparison of the two designs for trust and scalability. + +\emph{Wider-scale deployment:} The original goal of Tor was to +gain experience in deploying an anonymizing overlay network, and +learn from having actual users. We are now at a point in design +and development where we can start deploying a wider network. Once +we have many actual users, we will doubtlessly be better +able to evaluate some of our design decisions, including our +robustness/latency tradeoffs, our performance tradeoffs (including +cell size), our abuse-prevention mechanisms, and +our overall usability. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% commented out for anonymous submission +\section*{Acknowledgments} + We thank Peter Palfrader, Geoff Goodell, Adam Shostack, Joseph Sokol-Margolis, + John Bashinski, and Zack Brown + for editing and comments; + Matej Pfajfar, Andrei Serjantov, Marc Rennhard for design discussions; + Bram Cohen for congestion control discussions; + Adam Back for suggesting telescoping circuits; and + Cathy Meadows for formal analysis of the \emph{extend} protocol. + This work has been supported by ONR and DARPA. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\bibliographystyle{latex8} +\bibliography{tor-design} + +\end{document} + +% Style guide: +% U.S. spelling +% avoid contractions (it's, can't, etc.) +% prefer ``for example'' or ``such as'' to e.g. +% prefer ``that is'' to i.e. +% 'mix', 'mixes' (as noun) +% 'mix-net' +% 'mix', 'mixing' (as verb) +% 'middleman' [Not with a hyphen; the hyphen has been optional +% since Middle English.] +% 'nymserver' +% 'Cypherpunk', 'Cypherpunks', 'Cypherpunk remailer' +% 'Onion Routing design', 'onion router' [note capitalization] +% 'SOCKS' +% Try not to use \cite as a noun. +% 'Authorizating' sounds great, but it isn't a word. +% 'First, second, third', not 'Firstly, secondly, thirdly'. +% 'circuit', not 'channel' +% Typography: no space on either side of an em dash---ever. +% Hyphens are for multi-part words; en dashs imply movement or +% opposition (The Alice--Bob connection); and em dashes are +% for punctuation---like that. +% A relay cell; a control cell; a \emph{create} cell; a +% \emph{relay truncated} cell. Never ``a \emph{relay truncated}.'' +% +% 'Substitute ``Damn'' every time you're inclined to write ``very;'' your +% editor will delete it and the writing will be just as it should be.' +% -- Mark Twain diff --git a/tags/tor-0_0_8pre1/doc/tor-doc.css b/tags/tor-0_0_8pre1/doc/tor-doc.css new file mode 100644 index 0000000000..82d673b1ac --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-doc.css @@ -0,0 +1,89 @@ +/* ******************************************************* + $Id$ + + This file is maintained in CVS; edit the version in the + repository. + ******************************************************* + */ + +BODY { + background-color: #FFF; + color: #000; +} + +P, TD, TH, DD, DT, LI { + font-family: lucida, "Lucida Sans", "Geneva", sans-serif; +} + +TH, DT { + font-weight: bold; +} + +H1, H2, H3, H4, H5, H6 { + font-family: lucida, "Lucida Sans", "Geneva", sans-serif; +} + +H1 { + text-align: center; +} + +H2, H3, H4, H5, H6 { + text-align: left; +} + +H2 { + background-color: #AFA; + color: #000; + padding: 2px 2px 2px 2px; + border-width: 2px 2px 2px 2px; + border-color: #000; + border-style: solid; +} + +H3 { + background-color: #CCF; + color: #000; + padding: 2px 2px 2px 2px; + margin: 0 0 0 1em; + border-width: 2px 2px 2px 2px; + border-color: #000; + border-style: solid; +} + +DIV.answer { + margin: 0 0 0 2em; +} + +SPAN.heading { + background-color: #ABF; + color: #000; + border-width: 1px; + padding: 0 0.2em 0 0.2em; + border-color: #000; + border-style: solid; +} + +SPAN.date { + background-color: #FFF; + color: #0A0; + font-weight: bold; +} + +P.date { + background-color: #FFF; + color: #0A0; + font-weight: bold; + margin-bottom: 0; +} + +P.news { + margin-top: 0; + margin-left: 3em; +} + +P.credit { + font-size: smaller; + font-style: italic; + padding-left: 3px; + border-left: 3px solid; +} \ No newline at end of file diff --git a/tags/tor-0_0_8pre1/doc/tor-doc.html b/tags/tor-0_0_8pre1/doc/tor-doc.html new file mode 100644 index 0000000000..4e521b371b --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-doc.html @@ -0,0 +1,341 @@ + + +Tor: an anonymizing overlay network for TCP + + + + + + + + +

    Tor documentation

    + +

    The simple version: Tor provides a distributed network of servers +("onion routers"). Users bounce their TCP streams (web traffic, FTP, SSH, +etc.) around the routers. This makes it hard for recipients, observers, and +even the onion routers themselves to track the source of the stream.

    + +

    The complex version: Onion Routing is a connection-oriented anonymizing +communication service. Users choose a source-routed path through a set of +nodes, and negotiate a "virtual circuit" through the network, in which +each node knows its predecessor and successor, but no others. Traffic +flowing down the circuit is unwrapped by a symmetric key at each node, +which reveals the downstream node.

    + + +

    Why should I use Tor?

    + +

    Individuals need Tor for privacy: +

      +
    • Privacy in web browsing -- both from the remote website (so it can't +track and sell your behavior), and similarly from your local ISP. +
    • Safety in web browsing: if your local government doesn't approve +of its citizens visiting certain websites, they may monitor the sites +and put readers on a list of suspicious persons. +
    • Circumvention of local censorship: connect to resources (news +sites, instant messaging, etc) that are restricted from your +ISP/school/company/government. +
    • Socially sensitive communication: chat rooms and web forums for +rape and abuse survivors, or people with illnesses. +
    + +

    Journalists and NGOs need Tor for safety: +

      +
    • Allowing dissidents and whistleblowers to communicate more safely. +
    • Censorship-resistant publication, such as making available your +home-made movie anonymously via a Tor hidden +service; and reading, e.g. of news sites not permitted in some +countries. +
    • Allowing their agents to check back with their home website while +they're in a foreign country, without notifying everybody nearby that +they're working with that organization. +
    + +

    Companies need Tor for business security: +

      +
    • Competitive analysis: browse the competition's website safely. +
    • Protecting collaborations of sensitive business units or partners. +
    • Protecting procurement suppliers or patterns. +
    • Putting the "P" back in "VPN": traditional VPNs reveal the exact +amount and frequency of communication. Which locations have employees +working late? Which locations have employees consulting job-hunting +websites? Which research groups are communicating with your company's +patent lawyers? +
    + +

    Governments need Tor for traffic-analysis-resistant communication: +

      +
    • Open source intelligence gathering (hiding individual analysts is +not enough -- the organization itself may be sensitive). +
    • Defense in depth on open and classified networks -- networks +with a million users (even if they're all cleared) can't be made safe just +by hardening them to external threat. +
    • Dynamic and semi-trusted international coalitions: the network can +be shared without revealing the existence or amount of communication +between all parties. +
    • Networks partially under known hostile control: to block +communications, the enemy must take down the whole network. +
    • Politically sensitive negotations. +
    • Road warriors. +
    • Protecting procurement patterns. +
    • Anonymous tips. +
    + +

    Law enforcement needs Tor for safety: +

      +
    • Allowing anonymous tips or crime reporting +
    • Allowing agents to observe websites without notifying them that +they're being observed (or, more broadly, without having it be an +official visit from law enforcement). +
    • Surveillance and honeypots (sting operations) +
    + +

    Does the idea of sharing the Tor network with +all of these groups bother you? It shouldn't -- you need them for +your security.

    + + +

    Should I run a client or a server?

    + +

    You can run Tor in either client mode or server mode. By default, +everybody is a client. This means you don't relay traffic for +anybody but yourself.

    + +

    If you have less than 768kbit in both directions, you should stay +a client. Otherwise, please consider being a server, to help out the +network. (Currently each server uses 20-30 gigabytes of traffic +per month; but that may go up.)

    + +

    Note that you can be a server without allowing users to make +connections from your computer to the outside world. This is called being +a middleman server.

    + +

    Benefits of running a server include: +

      +
    • Clients are generally limited to 100KB/s, whereas servers can inject +or receive as much traffic as they want. +
    • You may get stronger anonymity, since your destination can't know +whether connections relayed through your computer originated at your +computer or not. +
    • You can also get stronger anonymity by configuring your Tor clients +to use your Tor server for entry or for exit. +
    • You're helping me with development and scalability testing. +
    • You're helping your fellow Internet users by providing a larger +network. Also, having servers in many different pieces of the Internet +gives users more robustness against curious telcos and brute force +attacks. +
    + +

    You can read more about setting up Tor as a +server below.

    + + +

    Installing Tor

    + +

    You can get the latest releases here.

    + +

    If you got Tor from a tarball, unpack it: tar xzf +tor-0.0.7.tar.gz; cd tor-0.0.7. Run ./configure, then +make, and then make install (as root if necessary). Then +you can launch tor from the command-line by running tor.

    + +

    If you got Tor from the Win32 .exe file, you +can just click-click it (you may need to install OpenSSL +0.9.7 first, if you get an error about missing +libeay32.dll.) You might also want to run Tor in a dos window, +so you can see its logs, and see its error messages if it +crashes. If you don't want the default configuration, fetch the torrc, edit it, +and use tor.exe -f torrc.

    + +

    Otherwise, if you got it prepackaged (e.g. in the Debian package or Gentoo +package), these steps are already done for you, and you may +even already have Tor started in the background (logging to +/var/log/something).

    + +

    In any case, see the next section for what to do with it now that +you've got it running.

    + + +

    Configuring a client

    + +

    Tor comes configured as a client by default. It uses a built-in +default configuration file, and most people won't need to change any of +the settings.

    + +

    The only setting you might need to change is "SocksAddress". +By default, your Tor client only listens for applications that connect +from localhost. Connections from other computers are refused. If you +want to torify applications on different computers than the Tor client, +you should copy torrc.sample to torrc (it's installed by default +to /usr/local/etc/tor/), change the SocksAddress line to +0.0.0.0, and then restart Tor.

    + +

    To test if it's working, point your browser +to socks4 or socks5 proxy at localhost port 9050. In +Mozilla, this is in edit|preferences|advanced|proxies. Go to http://www.junkbusters.com/cgi-bin/privacy +and see what IP it says you're coming from. (If you have a personal +firewall, be sure to allow local connections to port 9050. If your +firewall blocks outgoing connections, punch a hole so it can connect to +TCP *:9001-9004 and *:9030-9033. If you're using Safari as your browser, +keep in mind that OS X before 10.3 claims to support socks but does +not.)

    + +

    Once you've tested that it works, you should install privoxy, which is a filtering web +proxy that integrates well with Tor. Add the line
    +forward-socks4a / localhost:9050 .
    +(don't forget the dot) to its +config file. Then change your mozilla to http proxy at localhost port 8118 +(and no socks proxy). You should also set your SSL proxy to the same +thing, to hide your https traffic. Using privoxy will give you good html +scrubbing as well. +(See this explanation +for why direct socks gives you less anonymity.)

    + +

    You might want to use Tor with an application that doesn't +support socks directly. In this case, you should look at +using tsocks +to dynamically replace the system calls in your program to +route through Tor. If you want to use socks4a, consider using socat (specific instructions +are on this hidden +service url).

    + + +

    Configuring a server

    + +

    We're looking for people with reasonably reliable Internet connections, +that have at least 768kbit each way. Currently we don't use all of that, +but we want it available for burst traffic.

    + +

    The Tor server doesn't need to be run as root, and doesn't +need any special system permissions or kernel mods. If you're +the paranoid sort, feel free to put it into a chroot jail (some +hints), etc.

    + +

    First, copy torrc.sample to torrc (by default it's in +/usr/local/etc/tor/), and edit the middle part. Create the DataDirectory, +and make sure it's owned by whoever will be running tor. Fix your system +clock so it's not too far off. Make sure name resolution works. Open a +hole in your firewall so outsiders can connect to your ORPort.

    + +

    Then run tor to generate keys: tor. One of the files generated +in your DataDirectory is your 'fingerprint' file. Mail it to +tor-ops@freehaven.net.

    + +

    In that mail, be sure to tell us who you are, so we know whom to contact +if there's any problem. Also describe what kind of connectivity the new +server will have. If possible, PGP sign your mail.

    + +

    NOTE: You won't be able to use tor as a client or server +in this configuration until you've been added to the directory +and can authenticate to the other nodes.

    + +

    Once your fingerprint has been approved, you can click here or here and look at the +running-routers line to see if your server is part of the network.

    + + +

    Configuring a hidden service

    + +

    Tor allows clients and servers to offer hidden services. That +is, you can offer an apache, sshd, etc, without revealing your IP to its +users. This works via Tor's rendezvous point design: both sides build +a Tor circuit out, and they meet in the middle.

    + +

    If you're using Tor and Privoxy, +you can go to the hidden wiki +to see hidden services in action.

    + +

    To set up a hidden service, copy torrc.sample to torrc (by default it's +in /usr/local/etc/tor/), and edit the bottom part. Then run Tor. It will +create each HiddenServiceDir you have configured, and it will create a +'hostname' file which specifies the url (xyz.onion) for that service. You +can tell people the url, and they can connect to it via their Tor client, +assuming they're using a proxy (such as Privoxy) that speaks socks4a.

    + + +

    Setting up your own network

    + +

    +If you want to experiment locally with your own network, or you're cut +off from the Internet and want to be able to mess with Tor still, then +you may want to set up your own separate Tor network. + +

    +To set up your own Tor network, you need to run your own directory +servers, and you need to change the tarball so it points to your directory +servers rather than the default ones. + +

      +
    • 1: Grab the latest release. +
    • 2: For each directory server you want, +
        +
      • 2a: Set it up as a server (see "setting up a +server" above), with a least ORPort, DataDirectory, and Nickname +defined. +
      • 2b: Set "DirPort" to the intended port for serving directories. +
      • 2c: Set "RecommendedVersions" to a comma-separated list of acceptable +versions of the code for clients and servers to be running (see step +4c below). + +
      • 2d: Create an empty dirservers file (touch dirservers). Point +RouterFile at it in your torrc. +
      • 2e: Run it: tor -f torrc. This will generate your keys and a +router.desc (router descriptor) file. It will then exit with a complaint +that it can't open the fingerprint file; that's fine. +
      +
    • 3: Create the new dirservers file. You do this by concatenating the +"router.desc" files from each dirserver's DataDirectory: cat router1.desc +router2.desc ... > dirservers +
    • 4: Now you need to teach clients and servers to use the new +dirservers file. First, check out the tor cvs repository (instructions here -- be sure to check out the +tag that matches the version of the code you intend to use; and note that +the latest cvs version may not compile or work right). Then: +
        +
      • 4a: Edit src/or/config.c and change the default_dirservers_string array +so that it reflects the contents of the new dirservers file instead +of the old one. Be sure to get the quotes and newlines and semicolons +right. (This step sucks. Please suggest a better way to handle this +step. ;) +
      • 4b: Replace the dirservers file in your sandbox (in src/config/) +with the one from step 3. +
      • 4c: edit configure.in, change the AM_INIT_AUTOMAKE(tor, 0.0.6) +line so that it specifies a version that is specific to you, such as +0.0.6-arma. This will help you keep from being confused later. Be sure +to update the RecommendedVersions lines to include this version. +
      • 4d: run ./autogen.sh (you'll need a new enough set of auto* tools), +then make dist. +
      +
    • 5: Create a file called approved-routers in the DataDirectory +of each directory server. Collect the 'fingerprint' lines from the +DataDirectory of each server (including directory servers), and include +them (one per line) in each approved-routers file. You can hup the tor +process for each directory server to reload the approved-routers file +(so you don't have to restart the process). +
    + +

    Other doc resources

    + +
      +
    • Design paper +
    • Spec and rend-spec +
    • others +
    + + + + diff --git a/tags/tor-0_0_8pre1/doc/tor-spec.txt b/tags/tor-0_0_8pre1/doc/tor-spec.txt new file mode 100644 index 0000000000..522cf24e13 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-spec.txt @@ -0,0 +1,657 @@ +$Id$ + +Tor Spec + +Note: This is an attempt to specify Tor as it exists as implemented in +early March, 2004. It is not recommended that others implement this +design as it stands; future versions of Tor will implement improved +protocols. + +This is not a design document; most design criteria are not examined. For +more information on why Tor acts as it does, see tor-design.pdf. + +TODO: (very soon) + - REASON_CONNECTFAILED should include an IP. + - Copy prose from tor-design to make everything more readable. + + +0. Notation: + + PK -- a public key. + SK -- a private key + K -- a key for a symmetric cypher + + a|b -- concatenation of 'a' and 'b'. + + [A0 B1 C2] -- a three-byte sequence, containing the bytes with + hexadecimal values A0, B1, and C2, in that order. + + All numeric values are encoded in network (big-endian) order. + + Unless otherwise specified, all symmetric ciphers are AES in counter + mode, with an IV of all 0 bytes. Asymmetric ciphers are either RSA + with 1024-bit keys and exponents of 65537, or DH with the safe prime + from rfc2409, section 6.2, whose hex representation is: + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE65381FFFFFFFFFFFFFFFF" + +1. System overview + + Onion Routing is a distributed overlay network designed to anonymize + low-latency TCP-based applications such as web browsing, secure shell, + and instant messaging. Clients choose a path through the network and + build a ``circuit'', in which each node (or ``onion router'' or ``OR'') + in the path knows its predecessor and successor, but no other nodes in + the circuit. Traffic flowing down the circuit is sent in fixed-size + ``cells'', which are unwrapped by a symmetric key at each node (like + the layers of an onion) and relayed downstream. + +2. Connections + + There are two ways to connect to an onion router (OR). The first is + as an onion proxy (OP), which allows the OP to authenticate the OR + without authenticating itself. The second is as another OR, which + allows mutual authentication. + + Tor uses TLS for link encryption. All implementations MUST support + the TLS ciphersuite "TLS_EDH_RSA_WITH_DES_192_CBC3_SHA", and SHOULD + support "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" if it is available. + Implementations MAY support other ciphersuites, but MUST NOT + support any suite without ephemeral keys, symmetric keys of at + least 128 bits, and digests of at least 160 bits. + + An OR always sends a two-certificate chain, consisting of a self-signed + certificate containing the OR's identity key, and a second certificate + using a short-term connection key. The commonName of the second + certificate is the OR's nickname, and the commonName of the first + certificate is the OR's nickname, followed by a space and the string + "". + + All parties receiving certificates must confirm that the identity key is + as expected. (When initiating a connection, the expected identity key is + the one given in the directory; when creating a connection because of an + EXTEND cell, the expected identity key is the one given in the cell.) If + the key is not as expected, the party must close the connection. + + Once a TLS connection is established, the two sides send cells + (specified below) to one another. Cells are sent serially. All + cells are 512 bytes long. Cells may be sent embedded in TLS + records of any size or divided across TLS records, but the framing + of TLS records MUST NOT leak information about the type or contents + of the cells. + + OR-to-OR connections are never deliberately closed. When an OR + starts or receives a new directory, it tries to open new + connections to any OR it is not already connected to. + + OR-to-OP connections are not permanent. An OP should close a + connection to an OR if there are no circuits running over the + connection, and an amount of time (KeepalivePeriod, defaults to 5 + minutes) has passed. + +3. Cell Packet format + + The basic unit of communication for onion routers and onion + proxies is a fixed-width "cell". Each cell contains the following + fields: + + CircID [2 bytes] + Command [1 byte] + Payload (padded with 0 bytes) [509 bytes] + [Total size: 512 bytes] + + The CircID field determines which circuit, if any, the cell is + associated with. + + The 'Command' field holds one of the following values: + 0 -- PADDING (Padding) (See Sec 6.2) + 1 -- CREATE (Create a circuit) (See Sec 4) + 2 -- CREATED (Acknowledge create) (See Sec 4) + 3 -- RELAY (End-to-end data) (See Sec 5) + 4 -- DESTROY (Stop using a circuit) (See Sec 4) + + The interpretation of 'Payload' depends on the type of the cell. + PADDING: Payload is unused. + CREATE: Payload contains the handshake challenge. + CREATED: Payload contains the handshake response. + RELAY: Payload contains the relay header and relay body. + DESTROY: Payload is unused. + Upon receiving any other value for the command field, an OR must + drop the cell. + + The payload is padded with 0 bytes. + + PADDING cells are currently used to implement connection keepalive. + If there is no other traffic, ORs and OPs send one another a PADDING + cell every few minutes. + + CREATE, CREATED, and DESTROY cells are used to manage circuits; + see section 4 below. + + RELAY cells are used to send commands and data along a circuit; see + section 5 below. + +4. Circuit management + +4.1. CREATE and CREATED cells + + Users set up circuits incrementally, one hop at a time. To create a + new circuit, OPs send a CREATE cell to the first node, with the + first half of the DH handshake; that node responds with a CREATED + cell with the second half of the DH handshake plus the first 20 bytes + of derivative key data (see section 4.2). To extend a circuit past + the first hop, the OP sends an EXTEND relay cell (see section 5) + which instructs the last node in the circuit to send a CREATE cell + to extend the circuit. + + The payload for a CREATE cell is an 'onion skin', which consists + of the first step of the DH handshake data (also known as g^x). + + The data is encrypted to Bob's PK as follows: Suppose Bob's PK is + L octets long. If the data to be encrypted is shorter than L-42, + then it is encrypted directly (with OAEP padding). If the data is at + least as long as L-42, then a randomly generated 16-byte symmetric + key is prepended to the data, after which the first L-16-42 bytes + of the data are encrypted with Bob's PK; and the rest of the data is + encrypted with the symmetric key. + + So in this case, the onion skin on the wire looks like: + RSA-encrypted: + OAEP padding [42 bytes] + Symmetric key [16 bytes] + First part of g^x [70 bytes] + Symmetrically encrypted: + Second part of g^x [58 bytes] + + The relay payload for an EXTEND relay cell consists of: + Address [4 bytes] + Port [2 bytes] + Onion skin [186 bytes] + Public key hash [20 bytes] + + The port and address field denote the IPV4 address and port of the + next onion router in the circuit; the public key hash is the SHA1 hash of + the ASN1 encoding of the next onion router's identity key. + + [XXXX Before 0.0.8, EXTEND cells did not include the public key hash. + Servers running 0.0.8 distinguish the old-style cells based on the + length of payloads. (Servers running 0.0.7 blindly pass on the extend + cell regardless of length.) In a future release, old-style EXTEND + cells will not be supported.] + + The payload for a CREATED cell, or the relay payload for an + EXTENDED cell, contains: + DH data (g^y) [128 bytes] + Derivative key data (KH) [20 bytes] + + The CircID for a CREATE cell is an arbitrarily chosen 2-byte + integer, selected by the node (OP or OR) that sends the CREATE + cell. To prevent CircID collisions, when one OR sends a CREATE + cell to another, it chooses from only one half of the possible + values based on the ORs' nicknames: if the sending OR has a + lexicographically earlier nickname, it chooses a CircID with a high + bit of 0; otherwise, it chooses a CircID with a high bit of 1. + +4.2. Setting circuit keys + + Once the handshake between the OP and an OR is completed, both + servers can now calculate g^xy with ordinary DH. From the base key + material g^xy, they compute derivative key material as follows. + First, the server represents g^xy as a big-endian unsigned integer. + Next, the server computes 100 bytes of key data as K = SHA1(g^xy | + [00]) | SHA1(g^xy | [01]) | ... SHA1(g^xy | [04]) where "00" is + a single octet whose value is zero, [01] is a single octet whose + value is one, etc. The first 20 bytes of K form KH, bytes 21-40 form + the forward digest Df, 41-60 form the backward digest Db, 61-76 form + Kf, and 77-92 form Kb. + + KH is used in the handshake response to demonstrate knowledge of the + computed shared key. Df is used to seed the integrity-checking hash + for the stream of data going from the OP to the OR, and Db seeds the + integrity-checking hash for the data stream from the OR to the OP. Kf + is used to encrypt the stream of data going from the OP to the OR, and + Kb is used to encrypt the stream of data going from the OR to the OP. + +4.3. Creating circuits + + When creating a circuit through the network, the circuit creator + (OP) performs the following steps: + + 1. Choose an onion router as an exit node (R_N), such that the onion + router's exit policy does not exclude all pending streams + that need a circuit. + + 2. Choose a chain of (N-1) chain of N onion routers + (R_1...R_N-1) to constitute the path, such that no router + appears in the path twice. + + 3. If not already connected to the first router in the chain, + open a new connection to that router. + + 4. Choose a circID not already in use on the connection with the + first router in the chain; send a CREATE cell along the + connection, to be received by the first onion router. + + 5. Wait until a CREATED cell is received; finish the handshake + and extract the forward key Kf_1 and the backward key Kb_1. + + 6. For each subsequent onion router R (R_2 through R_N), extend + the circuit to R. + + To extend the circuit by a single onion router R_M, the OP performs + these steps: + + 1. Create an onion skin, encrypted to R_M's public key. + + 2. Send the onion skin in a relay EXTEND cell along + the circuit (see section 5). + + 3. When a relay EXTENDED cell is received, verify KH, and + calculate the shared keys. The circuit is now extended. + + When an onion router receives an EXTEND relay cell, it sends a CREATE + cell to the next onion router, with the enclosed onion skin as its + payload. The initiating onion router chooses some circID not yet + used on the connection between the two onion routers. (But see + section 4.1. above, concerning choosing circIDs based on + lexicographic order of nicknames.) + + As an extension (called router twins), if the desired next onion + router R in the circuit is down, and some other onion router R' + has the same public keys as R, then it's ok to extend to R' rather than R. + + When an onion router receives a CREATE cell, if it already has a + circuit on the given connection with the given circID, it drops the + cell. Otherwise, after receiving the CREATE cell, it completes the + DH handshake, and replies with a CREATED cell. Upon receiving a + CREATED cell, an onion router packs it payload into an EXTENDED relay + cell (see section 5), and sends that cell up the circuit. Upon + receiving the EXTENDED relay cell, the OP can retrieve g^y. + + (As an optimization, OR implementations may delay processing onions + until a break in traffic allows time to do so without harming + network latency too greatly.) + +4.4. Tearing down circuits + + Circuits are torn down when an unrecoverable error occurs along + the circuit, or when all streams on a circuit are closed and the + circuit's intended lifetime is over. Circuits may be torn down + either completely or hop-by-hop. + + To tear down a circuit completely, an OR or OP sends a DESTROY + cell to the adjacent nodes on that circuit, using the appropriate + direction's circID. + + Upon receiving an outgoing DESTROY cell, an OR frees resources + associated with the corresponding circuit. If it's not the end of + the circuit, it sends a DESTROY cell for that circuit to the next OR + in the circuit. If the node is the end of the circuit, then it tears + down any associated edge connections (see section 5.1). + + After a DESTROY cell has been processed, an OR ignores all data or + destroy cells for the corresponding circuit. + + (The rest of this section is not currently used; on errors, circuits + are destroyed, not truncated.) + + To tear down part of a circuit, the OP may send a RELAY_TRUNCATE cell + signaling a given OR (Stream ID zero). That OR sends a DESTROY + cell to the next node in the circuit, and replies to the OP with a + RELAY_TRUNCATED cell. + + When an unrecoverable error occurs along one connection in a + circuit, the nodes on either side of the connection should, if they + are able, act as follows: the node closer to the OP should send a + RELAY_TRUNCATED cell towards the OP; the node farther from the OP + should send a DESTROY cell down the circuit. + +4.5. Routing relay cells + + When an OR receives a RELAY cell, it checks the cell's circID and + determines whether it has a corresponding circuit along that + connection. If not, the OR drops the RELAY cell. + + Otherwise, if the OR is not at the OP edge of the circuit (that is, + either an 'exit node' or a non-edge node), it de/encrypts the payload + with AES/CTR, as follows: + 'Forward' relay cell (same direction as CREATE): + Use Kf as key; decrypt. + 'Back' relay cell (opposite direction from CREATE): + Use Kb as key; encrypt. + + The OR then decides whether it recognizes the relay cell, by + inspecting the payload as described in section 5.1 below. If the OR + recognizes the cell, it processes the contents of the relay cell. + Otherwise, it passes the decrypted relay cell along the circuit if + the circuit continues. If the OR at the end of the circuit + encounters an unrecognized relay cell, an error has occurred: the OR + sends a DESTROY cell to tear down the circuit. + + When a relay cell arrives at an OP, the OP decrypts the payload + with AES/CTR as follows: + OP receives data cell: + For I=N...1, + Decrypt with Kb_I. If the payload is recognized (see + section 5.1), then stop and process the payload. + + For more information, see section 5 below. + +5. Application connections and stream management + +5.1. Relay cells + + Within a circuit, the OP and the exit node use the contents of + RELAY packets to tunnel end-to-end commands and TCP connections + ("Streams") across circuits. End-to-end commands can be initiated + by either edge; streams are initiated by the OP. + + The payload of each unencrypted RELAY cell consists of: + Relay command [1 byte] + 'Recognized' [2 bytes] + StreamID [2 bytes] + Digest [4 bytes] + Length [2 bytes] + Data [498 bytes] + + The relay commands are: + 1 -- RELAY_BEGIN + 2 -- RELAY_DATA + 3 -- RELAY_END + 4 -- RELAY_CONNECTED + 5 -- RELAY_SENDME + 6 -- RELAY_EXTEND + 7 -- RELAY_EXTENDED + 8 -- RELAY_TRUNCATE + 9 -- RELAY_TRUNCATED + 10 -- RELAY_DROP + 11 -- RELAY_RESOLVE + 12 -- RELAY_RESOLVED + + The 'Recognized' field in any unencrypted relay payload is always + set to zero; the 'digest' field is computed as the first four bytes + of the running SHA-1 digest of all the bytes that have travelled + over this circuit, seeded from Df or Db respectively (obtained in + section 4.2 above), and including this RELAY cell's entire payload + (taken with the digest field set to zero). + + When the 'recognized' field of a RELAY cell is zero, and the digest + is correct, the cell is considered "recognized" for the purposes of + decryption (see section 4.5 above). + + All RELAY cells pertaining to the same tunneled stream have the + same stream ID. StreamIDs are chosen randomly by the OP. RELAY + cells that affect the entire circuit rather than a particular + stream use a StreamID of zero. + + The 'Length' field of a relay cell contains the number of bytes in + the relay payload which contain real payload data. The remainder of + the payload is padded with NUL bytes. + +5.2. Opening streams and transferring data + + To open a new anonymized TCP connection, the OP chooses an open + circuit to an exit that may be able to connect to the destination + address, selects an arbitrary StreamID not yet used on that circuit, + and constructs a RELAY_BEGIN cell with a payload encoding the address + and port of the destination host. The payload format is: + + ADDRESS | ':' | PORT | [00] + + where ADDRESS is be a DNS hostname, or an IPv4 address in + dotted-quad format; and where PORT is encoded in decimal. + + [What is the [00] for? -NM] + [It's so the payload is easy to parse out with string funcs -RD] + + Upon receiving this cell, the exit node resolves the address as + necessary, and opens a new TCP connection to the target port. If the + address cannot be resolved, or a connection can't be established, the + exit node replies with a RELAY_END cell. (See 5.4 below.) + Otherwise, the exit node replies with a RELAY_CONNECTED cell, whose + payload is the 4-byte IP address to which the connection was made. + + The OP waits for a RELAY_CONNECTED cell before sending any data. + Once a connection has been established, the OP and exit node + package stream data in RELAY_DATA cells, and upon receiving such + cells, echo their contents to the corresponding TCP stream. + RELAY_DATA cells sent to unrecognized streams are dropped. + + Relay RELAY_DROP cells are long-range dummies; upon receiving such + a cell, the OR or OP must drop it. + +5.3. Closing streams + + When an anonymized TCP connection is closed, or an edge node + encounters error on any stream, it sends a 'RELAY_END' cell along the + circuit (if possible) and closes the TCP connection immediately. If + an edge node receives a 'RELAY_END' cell for any stream, it closes + the TCP connection completely, and sends nothing more along the + circuit for that stream. + + The payload of a RELAY_END cell begins with a single 'reason' byte to + describe why the stream is closing, plus optional data (depending on + the reason.) The values are: + + 1 -- REASON_MISC (catch-all for unlisted reasons) + 2 -- REASON_RESOLVEFAILED (couldn't look up hostname) + 3 -- REASON_CONNECTFAILED (couldn't connect to host/port) + 4 -- REASON_EXITPOLICY (OR refuses to connect to host or port) + 5 -- REASON_DESTROY (circuit is being destroyed [???-NM]) + 6 -- REASON_DONE (anonymized TCP connection was closed) + 7 -- REASON_TIMEOUT (OR timed out while connecting [???-NM]) + + (With REASON_EXITPOLICY, the 4-byte IP address forms the optional + data; no other reason currently has extra data.) + + + *** [The rest of this section describes unimplemented functionality.] + + Because TCP connections can be half-open, we follow an equivalent + to TCP's FIN/FIN-ACK/ACK protocol to close streams. + + An exit connection can have a TCP stream in one of three states: + 'OPEN', 'DONE_PACKAGING', and 'DONE_DELIVERING'. For the purposes + of modeling transitions, we treat 'CLOSED' as a fourth state, + although connections in this state are not, in fact, tracked by the + onion router. + + A stream begins in the 'OPEN' state. Upon receiving a 'FIN' from + the corresponding TCP connection, the edge node sends a 'RELAY_FIN' + cell along the circuit and changes its state to 'DONE_PACKAGING'. + Upon receiving a 'RELAY_FIN' cell, an edge node sends a 'FIN' to + the corresponding TCP connection (e.g., by calling + shutdown(SHUT_WR)) and changing its state to 'DONE_DELIVERING'. + + When a stream in already in 'DONE_DELIVERING' receives a 'FIN', it + also sends a 'RELAY_FIN' along the circuit, and changes its state + to 'CLOSED'. When a stream already in 'DONE_PACKAGING' receives a + 'RELAY_FIN' cell, it sends a 'FIN' and changes its state to + 'CLOSED'. + + If an edge node encounters an error on any stream, it sends a + 'RELAY_END' cell (if possible) and closes the stream immediately. + +5.4. Remote hostname lookup + + To find the address associated with a hostname, the OP sends a + RELAY_RESOLVE cell containing the hostname to be resolved. The OR + replies with an RELAY_RESOLVED cell containing a status byte, and any + number of answers. Each answer is of the form: + Type (1 octet) + Length (1 octet) + Value (variable-width) + "Length" is the length of the Value field. "Type" is one of: + 0x04 -- IPv4 address + 0x06 -- IPv6 address + 0xF0 -- Error, transient + 0xF1 -- Error, nontransient + + If any answer has a type of 'Error', then no other answer may be given. + + The RELAY_RESOLVE cell must use a nonzero, distinct streamID; the + corresponding RELAY_RESOLVED cell must use the same streamID. No stream + is actually created by the OR when resolving the name. + +6. Flow control + +6.1. Link throttling + + Each node should do appropriate bandwidth throttling to keep its + user happy. + + Communicants rely on TCP's default flow control to push back when they + stop reading. + +6.2. Link padding + + Currently nodes are not required to do any sort of link padding or + dummy traffic. Because strong attacks exist even with link padding, + and because link padding greatly increases the bandwidth requirements + for running a node, we plan to leave out link padding until this + tradeoff is better understood. + +6.3. Circuit-level flow control + + To control a circuit's bandwidth usage, each OR keeps track of + two 'windows', consisting of how many RELAY_DATA cells it is + allowed to package for transmission, and how many RELAY_DATA cells + it is willing to deliver to streams outside the network. + Each 'window' value is initially set to 1000 data cells + in each direction (cells that are not data cells do not affect + the window). When an OR is willing to deliver more cells, it sends a + RELAY_SENDME cell towards the OP, with Stream ID zero. When an OR + receives a RELAY_SENDME cell with stream ID zero, it increments its + packaging window. + + Each of these cells increments the corresponding window by 100. + + The OP behaves identically, except that it must track a packaging + window and a delivery window for every OR in the circuit. + + An OR or OP sends cells to increment its delivery window when the + corresponding window value falls under some threshold (900). + + If a packaging window reaches 0, the OR or OP stops reading from + TCP connections for all streams on the corresponding circuit, and + sends no more RELAY_DATA cells until receiving a RELAY_SENDME cell. +[this stuff is badly worded; copy in the tor-design section -RD] + +6.4. Stream-level flow control + + Edge nodes use RELAY_SENDME cells to implement end-to-end flow + control for individual connections across circuits. Similarly to + circuit-level flow control, edge nodes begin with a window of cells + (500) per stream, and increment the window by a fixed value (50) + upon receiving a RELAY_SENDME cell. Edge nodes initiate RELAY_SENDME + cells when both a) the window is <= 450, and b) there are less than + ten cell payloads remaining to be flushed at that edge. + + +7. Directories and routers + +7.1. Extensible information format + +Router descriptors and directories both obey the following lightweight +extensible information format. + +The highest level object is a Document, which consists of one or more Items. +Every Item begins with a KeywordLine, followed by one or more Objects. A +KeywordLine begins with a Keyword, optionally followed by a space and more +non-newline characters, and ends with a newline. A Keyword is a sequence of +one or more characters in the set [A-Za-z0-9-]. An Object is a block of +encoded data in pseudo-Open-PGP-style armor. (cf. RFC 2440) + +More formally: + + Document ::= (Item | NL)+ + Item ::= KeywordLine Object* + KeywordLine ::= Keyword NL | Keyword SP ArgumentsChar+ NL + Keyword = KeywordChar+ + KeywordChar ::= 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' | '-' + ArgumentChar ::= any printing ASCII character except NL. + Object ::= BeginLine Base-64-encoded-data EndLine + BeginLine ::= "-----BEGIN " Keyword "-----" NL + EndLine ::= "-----END " Keyword "-----" NL + + The BeginLine and EndLine of an Object must use the same keyword. + +When interpreting a Document, software MUST reject any document containing a +KeywordLine that starts with a keyword it doesn't recognize. + +7.1. Router descriptor format. + +Every router descriptor MUST start with a "router" Item; MUST end with a +"router-signature" Item and an extra NL; and MUST contain exactly one +instance of each of the following Items: "published" "onion-key" "link-key" +"signing-key". Additionally, a router descriptor MAY contain any number of +"accept", "reject", and "opt" Items. Other than "router" and +"router-signature", the items may appear in any order. + +The items' formats are as follows: + "router" nickname address (ORPort SocksPort DirPort)? + "ports" ORPort SocksPort DirPort + "bandwidth" bandwidth-avg bandwidth-burst + "platform" string + "published" YYYY-MM-DD HH:MM:SS + "onion-key" NL a public key in PEM format + "signing-key" NL a public key in PEM format + "accept" string + "reject" string + "router-signature" NL "-----BEGIN SIGNATURE-----" NL Signature NL + "-----END SIGNATURE-----" + "opt" SP keyword string? NL,Object? + +ORport ::= port where the router listens for routers/proxies (speaking cells) +SocksPort ::= where the router listens for applications (speaking socks) +DirPort ::= where the router listens for directory download requests +bandwidth-avg ::= maximum average bandwidth, in bytes/s +bandwidth-burst ::= maximum bandwidth spike, in bytes/s +nickname ::= between 1 and 19 alphanumeric characters, case-insensitive. + +Bandwidth and ports are required; if they are not included in the router +line, they must appear in "bandwidth" and "ports" lines. + +"opt" is reserved for non-critical future extensions. + +7.2. Directory format + +A Directory begins with a "signed-directory" item, followed by one each of +the following, in any order: "recommended-software", "published", +"running-routers". It may include any number of "opt" items. After these +items, a directory includes any number of router descriptors, and a single +"directory-signature" item. + + "signed-directory" + "published" YYYY-MM-DD HH:MM:SS + "recommended-software" comma-separated-version-list + "running-routers" comma-separated-nickname-list + "directory-signature" nickname-of-dirserver NL Signature + +Note: The router descriptor for the directory server must appear first. +The signature is computed by computing the SHA-1 hash of the +directory, from the characters "signed-directory", through the newline +after "directory-signature". This digest is then padded with PKCS.1, +and signed with the directory server's signing key. + +If software encounters an unrecognized keyword in a single router descriptor, +it should reject only that router descriptor, and continue using the +others. If it encounters an unrecognized keyword in the directory header, +it should reject the entire directory. + +7.3. Behavior of a directory server + +lists nodes that are connected currently +speaks http on a socket, spits out directory on request + +----------- +(for emacs) + Local Variables: + mode:text + indent-tabs-mode:nil + fill-column:77 + End: diff --git a/tags/tor-0_0_8pre1/doc/tor.1.in b/tags/tor-0_0_8pre1/doc/tor.1.in new file mode 100644 index 0000000000..25a7ad8c91 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor.1.in @@ -0,0 +1,212 @@ +.TH TOR 1 "November 2003" "TOR" +.SH NAME +tor \- The second-generation onion router +.SH SYNOPSIS +.B tor +[\fIOPTION value\fR]... +.SH DESCRIPTION +.I tor +is a connection-oriented anonymizing communication +service. Users choose a source-routed path through a set of nodes, and +negotiate a "virtual circuit" through the network, in which each node +knows its predecessor and successor, but no others. Traffic flowing down +the circuit is unwrapped by a symmetric key at each node, which reveals +the downstream node. +.PP +Basically \fItor\fR provides a distributed network of servers ("onion +routers"). Users bounce their tcp streams -- web traffic, ftp, ssh, etc -- +around the routers, and recipients, observers, and even the routers +themselves have difficulty tracking the source of the stream. +.SH OPTIONS +\fB-h, -help\fP +Display a short help message and exit. +.TP +\fB-f \fR\fIFILE\fP +FILE contains further "option value" pairs. (Default: @CONFDIR@/torrc) +.TP +Other options can be specified either on the commandline (\fI--option value\fR), or in the configuration file (\fIoption value\fR). +.TP +\fBloglevel debug|info|warn|err\fP +Set the verboseness level of the primary log. (Default: warn) +.TP +\fBlogfile \fR\fIFILE\fP +Rather than logging to stdout, log to FILE. +.TP +\fBbandwidthrate \fR\fINUM\fP +A token bucket limits the average incoming bandwidth on this node to NUM bytes per second. (Default: 800000) +.TP +\fBbandwidthburst \fR\fINUM\fP +Limit the maximum token bucket size (also known as the burst) to NUM bytes. (Default: 10000000) +.TP +\fBdebuglogfile \fR\fIFILE\fP +In addition to other logging, we will log to FILE at log-level debug. +.TP +\fBgroup \fR\fIGID\fP +On startup, setgid to this user. +.TP +\fBkeepaliveperiod \fR\fINUM\fP +To keep firewalls from expiring connections, send a padding keepalive cell on open connections every NUM seconds. (Default: 300) +.TP +\fBpidfile \fR\fIFILE\fP +On startup, write our PID to FILE. On clean shutdown, remove FILE. +.TP +\fBrouterfile \fR\fIFILE\fP +FILE contains a list of directory servers, to bootstrap into the network. (Default: @CONFDIR@/dirservers) +.TP +\fBrunasdaemon \fR\fI0|1\fP +If 1, Tor forks and daemonizes to the background. (Default: 0) +.TP +\fBuser \fR\fIUID\fP +On startup, setuid to this user. + +.SH CLIENT OPTIONS +.PP +The following options are useful only for clients (that is, if \fBsocksport\fP is non-zero): +.TP +\fBentrynodes \fR\fInickname,nickname,...\fP +A list of preferred nodes to use for the first hop in the circuit, if possible. +.TP +\fBexitnodes \fR\fInickname,nickname,...\fP +A list of preferred nodes to use for the last hop in the circuit, if possible. +.TP +\fBexcludenodes \fR\iInickname,nickname,...\fP +A list of nodes to never use when building a circuit. +.TP +\fBnewcircuitperiod \fR\fINUM\fP +Every NUM seconds consider whether to build a new circuit. (Default: 60) +.TP +\fBpathlencoinweight \fR\fI0.0-1.0\fP +Paths are 3 hops plus a geometric distribution centered around this coinweight. Must be >=0.0 and <1.0. (Default: 0.3) +.TP +\fBsocksport \fR\fIPORT\fP +Bind to this port to listen for connections from socks-speaking applications. +.TP +\fBsocksbindaddress \fR\fIIP\fP +Bind to this address to listen for connections from socks-speaking applications. (Default: 127.0.0.1) You can also specify a port (e.g. 192.168.0.1:9100). This directive can be specified multiple times to bind to multiple addresses/ports. +.TP +\fBsockspolicy \fR\fIpolicy,policy,...\fP +Set an entrance policy for this server, so limit who can connect to the socks ports. The policies have the same form as exit policies below. + +.SH SERVER OPTIONS +.PP +The following options are useful only for servers (that is, if \fBorport\fP is non-zero): +.TP +\fBaddress address\fP +The fqdn of this server (e.g. moria.mit.edu). +.TP +\fBdatadirectory \fR\fIDIR\fP +Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor) +.TP +\fBexitpolicy \fR\fIpolicy,policy,...\fP +Set an exit policy for this server. Each policy is of the form +"\fBreject\fP \fIADDR\fP\fB/\fP\fIMASK\fP\fB:\fP\fIPORT\fP". +If \fB/\fP\fIMASK\fP is omitted then this policy just applies to the host +given. Instead of giving a host or network you can also use "\fB*\fP" to +denote the universe (0.0.0.0/0). \fIPORT\fP can either be a single port number +or an interval of ports: "\fIFROM_PORT\fP\fB-\fP\fITO_PORT\fP". + +For example, "reject 127.0.0.1:*,reject 192.168.1.0/24:*,accept *:*" would +reject any traffic destined for localhost and any 192.168.1.* address, but +accept anything else. + +This directive can be specified multiple times so you don't have to put +it all on one line. + +See RFC 3330 for more details about internal and reserved IP address +space. The default exit policy is: +.PD 0 +.RS 12 +.IP "reject 0.0.0.0/8" 0 +.IP "reject 169.254.0.0/16" 4 +.IP "reject 127.0.0.0/8" +.IP "reject 192.168.0.0/16" +.IP "reject 10.0.0.0/8" +.IP "reject 172.16.0.0/12" +.IP "accept *:20-22" +.IP "accept *:53" +.IP "accept *:79-81" +.IP "accept *:110" +.IP "accept *:143" +.IP "accept *:443" +.IP "accept *:873" +.IP "accept *:993" +.IP "accept *:995" 4 +.IP "accept *:1024-65535" +.IP "reject *:*" +.RE +.PD +.TP +\fBmaxonionspending \fR\fINUM\fP +If you have more than this number of onionskins queued for decrypt, reject new ones. (Default: 100) +.TP +\fBnickname \fR\fIname\fP +Set the server's nickname to 'name'. +.TP +\fBnumcpus \fR\fInum\fP +How many processes to use at once for decrypting onionskins. (Default: 1) +.TP +\fBorport \fR\fIPORT\fP +Bind to this port to listen for connections from Tor clients and servers. +.TP +\fBorbindaddress \fR\fIIP\fP +Bind to this address to listen for connections from Tor clients and servers. (Default: 0.0.0.0) + +.SH DIRECTORY SERVER OPTIONS +.PP +The following options are useful only for directory servers (that is, if \fBdirport\fP is non-zero): +.TP +\fBdirport \fR\fIPORT\fP +Bind the directory service to this port. +.TP +\fBdirbindaddress \fR\fIIP\fP +Bind the directory service to this address. (Default: 0.0.0.0) +.TP +\fBrecommendedversions \fR\fISTRING\fP +STRING is a command-separated list of Tor versions currently believed to be safe. The list is included in each directory, and nodes which pull down the directory learn whether they need to upgrade. + +.SH HIDDEN SERVER OPTIONS +.PP +The following options are used to configure a hidden service. +.TP +\fBhiddenservicedir \fR\fIDIRECTORY\fP +Store data files for a hidden service in DIRECTORY. Every hidden +service must have a separate directory. You may use this option multiple +times to specify multiple services. +.TP +\fBhiddenserviceport \fR\fIVIRTPORT \fR[\fITARGET\fR]\fP +Configure a virtual port VIRTPORT for a hidden service. You may use this +option multiple times; each time applies to the service using the most recent +hiddenservicedir. By default, this option maps the virtual port to the +same port on 127.0.0.1. You may override the target port, address, or both +by specifying a target of addr, port, or addr:port. +.TP +\fBhiddenservicenodes \fR\fInickname,nicknamme,...\fP +If possible, use the specified nodes as introduction points for the hidden +service. +.TP +\fBhiddenserviceexcludenodes \fR\fInickname,nicknamme,...\fP +Do not use the specified nodes as introduction points for the hidden +service. + +.SH FILES +.TP +.I @CONFDIR@/torrc +The configuration file, which contains "option value" pairs. +.TP +.I @CONFDIR@/dirservers +A list of directory servers, to bootstrap into the network. +.TP +.I @LOCALSTATEDIR@/lib/tor/ +The tor server stores keys/etc here. + +.SH SEE ALSO +.BR privoxy (1), +.BR tsocks (1) + +.BR http://freehaven.net/tor/ + +.SH BUGS +Plenty, probably. It's still in alpha. Please report them. +.SH AUTHORS +Roger Dingledine . + diff --git a/tags/tor-0_0_8pre1/doc/usenix.sty b/tags/tor-0_0_8pre1/doc/usenix.sty new file mode 100644 index 0000000000..4442f11574 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/usenix.sty @@ -0,0 +1,98 @@ +% usenix-2e.sty - to be used with latex2e (the new one) for USENIX. +% To use this style file, do this: +% +% \documentclass[twocolumn]{article} +% \usepackage{usenix-2e} +% and put {\rm ....} around the author names. +% +% $Id$ +% +% The following definitions are modifications of standard article.sty +% definitions, arranged to do a better job of matching the USENIX +% guidelines. +% It will automatically select two-column mode and the Times-Roman +% font. + +% +% USENIX papers are two-column. +% Times-Roman font is nice if you can get it (requires NFSS, +% which is in latex2e. + +\if@twocolumn\else\input twocolumn.sty\fi +\usepackage{times} + +% +% USENIX wants margins of: 7/8" side, 1" bottom, and 3/4" top. +% 0.25" gutter between columns. +% Gives active areas of 6.75" x 9.25" +% +\setlength{\textheight}{9.0in} +\setlength{\columnsep}{0.25in} +%%\setlength{\textwidth}{6.75in} +\setlength{\textwidth}{7.00in} +%\setlength{\footheight}{0.0in} +\setlength{\topmargin}{-0.25in} +\setlength{\headheight}{0.0in} +\setlength{\headsep}{0.0in} +\setlength{\evensidemargin}{-0.125in} +\setlength{\oddsidemargin}{-0.125in} + +% +% Usenix wants no page numbers for submitted papers, so that they can +% number them themselves. +% +\pagestyle{empty} + +% +% Usenix titles are in 14-point bold type, with no date, and with no +% change in the empty page headers. The whol author section is 12 point +% italic--- you must use {\rm } around the actual author names to get +% them in roman. +% +\def\maketitle{\par + \begingroup + \renewcommand\thefootnote{\fnsymbol{footnote}}% + \def\@makefnmark{\hbox to\z@{$\m@th^{\@thefnmark}$\hss}}% + \long\def\@makefntext##1{\parindent 1em\noindent + \hbox to1.8em{\hss$\m@th^{\@thefnmark}$}##1}% + \if@twocolumn + \twocolumn[\@maketitle]% + \else \newpage + \global\@topnum\z@ + \@maketitle \fi\@thanks + \endgroup + \setcounter{footnote}{0}% + \let\maketitle\relax + \let\@maketitle\relax + \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\let\thanks\relax} + +\def\@maketitle{\newpage + \vbox to 2.5in{ + \vspace*{\fill} + \vskip 2em + \begin{center}% + {\Large\bf \@title \par}% + \vskip 0.375in minus 0.300in + {\large\it + \lineskip .5em + \begin{tabular}[t]{c}\@author + \end{tabular}\par}% + \end{center}% + \par + \vspace*{\fill} +% \vskip 1.5em + } +} + +% +% The abstract is preceded by a 12-pt bold centered heading +\def\abstract{\begin{center}% +{\large\bf \abstractname\vspace{-.5em}\vspace{\z@}}% +\end{center}} +\def\endabstract{} + +% +% Main section titles are 12-pt bold. Others can be same or smaller. +% +\def\section{\@startsection {section}{1}{\z@}{-3.5ex plus-1ex minus + -.2ex}{2.3ex plus.2ex}{\reset@font\large\bf}} diff --git a/tags/tor-0_0_8pre1/src/.cvsignore b/tags/tor-0_0_8pre1/src/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/tags/tor-0_0_8pre1/src/Makefile.am b/tags/tor-0_0_8pre1/src/Makefile.am new file mode 100644 index 0000000000..ba296a6b0f --- /dev/null +++ b/tags/tor-0_0_8pre1/src/Makefile.am @@ -0,0 +1,6 @@ + +# leave in dependency order, since common must be built first + +SUBDIRS = common or config +DIST_SUBDIRS = common or config + diff --git a/tags/tor-0_0_8pre1/src/common/.cvsignore b/tags/tor-0_0_8pre1/src/common/.cvsignore new file mode 100644 index 0000000000..051d1bd50b --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/tags/tor-0_0_8pre1/src/common/Makefile.am b/tags/tor-0_0_8pre1/src/common/Makefile.am new file mode 100644 index 0000000000..205fca43d1 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/Makefile.am @@ -0,0 +1,8 @@ + +noinst_LIBRARIES = libor.a + +#CFLAGS = -Wall -Wpointer-arith -O2 + +libor_a_SOURCES = log.c crypto.c fakepoll.c util.c aes.c tortls.c + +noinst_HEADERS = log.h crypto.h fakepoll.h test.h util.h aes.h torint.h tortls.h strlcpy.c strlcat.c diff --git a/tags/tor-0_0_8pre1/src/common/aes.c b/tags/tor-0_0_8pre1/src/common/aes.c new file mode 100644 index 0000000000..45e93a9bef --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/aes.c @@ -0,0 +1,819 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file aes.c + * + * \brief Implementation of a simple AES counter mode. + **/ + +#include +#include +#include +#include "orconfig.h" +#include "aes.h" +#include "util.h" + +/*======================================================================*/ +/* From rijndael-alg-fst.h */ + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint8_t u8; + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 + +static int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]); + +/*======================================================================*/ +/* Interface to AES code, and counter implementation */ + +struct aes_cnt_cipher { + u32 rk[4*(MAXNR+1)]; + int nr; + u32 counter1; + u32 counter0; + u8 buf[16]; + u8 pos; +}; + +/** + * Helper function: set cipher's internal buffer to the encrypted + * value of the current counter. + */ +static void +_aes_fill_buf(aes_cnt_cipher_t *cipher) +{ + u32 counter0 = cipher->counter0; + u32 counter1 = cipher->counter1; + u8 buf[16]; + memset(buf, 0, 8); + buf[15] = (counter0 >> 0) & 0xff; + buf[14] = (counter0 >> 8) & 0xff; + buf[13] = (counter0 >> 16) & 0xff; + buf[12] = (counter0 >> 24) & 0xff; + buf[11] = (counter1 >> 0) & 0xff; + buf[10] = (counter1 >> 8) & 0xff; + buf[ 9] = (counter1 >> 16) & 0xff; + buf[ 8] = (counter1 >> 24) & 0xff; + + rijndaelEncrypt(cipher->rk, cipher->nr, buf, cipher->buf); +} + +/** + * Return a newly allocated counter-mode AES128 cipher implementation. + */ +aes_cnt_cipher_t* +aes_new_cipher() +{ + aes_cnt_cipher_t* result = tor_malloc(sizeof(aes_cnt_cipher_t)); + memset(result->rk, 0, 4*(MAXNR+1)); + memset(result->buf, 0, 16); + + 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. + */ +void +aes_set_key(aes_cnt_cipher_t *cipher, const unsigned char *key, int key_bits) +{ + cipher->nr = rijndaelKeySetupEnc(cipher->rk, key, key_bits); + cipher->counter0 = 0; + cipher->counter1 = 0; + cipher->pos = 0; + _aes_fill_buf(cipher); +} + +/** Release storage held by cipher + */ +void +aes_free_cipher(aes_cnt_cipher_t *cipher) +{ + assert(cipher); + memset(cipher, 0, sizeof(cipher)); + free(cipher); +} + +/** 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, int len, char *output) +{ + int c = cipher->pos; + if (!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 (! ++cipher->counter0) + ++cipher->counter1; + _aes_fill_buf(cipher); + } +} + +/** Return the current value of cipher's counter. */ +u64 +aes_get_counter(aes_cnt_cipher_t *cipher) +{ + u64 counter = cipher->pos; + counter |= ((u64)cipher->counter0) << 4; + counter |= ((u64)cipher->counter1) << 36; + return counter; +} + +/** Set cipher's counter to counter. */ +void +aes_set_counter(aes_cnt_cipher_t *cipher, u64 counter) +{ + cipher->pos = (u8)(counter & 0x0f); + cipher->counter0 = (u32) ((counter >> 4) & 0xffffffff); + cipher->counter1 = (u32) (counter >> 36); + _aes_fill_buf(cipher); +} + +/** Increment cipher's counter by delta. */ +void +aes_adjust_counter(aes_cnt_cipher_t *cipher, long delta) +{ + u64 counter = aes_get_counter(cipher); + counter += delta; + aes_set_counter(cipher, counter); +} + +/*======================================================================*/ +/* From rijndael-alg-fst.c */ + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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. + */ + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; + +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) + +#ifdef _MSC_VER +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int i = 0; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + if (keyBits == 128) { + for (;;) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + if (keyBits == 192) { + for (;;) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 12; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + if (keyBits == 256) { + for (;;) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 14; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) { + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ct , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ct + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ct + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ct + 12, s3); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/aes.h b/tags/tor-0_0_8pre1/src/common/aes.h new file mode 100644 index 0000000000..3c63a0e4f2 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/aes.h @@ -0,0 +1,36 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* Implements a minimal interface to counter-mode AES. */ + +#ifndef __AES_H +#define __AES_H + +/** + * \file aes.h + * \brief Headers for aes.c + */ + +#include "../common/torint.h" + +struct aes_cnt_cipher; +typedef struct aes_cnt_cipher aes_cnt_cipher_t; + +aes_cnt_cipher_t* aes_new_cipher(); +void aes_free_cipher(aes_cnt_cipher_t *cipher); +void aes_set_key(aes_cnt_cipher_t *cipher, const unsigned char *key, int key_bits); +void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, int len, char *output); +uint64_t aes_get_counter(aes_cnt_cipher_t *cipher); +void aes_set_counter(aes_cnt_cipher_t *cipher, uint64_t counter); +void aes_adjust_counter(aes_cnt_cipher_t *cipher, long delta); + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/crypto.c b/tags/tor-0_0_8pre1/src/common/crypto.c new file mode 100644 index 0000000000..6a9dc348e5 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/crypto.c @@ -0,0 +1,1491 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file crypto.c + * + * \brief Low-level cryptographic functions. + **/ + +#include "orconfig.h" + +#ifdef MS_WINDOWS +#define WIN32_WINNT 0x400 +#define _WIN32_WINNT 0x400 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#include + +#include +#include +#include +#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 + +#include "crypto.h" +#include "log.h" +#include "aes.h" +#include "util.h" + +#if OPENSSL_VERSION_NUMBER < 0x00905000l +#error "We require openssl >= 0.9.5" +#elif OPENSSL_VERSION_NUMBER < 0x00906000l +#define OPENSSL_095 +#endif + +/* Certain functions that return a success code in OpenSSL 0.9.6 return void + * (and don't indicate errors) in OpenSSL version 0.9.5. + * + * [OpenSSL 0.9.5 matters, because it ships with Redhat 6.2.] + */ +#ifdef OPENSSL_095 +#define RETURN_SSL_OUTCOME(exp) (exp); return 0 +#else +#define RETURN_SSL_OUTCOME(exp) return !(exp) +#endif + +/** Macro: is k a valid RSA public or private key? */ +#define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n) +/** Macro: is k a valid RSA private key? */ +#define PRIVATE_KEY_OK(k) ((k) && (k)->key && (k)->key->p) + +struct crypto_pk_env_t +{ + int refs; /* reference counting so we don't have to copy keys */ + RSA *key; +}; + +struct crypto_cipher_env_t +{ + unsigned char key[CIPHER_KEY_LEN]; + aes_cnt_cipher_t *cipher; +}; + +struct crypto_dh_env_t { + DH *dh; +}; + +/** Return the number of bytes added by padding method padding. + */ +static INLINE int +crypto_get_rsa_padding_overhead(int padding) { + switch(padding) + { + case RSA_NO_PADDING: return 0; + case RSA_PKCS1_OAEP_PADDING: return 42; + case RSA_PKCS1_PADDING: return 11; + 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_NO_PADDING: return RSA_NO_PADDING; + case PK_PKCS1_PADDING: return RSA_PKCS1_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) +{ + int 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 (doing) { + log(severity, "crypto error while %s: %s (in %s:%s)", doing, msg, lib,func); + } else { + log(severity, "crypto error: %s (in %s:%s)", msg, lib, func); + } + } +} + +/** Initialize the crypto library. + */ +int crypto_global_init() +{ + if (!_crypto_global_initialized) { + ERR_load_crypto_strings(); + _crypto_global_initialized = 1; + } + return 0; +} + +/** Uninitialize the crypto library. + */ +int crypto_global_cleanup() +{ + ERR_free_strings(); + return 0; +} + +/** used by tortls.c: wrap an RSA* in a crypto_pk_env_t. */ +crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa) +{ + crypto_pk_env_t *env; + tor_assert(rsa); + env = tor_malloc(sizeof(crypto_pk_env_t)); + env->refs = 1; + env->key = rsa; + return env; +} + +/** used by tortls.c: return the RSA* from a crypto_pk_env_t. */ +RSA *_crypto_pk_env_get_rsa(crypto_pk_env_t *env) +{ + return env->key; +} + +/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_env_t. Iff + * private is set, include the private-key portion of the key. */ +EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_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_env_t. + */ +DH *_crypto_dh_env_get_dh(crypto_dh_env_t *dh) +{ + return dh->dh; +} + +/** Allocate and return storage for a public key. The key itself will not yet + * be set. + */ +crypto_pk_env_t *crypto_new_pk_env(void) +{ + RSA *rsa; + + rsa = RSA_new(); + if (!rsa) return NULL; + return _crypto_new_pk_env_rsa(rsa); +} + +/** Release a reference to an asymmetric key; when all the references + * are released, free the key. + */ +void crypto_free_pk_env(crypto_pk_env_t *env) +{ + tor_assert(env); + + if(--env->refs > 0) + return; + + if (env->key) + RSA_free(env->key); + + free(env); +} + +/** Create a new symmetric cipher for a given key and encryption flag + * (1=encrypt, 0=decrypt). Return the crypto object on success; NULL + * on failure. + */ +crypto_cipher_env_t * +crypto_create_init_cipher(const char *key, int encrypt_mode) +{ + int r; + crypto_cipher_env_t *crypto = NULL; + + if (! (crypto = crypto_new_cipher_env())) { + log_fn(LOG_WARN, "Unable to allocate crypto object"); + return NULL; + } + + if (crypto_cipher_set_key(crypto, key)) { + crypto_log_errors(LOG_WARN, "setting symmetric key"); + goto error; + } + + if (encrypt_mode) + r = crypto_cipher_encrypt_init_cipher(crypto); + else + r = crypto_cipher_decrypt_init_cipher(crypto); + + if (r) + goto error; + return crypto; + + error: + if (crypto) + crypto_free_cipher_env(crypto); + return NULL; +} + +/** Allocate and return a new symmetric cipher. + */ +crypto_cipher_env_t *crypto_new_cipher_env() +{ + crypto_cipher_env_t *env; + + env = tor_malloc_zero(sizeof(crypto_cipher_env_t)); + env->cipher = aes_new_cipher(); + return env; +} + +/** Free a symmetric cipher. + */ +void crypto_free_cipher_env(crypto_cipher_env_t *env) +{ + tor_assert(env); + + tor_assert(env->cipher); + aes_free_cipher(env->cipher); + tor_free(env); +} + +/* public key crypto */ + +/** Generate a new public/private keypair in env. Return 0 on + * success, -1 on failure. + */ +int crypto_pk_generate_key(crypto_pk_env_t *env) +{ + tor_assert(env); + + if (env->key) + RSA_free(env->key); + env->key = RSA_generate_key(PK_BYTES*8,65537, NULL, NULL); + if (!env->key) { + crypto_log_errors(LOG_WARN, "generating RSA key"); + return -1; + } + + return 0; +} + +/** Read a PEM-encoded private key from src into env. + */ +static int crypto_pk_read_private_key_from_file(crypto_pk_env_t *env, + FILE *src) +{ + tor_assert(env && src); + + if (env->key) + RSA_free(env->key); + env->key = PEM_read_RSAPrivateKey(src, NULL, NULL, NULL); + if (!env->key) { + crypto_log_errors(LOG_WARN, "reading private key from file"); + 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_env_t *env, const char *keyfile) +{ + FILE *f_pr; + + tor_assert(env && keyfile); + + if(strspn(keyfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(keyfile)) { + /* filename contains nonlegal characters */ + return -1; + } + + /* open the keyfile */ + f_pr=fopen(keyfile,"rb"); + if (!f_pr) + return -1; + + /* read the private key */ + if(crypto_pk_read_private_key_from_file(env, f_pr) < 0) { + fclose(f_pr); + return -1; + } + fclose(f_pr); + + /* check the private key */ + if (crypto_pk_check_key(env) <= 0) + return -1; + + 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_env_t *env, char **dest, int *len) { + BUF_MEM *buf; + BIO *b; + + tor_assert(env && env->key && dest); + + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + + /* Now you can treat b as if it were a file. Just use the + * PEM_*_bio_* functions instead of the non-bio variants. + */ + if(!PEM_write_bio_RSAPublicKey(b, env->key)) { + crypto_log_errors(LOG_WARN, "writing public key to string"); + return -1; + } + + BIO_get_mem_ptr(b, &buf); + 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; /* null terminate it */ + *len = buf->length; + BUF_MEM_free(buf); + + return 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_env_t *env, const char *src, int len) { + BIO *b; + + tor_assert(env && src); + + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + + BIO_write(b, src, len); + + if (env->key) + 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_env_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); + s = tor_malloc(len+1); + strncpy(s, cp, len); + s[len] = '\0'; + r = write_str_to_file(fname, s); + BIO_free(bio); + free(s); + return r; +} + +/** Return true iff env has a valid key. + */ +int crypto_pk_check_key(crypto_pk_env_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; +} + +/** Compare the public-key components of a and b. Return -1 if a\b. + */ +int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b) { + int result; + + if (!a || !b) + return -1; + + if (!a->key || !b->key) + return -1; + + tor_assert(PUBLIC_KEY_OK(a)); + tor_assert(PUBLIC_KEY_OK(b)); + result = BN_cmp((a->key)->n, (b->key)->n); + if (result) + return result; + return BN_cmp((a->key)->e, (b->key)->e); +} + +/** Return the size of the public key modulus in env, in bytes. */ +int crypto_pk_keysize(crypto_pk_env_t *env) +{ + tor_assert(env && env->key); + + return RSA_size(env->key); +} + +/** Increase the reference count of env. + */ +crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *env) { + tor_assert(env && env->key); + + env->refs++; + return env; +} + +/** 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. + */ +int crypto_pk_public_encrypt(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to, int padding) +{ + int r; + tor_assert(env && from && to); + + r = RSA_public_encrypt(fromlen, (unsigned char*)from, 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. + */ +int crypto_pk_private_decrypt(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to, int padding, int warnOnFailure) +{ + int r; + tor_assert(env && from && to && env->key); + if (!env->key->p) + /* Not a private key */ + return -1; + + r = RSA_private_decrypt(fromlen, (unsigned char*)from, to, env->key, + crypto_get_rsa_padding(padding)); + if (r<0) { + crypto_log_errors(warnOnFailure?LOG_WARN:LOG_INFO, + "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. + */ +int crypto_pk_public_checksig(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to) +{ + int r; + tor_assert(env && from && to); + r = RSA_public_decrypt(fromlen, (unsigned char*)from, to, env->key, RSA_PKCS1_PADDING); + + if (r<0) { + crypto_log_errors(LOG_WARN, "checking RSA signature"); + return -1; + } + return r; +} + +/** 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. + */ +int crypto_pk_private_sign(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to) +{ + int r; + tor_assert(env && from && to); + if (!env->key->p) + /* Not a private key */ + return -1; + + r = RSA_private_encrypt(fromlen, (unsigned char*)from, to, env->key, RSA_PKCS1_PADDING); + if (r<0) { + crypto_log_errors(LOG_WARN, "generating 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_env_t *env, const unsigned char *data, int datalen, const unsigned char *sig, int siglen) +{ + char digest[DIGEST_LEN]; + char buf[PK_BYTES+1]; + int r; + + tor_assert(env && data && sig); + + if (crypto_digest(data,datalen,digest)<0) { + log_fn(LOG_WARN, "couldn't compute digest"); + return -1; + } + r = crypto_pk_public_checksig(env,sig,siglen,buf); + if (r != DIGEST_LEN) { + log_fn(LOG_WARN, "Invalid signature"); + return -1; + } + if (memcmp(buf, digest, DIGEST_LEN)) { + log_fn(LOG_WARN, "Signature mismatched with digest."); + return -1; + } + + return 0; +} + +/** 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. + */ +int crypto_pk_private_sign_digest(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to) +{ + char digest[DIGEST_LEN]; + if (crypto_digest(from,fromlen,digest)<0) + return -1; + return crypto_pk_private_sign(env,digest,DIGEST_LEN,to); +} + + +/** Perform a hybrid (public/secret) encryption on fromlen + * bytes of data from from, with padding type 'padding', + * storing the results on to. + * + * If no padding is used, the public key must be at least as large as + * from. + * + * 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_env_t *env, + const unsigned char *from, + int fromlen, unsigned char *to, + int padding, int force) +{ + int overhead, pkeylen, outlen, r, symlen; + crypto_cipher_env_t *cipher = NULL; + char buf[PK_BYTES+1]; + + tor_assert(env && from && to); + + overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding)); + pkeylen = crypto_pk_keysize(env); + + if (padding == PK_NO_PADDING && fromlen < pkeylen) + return -1; + + if (!force && fromlen+overhead <= pkeylen) { + /* It all fits in a single encrypt. */ + return crypto_pk_public_encrypt(env,from,fromlen,to,padding); + } + cipher = crypto_new_cipher_env(); + if (!cipher) return -1; + if (crypto_cipher_generate_key(cipher)<0) + goto err; + /* You can't just run around RSA-encrypting any bitstream: if it's + * greater than the RSA key, then OpenSSL will happily encrypt, and + * later decrypt to the wrong value. So we set the first bit of + * 'cipher->key' to 0 if we aren't padding. This means that our + * symmetric key is really only 127 bits. + */ + if (padding == PK_NO_PADDING) + cipher->key[0] &= 0x7f; + if (crypto_cipher_encrypt_init_cipher(cipher)<0) + goto err; + 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,buf,pkeylen-overhead,to,padding); + if (outlen!=pkeylen) { + goto err; + } + r = crypto_cipher_encrypt(cipher, + from+pkeylen-overhead-CIPHER_KEY_LEN, symlen, + to+outlen); + + if (r<0) goto err; + memset(buf, 0, sizeof(buf)); + crypto_free_cipher_env(cipher); + return outlen + symlen; + err: + memset(buf, 0, sizeof(buf)); + if (cipher) crypto_free_cipher_env(cipher); + return -1; +} + +/** Invert crypto_pk_public_hybrid_encrypt. */ +int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, + const unsigned char *from, + int fromlen, unsigned char *to, + int padding, int warnOnFailure) +{ + int overhead, pkeylen, outlen, r; + crypto_cipher_env_t *cipher = NULL; + char buf[PK_BYTES+1]; + + overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding)); + pkeylen = crypto_pk_keysize(env); + + if (fromlen <= pkeylen) { + return crypto_pk_private_decrypt(env,from,fromlen,to,padding,warnOnFailure); + } + outlen = crypto_pk_private_decrypt(env,from,pkeylen,buf,padding,warnOnFailure); + if (outlen<0) { + log_fn(warnOnFailure?LOG_WARN:LOG_INFO, "Error decrypting public-key data"); + return -1; + } + if (outlen < CIPHER_KEY_LEN) { + log_fn(warnOnFailure?LOG_WARN:LOG_INFO, "No room for a symmetric key"); + return -1; + } + cipher = crypto_create_init_cipher(buf, 0); + if (!cipher) { + return -1; + } + memcpy(to,buf+CIPHER_KEY_LEN,outlen-CIPHER_KEY_LEN); + outlen -= CIPHER_KEY_LEN; + r = crypto_cipher_decrypt(cipher, from+pkeylen, fromlen-pkeylen, + to+outlen); + if (r<0) + goto err; + memset(buf,0,sizeof(buf)); + crypto_free_cipher_env(cipher); + return outlen + (fromlen-pkeylen); + err: + memset(buf,0,sizeof(buf)); + if (cipher) crypto_free_cipher_env(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_env_t *pk, char *dest, int dest_len) +{ + int len; + unsigned char *buf, *cp; + len = i2d_RSAPublicKey(pk->key, NULL); + if (len < 0 || len > dest_len) + return -1; + cp = buf = tor_malloc(len+1); + len = i2d_RSAPublicKey(pk->key, &cp); + if (len < 0) { + crypto_log_errors(LOG_WARN,"encoding public key"); + tor_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); + tor_free(buf); + return len; +} + +/** Decode an ASN.1-encoded public key from str; return the result on + * success and NULL on failure. + */ +crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, int len) +{ + RSA *rsa; + unsigned char *buf; + /* This ifdef suppresses a type warning. Take out the first case once + * everybody is using openssl 0.9.7 or later. + */ +#if OPENSSL_VERSION_NUMBER < 0x00907000l + unsigned char *cp; +#else + const unsigned char *cp; +#endif + 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_env_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). + */ +int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out) +{ + unsigned char *buf, *bufp; + int len; + + len = i2d_RSAPublicKey(pk->key, NULL); + if (len < 0) + return -1; + buf = bufp = tor_malloc(len+1); + len = i2d_RSAPublicKey(pk->key, &bufp); + if (len < 0) { + crypto_log_errors(LOG_WARN,"encoding public key"); + free(buf); + return -1; + } + if (crypto_digest(buf, len, digest_out) < 0) { + free(buf); + return -1; + } + free(buf); + return 0; +} + +/** Given a private or public key pk, put a fingerprint of the + * public key into fp_out (must have at least FINGERPRINT_LEN+1 bytes of + * space). + * + * 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. + */ +int +crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out) +{ + unsigned char *bufp; + unsigned char digest[DIGEST_LEN]; + unsigned char buf[FINGERPRINT_LEN+1]; + int i; + if (crypto_pk_get_digest(pk, digest)) { + return -1; + } + bufp = buf; + for (i = 0; i < DIGEST_LEN; ++i) { + sprintf(bufp,"%02X",digest[i]); + bufp += 2; + if (i%2 && i != 19) { + *bufp++ = ' '; + } + } + *bufp = '\0'; + tor_assert(strlen(buf) == FINGERPRINT_LEN); + tor_assert(crypto_pk_check_fingerprint_syntax(buf)); + strcpy(fp_out, buf); + return 0; +} + +/** Return true iff s is in the correct format for a fingerprint. + */ +int +crypto_pk_check_fingerprint_syntax(const char *s) +{ + int i; + for (i = 0; i < FINGERPRINT_LEN; ++i) { + if ((i%5) == 4) { + if (!isspace((int)s[i])) return 0; + } else { + if (!isxdigit((int)s[i])) return 0; + } + } + if (s[FINGERPRINT_LEN]) return 0; + return 1; +} + +/* symmetric crypto */ + +/** Generate a new random key for the symmetric cipher in env. + * Return 0 on success, -1 on failure. Does not initialize the cipher. + */ +int crypto_cipher_generate_key(crypto_cipher_env_t *env) +{ + tor_assert(env); + + return crypto_rand(CIPHER_KEY_LEN, env->key); +} + +/** Set the symmetric key for the cipher in env to the first + * CIPHER_KEY_LEN bytes of key. Does not initialize the cipher. + */ +int crypto_cipher_set_key(crypto_cipher_env_t *env, const unsigned char *key) +{ + tor_assert(env && key); + + if (!env->key) + return -1; + + memcpy(env->key, key, CIPHER_KEY_LEN); + + return 0; +} + +/** Return a pointer to the key set for the cipher in env. + */ +const unsigned char *crypto_cipher_get_key(crypto_cipher_env_t *env) +{ + return env->key; +} + +/** Initialize the cipher in env for encryption. + */ +int crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env) +{ + tor_assert(env); + + aes_set_key(env->cipher, env->key, CIPHER_KEY_LEN*8); + return 0; +} + +/** Initialize the cipher in env for decryption. + */ +int crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env) +{ + tor_assert(env); + + aes_set_key(env->cipher, env->key, CIPHER_KEY_LEN*8); + return 0; +} + +/** 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_env_t *env, const unsigned char *from, unsigned int fromlen, unsigned char *to) +{ + tor_assert(env && env->cipher && from && fromlen && to); + + 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_env_t *env, const unsigned char *from, unsigned int fromlen, unsigned char *to) +{ + tor_assert(env && from && to); + + aes_crypt(env->cipher, from, fromlen, to); + return 0; +} + +/** Move the position of the cipher stream backwards by delta bytes. + */ +int +crypto_cipher_rewind(crypto_cipher_env_t *env, long delta) +{ + return crypto_cipher_advance(env, -delta); +} + +/** Move the position of the cipher stream forwards by delta bytes. + */ +int +crypto_cipher_advance(crypto_cipher_env_t *env, long delta) +{ + aes_adjust_counter(env->cipher, delta); + return 0; +} + +/* SHA-1 */ + +/** Compute the SHA1 digest of len bytes in data stored in + * m. Write the DIGEST_LEN byte result into digest. + */ +int crypto_digest(const unsigned char *m, int len, unsigned char *digest) +{ + tor_assert(m && digest); + return (SHA1(m,len,digest) == NULL); +} + +struct crypto_digest_env_t { + SHA_CTX d; +}; + +/** Allocate and return a new digest object. + */ +crypto_digest_env_t * +crypto_new_digest_env(void) +{ + crypto_digest_env_t *r; + r = tor_malloc(sizeof(crypto_digest_env_t)); + SHA1_Init(&r->d); + return r; +} + +/** Deallocate a digest object. + */ +void +crypto_free_digest_env(crypto_digest_env_t *digest) { + tor_free(digest); +} + +/** Add len bytes from data to the digest object. + */ +void +crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, + size_t len) +{ + tor_assert(digest); + tor_assert(data); + /* Using the SHA1_*() calls directly means we don't support doing + * sha1 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. + */ + SHA1_Update(&digest->d, (void*)data, len); +} + +/** 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 \<= DIGEST_LEN. + */ +void crypto_digest_get_digest(crypto_digest_env_t *digest, + char *out, size_t out_len) +{ + static char r[DIGEST_LEN]; + SHA_CTX tmpctx; + tor_assert(digest && out); + tor_assert(out_len <= DIGEST_LEN); + /* memcpy into a temporary ctx, since SHA1_Final clears the context */ + memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX)); + SHA1_Final(r, &tmpctx); + memcpy(out, r, out_len); +} + +/** Allocate and return a new digest object with the same state as + * digest + */ +crypto_digest_env_t * +crypto_digest_dup(const crypto_digest_env_t *digest) +{ + crypto_digest_env_t *r; + tor_assert(digest); + r = tor_malloc(sizeof(crypto_digest_env_t)); + memcpy(r,digest,sizeof(crypto_digest_env_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_env_t *into, + const crypto_digest_env_t *from) +{ + tor_assert(into && from); + memcpy(into,from,sizeof(crypto_digest_env_t)); +} + +/* DH */ + +/** Shared P parameter for our DH key exchanged. */ +static BIGNUM *dh_param_p = NULL; +/** Shared G parameter for our DH key exchanges. */ +static BIGNUM *dh_param_g = NULL; + +/** Initialize dh_param_p and dh_param_g if they are not already + * set. */ +static void init_dh_param() { + BIGNUM *p, *g; + int r; + if (dh_param_p && dh_param_g) + return; + + p = BN_new(); + g = BN_new(); + tor_assert(p && g); + +#if 0 + /* This is from draft-ietf-ipsec-ike-modp-groups-05.txt. It's a safe + prime, and supposedly it equals: + 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } + */ + r = BN_hex2bn(&p, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"); +#endif + + /* 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 }. + */ + /* See also rfc 3536 */ + r = BN_hex2bn(&p, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE65381FFFFFFFFFFFFFFFF"); + tor_assert(r); + + r = BN_set_word(g, 2); + tor_assert(r); + dh_param_p = p; + dh_param_g = g; +} + +/** Allocate and return a new DH object for a key echange. + */ +crypto_dh_env_t *crypto_dh_new() +{ + crypto_dh_env_t *res = NULL; + + if (!dh_param_p) + init_dh_param(); + + res = tor_malloc(sizeof(crypto_dh_env_t)); + res->dh = NULL; + + if (!(res->dh = DH_new())) + goto err; + + if (!(res->dh->p = BN_dup(dh_param_p))) + goto err; + + if (!(res->dh->g = BN_dup(dh_param_g))) + goto err; + + return res; + err: + crypto_log_errors(LOG_WARN, "creating DH object"); + if (res && res->dh) DH_free(res->dh); /* frees p and g too */ + if (res) free(res); + return NULL; +} + +/** Return the length of the DH key in dh, in bytes. + */ +int crypto_dh_get_bytes(crypto_dh_env_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_env_t *dh) +{ + if (!DH_generate_key(dh->dh)) { + crypto_log_errors(LOG_WARN, "generating DH key"); + return -1; + } + 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_env_t *dh, char *pubkey, int pubkey_len) +{ + int bytes; + tor_assert(dh); + if (!dh->dh->pub_key) { + if (crypto_dh_generate_public(dh)<0) + return -1; + } + + tor_assert(dh->dh->pub_key); + bytes = BN_num_bytes(dh->dh->pub_key); + if (pubkey_len < bytes) + return -1; + + memset(pubkey, 0, pubkey_len); + BN_bn2bin(dh->dh->pub_key, pubkey+(pubkey_len-bytes)); + + return 0; +} + +#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. + * + * (We generate key material by computing + * SHA1( g^xy || "\x00" ) || SHA1( g^xy || "\x01" ) || ... + * where || is concatenation.) + */ +int crypto_dh_compute_secret(crypto_dh_env_t *dh, + const char *pubkey, int pubkey_len, + char *secret_out, int secret_bytes_out) +{ + unsigned char hash[DIGEST_LEN]; + unsigned char *secret_tmp = NULL; + BIGNUM *pubkey_bn = NULL; + int secret_len; + int i; + tor_assert(dh); + tor_assert(secret_bytes_out/DIGEST_LEN <= 255); + + if (!(pubkey_bn = BN_bin2bn(pubkey, pubkey_len, NULL))) + goto error; + secret_tmp = tor_malloc(crypto_dh_get_bytes(dh)+1); + secret_len = DH_compute_key(secret_tmp, pubkey_bn, dh->dh); + /* sometimes secret_len might be less than 128, e.g., 127. that's ok. */ + for (i = 0; i < secret_bytes_out; i += DIGEST_LEN) { + secret_tmp[secret_len] = (unsigned char) i/DIGEST_LEN; + if (crypto_digest(secret_tmp, secret_len+1, hash)) + goto error; + memcpy(secret_out+i, hash, MIN(DIGEST_LEN, secret_bytes_out-i)); + } + secret_len = secret_bytes_out; + + goto done; + error: + secret_len = -1; + done: + crypto_log_errors(LOG_WARN, "completing DH handshake"); + if (pubkey_bn) + BN_free(pubkey_bn); + tor_free(secret_tmp); + return secret_len; +} +/** Free a DH key exchange object. + */ +void crypto_dh_free(crypto_dh_env_t *dh) +{ + tor_assert(dh && dh->dh); + DH_free(dh->dh); + free(dh); +} + +/* random numbers */ + +/** Seed OpenSSL's random number generator with DIGEST_LEN bytes from the + * operating system. + */ +int crypto_seed_rng() +{ +#ifdef MS_WINDOWS + static int provider_set = 0; + static HCRYPTPROV provider; + char buf[DIGEST_LEN+1]; + + if (!provider_set) { + if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, 0)) { + if (GetLastError() != NTE_BAD_KEYSET) { + log_fn(LOG_ERR,"Can't get CryptoAPI provider [1]"); + return -1; + } + /* Yes, we need to try it twice. */ + if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, + CRYPT_NEWKEYSET)) { + log_fn(LOG_ERR,"Can't get CryptoAPI provider [2]"); + return -1; + } + } + provider_set = 1; + } + if (!CryptGenRandom(provider, DIGEST_LEN, buf)) { + log_fn(LOG_ERR,"Can't get entropy from CryptoAPI."); + return -1; + } + RAND_seed(buf, DIGEST_LEN); + /* And add the current screen state to the entopy pool for + * good measure. */ + RAND_screen(); + return 0; +#else + static char *filenames[] = { + "/dev/srandom", "/dev/urandom", "/dev/random", NULL + }; + int fd; + int i, n; + char buf[DIGEST_LEN+1]; + + for (i = 0; filenames[i]; ++i) { + fd = open(filenames[i], O_RDONLY, 0); + if (fd<0) continue; + log_fn(LOG_INFO, "Seeding RNG from %s", filenames[i]); + n = read(fd, buf, DIGEST_LEN); + close(fd); + if (n != DIGEST_LEN) { + log_fn(LOG_WARN, "Error reading from entropy source"); + return -1; + } + RAND_seed(buf, DIGEST_LEN); + return 0; + } + + log_fn(LOG_WARN, "Cannot seed RNG -- no entropy source found."); + return -1; +#endif +} + +/** Write n bytes of strong random data to to. Return 0 on + * success, -1 on failure. + */ +int crypto_rand(unsigned int n, unsigned char *to) +{ + int r; + tor_assert(to); + r = RAND_bytes(to, n); + if (r == 0) + crypto_log_errors(LOG_WARN, "generating random data"); + return (r == 1) ? 0 : -1; +} + +/** Write n bytes of pseudorandom data to to. Return 0 on + * success, -1 on failure. + */ +void crypto_pseudo_rand(unsigned int n, unsigned char *to) +{ + tor_assert(to); + if (RAND_pseudo_bytes(to, n) == -1) { + log_fn(LOG_ERR, "RAND_pseudo_bytes failed unexpectedly."); + crypto_log_errors(LOG_WARN, "generating random data"); + exit(1); + } +} + +/** Return a pseudorandom integer, choosen uniformly from the values + * between 0 and max-1. */ +int crypto_pseudo_rand_int(unsigned int max) { + unsigned int val; + unsigned int cutoff; + tor_assert(max < UINT_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 = UINT_MAX - (UINT_MAX%max); + while(1) { + crypto_pseudo_rand(sizeof(val), (unsigned char*) &val); + if (val < cutoff) + return val % max; + } +} + +/** Base-64 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, int destlen, const char *src, int srclen) +{ + EVP_ENCODE_CTX ctx; + int len, ret; + + /* 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) + return -1; + + EVP_EncodeInit(&ctx); + EVP_EncodeUpdate(&ctx, dest, &len, (char*) src, srclen); + EVP_EncodeFinal(&ctx, dest+len, &ret); + ret += len; + return ret; +} + +/** Base-64 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. + */ +int +base64_decode(char *dest, int destlen, const char *src, int srclen) +{ + EVP_ENCODE_CTX ctx; + int len, ret; + /* 64 bytes of input -> *up to* 48 bytes of output. + Plus one more byte, in caes I'm wrong. + */ + if (destlen < ((srclen/64)+1)*49) + return -1; + + EVP_DecodeInit(&ctx); + EVP_DecodeUpdate(&ctx, dest, &len, (char*) src, srclen); + EVP_DecodeFinal(&ctx, dest, &ret); + ret += len; + return ret; +} + +/** Implements base32 encoding as in rfc3548. Limitation: Requires + * that srclen*8 is a multiple of 5. + */ +void +base32_encode(char *dest, int destlen, const char *src, int srclen) +{ + int nbits, i, bit, v, u; + nbits = srclen * 8; + + tor_assert((nbits%5) == 0); /* We need an even multiple of 5 bits. */ + tor_assert((nbits/5)+1 <= destlen); /* We need enough space. */ + + 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'; +} + +void base16_encode(char *dest, int destlen, const char *src, int srclen) +{ + const char *end; + char *cp; + + tor_assert(destlen >= srclen*2+1); + + cp = dest; + end = src+srclen; + while (src + +/** Length of the output of our message digest. */ +#define DIGEST_LEN 20 +/** Length of our symmetric cipher's keys. */ +#define CIPHER_KEY_LEN 16 +/** Length of our public keys. */ +#define PK_BYTES (1024/8) +/** Length of our DH keys. */ +#define DH_BYTES (1024/8) + +/** Constants used to indicate no padding for public-key encryption */ +#define PK_NO_PADDING 60000 +/** Constants used to indicate PKCS1 padding for public-key encryption */ +#define PK_PKCS1_PADDING 60001 +/** Constants used to indicate OAEP padding for public-key encryption */ +#define PK_PKCS1_OAEP_PADDING 60002 + +/** Number of bytes added for PKCS1 padding. */ +#define PKCS1_PADDING_OVERHEAD 11 +/** 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 + +typedef struct crypto_pk_env_t crypto_pk_env_t; +typedef struct crypto_cipher_env_t crypto_cipher_env_t; +typedef struct crypto_digest_env_t crypto_digest_env_t; +typedef struct crypto_dh_env_t crypto_dh_env_t; + +/* global state */ +int crypto_global_init(); +int crypto_global_cleanup(); + +/* environment setup */ +crypto_pk_env_t *crypto_new_pk_env(void); +void crypto_free_pk_env(crypto_pk_env_t *env); + +crypto_cipher_env_t *crypto_new_cipher_env(void); +void crypto_free_cipher_env(crypto_cipher_env_t *env); + +/* public key crypto */ +int crypto_pk_generate_key(crypto_pk_env_t *env); + +int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int *len); +int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, int len); +int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname); +int crypto_pk_check_key(crypto_pk_env_t *env); +int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, const char *keyfile); + +int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b); +crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig); +int crypto_pk_keysize(crypto_pk_env_t *env); + +int crypto_pk_public_encrypt(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to, int padding); +int crypto_pk_private_decrypt(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to, int padding, int warnOnFailure); +int crypto_pk_private_sign(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to); +int crypto_pk_private_sign_digest(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to); +int crypto_pk_public_checksig(crypto_pk_env_t *env, const unsigned char *from, int fromlen, unsigned char *to); +int crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const unsigned char *data, int datalen, const unsigned char *sig, int siglen); +int crypto_pk_public_hybrid_encrypt(crypto_pk_env_t *env, + const unsigned char *from, int fromlen, + unsigned char *to, int padding, int force); +int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, + const unsigned char *from, int fromlen, + unsigned char *to,int padding, + int warnOnFailure); + +int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, int dest_len); +crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, int len); +int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out); +int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out); +int crypto_pk_check_fingerprint_syntax(const char *s); + +int base64_encode(char *dest, int destlen, const char *src, int srclen); +int base64_decode(char *dest, int destlen, const char *src, int srclen); +#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" +void base32_encode(char *dest, int destlen, const char *src, int srclen); +void base16_encode(char *dest, int destlen, const char *src, int srclen); +int base16_decode(char *dest, int destlen, const char *src, int srclen); + +/* Key negotiation */ +crypto_dh_env_t *crypto_dh_new(); +int crypto_dh_get_bytes(crypto_dh_env_t *dh); +int crypto_dh_generate_public(crypto_dh_env_t *dh); +int crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey_out, + int pubkey_out_len); +int crypto_dh_compute_secret(crypto_dh_env_t *dh, + const char *pubkey, int pubkey_len, + char *secret_out, int secret_out_len); +void crypto_dh_free(crypto_dh_env_t *dh); + +/* symmetric crypto */ +int crypto_cipher_generate_key(crypto_cipher_env_t *env); +int crypto_cipher_set_key(crypto_cipher_env_t *env, const unsigned char *key); +int crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env); +int crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env); +const unsigned char *crypto_cipher_get_key(crypto_cipher_env_t *env); + +int crypto_cipher_encrypt(crypto_cipher_env_t *env, const unsigned char *from, unsigned int fromlen, unsigned char *to); +int crypto_cipher_decrypt(crypto_cipher_env_t *env, const unsigned char *from, unsigned int fromlen, unsigned char *to); + +/* only implemented for CRYPTO_CIPHER_AES_CTR */ +int crypto_cipher_rewind(crypto_cipher_env_t *env, long delta); +int crypto_cipher_advance(crypto_cipher_env_t *env, long delta); + +/* convenience function: wraps crypto_create_crypto_env, set_key, and init. */ +crypto_cipher_env_t *crypto_create_init_cipher(const char *key, int encrypt_mode); + +/* SHA-1 */ +int crypto_digest(const unsigned char *m, int len, unsigned char *digest); +crypto_digest_env_t *crypto_new_digest_env(); +void crypto_free_digest_env(crypto_digest_env_t *digest); +void crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, + size_t len); +void crypto_digest_get_digest(crypto_digest_env_t *digest, + char *out, size_t out_len); +crypto_digest_env_t *crypto_digest_dup(const crypto_digest_env_t *digest); +void crypto_digest_assign(crypto_digest_env_t *into, + const crypto_digest_env_t *from); + +/* random numbers */ +int crypto_seed_rng(); +int crypto_rand(unsigned int n, unsigned char *to); +void crypto_pseudo_rand(unsigned int n, unsigned char *to); +int crypto_pseudo_rand_int(unsigned int max); + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/fakepoll.c b/tags/tor-0_0_8pre1/src/common/fakepoll.c new file mode 100644 index 0000000000..f23c20e652 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/fakepoll.c @@ -0,0 +1,124 @@ +/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file fakepoll.c + * + * \brief On systems where poll() doesn't exist, fake it with select(). + **/ + +#include "orconfig.h" +#include "fakepoll.h" + +#define MAXCONNECTIONS 10000 /* XXXX copied from or.h */ +#define FD_SETSIZE MAXCONNECTIONS + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif + +#include +#include +#include "util.h" +#include "log.h" + +#ifndef USE_FAKE_POLL +int +tor_poll(struct pollfd *ufds, unsigned int nfds, int timeout) +{ + int i; + for (i=0;i= 0); + } + return poll(ufds,nfds,timeout); +} +#else + +int +tor_poll(struct pollfd *ufds, unsigned int nfds, int timeout) +{ + unsigned int idx; + int maxfd, fd; + int r; +#ifdef MS_WINDOWS + int any_fds_set = 0; +#endif + fd_set readfds, writefds, exceptfds; +#ifdef USING_FAKE_TIMEVAL +#undef timeval +#undef tv_sec +#undef tv_usec +#endif + struct timeval _timeout; + _timeout.tv_sec = timeout/1000; + _timeout.tv_usec = (timeout%1000)*1000; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + + maxfd = -1; + for (idx = 0; idx < nfds; ++idx) { + ufds[idx].revents = 0; + fd = ufds[idx].fd; + tor_assert(fd >= 0); + if (fd > maxfd) { + maxfd = fd; +#ifdef MS_WINDOWS + any_fds_set = 1; +#endif + } + if (ufds[idx].events & POLLIN) + FD_SET(fd, &readfds); + if (ufds[idx].events & POLLOUT) + FD_SET(fd, &writefds); + FD_SET(fd, &exceptfds); + } +#ifdef MS_WINDOWS + if (!any_fds_set) { + Sleep(timeout); + return 0; + } +#endif + r = select(maxfd+1, &readfds, &writefds, &exceptfds, + timeout == -1 ? NULL : &_timeout); + if (r <= 0) + return r; + r = 0; + for (idx = 0; idx < nfds; ++idx) { + fd = ufds[idx].fd; + if (FD_ISSET(fd, &readfds)) + ufds[idx].revents |= POLLIN; + if (FD_ISSET(fd, &writefds)) + ufds[idx].revents |= POLLOUT; + if (FD_ISSET(fd, &exceptfds)) + ufds[idx].revents |= POLLERR; + if (ufds[idx].revents) + ++r; + } + return r; +} +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/fakepoll.h b/tags/tor-0_0_8pre1/src/common/fakepoll.h new file mode 100644 index 0000000000..bf11237dd1 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/fakepoll.h @@ -0,0 +1,56 @@ +/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __FAKEPOLL_H +#define __FAKEPOLL_H + +/** + * \file fakepoll.h + * \brief Headers for fakepoll.c + */ + +#include "orconfig.h" + +#define POLL_NO_WARN + +#if defined(HAVE_POLL_H) +#include +#elif defined(HAVE_SYS_POLL_H) +#include +#endif + +/* If _POLL_EMUL_H_ is defined, then poll is just a just a thin wrapper around + * select. On Mac OS 10.3, this wrapper is kinda flaky, and we should + * use our own. + */ +#if !(defined(HAVE_POLL_H)||defined(HAVE_SYS_POLL_H))&&!defined(_POLL_EMUL_H_) +#define USE_FAKE_POLL +#endif + +#if defined USE_FAKE_POLL && !defined(_POLL_EMUL_H_) +struct pollfd { + int fd; + short events; + short revents; +}; + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 +#endif + +int tor_poll(struct pollfd *ufds, unsigned int nfds, int timeout); + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/log.c b/tags/tor-0_0_8pre1/src/common/log.c new file mode 100644 index 0000000000..aff313d8c5 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/log.c @@ -0,0 +1,301 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file log.c + * + * \brief Functions to send messages to log files or the console. + */ + +#include +#include +#include +#include +#include "orconfig.h" +#include "./util.h" +#include "./log.h" + + +#ifdef MS_WINDOWS +#define vsnprintf _vsnprintf +#define snprintf _snprintf +#endif + +/** 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. */ + FILE *file; /**< Stream to receive log messages. */ + int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */ + int loglevel; /**< Lowest severity level to send to this stream. */ + int max_loglevel; /**< Highest severity level to send to this stream. */ + int is_temporary; /**< Boolean: close after initializing logging subsystem.*/ +} logfile_t; + +/** 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: assert(0); return "UNKNOWN"; + } +} + +/** Linked list of logfile_t. */ +static logfile_t *logfiles = NULL; + +static INLINE size_t _log_prefix(char *buf, size_t buf_len, int severity) +{ + time_t t; + struct timeval now; + size_t n; + + tor_gettimeofday(&now); + t = (time_t)now.tv_sec; + + n = strftime(buf, buf_len, "%b %d %H:%M:%S", localtime(&t)); + n += snprintf(buf+n, buf_len-n, + ".%.3ld [%s] ", + (long)now.tv_usec / 1000, sev_to_string(severity)); + if(n > buf_len) + n = buf_len-1; /* the *nprintf funcs return how many bytes they + * _would_ print, if the output is truncated. + * Subtract one because the count doesn't include the \0 */ + return n; +} + +/** 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. **/ +static void 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; + if (lf->is_temporary) + /* If it's temporary, it isn't really a file. */ + return; + is_new = (ftell(lf->file) == 0); + if (reset && !is_new) + /* We are resetting, but we aren't at the start of the file; no + * need to log again. */ + return; + n = _log_prefix(buf, 250, LOG_NOTICE); + n += snprintf(buf+n, 250-n, "Tor %s opening %slog file.\n", VERSION, + is_new?"new ":""); + if (n>250) + n = 250; + buf[n+1]='\0'; + fputs(buf, lf->file); +} + +/** 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.) + */ +static INLINE void format_msg(char *buf, size_t buf_len, + int severity, const char *funcname, + const char *format, va_list ap) +{ + size_t n; + buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ + + n = _log_prefix(buf, buf_len, severity); + + if (funcname) { + n += snprintf(buf+n, buf_len-n, "%s(): ", funcname); + if(n > buf_len) + n = buf_len-1; + } + + n += vsnprintf(buf+n,buf_len-n,format,ap); + if(n > buf_len) + n = buf_len-1; + buf[n]='\n'; + buf[n+1]='\0'; +} + +/** 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 vsprintf(format,ap). + */ +static void +logv(int severity, const char *funcname, const char *format, va_list ap) +{ + char buf[10024]; + int formatted = 0; + logfile_t *lf; + + assert(format); + for (lf = logfiles; lf; lf = lf->next) { + if (severity < lf->loglevel || severity > lf->max_loglevel) + continue; + if (!lf->file) + continue; + + if (!formatted) { + format_msg(buf, 10024, severity, funcname, format, ap); + formatted = 1; + } + if(fputs(buf, lf->file) == EOF) { /* error */ + assert(0); /* XXX */ + } + if(fflush(lf->file) == EOF) { /* error */ + /* don't log the error! */ + assert(0); /* XXX fail for now. what's better to do? */ + } + } +} + +/** Output a message to the log. */ +void _log(int severity, const char *format, ...) +{ + va_list ap; + va_start(ap,format); + logv(severity, NULL, format, ap); + va_end(ap); +} + +/** Output a message to the log, prefixed with a function name fn. */ +void _log_fn(int severity, const char *fn, const char *format, ...) +{ + va_list ap; + va_start(ap,format); + logv(severity, fn, format, ap); + va_end(ap); +} + +/** Close all open log files. */ +void close_logs() +{ + logfile_t *victim; + while(logfiles) { + victim = logfiles; + logfiles = logfiles->next; + if (victim->needs_close) + fclose(victim->file); + tor_free(victim->filename); + tor_free(victim); + } +} + +/** Close and re-open all log files; used to rotate logs on SIGHUP. */ +void reset_logs() +{ + logfile_t *lf; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->needs_close) { + fclose(lf->file); + lf->file = fopen(lf->filename, "a"); + log_tor_version(lf, 1); + } + } +} + +/** Add a log handler to send all messages of severity loglevel + * or higher to stream. */ +void add_stream_log(int loglevelMin, int loglevelMax, const char *name, FILE *stream) +{ + logfile_t *lf; + lf = tor_malloc(sizeof(logfile_t)); + lf->filename = tor_strdup(name); + lf->needs_close = 0; + lf->loglevel = loglevelMin; + lf->max_loglevel = loglevelMax; + lf->file = stream; + lf->next = logfiles; + lf->is_temporary = 0; + logfiles = lf; +} + +/** Add a log handler to receive messages during startup (before the real + * logs are initialized). + */ +void add_temp_log(void) +{ + add_stream_log(LOG_INFO, LOG_ERR, "", stdout); + logfiles->is_temporary = 1; +} + +void close_temp_logs(void) +{ + logfile_t *lf, **p; + for (p = &logfiles; *p; ) { + if ((*p)->is_temporary) { + lf = *p; + *p = (*p)->next; + if (lf->needs_close) + fclose(lf->file); + tor_free(lf->filename); + tor_free(lf); + } else { + p = &((*p)->next); + } + } +} + +void mark_logs_temp(void) +{ + logfile_t *lf; + for (lf = logfiles; lf; lf = lf->next) + lf->is_temporary = 1; +} + +/** + * Add a log handler to send messages to filename. If opening + * the logfile fails, -1 is returned and errno is set appropriately + * (by fopen). + */ +int add_file_log(int loglevelMin, int loglevelMax, const char *filename) +{ + FILE *f; + f = fopen(filename, "a"); + if (!f) return -1; + add_stream_log(loglevelMin, loglevelMax, filename, f); + logfiles->needs_close = 1; + log_tor_version(logfiles, 0); + return 0; +} + +/** 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; +} + +int get_min_log_level(void) +{ + logfile_t *lf; + int min = LOG_ERR; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->loglevel < min) + min = lf->loglevel; + } + return min; +} + + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/log.h b/tags/tor-0_0_8pre1/src/common/log.h new file mode 100644 index 0000000000..7ce5ceee0a --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/log.h @@ -0,0 +1,83 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * /file log.h + * + * /brief Headers for logging functions. + **/ + +#ifndef __LOG_H + +/** + * \file log.h + * \brief Headers for log.c + */ + +#ifdef HAVE_SYSLOG_H +#include +#define LOG_WARN LOG_WARNING +#else +/** Debug-level severity: for hyper-verbose messages of no interest to + * anybody but developers. */ +#define LOG_DEBUG 0 +/** Info-level severity: for messages that appear frequently during normal + * operation. */ +#define LOG_INFO 1 +/** Notice-level severity: for messages that appear infrequently + * during normal operation; that the user will probably care about; + * and that are not errors. + */ +#define LOG_NOTICE 2 +/** Warn-level severity: for messages that only appear when something has gone + * wrong. */ +#define LOG_WARN 3 +/** Error-level severity: for messages that only appear when something has gone + * very wrong, and the Tor process can no longer proceed. */ +#define LOG_ERR 4 +#endif + +/* magic to make GCC check for proper format strings. */ +#ifdef __GNUC__ +#define CHECK_PRINTF(formatIdx, firstArg) \ + __attribute__ ((format (printf, formatIdx, firstArg))) +#else +#define CHECK_PRINTF(formatIdx, firstArg) +#endif + +int parse_log_level(const char *level); +void add_stream_log(int severityMin, int severityMax, const char *name, FILE *stream); +int add_file_log(int severityMin, int severityMax, const char *filename); +int get_min_log_level(void); +void close_logs(); +void reset_logs(); +void add_temp_log(void); +void close_temp_logs(void); +void mark_logs_temp(void); + +/* Outputs a message to stdout */ +void _log(int severity, const char *format, ...) CHECK_PRINTF(2,3); + +#ifdef __GNUC__ +void _log_fn(int severity, const char *funcname, const char *format, ...) + CHECK_PRINTF(3,4); +/** Log a message at level severity, using a pretty-printed version + * of the current function name. */ +#define log_fn(severity, args...) \ + _log_fn(severity, __PRETTY_FUNCTION__, args) +#else +#define log_fn _log +#endif +#define log _log /* hack it so we don't conflict with log() as much */ + +# define __LOG_H +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/strlcat.c b/tags/tor-0_0_8pre1/src/common/strlcat.c new file mode 100644 index 0000000000..b309648155 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/strlcat.c @@ -0,0 +1,73 @@ +/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/tags/tor-0_0_8pre1/src/common/strlcpy.c b/tags/tor-0_0_8pre1/src/common/strlcpy.c new file mode 100644 index 0000000000..2448ec06c8 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/strlcpy.c @@ -0,0 +1,64 @@ +/* $Id$ */ +/* $OpenBSD: strlcpy.c,v 1.2 1998/11/06 04:33:16 wvdputte Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.2 1998/11/06 04:33:16 wvdputte Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + if (n == 0) + return(strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(s - src); /* count does not include NUL */ +} diff --git a/tags/tor-0_0_8pre1/src/common/test.h b/tags/tor-0_0_8pre1/src/common/test.h new file mode 100644 index 0000000000..81d54b88f3 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/test.h @@ -0,0 +1,143 @@ +/* Copyright 2001,2002,2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __TEST_H +#define __TEST_H + +/** + * \file test.h + * \brief Headers for test.c + */ + +#include +#include + +#define STMT_BEGIN do { +#define STMT_END } while (0) + +#ifdef __GNUC__ +#define PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define PRETTY_FUNCTION "" +#endif + +extern int have_failed; + +#define test_fail() \ + STMT_BEGIN \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): assertion failed.", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION); \ + return; \ + STMT_END + +#define test_assert(expr) \ + STMT_BEGIN \ + if(expr) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): assertion failed: (%s)\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr); \ + return; \ + } STMT_END + +#define test_eq(expr1, expr2) \ + STMT_BEGIN \ + long v1=(long)(expr1), v2=(long)(expr2); \ + if(v1==v2) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s==%s)\n"\ + " (%ld != %ld)\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2, \ + v1, v2); \ + return; \ + } STMT_END + +#define test_neq(expr1, expr2) \ + STMT_BEGIN \ + long v1=(long)(expr1), v2=(long)(expr2); \ + if(v1!=v2) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s!=%s)\n"\ + " (%ld == %ld)\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2, \ + v1, v2); \ + return; \ + } STMT_END + +#define test_streq(expr1, expr2) \ + STMT_BEGIN \ + const char *v1=(expr1), *v2=(expr2); \ + if(!strcmp(v1,v2)) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s==%s)\n"\ + " (\"%s\" != \"%s\")\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2, \ + v1, v2); \ + return; \ + } STMT_END + +#define test_strneq(expr1, expr2) \ + STMT_BEGIN \ + const char *v1=(expr1), *v2=(expr2); \ + if(strcmp(v1,v2)) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s!=%s)\n"\ + " (\"%s\" == \"%s\")\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2, \ + v1, v2); \ + return; \ + } STMT_END + +#define test_memeq(expr1, expr2, len) \ + STMT_BEGIN \ + void *v1=(expr1), *v2=(expr2); \ + if(!memcmp(v1,v2,(len))) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s==%s)\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2); \ + return; \ + } STMT_END + +#define test_memneq(expr1, expr2, len) \ + STMT_BEGIN \ + void *v1=(expr1), *v2=(expr2); \ + if(memcmp(v1,v2,(len))) { printf("."); fflush(stdout); } else { \ + have_failed = 1; \ + printf("\nFile %s: line %d (%s): Assertion failed: (%s!=%s)\n", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION, \ + #expr1, #expr2); \ + return; \ + } STMT_END + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/torint.h b/tags/tor-0_0_8pre1/src/common/torint.h new file mode 100644 index 0000000000..07e291e51e --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/torint.h @@ -0,0 +1,191 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file torint.h + * \brief Header file to define uint32_t and friends + **/ + +#ifndef __TORINT_H +#define __TORINT_H + +#include "orconfig.h" + +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + + +#if (SIZEOF_INT8_T != 0) +#define HAVE_INT8_T +#endif +#if (SIZEOF_INT16_T != 0) +#define HAVE_INT16_T +#endif +#if (SIZEOF_INT32_T != 0) +#define HAVE_INT32_T +#endif +#if (SIZEOF_INT64_T != 0) +#define HAVE_INT64_T +#endif +#if (SIZEOF_UINT8_T != 0) +#define HAVE_UINT8_T +#endif +#if (SIZEOF_UINT16_T != 0) +#define HAVE_UINT16_T +#endif +#if (SIZEOF_UINT32_T != 0) +#define HAVE_UINT32_T +#endif +#if (SIZEOF_UINT64_T != 0) +#define HAVE_UINT64_T +#endif +#if (SIZEOF_INTPTR_T != 0) +#define HAVE_INTPTR_T +#endif +#if (SIZEOF_UINTPTR_T != 0) +#define HAVE_UINTPTR_T +#endif + +#if (SIZEOF_CHAR == 1) +#ifndef HAVE_INT8_T +typedef signed char int8_t; +#define HAVE_INT8_T +#endif +#ifndef HAVE_UINT8_T +typedef unsigned char uint8_t; +#define HAVE_UINT8_T +#endif +#endif + +#if (SIZEOF_SHORT == 2) +#ifndef HAVE_INT16_T +typedef signed short int16_t; +#define HAVE_INT16_T +#endif +#ifndef HAVE_UINT16_T +typedef unsigned short uint16_t; +#define HAVE_UINT16_T +#endif +#endif + + +#if (SIZEOF_INT == 2) +#ifndef HAVE_INT16_T +typedef signed int int16_t; +#define HAVE_INT16_T +#endif +#ifndef HAVE_UINT16_T +typedef unsigned int uint16_t; +#define HAVE_UINT16_T +#endif +#elif (SIZEOF_INT == 4) +#ifndef HAVE_INT32_T +typedef signed int int32_t; +#define HAVE_INT32_T +#endif +#ifndef HAVE_UINT32_T +typedef unsigned int uint32_t; +#define HAVE_UINT32_T +#endif +#endif + + +#if (SIZEOF_LONG == 4) +#ifndef HAVE_INT32_T +typedef signed long int32_t; +#define HAVE_INT32_T +#endif +#ifndef HAVE_UINT32_T +typedef unsigned long uint32_t; +#define HAVE_UINT32_T +#endif +#elif (SIZEOF_LONG == 8) +#ifndef HAVE_INT64_T +typedef signed long int64_t; +#define HAVE_INT64_T +#endif +#ifndef HAVE_UINT32_T +typedef unsigned long uint64_t; +#define HAVE_UINT32_T +#endif +#endif + +#if (SIZEOF_LONG_LONG == 8) +#ifndef HAVE_INT64_T +typedef signed long long int64_t; +#define HAVE_INT64_T +#endif +#ifndef HAVE_UINT64_T +typedef unsigned long long uint64_t; +#define HAVE_UINT64_T +#endif +#endif + +#if (SIZEOF___INT64 == 8) +#ifndef HAVE_INT64_T +typedef signed __int64 int64_t; +#define HAVE_INT64_T +#endif +#ifndef HAVE_UINT64_T +typedef unsigned __int64 uint64_t; +#define HAVE_UINT64_T +#endif +#endif + +#if (SIZEOF_VOID_P > 4 && SIZEOF_VOID_P <= 8) +#ifndef HAVE_INTPTR_T +typedef int64_t intptr_t; +#endif +#ifndef HAVE_UINTPTR_T +typedef uint64_t uintptr_t; +#endif +#elif (SIZEOF_VOID_P > 2 && SIZEOF_VOID_P <= 4) +#ifndef HAVE_INTPTR_T +typedef int32_t intptr_t; +#endif +#ifndef HAVE_UINTPTR_T +typedef uint32_t uintptr_t; +#endif +#else +#error "void * is either >8 bytes or <= 2. In either case, I am confused." +#endif + +#ifndef HAVE_INT8_T +#error "Missing type int8_t" +#endif +#ifndef HAVE_UINT8_T +#error "Missing type uint8_t" +#endif +#ifndef HAVE_INT16_T +#error "Missing type int16_t" +#endif +#ifndef HAVE_UINT16_T +#error "Missing type uint16_t" +#endif +#ifndef HAVE_INT32_T +#error "Missing type int32_t" +#endif +#ifndef HAVE_UINT32_T +#error "Missing type uint32_t" +#endif +#ifndef HAVE_INT64_T +#error "Missing type int64_t" +#endif +#ifndef HAVE_UINT64_T +#error "Missing type uint64_t" +#endif + +#endif /* __TORINT_H */ + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/tortls.c b/tags/tor-0_0_8pre1/src/common/tortls.c new file mode 100644 index 0000000000..78dea3b944 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/tortls.c @@ -0,0 +1,790 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file tortls.c + * + * \brief TLS wrappers for Tor. + **/ +/* (Unlike other tor functions, these + * are prefixed with tor_ in order to avoid conflicting with OpenSSL + * functions and variables.) + */ + +#include "./crypto.h" +#include "./tortls.h" +#include "./util.h" +#include "./log.h" +#include + +/* Copied from or.h */ +#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +#include +#include +#include +#include +#include +#include + +/** How long do identity certificates live? (sec) */ +#define IDENTITY_CERT_LIFETIME (365*24*60*60) +/** How much clock skew do we tolerate when checking certificates? (sec) */ +#define CERT_ALLOW_SKEW (90*60) + +typedef struct tor_tls_context_st { + SSL_CTX *ctx; + SSL_CTX *client_only_ctx; +} tor_tls_context; + +/** Holds a SSL object and its associated data. Members are only + * accessed from within tortls.c. + */ +struct tor_tls_st { + SSL *ssl; /**< An OpenSSL SSL object. */ + int socket; /**< The underlying file descriptor for this TLS connection. */ + enum { + TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, + TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED + } state; /**< The current SSL state, depending on which operations have + * completed successfully. */ + int isServer; + int wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last time. */ +}; + +static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa, + crypto_pk_env_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int lifetime); + +/** Global tls context. We keep it here because nobody else needs to + * touch it. */ +static tor_tls_context *global_tls_context = NULL; +/** True iff tor_tls_init() has been called. */ +static int tls_library_is_initialized = 0; + +/* Module-internal error codes. */ +#define _TOR_TLS_SYSCALL -6 +#define _TOR_TLS_ZERORETURN -5 + +/* These functions are declared in crypto.c but not exported. */ +EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private); +crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa); +DH *_crypto_dh_env_get_dh(crypto_dh_env_t *dh); + +/** Log all pending tls errors at level severity. Use + * doing to describe our current activities. + */ +static void +tls_log_errors(int severity, const char *doing) +{ + int 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 (doing) { + log(severity, "TLS error while %s: %s (in %s:%s)", doing, msg, lib,func); + } else { + log(severity, "TLS error: %s (in %s:%s)", msg, lib, func); + } + } +} + +#define CATCH_SYSCALL 1 +#define CATCH_ZERO 2 + +/** Given a TLS object and the result of an SSL_* call, use + * SSL_get_error to determine whether an error has occurred, and if so + * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}. + * If extra&CATCH_SYSCALL is true, return _TOR_TLS_SYSCALL instead of + * reporting syscall errors. If extra&CATCH_ZERO is true, return + * _TOR_TLS_ZERORETURN instead of reporting zero-return errors. + * + * If an error has occurred, log it at level severity and describe the + * current action as doing. + */ +static int +tor_tls_get_error(tor_tls *tls, int r, int extra, + const char *doing, int severity) +{ + int err = SSL_get_error(tls->ssl, r); + switch (err) { + case SSL_ERROR_NONE: + return TOR_TLS_DONE; + case SSL_ERROR_WANT_READ: + return TOR_TLS_WANTREAD; + case SSL_ERROR_WANT_WRITE: + return TOR_TLS_WANTWRITE; + case SSL_ERROR_SYSCALL: + if (extra&CATCH_SYSCALL) + return _TOR_TLS_SYSCALL; + if (r == 0) + log(severity, "TLS error: unexpected close while %s", doing); + else { + int e = tor_socket_errno(tls->socket); + log(severity, "TLS error: (errno=%d: %s)", + doing, e, tor_socket_strerror(e)); + } + tls_log_errors(severity, doing); + return TOR_TLS_ERROR; + case SSL_ERROR_ZERO_RETURN: + if (extra&CATCH_ZERO) + return _TOR_TLS_ZERORETURN; + log(severity, "TLS error: Zero return"); + tls_log_errors(severity, doing); + return TOR_TLS_ERROR; + default: + tls_log_errors(severity, doing); + return TOR_TLS_ERROR; + } +} + +/** Initialize OpenSSL, unless it has already been initialized. + */ +static void +tor_tls_init() { + if (!tls_library_is_initialized) { + SSL_library_init(); + SSL_load_error_strings(); + crypto_global_init(); + OpenSSL_add_all_algorithms(); + tls_library_is_initialized = 1; + } +} + +/** We need to give OpenSSL a callback to verify certificates. This is + * it: We always accept peer certs and complete the handshake. We + * don't validate them until later. + */ +static int always_accept_verify_cb(int preverify_ok, + X509_STORE_CTX *x509_ctx) +{ + return 1; +} + +/** Generate and sign an X509 certificate with the public key rsa, + * signed by the private key rsa_sign. The commonName of the + * certificate will be cname; the commonName of the issuer will be + * cname_sign. The cert will be valid for cert_lifetime seconds + * starting from now. Return a certificate on success, NULL on + * failure. + */ +static X509 * +tor_tls_create_certificate(crypto_pk_env_t *rsa, + crypto_pk_env_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int cert_lifetime) +{ + time_t start_time, end_time; + EVP_PKEY *sign_pkey = NULL, *pkey=NULL; + X509 *x509 = NULL; + X509_NAME *name = NULL, *name_issuer=NULL; + int nid; + + tor_tls_init(); + + start_time = time(NULL); + + tor_assert(rsa && cname && rsa_sign && cname_sign); + if (!(sign_pkey = _crypto_pk_env_get_evp_pkey(rsa_sign,1))) + goto error; + if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,0))) + goto error; + if (!(x509 = X509_new())) + goto error; + if (!(X509_set_version(x509, 2))) + goto error; + if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time))) + goto error; + + if (!(name = X509_NAME_new())) + goto error; + if ((nid = OBJ_txt2nid("organizationName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, + "TOR", -1, -1, 0))) goto error; + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, + (char*)cname, -1, -1, 0))) goto error; + if (!(X509_set_subject_name(x509, name))) + goto error; + + if (!(name_issuer = X509_NAME_new())) + goto error; + if ((nid = OBJ_txt2nid("organizationName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC, + "TOR", -1, -1, 0))) goto error; + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC, + (char*)cname_sign, -1, -1, 0))) goto error; + if (!(X509_set_issuer_name(x509, name_issuer))) + goto error; + + if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time)) + goto error; + end_time = start_time + cert_lifetime; + if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time)) + goto error; + if (!X509_set_pubkey(x509, pkey)) + goto error; + if (!X509_sign(x509, sign_pkey, EVP_sha1())) + goto error; + + goto done; + error: + tls_log_errors(LOG_WARN, "generating certificate"); + if (x509) { + X509_free(x509); + x509 = NULL; + } + done: + if (sign_pkey) + EVP_PKEY_free(sign_pkey); + if (pkey) + EVP_PKEY_free(pkey); + if (name) + X509_NAME_free(name); + if (name_issuer) + X509_NAME_free(name_issuer); + return x509; +} + + +#ifdef EVERYONE_HAS_AES +/* Everybody is running OpenSSL 0.9.7 or later, so no backward compatibility + * is needed. */ +#define CIPHER_LIST TLS1_TXT_DHE_RSA_WITH_AES_128_SHA +#elif defined(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) +/* Some people are running OpenSSL before 0.9.7, but we aren't. + * We can support AES and 3DES. + */ +#define CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \ + SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) +#else +/* We're running OpenSSL before 0.9.7. We only support 3DES. */ +#define CIPHER_LIST SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA +#endif + +/** Create a new TLS context. If we are going to be using it as a + * server, it must have isServer set to true, identity set to the + * identity key used to sign that certificate, and nickname set to + * the server's nickname. If we're only going to be a client, + * isServer should be false, identity should be NULL, and nickname + * should be NULL. Return -1 if failure, else 0. + * + * You can call this function multiple times. Each time you call it, + * it generates new certificates; all new connections will use + * the new SSL context. + */ +int +tor_tls_context_new(crypto_pk_env_t *identity, + int isServer, const char *nickname, + unsigned int key_lifetime) +{ + crypto_pk_env_t *rsa = NULL; + crypto_dh_env_t *dh = NULL; + EVP_PKEY *pkey = NULL; + tor_tls_context *result = NULL; + X509 *cert = NULL, *idcert = NULL; + char nn2[1024]; + int client_only; + SSL_CTX **ctx; + sprintf(nn2, "%s ", nickname); + + tor_tls_init(); + + if (isServer) { + /* Generate short-term RSA key. */ + if (!(rsa = crypto_new_pk_env())) + goto error; + if (crypto_pk_generate_key(rsa)<0) + goto error; + /* Create certificate signed by identity key. */ + cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, + key_lifetime); + /* Create self-signed certificate for identity key. */ + idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, + IDENTITY_CERT_LIFETIME); + if (!cert || !idcert) { + log(LOG_WARN, "Error creating certificate"); + goto error; + } + } + + result = tor_malloc(sizeof(tor_tls_context)); + result->ctx = result->client_only_ctx = NULL; + for (client_only=0; client_only <= 1; ++client_only) { + ctx = client_only ? &result->client_only_ctx : &result->ctx; +#ifdef EVERYONE_HAS_AES + /* Tell OpenSSL to only use TLS1 */ + if (!(*ctx = SSL_CTX_new(TLSv1_method()))) + goto error; +#else + /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */ + if (!(*ctx = SSL_CTX_new(SSLv23_method()))) + goto error; + SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2); +#endif + if (!SSL_CTX_set_cipher_list(*ctx, CIPHER_LIST)) + goto error; + if (!client_only) { + if (cert && !SSL_CTX_use_certificate(*ctx,cert)) + goto error; + if (idcert && !SSL_CTX_add_extra_chain_cert(*ctx,idcert)) + goto error; + } + SSL_CTX_set_session_cache_mode(*ctx, SSL_SESS_CACHE_OFF); + if (isServer && !client_only) { + tor_assert(rsa); + if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1))) + goto error; + if (!SSL_CTX_use_PrivateKey(*ctx, pkey)) + goto error; + EVP_PKEY_free(pkey); + pkey = NULL; + if (cert) { + if (!SSL_CTX_check_private_key(*ctx)) + goto error; + } + } + dh = crypto_dh_new(); + SSL_CTX_set_tmp_dh(*ctx, _crypto_dh_env_get_dh(dh)); + crypto_dh_free(dh); + SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, + always_accept_verify_cb); + /* let us realloc bufs that we're writing from */ + SSL_CTX_set_mode(*ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + } + /* Free the old context if one exists. */ + if (global_tls_context) { + /* This is safe even if there are open connections: OpenSSL does + * reference counting with SSL and SSL_CTX objects. */ + SSL_CTX_free(global_tls_context->ctx); + SSL_CTX_free(global_tls_context->client_only_ctx); + tor_free(global_tls_context); + } + global_tls_context = result; + return 0; + + error: + tls_log_errors(LOG_WARN, "creating TLS context"); + if (pkey) + EVP_PKEY_free(pkey); + if (rsa) + crypto_free_pk_env(rsa); + if (dh) + crypto_dh_free(dh); + if (result && result->ctx) + SSL_CTX_free(result->ctx); + if (result && result->client_only_ctx) + SSL_CTX_free(result->client_only_ctx); + if (result) + free(result); + if (cert) + X509_free(cert); + if (idcert) + X509_free(cert); + return -1; +} + +/** Create a new TLS object from a file descriptor, and a flag to + * determine whether it is functioning as a server. + */ +tor_tls * +tor_tls_new(int sock, int isServer, int use_no_cert) +{ + tor_tls *result = tor_malloc(sizeof(tor_tls)); + SSL_CTX *ctx; + tor_assert(global_tls_context); /* make sure somebody made it first */ + ctx = use_no_cert ? global_tls_context->client_only_ctx + : global_tls_context->ctx; + if (!(result->ssl = SSL_new(ctx))) + return NULL; + result->socket = sock; + SSL_set_fd(result->ssl, sock); + result->state = TOR_TLS_ST_HANDSHAKE; + result->isServer = isServer; + result->wantwrite_n = 0; + return result; +} + +/** Release resources associated with a TLS object. Does not close the + * underlying file descriptor. + */ +void +tor_tls_free(tor_tls *tls) +{ + SSL_free(tls->ssl); + free(tls); +} + +/** Underlying function for TLS reading. Reads up to len + * characters from tls into cp. On success, returns the + * number of characters read. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +int +tor_tls_read(tor_tls *tls, char *cp, int len) +{ + int r, err; + tor_assert(tls && tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + r = SSL_read(tls->ssl, cp, len); + if (r > 0) + return r; + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_INFO); + log_fn(LOG_DEBUG,"returned r=%d, err=%d",r,err); + if (err == _TOR_TLS_ZERORETURN) { + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_CLOSE; + } else { + tor_assert(err != TOR_TLS_DONE); + return err; + } +} + +/** Underlying function for TLS writing. Write up to n + * characters from cp onto tls. On success, returns the + * number of characters written. On failure, returns TOR_TLS_ERROR, + * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. + */ +int +tor_tls_write(tor_tls *tls, char *cp, int n) +{ + int r, err; + tor_assert(tls && tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_OPEN); + if (n == 0) + return 0; + if(tls->wantwrite_n) { + /* if WANTWRITE last time, we must use the _same_ n as before */ + tor_assert(n >= tls->wantwrite_n); + log_fn(LOG_DEBUG,"resuming pending-write, (%d to flush, reusing %d)", + n, tls->wantwrite_n); + n = tls->wantwrite_n; + tls->wantwrite_n = 0; + } + r = SSL_write(tls->ssl, cp, n); + err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO); + if (err == TOR_TLS_DONE) { + return r; + } + if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) { +// log_fn(LOG_INFO,"wantwrite or wantread. remembering the number %d.",n); + tls->wantwrite_n = n; + } + return err; +} + +/** Perform initial handshake on tls. When finished, returns + * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, + * or TOR_TLS_WANTWRITE. + */ +int +tor_tls_handshake(tor_tls *tls) +{ + int r; + tor_assert(tls && tls->ssl); + tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); + if (tls->isServer) { + r = SSL_accept(tls->ssl); + } else { + r = SSL_connect(tls->ssl); + } + r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO); + if (r == TOR_TLS_DONE) { + tls->state = TOR_TLS_ST_OPEN; + } + return r; +} + +/** Shut down an open tls connection tls. When finished, returns + * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, + * or TOR_TLS_WANTWRITE. + */ +int +tor_tls_shutdown(tor_tls *tls) +{ + int r, err; + char buf[128]; + tor_assert(tls && tls->ssl); + + while (1) { + if (tls->state == TOR_TLS_ST_SENTCLOSE) { + /* If we've already called shutdown once to send a close message, + * we read until the other side has closed too. + */ + do { + r = SSL_read(tls->ssl, buf, 128); + } while (r>0); + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down", + LOG_INFO); + if (err == _TOR_TLS_ZERORETURN) { + tls->state = TOR_TLS_ST_GOTCLOSE; + /* fall through... */ + } else { + return err; + } + } + + r = SSL_shutdown(tls->ssl); + if (r == 1) { + /* If shutdown returns 1, the connection is entirely closed. */ + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_DONE; + } + err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down", + LOG_INFO); + if (err == _TOR_TLS_SYSCALL) { + /* The underlying TCP connection closed while we were shutting down. */ + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_DONE; + } else if (err == _TOR_TLS_ZERORETURN) { + /* The TLS connection says that it sent a shutdown record, but + * isn't done shutting down yet. Make sure that this hasn't + * happened before, then go back to the start of the function + * and try to read. + */ + if (tls->state == TOR_TLS_ST_GOTCLOSE || + tls->state == TOR_TLS_ST_SENTCLOSE) { + log(LOG_WARN, + "TLS returned \"half-closed\" value while already half-closed"); + return TOR_TLS_ERROR; + } + tls->state = TOR_TLS_ST_SENTCLOSE; + /* fall through ... */ + } else { + return err; + } + } /* end loop */ +} + +/** Return true iff this TLS connection is authenticated. + */ +int +tor_tls_peer_has_cert(tor_tls *tls) +{ + X509 *cert; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + return 0; + X509_free(cert); + return 1; +} + +/** Return the nickname (if any) that the peer connected on tls + * claims to have. + */ +int +tor_tls_get_peer_cert_nickname(tor_tls *tls, char *buf, int buflen) +{ + X509 *cert = NULL; + X509_NAME *name = NULL; + int nid; + int lenout; + + if (!(cert = SSL_get_peer_certificate(tls->ssl))) { + log_fn(LOG_WARN, "Peer has no certificate"); + goto error; + } + if (!(name = X509_get_subject_name(cert))) { + log_fn(LOG_WARN, "Peer certificate has no subject name"); + goto error; + } + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) + goto error; + + lenout = X509_NAME_get_text_by_NID(name, nid, buf, buflen); + if (lenout == -1) + goto error; + if (((int)strspn(buf, LEGAL_NICKNAME_CHARACTERS)) < lenout) { + log_fn(LOG_WARN, "Peer certificate nickname has illegal characters."); + goto error; + } + return 0; + error: + if (cert) + X509_free(cert); + if (name) + X509_NAME_free(name); + return -1; +} + +static void log_cert_lifetime(X509 *cert, const char *problem) +{ + BIO *bio = NULL; + BUF_MEM *buf; + char *s1=NULL, *s2=NULL; + char mytime[33]; + time_t now = time(NULL); + + if (problem) + log_fn(LOG_WARN,"Certificate %s: is your system clock set incorrectly?", + problem); + + if (!(bio = BIO_new(BIO_s_mem()))) { + log_fn(LOG_WARN, "Couldn't allocate BIO!"); goto end; + } + if (!(ASN1_TIME_print(bio, X509_get_notBefore(cert)))) { + tls_log_errors(LOG_WARN, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s1 = tor_strndup(buf->data, buf->length); + + BIO_reset(bio); + if (!(ASN1_TIME_print(bio, X509_get_notAfter(cert)))) { + tls_log_errors(LOG_WARN, "printing certificate lifetime"); + goto end; + } + BIO_get_mem_ptr(bio, &buf); + s2 = tor_strndup(buf->data, buf->length); + + strftime(mytime, 32, "%b %d %H:%M:%S %Y GMT", gmtime(&now)); + + log_fn(LOG_WARN, "(certificate lifetime runs from %s through %s. Your time is %s.)",s1,s2,mytime); + + end: + if (bio) + BIO_free(bio); + if (s1) + tor_free(s1); + if (s2) + tor_free(s2); +} + +/** If the provided tls connection is authenticated and has a + * certificate that is currently valid and signed, then set + * *identity_key to the identity certificate's key and return + * 0. Else, return -1. + */ +int +tor_tls_verify(tor_tls *tls, crypto_pk_env_t **identity_key) +{ + X509 *cert = NULL, *id_cert = NULL; + STACK_OF(X509) *chain = NULL; + EVP_PKEY *id_pkey = NULL; + RSA *rsa; + int num_in_chain; + time_t now, t; + int r = -1, i; + + *identity_key = NULL; + + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + goto done; + if (!(chain = SSL_get_peer_cert_chain(tls->ssl))) + goto done; + num_in_chain = sk_X509_num(chain); + log_fn(LOG_DEBUG,"Number of certs in chain: %d", num_in_chain); + /* 1 means we're receiving (server-side), and it's just the id_cert. + * 2 means we're connecting (client-side), and it's both the link + * cert and the id_cert. + */ + if (num_in_chain < 1) { + log_fn(LOG_WARN,"Unexpected number of certificates in chain (%d)", + num_in_chain); + goto done; + } + for (i=0; i 0) { + log_cert_lifetime(cert, "not yet valid"); + goto done; + } + t = now - CERT_ALLOW_SKEW; + if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { + log_cert_lifetime(cert, "already expired"); + goto done; + } + + if (!(id_pkey = X509_get_pubkey(id_cert)) || + X509_verify(cert, id_pkey) <= 0) { + log_fn(LOG_WARN,"X509_verify on cert and pkey returned <= 0"); + tls_log_errors(LOG_WARN,"verifying certificate"); + goto done; + } + + rsa = EVP_PKEY_get1_RSA(id_pkey); + if (!rsa) + goto done; + *identity_key = _crypto_new_pk_env_rsa(rsa); + + r = 0; + + done: + if (cert) + X509_free(cert); + if (id_pkey) + EVP_PKEY_free(id_pkey); + + /* This should never get invoked, but let's make sure in case OpenSSL + * acts unexpectedly. */ + tls_log_errors(LOG_WARN, "finishing tor_tls_verify"); + + return r; +} + +/** Return the number of bytes available for reading from tls. + */ +int +tor_tls_get_pending_bytes(tor_tls *tls) +{ + tor_assert(tls); +#if OPENSSL_VERSION_NUMBER < 0x0090700fl + if (tls->ssl->rstate == SSL_ST_READ_BODY) + return 0; + if (tls->ssl->s3->rrec.type != SSL3_RT_APPLICATION_DATA) + return 0; +#endif + return SSL_pending(tls->ssl); + +} + +/** Return the number of bytes read across the underlying socket. */ +unsigned long tor_tls_get_n_bytes_read(tor_tls *tls) +{ + tor_assert(tls); + return BIO_number_read(SSL_get_rbio(tls->ssl)); +} +/** Return the number of bytes written across the underlying socket. */ +unsigned long tor_tls_get_n_bytes_written(tor_tls *tls) +{ + tor_assert(tls); + return BIO_number_written(SSL_get_wbio(tls->ssl)); +} + +/** Implement assert_no_tls_errors: If there are any pending OpenSSL + * errors, log an error message and assert(0). */ +void _assert_no_tls_errors(const char *fname, int line) +{ + if (ERR_peek_error() == 0) + return; + log_fn(LOG_ERR, "Unhandled OpenSSL errors found at %s:%d: ", + fname, line); + tls_log_errors(LOG_ERR, NULL); + + tor_assert(0); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/tortls.h b/tags/tor-0_0_8pre1/src/common/tortls.h new file mode 100644 index 0000000000..3346a878e2 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/tortls.h @@ -0,0 +1,55 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef _TORTLS_H +#define _TORTLS_H + +/** + * \file tortls.h + * \brief Headers for tortls.c + **/ + +#include "../common/crypto.h" + +/* Opaque structure to hold a TLS connection. */ +typedef struct tor_tls_st tor_tls; + +/* Possible return values for most tor_tls_* functions. */ +#define TOR_TLS_ERROR -4 +#define TOR_TLS_CLOSE -3 +#define TOR_TLS_WANTREAD -2 +#define TOR_TLS_WANTWRITE -1 +#define TOR_TLS_DONE 0 + +int tor_tls_context_new(crypto_pk_env_t *rsa, int isServer, + const char *nickname, unsigned int key_lifetime); +tor_tls *tor_tls_new(int sock, int is_server, int use_no_cert); +void tor_tls_free(tor_tls *tls); +int tor_tls_peer_has_cert(tor_tls *tls); +int tor_tls_get_peer_cert_nickname(tor_tls *tls, char *buf, int buflen); +int tor_tls_verify(tor_tls *tls, crypto_pk_env_t **identity); +int tor_tls_read(tor_tls *tls, char *cp, int len); +int tor_tls_write(tor_tls *tls, char *cp, int n); +int tor_tls_handshake(tor_tls *tls); +int tor_tls_shutdown(tor_tls *tls); +int tor_tls_get_pending_bytes(tor_tls *tls); + +unsigned long tor_tls_get_n_bytes_read(tor_tls *tls); +unsigned long tor_tls_get_n_bytes_written(tor_tls *tls); + +/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. + */ +#define assert_no_tls_errors() _assert_no_tls_errors(__FILE__,__LINE__) + +void _assert_no_tls_errors(const char *fname, int line); + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/util.c b/tags/tor-0_0_8pre1/src/common/util.c new file mode 100644 index 0000000000..be4f60028a --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/util.c @@ -0,0 +1,1826 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file util.c + * + * \brief Common functions for strings, IO, network, data structures, + * process control, and cross-platform portability. + **/ + +#include "orconfig.h" + +#ifdef MS_WINDOWS +#define WIN32_WINNT 0x400 +#define _WIN32_WINNT 0x400 +#define WIN32_LEAN_AND_MEAN +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include "util.h" +#include "log.h" +#include "crypto.h" +#include "../or/tree.h" + +#ifdef HAVE_UNAME +#include +#endif +#ifdef HAVE_CTYPE_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_LIMITS_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include /* FreeBSD needs this to know what version it is */ +#endif +#ifdef HAVE_SYS_LIMITS_H +#include +#endif +#ifdef HAVE_MACHINE_LIMITS_H +#ifndef __FreeBSD__ + /* FreeBSD has a bug where it complains that this file is obsolete, + and I should migrate to using sys/limits. It complains even when + I include both. */ +#include +#endif +#endif +#ifdef HAVE_SYS_TYPES_H +#include /* Must be included before sys/stat.h for Ultrix */ +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_STAT_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 + +/* used by inet_addr, not defined on solaris anywhere!? */ +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif + +/* Inline the strl functions if the plaform doesn't have them. */ +#ifndef HAVE_STRLCPY +#include "strlcpy.c" +#endif +#ifndef HAVE_STRLCAT +#include "strlcat.c" +#endif + +/** Allocate a chunk of size bytes of memory, and return a pointer to + * result. On error, log and terminate the process. (Same as malloc(size), + * but never returns NULL.) + */ +void *tor_malloc(size_t size) { + void *result; + + /* Some libcs don't do the right thing on size==0. Override them. */ + if (size==0) { + size=1; + } + result = malloc(size); + + if(!result) { + log_fn(LOG_ERR, "Out of memory. Dying."); + exit(1); + } +// memset(result,'X',size); /* deadbeef to encourage bugs */ + return result; +} + +/* Allocate a chunk of size bytes of memory, fill the memory with + * zero bytes, and return a pointer to the result. Log and terminate + * the process on error. (Same as calloc(size,1), but never returns NULL.) + */ +void *tor_malloc_zero(size_t size) { + void *result = tor_malloc(size); + memset(result, 0, size); + return result; +} + +/** Change the size of the memory block pointed to by ptr to size + * bytes long; return the new memory block. On error, log and + * terminate. (Like realloc(ptr,size), but never returns NULL.) + */ +void *tor_realloc(void *ptr, size_t size) { + void *result; + + result = realloc(ptr, size); + if (!result) { + log_fn(LOG_ERR, "Out of memory. Dying."); + exit(1); + } + return result; +} + +/** Return a newly allocated copy of the NUL-terminated string s. On + * error, log and terminate. (Like strdup(s), but never returns + * NULL.) + */ +char *tor_strdup(const char *s) { + char *dup; + tor_assert(s); + + dup = strdup(s); + if(!dup) { + log_fn(LOG_ERR,"Out of memory. Dying."); + exit(1); + } + return dup; +} + +/** Allocate and return a new string containing the first n + * characters of s. If s is longer than n + * characters, only the first n are copied. The result is + * always NUL-terminated. (Like strndup(s,n), but never returns + * NULL.) + */ +char *tor_strndup(const char *s, size_t n) { + char *dup; + tor_assert(s); + dup = tor_malloc(n+1); + strncpy(dup, s, n); + dup[n] = 0; + return dup; +} + + +#ifndef UNALIGNED_INT_ACCESS_OK +/** + * Read a 16-bit value beginning at cp. Equaivalent to + * *(uint16_t*)(cp), but will not cause segfaults on platforms that forbid + * unaligned memory access. + */ +uint16_t get_uint16(const char *cp) +{ + uint16_t v; + memcpy(&v,cp,2); + return v; +} +/** + * Read a 32-bit value beginning at cp. Equaivalent to + * *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid + * unaligned memory access. + */ +uint32_t get_uint32(const char *cp) +{ + uint32_t v; + memcpy(&v,cp,4); + 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(char *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(char *cp, uint32_t v) +{ + memcpy(cp,&v,4); +} +#endif + +/** Encode the first fromlen bytes stored at from in hexidecimal; + * write the result as a NUL-terminated string to to. to must + * have at least (2*fromlen)+1 bytes of free space. + */ +void hex_encode(const char *from, int fromlen, char *to) +{ + const unsigned char *fp = from; + static const char TABLE[] = "0123456789abcdef"; + tor_assert(from && fromlen>=0 && to); + while (fromlen--) { + *to++ = TABLE[*fp >> 4]; + *to++ = TABLE[*fp & 7]; + ++fp; + } + *to = '\0'; +} + +/** Return a pointer to a NUL-terminated hexidecimal string encoding + * the first fromlen bytes of from. (fromlen must be \<= 32.) The + * result does not need to be deallocated, but repeated calls to + * hex_str will trash old results. + */ +const char *hex_str(const char *from, int fromlen) +{ + static char buf[65]; + if (fromlen>(sizeof(buf)-1)/2) + fromlen = (sizeof(buf)-1)/2; + hex_encode(from,fromlen,buf); + return buf; +} + +/***** + * smartlist_t: a simple resizeable array abstraction. + *****/ + +/* All newly allocated smartlists have this capacity. + */ +#define SMARTLIST_DEFAULT_CAPACITY 32 + + +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; +}; + +/** Allocate and return an empty smartlist. + */ +smartlist_t *smartlist_create() { + 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) { + free(sl->list); + free(sl); +} + +/** Change the capacity of the smartlist to n, so that we can grow + * the list up to n elements with no further reallocation or wasted + * space. If n is less than or equal to the number of elements + * currently in the list, reduce the list's capacity as much as + * possible without losing elements. + */ +void smartlist_set_capacity(smartlist_t *sl, int n) { + if (n < sl->num_used) + n = sl->num_used; + if (sl->capacity != n) { + sl->capacity = n; + sl->list = tor_realloc(sl->list, sizeof(void*)*sl->capacity); + } +} + +/** Remove all elements from the list. + */ +void smartlist_clear(smartlist_t *sl) { + sl->num_used = 0; +} + +/** Set the list's new length to len (which must be \<= the list's + * current size). Remove the last smartlist_len(sl)-len elements from the + * list. + */ +void smartlist_truncate(smartlist_t *sl, int len) +{ + tor_assert(len <= sl->num_used); + sl->num_used = len; +} + +/** Append element to the end of the list. */ +void smartlist_add(smartlist_t *sl, void *element) { + if (sl->num_used >= sl->capacity) { + sl->capacity *= 2; + sl->list = tor_realloc(sl->list, sizeof(void*)*sl->capacity); + } + sl->list[sl->num_used++] = element; +} + +/** Append each element from S2 to the end of S1. */ +void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2) +{ + SMARTLIST_FOREACH(s2, void *, element, smartlist_add(sl, element)); +} + +/** Remove all elements E from sl such that E==element. Does not preserve + * the order of s1. + */ +void smartlist_remove(smartlist_t *sl, 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 */ + } +} + +/** Return true iff some element E of sl has E==element. + */ +int smartlist_isin(const smartlist_t *sl, void *element) { + int i; + for(i=0; i < sl->num_used; i++) + if(sl->list[i] == element) + return 1; + return 0; +} + +/** Return true iff some element E of sl2 has smartlist_isin(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_isin(sl1, sl2->list[i])) + return 1; + return 0; +} + +/** Remove every element E of sl1 such that !smartlist_isin(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_isin(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_isin(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]); +} + +/** Return a randomly chosen element of sl; or NULL if sl is empty. + */ +void *smartlist_choose(const smartlist_t *sl) { + if(sl->num_used) + return sl->list[crypto_pseudo_rand_int(sl->num_used)]; + return NULL; /* no elements to choose from */ +} + +/** Return the idxth element of sl. + */ +void *smartlist_get(const smartlist_t *sl, int idx) +{ + tor_assert(sl && idx>=0 && idx < sl->num_used); + return sl->list[idx]; +} +/** Change the value of the idxth element of sl to val; return the old + * value of the idxth element. + */ +void *smartlist_set(smartlist_t *sl, int idx, void *val) +{ + void *old; + tor_assert(sl && idx>=0 && idx < sl->num_used); + old = sl->list[idx]; + sl->list[idx] = val; + return old; +} +/** Remove the idxth element of sl; if idx is not the last + * element, swap the last element of sl into the idxth space. + * Return the old value of the idxth element. + */ +void *smartlist_del(smartlist_t *sl, int idx) +{ + void *old; + tor_assert(sl && idx>=0 && idx < sl->num_used); + old = sl->list[idx]; + sl->list[idx] = sl->list[--sl->num_used]; + return old; +} +/** 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) +{ + void *old; + tor_assert(sl && idx>=0 && idx < sl->num_used); + old = sl->list[idx]; + --sl->num_used; + if (idx < sl->num_used) + memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx)); + return old; +} +/** Return the number of items in sl. + */ +int smartlist_len(const smartlist_t *sl) +{ + return sl->num_used; +} +/** 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 && idx >= 0 && idx <= sl->num_used); + if (idx == sl->num_used) { + smartlist_add(sl, val); + } else { + /* Ensure sufficient capacity */ + if (sl->num_used >= sl->capacity) { + sl->capacity *= 2; + sl->list = tor_realloc(sl->list, sizeof(void*)*sl->capacity); + } + /* 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; + } +} + +/* Splay-tree implementation of string-to-void* map + */ +struct strmap_entry_t { + SPLAY_ENTRY(strmap_entry_t) node; + char *key; + void *val; +}; + +struct strmap_t { + SPLAY_HEAD(strmap_tree, strmap_entry_t) head; +}; + +static int compare_strmap_entries(struct strmap_entry_t *a, + struct strmap_entry_t *b) +{ + return strcmp(a->key, b->key); +} + +SPLAY_PROTOTYPE(strmap_tree, strmap_entry_t, node, compare_strmap_entries); +SPLAY_GENERATE(strmap_tree, strmap_entry_t, node, compare_strmap_entries); + +/** Create a new empty map from strings to void*'s. + */ +strmap_t* strmap_new(void) +{ + strmap_t *result; + result = tor_malloc(sizeof(strmap_t)); + SPLAY_INIT(&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 && key && val); + search.key = (char*)key; + resolve = SPLAY_FIND(strmap_tree, &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; + SPLAY_INSERT(strmap_tree, &map->head, resolve); + return NULL; + } +} + +/** Return the current value associated with key, or NULL if no + * value is set. + */ +void* strmap_get(strmap_t *map, const char *key) +{ + strmap_entry_t *resolve; + strmap_entry_t search; + tor_assert(map && key); + search.key = (char*)key; + resolve = SPLAY_FIND(strmap_tree, &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 && key); + search.key = (char*)key; + resolve = SPLAY_FIND(strmap_tree, &map->head, &search); + if (resolve) { + oldval = resolve->val; + SPLAY_REMOVE(strmap_tree, &map->head, resolve); + tor_free(resolve->key); + 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(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; +} + +/** Invoke fn() on every entry of the map, in order. For every entry, + * fn() is invoked with that entry's key, that entry's value, and the + * value of data supplied to strmap_foreach. fn() must return a new + * (possibly unmodified) value for each entry: if fn() returns NULL, the + * entry is removed. + * + * Example: + * \code + * static void* upcase_and_remove_empty_vals(const char *key, void *val, + * void* data) { + * char *cp = (char*)val; + * if (!*cp) { // val is an empty string. + * free(val); + * return NULL; + * } else { + * for (; *cp; cp++) + * *cp = toupper(*cp); + * } + * return val; + * } + * } + * + * ... + * + * strmap_foreach(map, upcase_and_remove_empty_vals, NULL); + * \endcode + */ +void strmap_foreach(strmap_t *map, + void* (*fn)(const char *key, void *val, void *data), + void *data) +{ + strmap_entry_t *ptr, *next; + tor_assert(map && fn); + for (ptr = SPLAY_MIN(strmap_tree, &map->head); ptr != NULL; ptr = next) { + /* This remove-in-place usage is specifically blessed in tree(3). */ + next = SPLAY_NEXT(strmap_tree, &map->head, ptr); + ptr->val = fn(ptr->key, ptr->val, data); + if (!ptr->val) { + SPLAY_REMOVE(strmap_tree, &map->head, ptr); + tor_free(ptr->key); + tor_free(ptr); + } + } +} + +/** 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(iter); + * free(val); + * } else { + * for(;*cp;cp++) *cp = toupper(*cp); + * iter = strmap_iter_next(iter); + * } + * } + * \endcode + * + */ +strmap_iter_t *strmap_iter_init(strmap_t *map) +{ + tor_assert(map); + return SPLAY_MIN(strmap_tree, &map->head); +} +/** Advance the iterator iter for map a single step to the next entry. + */ +strmap_iter_t *strmap_iter_next(strmap_t *map, strmap_iter_t *iter) +{ + tor_assert(map && iter); + return SPLAY_NEXT(strmap_tree, &map->head, iter); +} +/** Advance the iterator iter a single step to the next entry, removing + * the current entry. + */ +strmap_iter_t *strmap_iter_next_rmv(strmap_t *map, strmap_iter_t *iter) +{ + strmap_iter_t *next; + tor_assert(map && iter); + next = SPLAY_NEXT(strmap_tree, &map->head, iter); + SPLAY_REMOVE(strmap_tree, &map->head, iter); + tor_free(iter->key); + tor_free(iter); + return next; +} +/** 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 && keyp && 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; +} +/** 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; + for (ent = SPLAY_MIN(strmap_tree, &map->head); ent != NULL; ent = next) { + next = SPLAY_NEXT(strmap_tree, &map->head, ent); + SPLAY_REMOVE(strmap_tree, &map->head, ent); + tor_free(ent->key); + if (free_val) + tor_free(ent->val); + } + tor_assert(SPLAY_EMPTY(&map->head)); + tor_free(map); +} + +/* + * String manipulation + */ + +/** Convert all alphabetic characters in the nul-terminated string s to + * lowercase. */ +void tor_strlower(char *s) +{ + while (*s) { + *s = tolower(*s); + ++s; + } +} + + +/** Return a pointer to the first char of s that is not whitespace and + * not a comment, or to the terminating NUL if no such character exists. + */ +const char *eat_whitespace(const char *s) { + tor_assert(s); + + while(isspace((int)*s) || *s == '#') { + while(isspace((int)*s)) + s++; + if(*s == '#') { /* read to a \n or \0 */ + while(*s && *s != '\n') + s++; + if(!*s) + return s; + } + } + return s; +} + +/** Return a pointer to the first char of s that is not a space or a tab, + * or to the terminating NUL if no such character exists. */ +const char *eat_whitespace_no_nl(const char *s) { + while(*s == ' ' || *s == '\t') + ++s; + return s; +} + +/** Return a pointer to the first char of s that is whitespace or #, + * or to the terminating NUL if no such character exists. + */ +const char *find_whitespace(const char *s) { + tor_assert(s); + + while(*s && !isspace((int)*s) && *s != '#') + s++; + + return s; +} + +/* + * Time + */ + +/** 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 HAVE_GETTIMEOFDAY + if (gettimeofday(timeval, NULL)) { + log_fn(LOG_ERR, "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; +} + +/** Return the number of microseconds elapsed between *start and *end. + * If start is after end, return 0. + */ +long +tv_udiff(struct timeval *start, struct timeval *end) +{ + long udiff; + long secdiff = end->tv_sec - start->tv_sec; + + if (secdiff+1 > LONG_MAX/1000000) { + log_fn(LOG_WARN, "comparing times too far apart."); + return LONG_MAX; + } + + udiff = secdiff*1000000L + (end->tv_usec - start->tv_usec); + if(udiff < 0) { + log_fn(LOG_INFO, "start (%ld.%ld) is after end (%ld.%ld). Returning 0.", + (long)start->tv_sec, (long)start->tv_usec, (long)end->tv_sec, (long)end->tv_usec); + return 0; + } + return udiff; +} + +/** Return -1 if *a \< *b, 0 if *a==*b, and 1 if *a \> *b. + */ +int tv_cmp(struct timeval *a, struct timeval *b) { + if (a->tv_sec > b->tv_sec) + return 1; + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_usec > b->tv_usec) + return 1; + if (a->tv_usec < b->tv_usec) + return -1; + return 0; +} + +/** Increment *a by the number of seconds and microseconds in *b. + */ +void tv_add(struct timeval *a, struct timeval *b) { + a->tv_usec += b->tv_usec; + a->tv_sec += b->tv_sec + (a->tv_usec / 1000000); + a->tv_usec %= 1000000; +} + +/** Increment *a by ms milliseconds. + */ +void tv_addms(struct timeval *a, long ms) { + a->tv_usec += (ms * 1000) % 1000000; + a->tv_sec += ((ms * 1000) / 1000000) + (a->tv_usec / 1000000); + a->tv_usec %= 1000000; +} + + +#define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400))) +static int n_leapdays(int y1, int y2) { + --y1; + --y2; + return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); +} +/** Number of days per month in non-leap year; used by tor_timegm. */ +static const int days_per_month[] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/** Return a time_t given a struct tm. The result is given in GMT, and + * does not account for leap seconds. + */ +time_t tor_timegm (struct tm *tm) { + /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. + * It's way more brute-force than fiddling with tzset(). + */ + time_t ret; + unsigned long year, days, hours, minutes; + int i; + year = tm->tm_year + 1900; + tor_assert(year >= 1970); + tor_assert(tm->tm_mon >= 0 && tm->tm_mon <= 11); + days = 365 * (year-1970) + n_leapdays(1970,year); + for (i = 0; i < tm->tm_mon; ++i) + days += days_per_month[i]; + if (tm->tm_mon > 1 && IS_LEAPYEAR(year)) + ++days; + days += tm->tm_mday - 1; + hours = days*24 + tm->tm_hour; + + minutes = hours*60 + tm->tm_min; + ret = minutes*60 + tm->tm_sec; + return ret; +} + +/* + * Low-level I/O. + */ + +/** Write count bytes from buf to fd. isSocket + * must be 1 if fd was returned by socket() or accept(), and 0 if fd + * was returned by open(). Return the number of bytes written, or -1 + * on error. Only use if fd is a blocking fd. */ +int write_all(int fd, const char *buf, size_t count, int isSocket) { + size_t written = 0; + int result; + + while(written != count) { + if (isSocket) + result = send(fd, buf+written, count-written, 0); + else + result = write(fd, buf+written, count-written); + if(result<0) + return -1; + written += result; + } + return count; +} + +/** Read count bytes from fd to buf. isSocket must be 1 if fd + * was returned by socket() or accept(), and 0 if fd was returned by + * open(). Return the number of bytes read, or -1 on error. Only use + * if fd is a blocking fd. */ +int read_all(int fd, char *buf, size_t count, int isSocket) { + size_t numread = 0; + int result; + + while(numread != count) { + if (isSocket) + result = recv(fd, buf+numread, count-numread, 0); + else + result = read(fd, buf+numread, count-numread); + if(result<=0) + return -1; + numread += result; + } + return count; +} + +/** Turn socket into a nonblocking socket. + */ +void set_socket_nonblocking(int socket) +{ +#ifdef MS_WINDOWS + /* Yes means no and no means yes. Do you not want to be nonblocking? */ + int nonblocking = 0; + ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking); +#else + fcntl(socket, F_SETFL, O_NONBLOCK); +#endif +} + +/* + * Process control + */ + +/** 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. + */ +int spawn_func(int (*func)(void *), void *data) +{ +#ifdef MS_WINDOWS + int rv; + rv = _beginthread(func, 0, data); + if (rv == (unsigned long) -1) + 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() +{ +#ifdef MS_WINDOWS + _endthread(); +#else + exit(0); +#endif +} + + +/** + * 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). + **/ +int +tor_socketpair(int family, int type, int protocol, int fd[2]) +{ +#ifdef HAVE_SOCKETPAIR + return socketpair(family, type, protocol, fd); +#else + /* 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. + */ + int listener = -1; + int connector = -1; + int acceptor = -1; + struct sockaddr_in listen_addr; + struct sockaddr_in connect_addr; + int size; + + if (protocol +#ifdef AF_UNIX + || family != AF_UNIX +#endif + ) { +#ifdef MS_WINDOWS + errno = WSAEAFNOSUPPORT; +#else + errno = EAFNOSUPPORT; +#endif + return -1; + } + if (!fd) { + errno = EINVAL; + return -1; + } + + listener = socket(AF_INET, type, 0); + if (listener == -1) + return -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 choses 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 = socket(AF_INET, type, 0); + if (connector == -1) + 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 = accept(listener, (struct sockaddr *) &listen_addr, &size); + if (acceptor == -1) + goto tidy_up_and_fail; + if (size != sizeof(listen_addr)) + goto abort_tidy_up_and_fail; + tor_close_socket(listener); + /* 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; + } + fd[0] = connector; + fd[1] = acceptor; + return 0; + + abort_tidy_up_and_fail: +#ifdef MS_WINDOWS + errno = WSAECONNABORTED; +#else + errno = ECONNABORTED; /* I hope this is portable and appropriate. */ +#endif + tidy_up_and_fail: + { + int save_errno = errno; + if (listener != -1) + tor_close_socket(listener); + if (connector != -1) + tor_close_socket(connector); + if (acceptor != -1) + tor_close_socket(acceptor); + errno = save_errno; + return -1; + } +#endif +} + +/** + * 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.) + */ +#ifdef MS_WINDOWS +int tor_socket_errno(int sock) +{ + int optval, optvallen=sizeof(optval); + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK && sock >= 0) { + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval, &optvallen)) + return err; + if (optval) + return optval; + } + return err; +} +#endif + +#ifdef MS_WINDOWS +#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 avaialable"), + 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 + +/* + * Filesystem operations. + */ + +/** Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't + * exist, FN_FILE if it is a regular file, or FN_DIR if it's a + * directory. */ +file_status_t file_status(const char *fname) +{ + struct stat st; + if (stat(fname, &st)) { + if (errno == ENOENT) { + return FN_NOENT; + } + return FN_ERROR; + } + if (st.st_mode & S_IFDIR) + return FN_DIR; + else if (st.st_mode & S_IFREG) + return FN_FILE; + else + return FN_ERROR; +} + +/** Check whether dirname exists and is private. If yes return 0. If + * it does not exist, and create is set, try to create it and return 0 + * on success. Else return -1. */ +int check_private_dir(const char *dirname, int create) +{ + int r; + struct stat st; + tor_assert(dirname); + if (stat(dirname, &st)) { + if (errno != ENOENT) { + log(LOG_WARN, "Directory %s cannot be read: %s", dirname, + strerror(errno)); + return -1; + } + if (!create) { + log(LOG_WARN, "Directory %s does not exist.", dirname); + return -1; + } + log(LOG_INFO, "Creating directory %s", dirname); +#ifdef MS_WINDOWS + r = mkdir(dirname); +#else + r = mkdir(dirname, 0700); +#endif + if (r) { + log(LOG_WARN, "Error creating directory %s: %s", dirname, + strerror(errno)); + return -1; + } else { + return 0; + } + } + if (!(st.st_mode & S_IFDIR)) { + log(LOG_WARN, "%s is not a directory", dirname); + return -1; + } +#ifndef MS_WINDOWS + if (st.st_uid != getuid()) { + log(LOG_WARN, "%s is not owned by this UID (%d). You must fix this to proceed.", dirname, (int)getuid()); + return -1; + } + if (st.st_mode & 0077) { + log(LOG_WARN, "Fixing permissions on directory %s", dirname); + if (chmod(dirname, 0700)) { + log(LOG_WARN, "Could not chmod directory %s: %s", dirname, + strerror(errno)); + return -1; + } else { + return 0; + } + } +#endif + return 0; +} + +/** Create a file named fname with the contents str. Overwrite the + * previous fname if possible. Return 0 on success, -1 on failure. + * + * This function replaces the old file atomically, if possible. + */ +int +write_str_to_file(const char *fname, const char *str) +{ + char tempname[1024]; + int fd; + FILE *file; + if ((strlcpy(tempname,fname,1024) >= 1024) || + (strlcat(tempname,".tmp",1024) >= 1024)) { + log(LOG_WARN, "Filename %s.tmp too long (>1024 chars)", fname); + return -1; + } + if ((fd = open(tempname, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + log(LOG_WARN, "Couldn't open %s for writing: %s", tempname, + strerror(errno)); + return -1; + } + if (!(file = fdopen(fd, "w"))) { + log(LOG_WARN, "Couldn't fdopen %s for writing: %s", tempname, + strerror(errno)); + close(fd); + return -1; + } + if (fputs(str,file) == EOF) { + log(LOG_WARN, "Error writing to %s: %s", tempname, strerror(errno)); + fclose(file); + return -1; + } + fclose(file); + +#ifdef MS_WINDOWS + /* On Windows, rename doesn't replace. We could call ReplaceFile, but + * that's hard, and we can probably sneak by without atomicity. */ + switch (file_status(fname)) { + case FN_ERROR: + log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno)); + return -1; + case FN_DIR: + log(LOG_WARN, "Error replacing %s: is directory", fname); + return -1; + case FN_FILE: + if (unlink(fname)) { + log(LOG_WARN, "Error replacing %s while removing old copy: %s", + fname, strerror(errno)); + return -1; + } + break; + case FN_NOENT: + ; + } +#endif + if (rename(tempname, fname)) { + log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno)); + return -1; + } + return 0; +} + +/** Read the contents of filename into a newly allocated string; return the + * string on success or NULL on failure. + */ +char *read_file_to_str(const char *filename) { + int fd; /* router file */ + struct stat statbuf; + char *string; + + tor_assert(filename); + + if(strcspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) { + log_fn(LOG_WARN,"Filename %s contains illegal characters.",filename); + return NULL; + } + + if(stat(filename, &statbuf) < 0) { + log_fn(LOG_INFO,"Could not stat %s.",filename); + return NULL; + } + + fd = open(filename,O_RDONLY,0); + if (fd<0) { + log_fn(LOG_WARN,"Could not open %s.",filename); + return NULL; + } + + string = tor_malloc(statbuf.st_size+1); + + if(read_all(fd,string,statbuf.st_size,0) != statbuf.st_size) { + log_fn(LOG_WARN,"Couldn't read all %ld bytes of file '%s'.", + (long)statbuf.st_size,filename); + free(string); + close(fd); + return NULL; + } + close(fd); + + string[statbuf.st_size] = 0; /* null terminate it */ + return string; +} + +/** read lines from f (no more than maxlen-1 bytes each) until we + * get a non-whitespace line. If it isn't of the form "key value" + * (value can have spaces), return -1. + * Point *key to the first word in line, point *value * to the second. + * Put a \0 at the end of key, remove everything at the end of value + * that is whitespace or comment. + * Return 1 if success, 0 if no more lines, -1 if error. + */ +int parse_line_from_file(char *line, int maxlen, FILE *f, char **key_out, char **value_out) { + char *s, *key, *end, *value; + +try_next_line: + if(!fgets(line, maxlen, f)) { + if(feof(f)) + return 0; + return -1; /* real error */ + } + + if((s = strchr(line,'#'))) /* strip comments */ + *s = 0; /* stop the line there */ + + /* remove end whitespace */ + s = strchr(line, 0); /* now we're at the null */ + do { + *s = 0; + s--; + } while (s >= line && isspace((int)*s)); + + key = line; + while(isspace((int)*key)) + key++; + if(*key == 0) + goto try_next_line; /* this line has nothing on it */ + end = key; + while(*end && !isspace((int)*end)) + end++; + value = end; + while(*value && isspace((int)*value)) + value++; + + if(!*end || !*value) { /* only a key on this line. no value. */ + *end = 0; + log_fn(LOG_WARN,"Line has keyword '%s' but no value. Failing.",key); + return -1; + } + *end = 0; /* null it out */ + + log_fn(LOG_DEBUG,"got keyword '%s', value '%s'", key, value); + *key_out = key, *value_out = value; + return 1; +} + +/** Expand any homedir prefix on 'filename'; return a newly allocated + * string. */ +char *expand_filename(const char *filename) +{ + tor_assert(filename); + /* XXXX Should eventually check for ~username/ */ + if (!strncmp(filename,"~/",2)) { + const char *home = getenv("HOME"); + char *result; + if (!home) { + log_fn(LOG_ERR, "Couldn't find $HOME environment variable while expanding %s", filename); + return NULL; + } + /* minus two characters for ~/, plus one for /, plus one for NUL. */ + result = tor_malloc(strlen(home)+strlen(filename)+16); + sprintf(result,"%s/%s",home,filename+2); + return result; + } else { + return tor_strdup(filename); + } +} + +/** 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) { + + if (((ip & 0xff000000) == 0x0a000000) || /* 10/8 */ + ((ip & 0xff000000) == 0x00000000) || /* 0/8 */ + ((ip & 0xff000000) == 0x7f000000) || /* 127/8 */ + ((ip & 0xffff0000) == 0xa9fe0000) || /* 169.254/16 */ + ((ip & 0xfff00000) == 0xac100000) || /* 172.16/12 */ + ((ip & 0xffff0000) == 0xc0a80000)) /* 192.168/16 */ + return 1; + return 0; +} + +/** Return true iff ip (in host order) is judged to be on the + * same network as us. For now, check if it's an internal IP. For XXX008, + * also check if it's on the same class C network as our public IP. + */ +int is_local_IP(uint32_t ip) { + return is_internal_IP(ip); +} + +/* 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) */ + snprintf(uname_result, 255, "%s %s %s", + u.sysname, u.nodename, u.machine); + uname_result[255] = '\0'; + } else +#endif + { + strcpy(uname_result, "Unknown platform"); + } + uname_result_is_set = 1; + } + return uname_result; +} + +#ifndef MS_WINDOWS +/* Based on code contributed by christian grothoff */ +static int start_daemon_called = 0; +static int finish_daemon_called = 0; +static int daemon_filedes[2]; +/** Start putting the process into daemon mode: fork and drop all resources + * except standard fds. The parent process never returns, but stays around + * until finish_daemon is called. (Note: it's safe to call this more + * than once: calls after the first are ignored.) + */ +void start_daemon(const char *desired_cwd) +{ + pid_t pid; + + if (start_daemon_called) + return; + start_daemon_called = 1; + + if(!desired_cwd) + desired_cwd = "/"; + /* Don't hold the wrong FS mounted */ + if (chdir(desired_cwd) < 0) { + log_fn(LOG_ERR,"chdir to %s failed. Exiting.",desired_cwd); + exit(1); + } + + pipe(daemon_filedes); + pid = fork(); + if (pid < 0) { + log_fn(LOG_ERR,"fork failed. Exiting."); + exit(1); + } + if (pid) { /* Parent */ + int ok; + char c; + + close(daemon_filedes[1]); /* we only read */ + ok = -1; + while (0 < read(daemon_filedes[0], &c, sizeof(char))) { + if (c == '.') + ok = 1; + } + fflush(stdout); + if (ok == 1) + exit(0); + else + exit(1); /* child reported error */ + } else { /* Child */ + close(daemon_filedes[0]); /* we only write */ + + pid = setsid(); /* Detach from controlling terminal */ + /* + * Fork one more time, so the parent (the session group leader) can exit. + * This means that we, as a non-session group leader, can never regain a + * controlling terminal. This part is recommended by Stevens's + * _Advanced Programming in the Unix Environment_. + */ + if (fork() != 0) { + exit(0); + } + return; + } +} + +/** Finish putting the process into daemon mode: drop standard fds, and tell + * the parent process to exit. (Note: it's safe to call this more than once: + * calls after the first are ignored. Calls start_daemon first if it hasn't + * been called already.) + */ +void finish_daemon(void) +{ + int nullfd; + char c = '.'; + if (finish_daemon_called) + return; + if (!start_daemon_called) + start_daemon(NULL); + finish_daemon_called = 1; + + nullfd = open("/dev/null", + O_CREAT | O_RDWR | O_APPEND); + if (nullfd < 0) { + log_fn(LOG_ERR,"/dev/null can't be opened. Exiting."); + exit(1); + } + /* close fds linking to invoking terminal, but + * close usual incoming fds, but redirect them somewhere + * useful so the fds don't get reallocated elsewhere. + */ + if (dup2(nullfd,0) < 0 || + dup2(nullfd,1) < 0 || + dup2(nullfd,2) < 0) { + log_fn(LOG_ERR,"dup2 failed. Exiting."); + exit(1); + } + write(daemon_filedes[1], &c, sizeof(char)); /* signal success */ + close(daemon_filedes[1]); +} +#else +/* defined(MS_WINDOWS) */ +void start_daemon(char *cp) {} +void finish_daemon(void) {} +#endif + +/** Write the current process ID, followed by NL, into filename. + */ +void write_pidfile(char *filename) { +#ifndef MS_WINDOWS + FILE *pidfile; + + if ((pidfile = fopen(filename, "w")) == NULL) { + log_fn(LOG_WARN, "Unable to open %s for writing: %s", filename, + strerror(errno)); + } else { + fprintf(pidfile, "%d\n", (int)getpid()); + fclose(pidfile); + } +#endif +} + +/** Call setuid and setgid to run as user:group. Return 0 on + * success. On failure, log and return -1. + */ +int switch_id(char *user, char *group) { +#ifndef MS_WINDOWS + struct passwd *pw = NULL; + struct group *gr = NULL; + + if (user) { + pw = getpwnam(user); + if (pw == NULL) { + log_fn(LOG_ERR,"User '%s' not found.", user); + return -1; + } + } + + /* switch the group first, while we still have the privileges to do so */ + if (group) { + gr = getgrnam(group); + if (gr == NULL) { + log_fn(LOG_ERR,"Group '%s' not found.", group); + return -1; + } + + if (setgid(gr->gr_gid) != 0) { + log_fn(LOG_ERR,"Error setting GID: %s", strerror(errno)); + return -1; + } + } else if (user) { + if (setgid(pw->pw_gid) != 0) { + log_fn(LOG_ERR,"Error setting GID: %s", strerror(errno)); + return -1; + } + } + + /* now that the group is switched, we can switch users and lose + privileges */ + if (user) { + if (setuid(pw->pw_uid) != 0) { + log_fn(LOG_ERR,"Error setting UID: %s", strerror(errno)); + return -1; + } + } + + return 0; +#endif + + log_fn(LOG_ERR, + "User or group specified, but switching users is not supported."); + + return -1; +} + +/** 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 *c, struct in_addr* addr) +{ +#ifdef HAVE_INET_ATON + return inet_aton(c, addr); +#else + uint32_t r; + tor_assert(c && addr); + if (strcmp(c, "255.255.255.255") == 0) { + addr->s_addr = 0xFFFFFFFFu; + return 1; + } + r = inet_addr(c); + if (r == INADDR_NONE) + return 0; + addr->s_addr = r; + return 1; +#endif +} + +/** Similar behavior to Unix gethostbyname: resolve name, and set + * *addr to the proper IP address, in network 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) +{ + /* Perhaps eventually this should be replaced by a tor_getaddrinfo or + * something. + */ + struct in_addr iaddr; + struct hostent *ent; + tor_assert(addr); + if (!*name) { + /* Empty address is an error. */ + return -1; + } else if (tor_inet_aton(name, &iaddr)) { + /* It's an IP. */ + memcpy(addr, &iaddr.s_addr, 4); + return 0; + } else { + ent = gethostbyname(name); + if (ent) { + /* break to remind us if we move away from IPv4 */ + tor_assert(ent->h_length == 4); + memcpy(addr, ent->h_addr, 4); + return 0; + } + memset(addr, 0, 4); +#ifdef MS_WINDOWS + return (WSAGetLastError() == WSATRY_AGAIN) ? 1 : -1; +#else + return (h_errno == TRY_AGAIN) ? 1 : -1; +#endif + } +} + +#ifndef MS_WINDOWS +struct tor_mutex_t { +}; +tor_mutex_t *tor_mutex_new(void) { return NULL; } +void tor_mutex_acquire(tor_mutex_t *m) { } +void tor_mutex_release(tor_mutex_t *m) { } +void tor_mutex_free(tor_mutex_t *m) { } +#else +struct tor_mutex_t { + HANDLE handle; +}; +tor_mutex_t *tor_mutex_new(void) +{ + tor_mutex_t *m; + m = tor_malloc_zero(sizeof(tor_mutex_t)); + m->handle = CreateMutex(NULL, FALSE, NULL); + tor_assert(m->handle != NULL); + return m; +} +void tor_mutex_free(tor_mutex_t *m) +{ + CloseHandle(m->handle); + tor_free(m); +} +void tor_mutex_acquire(tor_mutex_t *m) +{ + DWORD r; + r = WaitForSingleObject(m->handle, INFINITE); + switch (r) { + case WAIT_ABANDONED: /* holding thread exited. */ + case WAIT_OBJECT_0: /* we got the mutex normally. */ + break; + case WAIT_TIMEOUT: /* Should never happen. */ + tor_assert(0); + break; + case WAIT_FAILED: + log_fn(LOG_WARN, "Failed to acquire mutex: %d", GetLastError()); + } +} +void tor_mutex_release(tor_mutex_t *m) +{ + BOOL r; + r = ReleaseMutex(m->handle); + if (!r) { + log_fn(LOG_WARN, "Failed to release mutex: %d", GetLastError()); + } +} + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/common/util.h b/tags/tor-0_0_8pre1/src/common/util.h new file mode 100644 index 0000000000..0cc08e9a10 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/common/util.h @@ -0,0 +1,283 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file util.h + * \brief Headers for util.c + **/ + +#ifndef __UTIL_H +#define __UTIL_H + +#include "orconfig.h" +#include "torint.h" +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif + +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif +#ifndef HAVE_GETTIMEOFDAY +#ifdef HAVE_FTIME +#define USING_FAKE_TIMEVAL +#include +struct timeval { + time_t tv_sec; + unsigned int tv_usec; +}; +#endif +#endif + +#ifdef MS_WINDOWS +/* Windows names string functions differently from most other platforms. */ +#define strncasecmp strnicmp +#define strcasecmp stricmp +/* "inline" is __inline on windows. " */ +#define INLINE __inline +/* Windows compilers before VC7 don't have __FUNCTION__. */ +#if _MSC_VER < 1300 +#define __FUNCTION__ "???" +#endif +#else +#define INLINE inline +#endif + +/** Replace assert() with a variant that sends failures to the log before + * calling assert() normally. + */ +#ifdef NDEBUG +#define tor_assert(expr) do {} while(0) +#else +#define tor_assert(expr) do { \ + if (!(expr)) { \ + log(LOG_ERR, "%s:%d: %s: Assertion %s failed; aborting.", \ + __FILE__, __LINE__, __FUNCTION__, #expr); \ + assert(expr); /* write to console too. */ \ + abort(); /* unreached */ \ + } } while (0) +#endif + +#ifdef MS_WINDOWS +/** 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. + */ +#define tor_close_socket(s) closesocket(s) +#else +#define tor_close_socket(s) close(s) +#endif + +/** Legal characters in a filename */ +#ifdef MS_WINDOWS +#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/\\ " +#else +#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/ " +#endif + +size_t strlcat(char *dst, const char *src, size_t siz); +size_t strlcpy(char *dst, const char *src, size_t siz); + +void *tor_malloc(size_t size); +void *tor_malloc_zero(size_t size); +void *tor_realloc(void *ptr, size_t size); +char *tor_strdup(const char *s); +char *tor_strndup(const char *s, size_t n); +#define tor_free(p) do {if(p) {free(p); (p)=NULL;}} while(0) +void tor_strlower(char *s); + +/* Some platforms segfault when you try to access a multi-byte type + * that isn't aligned to a word boundary. The macros and/or functions + * below can be used to access unaligned data on any platform. + */ +#ifdef UNALIGNED_INT_ACCESS_OK +#define get_uint16(cp) (*(uint16_t*)(cp)) +#define get_uint32(cp) (*(uint32_t*)(cp)) +#define set_uint16(cp,v) do { *(uint16_t*)(cp) = (v); } while (0) +#define set_uint32(cp,v) do { *(uint32_t*)(cp) = (v); } while (0) +#else +#if 1 +uint16_t get_uint16(const char *cp); +uint32_t get_uint32(const char *cp); +void set_uint16(char *cp, uint16_t v); +void set_uint32(char *cp, uint32_t v); +#else +#define get_uint16(cp) \ + ( ((*(((uint8_t*)(cp))+0))<<8) + \ + ((*(((uint8_t*)(cp))+1)) ) ) +#define get_uint32(cp) \ + ( ((*(((uint8_t*)(cp))+0))<<24) + \ + ((*(((uint8_t*)(cp))+1))<<16) + \ + ((*(((uint8_t*)(cp))+2))<<8 ) + \ + ((*(((uint8_t*)(cp))+3)) ) ) +#define set_uint16(cp,v) \ + do { \ + uint16_t u16v = (v); \ + *(((uint8_t*)(cp))+0) = (v >> 8)&0xff; \ + *(((uint8_t*)(cp))+1) = (v >> 0)&0xff; \ + } while (0) +#define set_uint32(cp,val) \ + do { \ + uint32_t u32v = (v); \ + *(((uint8_t*)(cp))+0) = s32 >> 24)&0xff; \ + *(((uint8_t*)(cp))+1) = s32 >> 16)&0xff; \ + *(((uint8_t*)(cp))+2) = s32 >> 8)&0xff; \ + *(((uint8_t*)(cp))+3) = s32 >> 0)&0xff; \ + } while (0) +#endif +#endif + +void hex_encode(const char *from, int fromlen, char *to); +const char *hex_str(const char *from, int fromlen); + +/** Generic resizeable array. */ +typedef struct smartlist_t smartlist_t; + +smartlist_t *smartlist_create(); +void smartlist_free(smartlist_t *sl); +void smartlist_set_capacity(smartlist_t *sl, int n); +void smartlist_clear(smartlist_t *sl); +void smartlist_truncate(smartlist_t *sl, int n); +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, void *element); +int smartlist_isin(const smartlist_t *sl, void *element); +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); +void *smartlist_choose(const smartlist_t *sl); +void *smartlist_get(const smartlist_t *sl, int idx); +void *smartlist_set(smartlist_t *sl, int idx, void *val); +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); +int smartlist_len(const smartlist_t *sl); +#define SMARTLIST_FOREACH(sl, type, var, cmd) \ + do { \ + int sl_idx, sl_len=smartlist_len(sl); \ + type var; \ + for(sl_idx = 0; sl_idx < sl_len; ++sl_idx) { \ + var = smartlist_get((sl),sl_idx); \ + do {cmd;} while(0); \ + } } while (0) + +/* Map from const char * to void*. Implemented with a splay tree. */ +typedef struct strmap_t strmap_t; +typedef struct strmap_entry_t strmap_entry_t; +typedef struct strmap_entry_t strmap_iter_t; +strmap_t* strmap_new(void); +void* strmap_set(strmap_t *map, const char *key, void *val); +void* strmap_get(strmap_t *map, const char *key); +void* strmap_remove(strmap_t *map, const char *key); +void* strmap_set_lc(strmap_t *map, const char *key, void *val); +void* strmap_get_lc(strmap_t *map, const char *key); +void* strmap_remove_lc(strmap_t *map, const char *key); +typedef void* (*strmap_foreach_fn)(const char *key, void *val, void *data); +void strmap_foreach(strmap_t *map, strmap_foreach_fn fn, void *data); +void strmap_free(strmap_t *map, void (*free_val)(void*)); + +strmap_iter_t *strmap_iter_init(strmap_t *map); +strmap_iter_t *strmap_iter_next(strmap_t *map, strmap_iter_t *iter); +strmap_iter_t *strmap_iter_next_rmv(strmap_t *map, strmap_iter_t *iter); +void strmap_iter_get(strmap_iter_t *iter, const char **keyp, void **valp); + +int strmap_iter_done(strmap_iter_t *iter); + +/* String manipulation */ +const char *eat_whitespace(const char *s); +const char *eat_whitespace_no_nl(const char *s); +const char *find_whitespace(const char *s); + +/* Time helpers */ +void tor_gettimeofday(struct timeval *timeval); +long tv_udiff(struct timeval *start, struct timeval *end); +void tv_addms(struct timeval *a, long ms); +void tv_add(struct timeval *a, struct timeval *b); +int tv_cmp(struct timeval *a, struct timeval *b); +time_t tor_timegm(struct tm *tm); + +int write_all(int fd, const char *buf, size_t count, int isSocket); +int read_all(int fd, char *buf, size_t count, int isSocket); + +void set_socket_nonblocking(int socket); + +typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR} file_status_t; + +file_status_t file_status(const char *filename); +int check_private_dir(const char *dirname, int create); +int write_str_to_file(const char *fname, const char *str); +char *read_file_to_str(const char *filename); +int parse_line_from_file(char *line, int maxlen, FILE *f, char **key_out, char **value_out); +char *expand_filename(const char *filename); + +int spawn_func(int (*func)(void *), void *data); +void spawn_exit(); + +/* Because we use threads instead of processes on Windows, we need locking on Windows. + * On Unixy platforms, these functions are no-ops. */ +typedef struct tor_mutex_t tor_mutex_t; +tor_mutex_t *tor_mutex_new(void); +void tor_mutex_acquire(tor_mutex_t *m); +void tor_mutex_release(tor_mutex_t *m); +void tor_mutex_free(tor_mutex_t *m); + +int tor_socketpair(int family, int type, int protocol, int fd[2]); + +int is_internal_IP(uint32_t ip); +int is_local_IP(uint32_t ip); + +const char *get_uname(void); + +void start_daemon(const char *desired_cwd); +void finish_daemon(void); + +void write_pidfile(char *filename); +int switch_id(char *user, char *group); + +struct in_addr; +int tor_inet_aton(const char *cp, struct in_addr *addr); +int tor_lookup_hostname(const char *name, uint32_t *addr); + +/* For stupid historical reasons, windows sockets have an independent + * set of errnos, and an independent way to get them. Also, you can't + * always believe WSAEWOULDBLOCK. Use the macros below to compare + * errnos against expected values, and use tor_socket_errno to find + * the actual errno after a socket operation fails. + */ +#ifdef MS_WINDOWS +/** Return true if e is EAGAIN or the local equivalent. */ +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK) +/** Return true if e is EINPROGRESS or the local equivalent. */ +#define ERRNO_IS_EINPROGRESS(e) ((e) == WSAEINPROGRESS) +/** Return true if e is EINPROGRESS or the local equivalent as returned by + * a call to connect(). */ +#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == WSAEINPROGRESS || (e)== WSAEINVAL) +int tor_socket_errno(int sock); +const char *tor_socket_strerror(int e); +#else +#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN) +#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS) +#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS) +#define tor_socket_errno(sock) (errno) +#define tor_socket_strerror(e) strerror(e) +#endif + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/config/.cvsignore b/tags/tor-0_0_8pre1/src/config/.cvsignore new file mode 100644 index 0000000000..369696bbed --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +sample-server-torrc +torrc +torrc.sample diff --git a/tags/tor-0_0_8pre1/src/config/Makefile.am b/tags/tor-0_0_8pre1/src/config/Makefile.am new file mode 100644 index 0000000000..13ed39d7fa --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/Makefile.am @@ -0,0 +1,5 @@ +confdir = $(sysconfdir)/tor + +EXTRA_DIST = dirservers + +conf_DATA = dirservers torrc.sample diff --git a/tags/tor-0_0_8pre1/src/config/dirservers b/tags/tor-0_0_8pre1/src/config/dirservers new file mode 100644 index 0000000000..48ab71d3aa --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/dirservers @@ -0,0 +1,110 @@ +# This is no longer a manually-editable file. It's just a +# concatenation of a bunch of signed router descriptors. + +router moria1 18.244.0.188 9001 9021 9031 +platform Tor 0.0.6rc1 on Linux moria.mit.edu i686 +published 2004-04-25 21:54:28 +bandwidth 800000 10000000 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANoIvHieyHUTzIacbnWOnyTyzGrLOdXqbcjz2GGMxyHEd5K1bO1ZBNHP +9i5qLQpN5viFk2K2rEGuG8tFgDEzSWZEtBqv3NVfUdiumdERWMBwlaQ0MVK4C+jf +y5gZ8KI3o9ZictgPS1AQF+Kk932/vIHTuRIUKb4ILTnQilNvID0NAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMHa0ZC/jo2Q2DrwKYF/6ZbmZ27PFYG91u4gUzzmZ/VXLpZ8wNzEV3oW +nt+I61048fBiC1frT1/DZ351n2bLSk9zJbB6jyGZJn0380FPRX3+cXyXS0Gq8Ril +xkhMQf5XuNFUb8UmYPSOH4WErjvYjKvU+gfjbK/82Jo9SuHpYz+BAgMBAAE= +-----END RSA PUBLIC KEY----- +reject 0.0.0.0/255.0.0.0:* +reject 169.254.0.0/255.255.0.0:* +reject 127.0.0.0/255.0.0.0:* +reject 192.168.0.0/255.255.0.0:* +reject 10.0.0.0/255.0.0.0:* +reject 172.16.0.0/255.240.0.0:* +accept *:20-22 +accept *:53 +accept *:79-80 +accept *:110 +accept *:143 +accept *:443 +accept *:873 +accept *:993 +accept *:995 +accept *:1024-65535 +reject *:* +router-signature +-----BEGIN SIGNATURE----- +o1eAoRHDAEAXsnh5wN++vIwrupd+DbAJ2p3wxHDrmqxTpygzxxCnyQyhMfX03ua2 +4iplyNlwyFwzWcw0sk31otlO2HBYXT1V9G0YxGtKMOeOBMHjfGbUjGvEALHzWi4z +8DXGJp13zgnUyP4ZA6xaGROwcT6oB5e7UlztvvpGxTg= +-----END SIGNATURE----- + +router moria2 18.244.0.188 9002 9022 9032 +platform Tor 0.0.6rc1 on Linux moria.mit.edu i686 +published 2004-04-25 21:54:30 +bandwidth 800000 10000000 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAM4Cc/npgYC54XrYLC+grVxJp7PDmNO2DRRJOxKttBBtvLpnR1UaueTi +kyknT5kmlx+ihgZF/jmye//2dDUp2+kK/kSkpRV4xnDLXZmed+sNSQxqmm9TtZQ9 +/hjpxhp5J9HmUTYhntBs+4E4CUKokmrI6oRLoln4SA39AX9QLPcnAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48 +rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0 +nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE= +-----END RSA PUBLIC KEY----- +reject 0.0.0.0/255.0.0.0:* +reject 169.254.0.0/255.255.0.0:* +reject 127.0.0.0/255.0.0.0:* +reject 192.168.0.0/255.255.0.0:* +reject 10.0.0.0/255.0.0.0:* +reject 172.16.0.0/255.240.0.0:* +accept *:20-22 +accept *:53 +accept *:79-80 +accept *:110 +accept *:143 +accept *:443 +accept *:873 +accept *:993 +accept *:995 +accept *:1024-65535 +reject *:* +router-signature +-----BEGIN SIGNATURE----- +RKROLwP1ExjTZeg6wuN0pzYqed9IJUd5lAe9hp4ritbnmJAgS6qfww6jgx61CfUR +6SElhOLE7Q77jAdoL45Ji5pn/Y+Q+E+5lJm1E/ed9ha+YsOPaOc7z6GQ7E4mihCL +gI1vsw92+P1Ty4RHj6fyD9DhbV19nh2Qs+pvGJOS2FY= +-----END SIGNATURE----- + +router tor26 62.116.124.106 9001 9050 9030 +platform Tor 0.0.6 on Linux seppia i686 +published 2004-05-06 21:33:23 +bandwidth 500000 10000000 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMEHdDnpj3ik1AF1xe/VqjoguH2DbANifYqXXfempu0fS+tU9FGo6dU/ +fnVHAZwL9Ek9k2rMzumShi1RduK9p035R/Gk+PBBcLfvwYJ/Nat+ZO/L8jn/3bZe +ieQd9CKj2LjNGKpRNry37vkwMGIOIlegwK+2us8aXJ7sIvlNts0TAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMQgV2gXLbXgesWgeAsj8P1Uvm/zibrFXqwDq27lLKNgWGYGX2ax3LyT +3nzI1Y5oLs4kPKTsMM5ft9aokwf417lKoCRlZc9ptfRbgxDx90c9GtWVmkrmDvCK +ae59TMoXIiGfZiwWT6KKq5Zm9/Fu2Il3B2vHGkKJYKixmiBJRKp/AgMBAAE= +-----END RSA PUBLIC KEY----- +accept 62.245.184.24:25 +accept 62.116.124.106:6666-6670 +accept *:48099 +reject *:* +router-signature +-----BEGIN SIGNATURE----- +qh/xRoqfLNFzPaB8VdpbdMAwRyuk5qjx4LeLVQ2pDwTZ55PqmG99+VKUNte2WTTD +7dZEA7um2rueohGe4nYmvbhJWr20/I0ZxmWDRDvFy0b5nwzDMGvLvDw95Zu/XJQ2 +md32NE3y9VZCfbCN+GlvETX3fdR3Svzcm8Kzesg2/s4= +-----END SIGNATURE----- + diff --git a/tags/tor-0_0_8pre1/src/config/moria1-orrc b/tags/tor-0_0_8pre1/src/config/moria1-orrc new file mode 100644 index 0000000000..a2d4c43d15 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/moria1-orrc @@ -0,0 +1,12 @@ + +DataDirectory moria1 +Nickname moria1 + +ORPort 9001 +DirPort 9031 + +# List of routers +RouterFile ../config/dirservers + +ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:* + diff --git a/tags/tor-0_0_8pre1/src/config/moria2-orrc b/tags/tor-0_0_8pre1/src/config/moria2-orrc new file mode 100644 index 0000000000..b29db3e05c --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/moria2-orrc @@ -0,0 +1,12 @@ + +DataDirectory moria2 +Nickname moria2 + +ORPort 9002 +DirPort 9032 + +# List of routers +RouterFile ../config/dirservers + +ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:* + diff --git a/tags/tor-0_0_8pre1/src/config/moria3-orrc b/tags/tor-0_0_8pre1/src/config/moria3-orrc new file mode 100644 index 0000000000..4c86445eff --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/moria3-orrc @@ -0,0 +1,12 @@ + +DataDirectory moria3 +Nickname moria3 + +ORPort 9003 +DirPort 9033 + +# List of routers +RouterFile ../config/dirservers + +ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:* + diff --git a/tags/tor-0_0_8pre1/src/config/torrc.sample.in b/tags/tor-0_0_8pre1/src/config/torrc.sample.in new file mode 100644 index 0000000000..9b1b80af5d --- /dev/null +++ b/tags/tor-0_0_8pre1/src/config/torrc.sample.in @@ -0,0 +1,78 @@ +# Configuration file for a typical tor user + +# List of routers. Tor nodes start out knowing about the directory +# servers, and from them they get a list of currently up nodes. +RouterFile @CONFDIR@/dirservers + +# Comment out if you don't want to allow applications to connect. +SocksPort 9050 +SocksBindAddress 127.0.0.1 # accept connections only from localhost +#SocksBindAddress 192.168.0.1:9100 # listen on a chosen IP/port + +# Entry policies to allow/deny SOCKS requests based on IP. By default, +# we accept all requests to SocksBindAddress. +# +#SocksPolicy accept 192.168.0.1/16:* +#SocksPolicy reject *:* + +# By default, we send log messages to stdout. If you want +# them to go somewhere else, uncomment one or more of these example +# configurations. +### Send all messages of level 'warn' or higher to /var/log/tor/warnings +#LogFile /var/log/tor/warnings +#LogLevel warn +### Send all debug and info messages to /var/log/tor/debug +#LogFile /var/log/tor/debug +#LogLevel debug-info +### Send all debug messages ONLY to /var/log/tor/debug +#LogFile /var/log/tor/debug +#LogLevel debug-debug + +# Uncomment this to start the process in the background +#RunAsDaemon 1 + +##################### Below is just for servers ##################### + +## NOTE: If you enable these, you won't be able to use tor as a client +## or server until you've been added to the directory and can +## authenticate to the other nodes. See the README for details. + +## The directory for keeping all the keys/etc for this server +#DataDirectory @LOCALSTATEDIR@/lib/tor + +#Nickname moria # A unique handle for this server +#Address moria.seul.org # The IP or fqdn for this server +#ContactInfo 1024D/28988BF5 Roger Dingledine + +#ORPort 9001 # where to listen for tor connections +#ORBindAddress 0.0.0.0 # accept connections from anywhere +#ORBindAddress 0.0.0.0:9090 # accept connections from anywhere, and + # listen on a port other than the one we + # advertised. + +# Uncomment this to mirror the directory for others (please do) +#DirPort 9030 + +## A comma-separated list of exit policies. If you want to *replace* +## the default exit policy, end this with either a reject *:* or an +## accept *:*. Otherwise, you're *augmenting* (prepending to) the +## default exit policy. Leave commented to just use the default. +#ExitPolicy accept *:6660-6667 +#ExitPolicy reject 192.168.0.1:* +#ExitPolicy reject *:* + +##################### Below is just for location-hidden services ### + +## Look in /tmp/hidden_service/hostname for the url to tell people. +## HiddenServicePort x y:z says to redirect a port x request from the +## client to y:z. + +#HiddenServiceDir /tmp/hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 + +#HiddenServiceDir /tmp/other_hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 +#HiddenServicePort 22 127.0.0.1:22 +#HiddenServiceNodes moria1,moria2 +#HiddenServiceExcludeNodes bad,otherbad + diff --git a/tags/tor-0_0_8pre1/src/or/.cvsignore b/tags/tor-0_0_8pre1/src/or/.cvsignore new file mode 100644 index 0000000000..14f9ecd6db --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +tor +test +.deps diff --git a/tags/tor-0_0_8pre1/src/or/Makefile.am b/tags/tor-0_0_8pre1/src/or/Makefile.am new file mode 100644 index 0000000000..a071bceba6 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/Makefile.am @@ -0,0 +1,28 @@ +TESTS = test + +noinst_PROGRAMS = test + +bin_PROGRAMS = tor + +tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \ + circuituse.c command.c config.c \ + connection.c connection_edge.c connection_or.c \ + cpuworker.c directory.c dirserv.c dns.c main.c \ + onion.c relay.c rendcommon.c rendclient.c rendmid.c \ + rendservice.c rephist.c router.c routerlist.c routerparse.c \ + tor_main.c + +tor_LDADD = ../common/libor.a + +test_SOURCES = buffers.c circuitbuild.c circuitlist.c \ + circuituse.c command.c config.c \ + connection.c connection_edge.c connection_or.c \ + cpuworker.c directory.c dirserv.c dns.c main.c \ + onion.c relay.c rendcommon.c rendclient.c rendmid.c \ + rendservice.c rephist.c router.c routerlist.c routerparse.c \ + test.c + +test_LDADD = ../common/libor.a + +noinst_HEADERS = or.h tree.h + diff --git a/tags/tor-0_0_8pre1/src/or/buffers.c b/tags/tor-0_0_8pre1/src/or/buffers.c new file mode 100644 index 0000000000..661b1e1587 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/buffers.c @@ -0,0 +1,628 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file buffers.c + * \brief Abstractions for buffered IO. + **/ + +#include "or.h" + +#define BUFFER_MAGIC 0xB0FFF312u +struct buf_t { + uint32_t magic; /**< Magic cookie for debugging: Must be set to BUFFER_MAGIC */ + char *mem; /**< Storage for data in the buffer */ + size_t len; /**< Maximum amount of data that mem can hold. */ + size_t datalen; /**< Number of bytes currently in mem. */ +}; + +/** Size, in bytes, for newly allocated buffers. Should be a power of 2. */ +#define INITIAL_BUF_SIZE (4*1024) +/** Maximum size, in bytes, for resized buffers. */ +#define MAX_BUF_SIZE (1024*1024*10) +/** Size, in bytes, for minimum 'shrink' size for buffers. Buffers may start + * out smaller than this, but they will never autoshrink to less + * than this size. */ +#define MIN_BUF_SHRINK_SIZE (16*1024) + +/** Change a buffer's capacity. new_capacity must be \<= buf->datalen. */ +static INLINE void buf_resize(buf_t *buf, size_t new_capacity) +{ + tor_assert(buf->datalen <= new_capacity); + tor_assert(new_capacity); + buf->mem = tor_realloc(buf->mem, new_capacity); + buf->len = new_capacity; +} + +/** If the buffer is not large enough to hold capacity bytes, resize + * it so that it can. (The new size will be a power of 2 times the old + * size.) + */ +static INLINE int buf_ensure_capacity(buf_t *buf, size_t capacity) +{ + size_t new_len; + if (buf->len >= capacity) /* Don't grow if we're already big enough. */ + return 0; + if (capacity > MAX_BUF_SIZE) /* Don't grow past the maximum. */ + return -1; + /* Find the smallest new_len equal to (2**X)*len for some X; such that + * new_len is at least capacity. + */ + new_len = buf->len*2; + while (new_len < capacity) + new_len *= 2; + /* Resize the buffer. */ + log_fn(LOG_DEBUG,"Growing buffer from %d to %d bytes.", + (int)buf->len, (int)new_len); + buf_resize(buf,new_len); + return 0; +} + +/** If the buffer is at least 2*MIN_BUF_SHRINK_SIZE bytes in capacity, + * and if the buffer is less than 1/4 full, shrink the buffer until + * one of the above no longer holds. (We shrink the buffer by + * dividing by powers of 2.) + */ +static INLINE void buf_shrink_if_underfull(buf_t *buf) { + size_t new_len; + /* If the buffer is at least .25 full, or if shrinking the buffer would + * put it onder MIN_BUF_SHRINK_SIZE, don't do it. */ + if (buf->datalen >= buf->len/4 || buf->len < 2*MIN_BUF_SHRINK_SIZE) + return; + /* Shrink new_len by powers of 2 until: datalen is at least 1/4 of + * new_len, OR shrinking new_len more would put it under + * MIN_BUF_SHRINK_SIZE. + */ + new_len = buf->len / 2; + while (buf->datalen < new_len/4 && new_len/2 > MIN_BUF_SHRINK_SIZE) + new_len /= 2; + log_fn(LOG_DEBUG,"Shrinking buffer from %d to %d bytes.", + (int)buf->len, (int)new_len); + buf_resize(buf, new_len); +} + +/** 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); + buf->datalen -= n; + memmove(buf->mem, buf->mem+n, buf->datalen); + buf_shrink_if_underfull(buf); +} + +/** Make sure that the memory in buf ends with a zero byte. */ +static INLINE int buf_nul_terminate(buf_t *buf) +{ + if (buf_ensure_capacity(buf,buf->datalen+1)<0) + return -1; + buf->mem[buf->datalen] = '\0'; + return 0; +} + +/** Create and return a new buf with capacity size. + */ +buf_t *buf_new_with_capacity(size_t size) { + buf_t *buf; + buf = tor_malloc(sizeof(buf_t)); + buf->magic = BUFFER_MAGIC; + buf->mem = tor_malloc(size); + buf->len = size; + buf->datalen = 0; +// memset(buf->mem,0,size); + + assert_buf_ok(buf); + return buf; +} + +/** Allocate and return a new buffer with default capacity. */ +buf_t *buf_new() +{ + return buf_new_with_capacity(INITIAL_BUF_SIZE); +} + +/** Remove all data from buf */ +void buf_clear(buf_t *buf) +{ + buf->datalen = 0; +} + +/** Return the number of bytes stored in buf */ +size_t buf_datalen(const buf_t *buf) +{ + return buf->datalen; +} + +/** Return the maximum bytes that can be stored in buf before buf + * needs to resize. */ +size_t buf_capacity(const buf_t *buf) +{ + return buf->len; +} + +/** For testing only: Return a pointer to the raw memory stored in buf. + */ +const char *_buf_peek_raw_buffer(const buf_t *buf) +{ + return buf->mem; +} + +/** Release storage held by buf. + */ +void buf_free(buf_t *buf) { + assert_buf_ok(buf); + buf->magic = 0xDEADBEEF; + tor_free(buf->mem); + tor_free(buf); +} + +/** Read from socket s, writing onto end of buf. Read at most + * at_most bytes, resizing the buffer as necessary. If read() + * returns 0, set *reached_eof to 1 and return 0. Return -1 on error; + * else return the number of bytes read. Return 0 if read() would + * block. + */ +int read_to_buf(int s, size_t at_most, buf_t *buf, int *reached_eof) { + + int read_result; + + assert_buf_ok(buf); + tor_assert(reached_eof && (s>=0)); + + if (buf_ensure_capacity(buf,buf->datalen+at_most)) + return -1; + + if(at_most + buf->datalen > buf->len) + at_most = buf->len - buf->datalen; /* take the min of the two */ + + if(at_most == 0) + return 0; /* we shouldn't read anything */ + +// log_fn(LOG_DEBUG,"reading at most %d bytes.",at_most); + read_result = recv(s, buf->mem+buf->datalen, at_most, 0); + if (read_result < 0) { + if(!ERRNO_IS_EAGAIN(tor_socket_errno(s))) { /* it's a real error */ + return -1; + } + return 0; /* would block. */ + } else if (read_result == 0) { + log_fn(LOG_DEBUG,"Encountered eof"); + *reached_eof = 1; + return 0; + } else { /* we read some bytes */ + buf->datalen += read_result; + log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf.",read_result, + (int)buf->datalen); + return read_result; + } +} + +/** As read_to_buf, but reads from a TLS connection. + */ +int read_to_buf_tls(tor_tls *tls, size_t at_most, buf_t *buf) { + int r; + tor_assert(tls); + assert_buf_ok(buf); + + log_fn(LOG_DEBUG,"start: %d on buf, %d pending, at_most %d.", + (int)buf_datalen(buf), (int)tor_tls_get_pending_bytes(tls), + (int)at_most); + + if (buf_ensure_capacity(buf, at_most+buf->datalen)) + return TOR_TLS_ERROR; + + if (at_most + buf->datalen > buf->len) + at_most = buf->len - buf->datalen; + + if (at_most == 0) + return 0; + + log_fn(LOG_DEBUG,"before: %d on buf, %d pending, at_most %d.", + (int)buf_datalen(buf), (int)tor_tls_get_pending_bytes(tls), + (int)at_most); + + assert_no_tls_errors(); + r = tor_tls_read(tls, buf->mem+buf->datalen, at_most); + if (r<0) + return r; + buf->datalen += r; + log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf; %d pending",r, + (int)buf->datalen,(int)tor_tls_get_pending_bytes(tls)); + return r; +} + +/** Write data from buf to the socket s. Write at most + * *buf_flushlen 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(int s, buf_t *buf, int *buf_flushlen) +{ + int write_result; + + assert_buf_ok(buf); + tor_assert(buf_flushlen && (s>=0) && ((unsigned)*buf_flushlen <= buf->datalen)); + + if(*buf_flushlen == 0) /* nothing to flush */ + return 0; + + write_result = send(s, buf->mem, *buf_flushlen, 0); + if (write_result < 0) { + if(!ERRNO_IS_EAGAIN(tor_socket_errno(s))) { /* it's a real error */ + return -1; + } + log_fn(LOG_DEBUG,"write() would block, returning."); + return 0; + } else { + *buf_flushlen -= write_result; + buf_remove_from_front(buf, write_result); + log_fn(LOG_DEBUG,"%d: flushed %d bytes, %d ready to flush, %d remain.", + s,write_result,*buf_flushlen,(int)buf->datalen); + + return write_result; + } +} + +/** As flush_buf, but writes data to a TLS connection. + */ +int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen) +{ + int r; + assert_buf_ok(buf); + tor_assert(tls && buf_flushlen); + + /* we want to let tls write even if flushlen is zero, because it might + * have a partial record pending */ + r = tor_tls_write(tls, buf->mem, *buf_flushlen); + if (r < 0) { + return r; + } + *buf_flushlen -= r; + buf_remove_from_front(buf, r); + log_fn(LOG_DEBUG,"flushed %d bytes, %d ready to flush, %d remain.", + r,*buf_flushlen,(int)buf->datalen); + return r; +} + +/** 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, int string_len, buf_t *buf) { + + /* append string to buf (growing as needed, return -1 if "too big") + * return total number of bytes on the buf + */ + + tor_assert(string); + assert_buf_ok(buf); + + if (buf_ensure_capacity(buf, buf->datalen+string_len)) { + log_fn(LOG_WARN, "buflen too small, can't hold %d bytes.", (int)buf->datalen+string_len); + return -1; + } + + memcpy(buf->mem+buf->datalen, string, string_len); + buf->datalen += string_len; + log_fn(LOG_DEBUG,"added %d bytes to buf (now %d total).",string_len, (int)buf->datalen); + return buf->datalen; +} + +/** 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. */ + + tor_assert(string); + tor_assert(string_len <= buf->datalen); /* make sure we don't ask for too much */ + assert_buf_ok(buf); + + memcpy(string,buf->mem,string_len); + buf_remove_from_front(buf, string_len); + return buf->datalen; +} + +/** 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. + * + * Else, change nothing and return 0. + */ +int fetch_from_buf_http(buf_t *buf, + char **headers_out, int max_headerlen, + char **body_out, int *body_used, int max_bodylen) { + char *headers, *body, *p; + int headerlen, bodylen, contentlen; + + assert_buf_ok(buf); + + headers = buf->mem; + if (buf_nul_terminate(buf)<0) { + log_fn(LOG_WARN,"Couldn't nul-terminate buffer"); + return -1; + } + body = strstr(headers,"\r\n\r\n"); + if (!body) { + log_fn(LOG_DEBUG,"headers not all here yet."); + return 0; + } + body += 4; /* Skip the the CRLFCRLF */ + headerlen = body-headers; /* includes the CRLFCRLF */ + bodylen = buf->datalen - headerlen; + log_fn(LOG_DEBUG,"headerlen %d, bodylen %d.", headerlen, bodylen); + + if(headers_out && max_headerlen <= headerlen) { + log_fn(LOG_WARN,"headerlen %d larger than %d. Failing.", headerlen, max_headerlen-1); + return -1; + } + if(body_out && max_bodylen <= bodylen) { + log_fn(LOG_WARN,"bodylen %d larger than %d. Failing.", bodylen, max_bodylen-1); + return -1; + } + +#define CONTENT_LENGTH "\r\nContent-Length: " + p = strstr(headers, CONTENT_LENGTH); + if (p) { + contentlen = atoi(p+strlen(CONTENT_LENGTH)); + /* if content-length is malformed, then our body length is 0. fine. */ + log_fn(LOG_DEBUG,"Got a contentlen of %d.",contentlen); + if(bodylen < contentlen) { + log_fn(LOG_DEBUG,"body not all here yet."); + return 0; /* not all there yet */ + } + if(bodylen > contentlen) { + bodylen = contentlen; + log_fn(LOG_DEBUG,"bodylen reduced to %d.",bodylen); + } + } + /* all happy. copy into the appropriate places, and return 1 */ + if(headers_out) { + *headers_out = tor_malloc(headerlen+1); + memcpy(*headers_out,buf->mem,headerlen); + (*headers_out)[headerlen] = 0; /* null terminate it */ + } + if(body_out) { + tor_assert(body_used); + *body_used = bodylen; + *body_out = tor_malloc(bodylen+1); + memcpy(*body_out,buf->mem+headerlen,bodylen); + (*body_out)[bodylen] = 0; /* null terminate it */ + } + buf_remove_from_front(buf, headerlen+bodylen); + return 1; +} + +/** 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 returning 0 or -1, req->address and req->port are undefined. + */ +int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) { + unsigned char len; + char *tmpbuf=NULL; + uint32_t destip; + enum {socks4, socks4a} socks4_prot = socks4a; + char *next, *startaddr; + struct in_addr in; + + if(buf->datalen < 2) /* version and another byte */ + return 0; + switch(*(buf->mem)) { /* which version of socks? */ + + case 5: /* socks5 */ + + if(req->socks_version != 5) { /* we need to negotiate a method */ + unsigned char nummethods = (unsigned char)*(buf->mem+1); + tor_assert(!req->socks_version); + if(buf->datalen < 2u+nummethods) + return 0; + if(!nummethods || !memchr(buf->mem+2, 0, nummethods)) { + log_fn(LOG_WARN,"socks5: offered methods don't include 'no auth'. Rejecting."); + req->replylen = 2; /* 2 bytes of response */ + req->reply[0] = 5; /* socks5 reply */ + req->reply[1] = '\xFF'; /* reject all methods */ + return -1; + } + buf_remove_from_front(buf,2+nummethods);/* remove packet from buf */ + + req->replylen = 2; /* 2 bytes of response */ + req->reply[0] = 5; /* socks5 reply */ + req->reply[1] = 0; /* choose the 'no auth' method */ + req->socks_version = 5; /* remember that we've already negotiated auth */ + log_fn(LOG_DEBUG,"socks5: accepted method 0"); + return 0; + } + /* we know the method; read in the request */ + log_fn(LOG_DEBUG,"socks5: checking request"); + if(buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */ + return 0; /* not yet */ + req->command = (unsigned char) *(buf->mem+1); + if(req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE) { + /* not a connect or resolve? we don't support it. */ + log_fn(LOG_WARN,"socks5: command %d not recognized. Rejecting.", + req->command); + return -1; + } + switch(*(buf->mem+3)) { /* address type */ + case 1: /* IPv4 address */ + log_fn(LOG_DEBUG,"socks5: ipv4 address type"); + if(buf->datalen < 10) /* ip/port there? */ + return 0; /* not yet */ + destip = ntohl(*(uint32_t*)(buf->mem+4)); + in.s_addr = htonl(destip); + tmpbuf = inet_ntoa(in); + if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) { + log_fn(LOG_WARN,"socks5 IP takes %d bytes, which doesn't fit in %d. Rejecting.", + (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN); + return -1; + } + strcpy(req->address,tmpbuf); + req->port = ntohs(*(uint16_t*)(buf->mem+8)); + buf_remove_from_front(buf, 10); + return 1; + case 3: /* fqdn */ + log_fn(LOG_DEBUG,"socks5: fqdn address type"); + len = (unsigned char)*(buf->mem+4); + if(buf->datalen < 7u+len) /* addr/port there? */ + return 0; /* not yet */ + if(len+1 > MAX_SOCKS_ADDR_LEN) { + log_fn(LOG_WARN,"socks5 hostname is %d bytes, which doesn't fit in %d. Rejecting.", + len+1,MAX_SOCKS_ADDR_LEN); + return -1; + } + memcpy(req->address,buf->mem+5,len); + req->address[len] = 0; + req->port = ntohs(get_uint16(buf->mem+5+len)); + buf_remove_from_front(buf, 5+len+2); + return 1; + default: /* unsupported */ + log_fn(LOG_WARN,"socks5: unsupported address type %d. Rejecting.",*(buf->mem+3)); + return -1; + } + tor_assert(0); + case 4: /* socks4 */ + /* http://archive.socks.permeo.com/protocol/socks4.protocol */ + /* http://archive.socks.permeo.com/protocol/socks4a.protocol */ + + req->socks_version = 4; + if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */ + return 0; /* not yet */ + + req->command = (unsigned char) *(buf->mem+1); + if(req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE) { + /* not a connect or resolve? we don't support it. */ + log_fn(LOG_WARN,"socks4: command %d not recognized. Rejecting.", + req->command); + return -1; + } + + req->port = ntohs(*(uint16_t*)(buf->mem+2)); + destip = ntohl(*(uint32_t*)(buf->mem+4)); + if((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) { + log_fn(LOG_WARN,"socks4: Port or DestIP is zero. Rejecting."); + return -1; + } + if(destip >> 8) { + log_fn(LOG_DEBUG,"socks4: destip not in form 0.0.0.x."); + in.s_addr = htonl(destip); + tmpbuf = inet_ntoa(in); + if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) { + log_fn(LOG_WARN,"socks4 addr (%d bytes) too long. Rejecting.", + (int)strlen(tmpbuf)); + return -1; + } + log_fn(LOG_DEBUG,"socks4: successfully read destip (%s)", tmpbuf); + socks4_prot = socks4; + } + + next = memchr(buf->mem+SOCKS4_NETWORK_LEN, 0, + buf->datalen-SOCKS4_NETWORK_LEN); + if(!next) { + log_fn(LOG_DEBUG,"socks4: Username not here yet."); + return 0; + } + + startaddr = next+1; + if(socks4_prot == socks4a) { + next = memchr(startaddr, 0, buf->mem+buf->datalen-startaddr); + if(!next) { + log_fn(LOG_DEBUG,"socks4: Destaddr not here yet."); + return 0; + } + if(MAX_SOCKS_ADDR_LEN <= next-startaddr) { + log_fn(LOG_WARN,"socks4: Destaddr too long. Rejecting."); + return -1; + } + } + log_fn(LOG_DEBUG,"socks4: Everything is here. Success."); + strcpy(req->address, socks4_prot == socks4 ? tmpbuf : startaddr); + /* XXX on very old netscapes (socks4) the next line triggers an + * assert, because next-buf->mem+1 is greater than buf->datalen. + */ + buf_remove_from_front(buf, next-buf->mem+1); /* next points to the final \0 on inbuf */ + return 1; + + case 'G': /* get */ + case 'H': /* head */ + case 'P': /* put/post */ + case 'C': /* connect */ + strcpy(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 provides a SOCKS proxy. Please configure your\n" +"client accordingly.\n" +"

    \n" +"

    \n" +"See http://freehaven.net/tor/cvs/INSTALL for more information.\n" +"\n" +"

    \n" +"\n" +"\n" +); + req->replylen = strlen(req->reply)+1; + /* fall through */ + default: /* version is not socks4 or socks5 */ + log_fn(LOG_WARN,"Socks version %d not recognized. (Tor is not an http proxy.)", + *(buf->mem)); + return -1; + } +} + +/** Log an error and exit if buf is corrupted. + */ +void assert_buf_ok(buf_t *buf) +{ + tor_assert(buf); + tor_assert(buf->magic == BUFFER_MAGIC); + tor_assert(buf->mem); + tor_assert(buf->datalen <= buf->len); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/circuitbuild.c b/tags/tor-0_0_8pre1/src/or/circuitbuild.c new file mode 100644 index 0000000000..40c57e01b0 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/circuitbuild.c @@ -0,0 +1,1203 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuitbuild.c + * \brief The actual details of building circuits. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +/** A global list of all circuits at this hop. */ +extern circuit_t *global_circuitlist; + +/********* END VARIABLES ************/ + +static int +circuit_deliver_create_cell(circuit_t *circ, char *payload); +static cpath_build_state_t * +onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest); +static int +onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t + *state, routerinfo_t **router_out); +static int decide_circ_id_type(char *local_nick, char *remote_nick); +static int count_acceptable_routers(smartlist_t *routers); + +/** Iterate over values of circ_id, starting from conn-\>next_circ_id, + * and with the high bit specified by circ_id_type (see + * decide_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 uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type) { + uint16_t test_circ_id; + int attempts=0; + uint16_t high_bit; + + tor_assert(conn && conn->type == CONN_TYPE_OR); + high_bit = (circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; + do { + /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a + * circID such that (high_bit|test_circ_id) is not already used. */ + test_circ_id = conn->next_circ_id++; + if (test_circ_id == 0 || test_circ_id >= 1<<15) { + test_circ_id = 1; + conn->next_circ_id = 2; + } + if(++attempts > 1<<15) { + /* Make sure we don't loop forever if all circ_id's are used. This + * matters because it's an external DoS vulnerability. + */ + log_fn(LOG_WARN,"No unused circ IDs. Failing."); + return 0; + } + test_circ_id |= high_bit; + } while(circuit_get_by_circ_id_conn(test_circ_id, conn)); + return test_circ_id; +} + +/** 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, circuit_t *circ) { + char buf[1024]; + char *s = buf; + struct crypt_path_t *hop; + char *states[] = {"closed", "waiting for keys", "open"}; + routerinfo_t *router; + tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath); + + snprintf(s, sizeof(buf)-1, "circ (length %d, exit %s): ", + circ->build_state->desired_path_len, circ->build_state->chosen_exit_name); + hop=circ->cpath; + do { + s = buf + strlen(buf); + router = router_get_by_digest(hop->identity_digest); + if(router) { + snprintf(s, sizeof(buf) - (s - buf), "%s(%s) ", + router->nickname, states[hop->state]); + } else { + if(circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { + snprintf(s, sizeof(buf) - (s - buf), "(rendjoin hop)"); + } else { + snprintf(s, sizeof(buf) - (s - buf), "UNKNOWN "); + } + } + hop=hop->next; + } while(hop!=circ->cpath); + log_fn(severity,"%s",buf); +} + +/** 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. + */ +void circuit_rep_hist_note_result(circuit_t *circ) { + struct crypt_path_t *hop; + char *prev_digest = NULL; + routerinfo_t *router; + hop = circ->cpath; + if(!hop) { + /* XXX + * if !hop, then we're not the beginning of this circuit. + * for now, just forget about it. later, we should remember when + * extends-through-us failed, too. + */ + return; + } + if (server_mode()) { + routerinfo_t *me = router_get_my_routerinfo(); + tor_assert(me); + prev_digest = me->identity_digest; + } + do { + router = router_get_by_digest(hop->identity_digest); + if (router) { + if (prev_digest) { + if (hop->state == CPATH_STATE_OPEN) + rep_hist_note_extend_succeeded(prev_digest, router->identity_digest); + else { + rep_hist_note_extend_failed(prev_digest, router->identity_digest); + break; + } + } + prev_digest = router->identity_digest; + } else { + prev_digest = NULL; + } + hop=hop->next; + } while (hop!=circ->cpath); +} + +/** A helper function for circuit_dump_by_conn() below. Log a bunch + * of information about circuit circ. + */ +static void +circuit_dump_details(int severity, circuit_t *circ, int poll_index, + char *type, int this_circid, int other_circid) { + struct crypt_path_t *hop; + log(severity,"Conn %d has %s circuit: circID %d (other side %d), state %d (%s), born %d", + poll_index, type, this_circid, other_circid, circ->state, + circuit_state_to_string[circ->state], (int)circ->timestamp_created); + if(CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ + if(circ->state == CIRCUIT_STATE_BUILDING) + log(severity,"Building: desired len %d, planned exit node %s.", + circ->build_state->desired_path_len, circ->build_state->chosen_exit_name); + for(hop=circ->cpath;hop->next != circ->cpath; hop=hop->next) + log(severity,"hop: state %d, addr 0x%.8x, port %d", hop->state, + (unsigned int)hop->addr, + (int)hop->port); + } +} + +/** 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; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn) + circuit_dump_details(severity, circ, conn->poll_index, "App-ward", + circ->p_circ_id, circ->n_circ_id); + for(tmpconn=circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + circuit_dump_details(severity, circ, conn->poll_index, "App-ward", + circ->p_circ_id, circ->n_circ_id); + } + } + if(circ->n_conn == conn) + circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", + circ->n_circ_id, circ->p_circ_id); + for(tmpconn=circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", + circ->n_circ_id, circ->p_circ_id); + } + } + } +} + +/** Build a new circuit for purpose. If exit_digest + * 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. + */ +circuit_t *circuit_establish_circuit(uint8_t purpose, + const char *exit_digest) { + routerinfo_t *firsthop; + connection_t *n_conn; + circuit_t *circ; + + circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */ + circ->state = CIRCUIT_STATE_OR_WAIT; + circ->build_state = onion_new_cpath_build_state(purpose, exit_digest); + circ->purpose = purpose; + + if (! circ->build_state) { + log_fn(LOG_INFO,"Generating cpath failed."); + circuit_mark_for_close(circ); + return NULL; + } + + onion_extend_cpath(&circ->cpath, circ->build_state, &firsthop); + if(!CIRCUIT_IS_ORIGIN(circ)) { + log_fn(LOG_INFO,"Generating first cpath hop failed."); + circuit_mark_for_close(circ); + return NULL; + } + + /* now see if we're already connected to the first OR in 'route' */ + + log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'", + firsthop->address,firsthop->or_port); + /* imprint the circuit with its future n_conn->id */ + memcpy(circ->n_conn_id_digest, firsthop->identity_digest, DIGEST_LEN); + n_conn = connection_get_by_identity_digest(firsthop->identity_digest, + CONN_TYPE_OR); + if(!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */ + circ->n_addr = firsthop->addr; + circ->n_port = firsthop->or_port; + + if(!n_conn) { /* launch the connection */ + n_conn = connection_or_connect(firsthop->addr, firsthop->or_port, + firsthop->identity_digest); + if(!n_conn) { /* connect failed, forget the whole thing */ + log_fn(LOG_INFO,"connect to firsthop failed. Closing."); + circuit_mark_for_close(circ); + return NULL; + } + } + + log_fn(LOG_DEBUG,"connecting in progress (or finished). Good."); + /* return success. The onion/circuit/etc will be taken care of automatically + * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. + */ + return circ; + } else { /* it (or a twin) is already open. use it. */ + circ->n_addr = n_conn->addr; + circ->n_port = n_conn->port; + circ->n_conn = n_conn; + log_fn(LOG_DEBUG,"Conn open. Delivering first onion skin."); + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"circuit_send_next_onion_skin failed."); + circuit_mark_for_close(circ); + return NULL; + } + } + return circ; +} + +/** Find circuits that are waiting on or_conn to become open, + * if any, and get them to send their create cells forward. + */ +void circuit_n_conn_done(connection_t *or_conn, int success) { + circuit_t *circ; + + log_fn(LOG_DEBUG,"or_conn to %s, success=%d", or_conn->nickname, success); + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + if(!circ->n_conn && + circ->n_addr == or_conn->addr && + circ->n_port == or_conn->port && + !memcmp(or_conn->identity_digest, circ->n_conn_id_digest, DIGEST_LEN)) { + tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT); + if(!success) { /* or_conn failed; close circ */ + log_fn(LOG_INFO,"or_conn failed. Closing circ."); + circuit_mark_for_close(circ); + continue; + } + log_fn(LOG_DEBUG,"Found circ %d, sending create cell.", circ->n_circ_id); + circ->n_conn = or_conn; + memcpy(circ->n_conn_id_digest, or_conn->identity_digest, DIGEST_LEN); + if(CIRCUIT_IS_ORIGIN(circ)) { + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing."); + circuit_mark_for_close(circ); + continue; + /* XXX could this be bad, eg if next_onion_skin failed because conn died? */ + } + } else { + /* pull the create cell out of circ->onionskin, and send it */ + if(circuit_deliver_create_cell(circ, circ->onionskin) < 0) { + circuit_mark_for_close(circ); + continue; + } + } + } + } +} + +static int +circuit_deliver_create_cell(circuit_t *circ, char *payload) { + int circ_id_type; + cell_t cell; + + tor_assert(circ && circ->n_conn && circ->n_conn->type == CONN_TYPE_OR); + tor_assert(payload); + + /* XXXX008 How can we keep a good upgrade path here? We should + * compare keys, not nicknames...but older servers will compare nicknames. + * Should we check server version from the most recent directory? Hm. + */ + circ_id_type = decide_circ_id_type(options.Nickname, + circ->n_conn->nickname); + circ->n_circ_id = get_unique_circ_id_by_conn(circ->n_conn, circ_id_type); + if(!circ->n_circ_id) { + log_fn(LOG_WARN,"failed to get unique circID."); + return -1; + } + log_fn(LOG_DEBUG,"Chosen circID %u.",circ->n_circ_id); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATE; + cell.circ_id = circ->n_circ_id; + + memcpy(cell.payload, payload, ONIONSKIN_CHALLENGE_LEN); + connection_or_write_cell_to_buf(&cell, circ->n_conn); + return 0; +} + +extern int has_completed_circuit; + +/** 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 -1 if we want to tear down circ, else return 0. + */ +int circuit_send_next_onion_skin(circuit_t *circ) { + crypt_path_t *hop; + routerinfo_t *router; + int r; + char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN]; + char *onionskin; + int payload_len; + + tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); + + if(circ->cpath->state == CPATH_STATE_CLOSED) { + log_fn(LOG_DEBUG,"First skin; sending create cell."); + + router = router_get_by_digest(circ->n_conn->identity_digest); + if (!router) { + log_fn(LOG_WARN,"Couldn't find routerinfo for %s", + circ->n_conn->nickname); + return -1; + } + + if(onion_skin_create(router->onion_pkey, + &(circ->cpath->handshake_state), + payload) < 0) { + log_fn(LOG_WARN,"onion_skin_create (first hop) failed."); + return -1; + } + + if(circuit_deliver_create_cell(circ, payload) < 0) + return -1; + + circ->cpath->state = CPATH_STATE_AWAITING_KEYS; + circ->state = CIRCUIT_STATE_BUILDING; + log_fn(LOG_DEBUG,"first skin; finished sending create cell."); + } else { + tor_assert(circ->cpath->state == CPATH_STATE_OPEN); + tor_assert(circ->state == CIRCUIT_STATE_BUILDING); + log_fn(LOG_DEBUG,"starting to send subsequent skin."); + r = onion_extend_cpath(&circ->cpath, circ->build_state, &router); + if (r==1) { + /* done building the circuit. whew. */ + circ->state = CIRCUIT_STATE_OPEN; + log_fn(LOG_INFO,"circuit built!"); + circuit_reset_failure_count(); + if(!has_completed_circuit) { + has_completed_circuit=1; + log_fn(LOG_NOTICE,"Tor has successfully opened a circuit. Looks like it's working."); + } + circuit_rep_hist_note_result(circ); + circuit_has_opened(circ); /* do other actions as necessary */ + return 0; + } else if (r<0) { + log_fn(LOG_INFO,"Unable to extend circuit path."); + return -1; + } + hop = circ->cpath->prev; + + *(uint32_t*)payload = htonl(hop->addr); + *(uint16_t*)(payload+4) = htons(hop->port); + + onionskin = payload+2+4; + memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN, hop->identity_digest, DIGEST_LEN); + payload_len = 2+4+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN; + + if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), onionskin) < 0) { + log_fn(LOG_WARN,"onion_skin_create failed."); + return -1; + } + + log_fn(LOG_DEBUG,"Sending extend relay cell."); + /* send it to hop->prev, because it will transfer + * it to a create cell and then send to hop */ + if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND, + payload, payload_len, hop->prev) < 0) + return 0; /* circuit is closed */ + + hop->state = CPATH_STATE_AWAITING_KEYS; + } + return 0; +} + +/** Take the 'extend' cell, pull out addr/port plus the onion skin. Make + * sure we're connected to the next hop, and pass it the onion skin in + * a create cell. + */ +int circuit_extend(cell_t *cell, circuit_t *circ) { + connection_t *n_conn; + relay_header_t rh; + int old_format; + char *onionskin; + char *id_digest=NULL; + + if(circ->n_conn) { + log_fn(LOG_WARN,"n_conn already set. Bug/attack. Closing."); + return -1; + } + + relay_header_unpack(&rh, cell->payload); + + if (rh.length == 4+2+ONIONSKIN_CHALLENGE_LEN) { + /* Once this format is no longer supported, nobody will use + * connection_*_get_by_addr_port. */ + old_format = 1; + } else if (rh.length == 4+2+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN) { + old_format = 0; + } else { + log_fn(LOG_WARN, "Wrong length on extend cell. Closing circuit."); + return -1; + } + + circ->n_addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); + circ->n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4)); + + if (old_format) { + n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + onionskin = cell->payload+RELAY_HEADER_SIZE+4+2; + } else { + onionskin = cell->payload+RELAY_HEADER_SIZE+4+2; + id_digest = cell->payload+RELAY_HEADER_SIZE+4+2+ONIONSKIN_CHALLENGE_LEN; + n_conn = connection_get_by_identity_digest(id_digest, CONN_TYPE_OR); + } + + if(!n_conn) { /* we should try to open a connection */ + /* Note that this will close circuits where the onion has the same + * router twice in a row in the path. I think that's ok. + */ + routerinfo_t *router; + struct in_addr in; + in.s_addr = htonl(circ->n_addr); + log_fn(LOG_INFO,"Next router (%s:%d) not connected. Connecting.", + inet_ntoa(in), circ->n_port); + + if (old_format) { + router = router_get_by_addr_port(circ->n_addr, circ->n_port); + if(!router) { + log_fn(LOG_INFO,"Next hop is an unknown router. Closing."); + return -1; + } + id_digest = router->identity_digest; + } else { /* new format */ + router = router_get_by_digest(id_digest); +#if 0 + if(router) { /* addr/port might be different */ + circ->n_addr = router->addr; + circ->n_port = router->or_port; + } +#endif + } + tor_assert(id_digest); + + memcpy(circ->onionskin, onionskin, ONIONSKIN_CHALLENGE_LEN); + circ->state = CIRCUIT_STATE_OR_WAIT; + + /* imprint the circuit with its future n_conn->id */ + memcpy(circ->n_conn_id_digest, id_digest, DIGEST_LEN); + + n_conn = connection_or_connect(circ->n_addr, circ->n_port, id_digest); + if(!n_conn) { + log_fn(LOG_INFO,"Launching n_conn failed. Closing."); + return -1; + } + log_fn(LOG_DEBUG,"connecting in progress (or finished). Good."); + /* return success. The onion/circuit/etc will be taken care of automatically + * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. + */ + return 0; + } + + circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ + circ->n_port = n_conn->port; + + circ->n_conn = n_conn; + memcpy(circ->n_conn_id_digest, n_conn->identity_digest, DIGEST_LEN); + log_fn(LOG_DEBUG,"n_conn is %s:%u",n_conn->address,n_conn->port); + + if(circuit_deliver_create_cell(circ, onionskin) < 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, char *key_data, int reverse) +{ + crypto_digest_env_t *tmp_digest; + crypto_cipher_env_t *tmp_crypto; + + tor_assert(cpath && key_data); + tor_assert(!(cpath->f_crypto || cpath->b_crypto || + cpath->f_digest || cpath->b_digest)); + + log_fn(LOG_DEBUG,"hop init digest forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)key_data, (unsigned int)*(uint32_t*)(key_data+20)); + cpath->f_digest = crypto_new_digest_env(); + crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); + cpath->b_digest = crypto_new_digest_env(); + crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); + + log_fn(LOG_DEBUG,"hop init cipher forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)(key_data+40), (unsigned int)*(uint32_t*)(key_data+40+16)); + if (!(cpath->f_crypto = + crypto_create_init_cipher(key_data+(2*DIGEST_LEN),1))) { + log(LOG_WARN,"forward cipher initialization failed."); + return -1; + } + if (!(cpath->b_crypto = + crypto_create_init_cipher(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN,0))) { + log(LOG_WARN,"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 or extended cell came back to us on the circuit, + * and it included reply (the second DH key, plus KH). + * + * Calculate the appropriate keys and digests, make sure KH is + * correct, and initialize this hop of the cpath. + * + * Return -1 if we want to mark circ for close, else return 0. + */ +int circuit_finish_handshake(circuit_t *circ, char *reply) { + unsigned char keys[CPATH_KEY_MATERIAL_LEN]; + crypt_path_t *hop; + + tor_assert(CIRCUIT_IS_ORIGIN(circ)); + if(circ->cpath->state == CPATH_STATE_AWAITING_KEYS) + hop = circ->cpath; + else { + for(hop=circ->cpath->next; + hop != circ->cpath && hop->state == CPATH_STATE_OPEN; + hop=hop->next) ; + if(hop == circ->cpath) { /* got an extended when we're all done? */ + log_fn(LOG_WARN,"got extended when circ already built? Closing."); + return -1; + } + } + tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS); + + if(onion_skin_client_handshake(hop->handshake_state, reply, keys, + DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) { + log_fn(LOG_WARN,"onion_skin_client_handshake failed."); + return -1; + } + + crypto_dh_free(hop->handshake_state); /* don't need it anymore */ + hop->handshake_state = NULL; + /* Remember hash of g^xy */ + memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN); + + if (circuit_init_cpath_crypto(hop, keys, 0)<0) { + return -1; + } + + hop->state = CPATH_STATE_OPEN; + log_fn(LOG_INFO,"finished"); + circuit_log_path(LOG_INFO,circ); + return 0; +} + +/** We received a relay truncated cell on circ. + * + * Since we don't ask for truncates currently, getting a truncated + * means that a connection broke or an extend failed. For now, + * just give up: for circ to close, and return 0. + */ +int circuit_truncated(circuit_t *circ, crypt_path_t *layer) { + crypt_path_t *victim; + connection_t *stream; + + tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); + tor_assert(layer); + + /* XXX Since we don't ask for truncates currently, getting a truncated + * means that a connection broke or an extend failed. For now, + * just give up. + */ + circuit_mark_for_close(circ); + return 0; + + while(layer->next != circ->cpath) { + /* we need to clear out layer->next */ + victim = layer->next; + log_fn(LOG_DEBUG, "Killing a layer of the cpath."); + + for(stream = circ->p_streams; stream; stream=stream->next_stream) { + if(stream->cpath_layer == victim) { + log_fn(LOG_INFO, "Marking stream %d for close.", stream->stream_id); + /* no need to send 'end' relay cells, + * because the other side's already dead + */ + stream->has_sent_end = 1; + connection_mark_for_close(stream); + } + } + + layer->next = victim->next; + circuit_free_cpath_node(victim); + } + + log_fn(LOG_INFO, "finished"); + return 0; +} + +/** Decide whether the first bit of the circuit ID will be + * 0 or 1, to avoid conflicts where each side randomly chooses + * the same circuit ID. + * + * Return CIRC_ID_TYPE_LOWER if local_nick is NULL, or if + * local_nick is lexographically smaller than remote_nick. + * Else return CIRC_ID_TYPE_HIGHER. + */ +static int decide_circ_id_type(char *local_nick, char *remote_nick) { + int result; + + tor_assert(remote_nick); + if(!local_nick) + return CIRC_ID_TYPE_LOWER; + result = strcasecmp(local_nick, remote_nick); + tor_assert(result); + if(result < 0) + return CIRC_ID_TYPE_LOWER; + return CIRC_ID_TYPE_HIGHER; +} + +/** Given a response payload and keys, initialize, then send a created + * cell back. + */ +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) { + cell_t cell; + crypt_path_t *tmp_cpath; + + tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t)); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATED; + cell.circ_id = circ->p_circ_id; + + circ->state = CIRCUIT_STATE_OPEN; + + log_fn(LOG_DEBUG,"Entering."); + + memcpy(cell.payload, payload, ONIONSKIN_REPLY_LEN); + + log_fn(LOG_INFO,"init digest forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)(keys), (unsigned int)*(uint32_t*)(keys+20)); + if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { + log_fn(LOG_WARN,"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; + tor_free(tmp_cpath); + + memcpy(circ->handshake_digest, cell.payload+DH_KEY_LEN, DIGEST_LEN); + + connection_or_write_cell_to_buf(&cell, circ->p_conn); + log_fn(LOG_DEBUG,"Finished sending 'created' cell."); + + return 0; +} + +/** Choose a length for a circuit of purpose purpose. + * Default length is 3 + the number of endpoints that would give something + * away. If the routerlist routers doesn't have enough routers + * to handle the desired path length, return as large a path length as + * is feasible, except if it's less than 2, in which case return -1. + */ +static int new_route_len(double cw, uint8_t purpose, smartlist_t *routers) { + int num_acceptable_routers; + int routelen; + + tor_assert((cw >= 0) && (cw < 1) && routers); /* valid parameters */ + +#ifdef TOR_PERF + routelen = 2; +#else + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_C_INTRODUCING) + routelen = 4; + else if(purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) + routelen = 4; + else { + log_fn(LOG_WARN,"Unhandled purpose %d", purpose); + return -1; + } +#endif +#if 0 + for(routelen = 3; ; routelen++) { /* 3, increment until coinflip says we're done */ + if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */ + break; + } +#endif + log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, + smartlist_len(routers)); + + num_acceptable_routers = count_acceptable_routers(routers); + + if(num_acceptable_routers < 2) { + log_fn(LOG_INFO,"Not enough acceptable routers (%d). Discarding this circuit.", + num_acceptable_routers); + return -1; + } + + if(num_acceptable_routers < routelen) { + log_fn(LOG_INFO,"Not enough routers: cutting routelen from %d to %d.", + routelen, num_acceptable_routers); + routelen = num_acceptable_routers; + } + + return routelen; +} + +/** 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 routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) +{ + int *n_supported; + int i, j; + int n_pending_connections = 0; + connection_t **carray; + int n_connections; + int best_support = -1; + int n_best_support=0; + smartlist_t *sl, *preferredexits, *excludedexits; + routerinfo_t *router; + + get_connection_array(&carray, &n_connections); + + /* 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. + */ + for (i = 0; i < n_connections; ++i) { + if (carray[i]->type == CONN_TYPE_AP && + carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT && + !carray[i]->marked_for_close && + !circuit_stream_is_being_handled(carray[i])) + ++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.) + */ + n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers)); + for (i = 0; i < smartlist_len(dir->routers); ++i) { /* iterate over routers */ + router = smartlist_get(dir->routers, i); + if(router_is_me(router)) { + 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(!router->is_running) { + n_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.", + router->nickname, i); + continue; /* skip routers that are known to be down */ + } + if(!router->is_verified) { + n_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- unverified router.", + router->nickname, i); + /* XXX008 maybe one day allow unverified routers as exits */ + continue; /* skip unverified routers */ + } + if(router_exit_policy_rejects_all(router)) { + 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; + for (j = 0; j < n_connections; ++j) { /* iterate over connections */ + if (carray[j]->type != CONN_TYPE_AP || + carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT || + carray[j]->marked_for_close || + circuit_stream_is_being_handled(carray[j])) + continue; /* Skip everything but APs in CIRCUIT_WAIT */ + if(connection_ap_can_use_exit(carray[j], router)) { + ++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); + } + } /* End looping over connections. */ + 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; + } + } + log_fn(LOG_INFO, "Found %d servers that might support %d/%d pending connections.", + n_best_support, best_support, n_pending_connections); + + preferredexits = smartlist_create(); + add_nickname_list_to_smartlist(preferredexits,options.ExitNodes); + + excludedexits = smartlist_create(); + add_nickname_list_to_smartlist(excludedexits,options.ExcludeNodes); + + sl = smartlist_create(); + + /* If any routers definitely support any pending connections, choose one + * at random. */ + if (best_support > 0) { + for (i = 0; i < smartlist_len(dir->routers); i++) + if (n_supported[i] == best_support) + smartlist_add(sl, smartlist_get(dir->routers, i)); + + smartlist_subtract(sl,excludedexits); + if (smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = smartlist_choose(sl); + } else { + /* Either there are no pending connections, or no routers even seem to + * possibly support any of them. Choose a router at random. */ + if (best_support == -1) { + log(LOG_WARN, "All routers are down or middleman -- choosing a doomed exit at random."); + } + for(i = 0; i < smartlist_len(dir->routers); i++) + if(n_supported[i] != -1) + smartlist_add(sl, smartlist_get(dir->routers, i)); + + smartlist_subtract(sl,excludedexits); + if (smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = smartlist_choose(sl); + } + + smartlist_free(preferredexits); + smartlist_free(excludedexits); + smartlist_free(sl); + tor_free(n_supported); + if(router) { + log_fn(LOG_INFO, "Chose exit server '%s'", router->nickname); + return router; + } + log_fn(LOG_WARN, "No exit routers seem to be running; can't choose an exit."); + 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 routerinfo_t *choose_good_exit_server(uint8_t purpose, routerlist_t *dir) +{ + routerinfo_t *r; + switch(purpose) { + case CIRCUIT_PURPOSE_C_GENERAL: + return choose_good_exit_server_general(dir); + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + r = router_choose_random_node(options.RendNodes, options.RendExcludeNodes, NULL); + return r; + default: + log_fn(LOG_WARN,"unhandled purpose %d", purpose); + tor_assert(0); + } + return NULL; /* never reached */ +} + +/** Allocate a cpath_build_state_t, populate it based on + * purpose and exit_digest (if specified), and + * return it. + */ +static cpath_build_state_t * +onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest) +{ + routerlist_t *rl; + int r; + cpath_build_state_t *info; + routerinfo_t *exit; + router_get_routerlist(&rl); + r = new_route_len(options.PathlenCoinWeight, purpose, rl->routers); + if (r < 0) + return NULL; + info = tor_malloc_zero(sizeof(cpath_build_state_t)); + info->desired_path_len = r; + if(exit_digest) { /* the circuit-builder pre-requested one */ + memcpy(info->chosen_exit_digest, exit_digest, DIGEST_LEN); + exit = router_get_by_digest(exit_digest); + if (exit) { + info->chosen_exit_name = tor_strdup(exit->nickname); + } else { + info->chosen_exit_name = tor_malloc(HEX_DIGEST_LEN+1); + base16_encode(info->chosen_exit_name, HEX_DIGEST_LEN+1, + exit_digest, DIGEST_LEN); + } + log_fn(LOG_INFO,"Using requested exit node '%s'", info->chosen_exit_name); + } else { /* we have to decide one */ + exit = choose_good_exit_server(purpose, rl); + if(!exit) { + log_fn(LOG_WARN,"failed to choose an exit server"); + tor_free(info); + return NULL; + } + memcpy(info->chosen_exit_digest, exit->identity_digest, DIGEST_LEN); + info->chosen_exit_name = tor_strdup(exit->nickname); + } + return info; +} + +/** Return the number of routers in routers that are currently up + * and available for building circuits through. Count sets of twins only + * once. + */ +static int count_acceptable_routers(smartlist_t *routers) { + int i, j, n; + int num=0; + connection_t *conn; + routerinfo_t *r, *r2; + + n = smartlist_len(routers); + for(i=0;inickname); + if(r->is_running == 0) { + log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i); + goto next_i_loop; + } + if(r->is_verified == 0) { + log_fn(LOG_DEBUG,"Nope, the directory says %d is not verified.",i); + goto next_i_loop; /* XXX008 */ + } + if(clique_mode()) { + conn = connection_get_by_identity_digest(r->identity_digest, + CONN_TYPE_OR); + if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) { + log_fn(LOG_DEBUG,"Nope, %d is not connected.",i); + goto next_i_loop; + } + } + for(j=0;jonion_pkey, r2->onion_pkey)) { + /* these guys are twins. so we've already counted him. */ + log_fn(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j); + goto next_i_loop; + } + } + num++; + log_fn(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num); + next_i_loop: + ; /* C requires an explicit statement after the label */ + } + + return num; +} + +/** Go through smartlist sl of routers, and remove all elements that + * have the same onion key as twin. + */ +static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) { + int i; + routerinfo_t *r; + + if(twin == NULL) + return; + + for(i=0; i < smartlist_len(sl); i++) { + r = smartlist_get(sl,i); + if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) { + smartlist_del(sl,i--); + } + } +} + +/** 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; + } +} + +/** Choose a suitable next hop in the cpath head_ptr, + * based on state. Add the hop info to head_ptr, and return a + * pointer to the chosen router in router_out. + */ +static int +onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t + *state, routerinfo_t **router_out) +{ + int cur_len; + crypt_path_t *cpath, *hop; + routerinfo_t *r; + routerinfo_t *choice; + int i; + smartlist_t *sl, *excludednodes; + + tor_assert(head_ptr); + tor_assert(router_out); + + if (!*head_ptr) { + cur_len = 0; + } else { + cur_len = 1; + for (cpath = *head_ptr; cpath->next != *head_ptr; cpath = cpath->next) { + ++cur_len; + } + } + if (cur_len >= state->desired_path_len) { + log_fn(LOG_DEBUG, "Path is complete: %d steps long", + state->desired_path_len); + return 1; + } + log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len, + state->desired_path_len); + + excludednodes = smartlist_create(); + add_nickname_list_to_smartlist(excludednodes,options.ExcludeNodes); + + if(cur_len == state->desired_path_len - 1) { /* Picking last node */ + log_fn(LOG_DEBUG, "Contemplating last hop: choice already made: %s", + state->chosen_exit_name); + choice = router_get_by_digest(state->chosen_exit_digest); + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Discarding this circuit.", + state->chosen_exit_name); + return -1; + } + } else if(cur_len == 0) { /* picking first node */ + /* try the nodes in EntryNodes first */ + sl = smartlist_create(); + add_nickname_list_to_smartlist(sl,options.EntryNodes); + /* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */ + remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + if(!choice) { + sl = smartlist_create(); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + } + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"No acceptable routers while picking entry node. Discarding this circuit."); + return -1; + } + } else { + log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice."); + sl = smartlist_create(); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) { + r = router_get_by_digest(cpath->identity_digest); + tor_assert(r); + remove_twins_from_smartlist(sl,r); + } + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Discarding this circuit."); + return -1; + } + } + + log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)", + choice->nickname, cur_len, state->chosen_exit_name); + + hop = tor_malloc_zero(sizeof(crypt_path_t)); + + /* link hop into the cpath, at the end. */ + onion_append_to_cpath(head_ptr, hop); + + hop->state = CPATH_STATE_CLOSED; + + hop->port = choice->or_port; + hop->addr = choice->addr; + memcpy(hop->identity_digest, choice->identity_digest, DIGEST_LEN); + + hop->package_window = CIRCWINDOW_START; + hop->deliver_window = CIRCWINDOW_START; + + log_fn(LOG_DEBUG, "Extended circuit path with %s for hop %d", + choice->nickname, cur_len); + + *router_out = choice; + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/circuitlist.c b/tags/tor-0_0_8pre1/src/or/circuitlist.c new file mode 100644 index 0000000000..8c40b53eca --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/circuitlist.c @@ -0,0 +1,503 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuitlist.c + * \brief Manage the global circuit list. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +/** A global list of all circuits at this hop. */ +circuit_t *global_circuitlist=NULL; + +/** Array of strings to make circ-\>state human-readable */ +char *circuit_state_to_string[] = { + "doing handshakes", /* 0 */ + "processing the onion", /* 1 */ + "connecting to firsthop", /* 2 */ + "open" /* 3 */ +}; + +/********* END VARIABLES ************/ + +static void circuit_free(circuit_t *circ); +static void circuit_free_cpath(crypt_path_t *cpath); + +/** Add circ to the global list of circuits. This is called only from + * within circuit_new. + */ +static void circuit_add(circuit_t *circ) { + if(!global_circuitlist) { /* first one */ + global_circuitlist = circ; + circ->next = NULL; + } else { + circ->next = global_circuitlist; + global_circuitlist = circ; + } +} + +/** Detach from the global circuit list, and deallocate, all + * circuits that have been marked for close. + */ +void circuit_close_all_marked(void) +{ + circuit_t *tmp,*m; + + while (global_circuitlist && global_circuitlist->marked_for_close) { + tmp = global_circuitlist->next; + circuit_free(global_circuitlist); + global_circuitlist = tmp; + } + + tmp = global_circuitlist; + while (tmp && tmp->next) { + if (tmp->next->marked_for_close) { + m = tmp->next->next; + circuit_free(tmp->next); + tmp->next = m; + /* Need to check new tmp->next; don't advance tmp. */ + } else { + /* Advance tmp. */ + tmp = tmp->next; + } + } +} + +/** Allocate space for a new circuit, initializing with p_circ_id + * and p_conn. Add it to the global circuit list. + */ +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) { + circuit_t *circ; + + circ = tor_malloc_zero(sizeof(circuit_t)); + circ->magic = CIRCUIT_MAGIC; + + circ->timestamp_created = time(NULL); + + circ->p_circ_id = p_circ_id; + circ->p_conn = p_conn; + + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + + /* CircIDs */ + circ->p_circ_id = p_circ_id; + /* circ->n_circ_id remains 0 because we haven't identified the next hop yet */ + + circ->package_window = CIRCWINDOW_START; + circ->deliver_window = CIRCWINDOW_START; + + circ->next_stream_id = crypto_pseudo_rand_int(1<<16); + + circuit_add(circ); + + return circ; +} + +/** Deallocate space associated with circ. + */ +static void circuit_free(circuit_t *circ) { + tor_assert(circ); + tor_assert(circ->magic == CIRCUIT_MAGIC); + if (circ->n_crypto) + crypto_free_cipher_env(circ->n_crypto); + if (circ->p_crypto) + crypto_free_cipher_env(circ->p_crypto); + if (circ->n_digest) + crypto_free_digest_env(circ->n_digest); + if (circ->p_digest) + crypto_free_digest_env(circ->p_digest); + if(circ->build_state) { + tor_free(circ->build_state->chosen_exit_name); + if (circ->build_state->pending_final_cpath) + circuit_free_cpath_node(circ->build_state->pending_final_cpath); + } + tor_free(circ->build_state); + circuit_free_cpath(circ->cpath); + if (circ->rend_splice) { + circ->rend_splice->rend_splice = NULL; + } + + memset(circ, 0xAA, sizeof(circuit_t)); /* poison memory */ + free(circ); +} + +/** Deallocate space associated with the linked list cpath. */ +static void circuit_free_cpath(crypt_path_t *cpath) { + crypt_path_t *victim, *head=cpath; + + if(!cpath) + return; + + /* it's a doubly linked 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); +} + +/** Deallocate space associated with the cpath node victim. */ +/* XXX rewrite so the call from circuitbuild isn't necessary */ +void circuit_free_cpath_node(crypt_path_t *victim) { + if(victim->f_crypto) + crypto_free_cipher_env(victim->f_crypto); + if(victim->b_crypto) + crypto_free_cipher_env(victim->b_crypto); + if(victim->f_digest) + crypto_free_digest_env(victim->f_digest); + if(victim->b_digest) + crypto_free_digest_env(victim->b_digest); + if(victim->handshake_state) + crypto_dh_free(victim->handshake_state); + free(victim); +} + +/** Return a circ such that: + * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and + * - circ is attached to conn, either as p_conn, n-conn, or + * in p_streams or n_streams. + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + + if(circ->p_circ_id == circ_id) { + if(circ->p_conn == conn) + return circ; + for(tmpconn = circ->p_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + } + if(circ->n_circ_id == circ_id) { + if(circ->n_conn == conn) + return circ; + for(tmpconn = circ->n_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + for(tmpconn = circ->resolving_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + } + } + return NULL; +} + +/** Return a circ such that circ is attached to conn, either as + * p_conn, n-conn, or in p_streams or n_streams or resolving_streams. + * + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_conn(connection_t *conn) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + + if(circ->p_conn == conn) + return circ; + if(circ->n_conn == conn) + return circ; + for(tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + for(tmpconn = circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + for(tmpconn = circ->resolving_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + } + return NULL; +} + +/** Return a circ such that: + * - circ-\>rend_query is equal to rend_query, and + * - circ-\>purpose is equal to purpose. + * + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose) { + circuit_t *circ; + + for (circ = global_circuitlist; circ; circ = circ->next) { + if (!circ->marked_for_close && + circ->purpose == purpose && + !rend_cmp_service_ids(rend_query, circ->rend_query)) + return circ; + } + return NULL; +} + +/** Return the first circuit in global_circuitlist after start whose + * rend_pk_digest field is digest and whose purpose is purpose. Returns + * NULL if no circuit is found. If start is NULL, begin at the start of + * the list. + */ +circuit_t * +circuit_get_next_by_pk_and_purpose(circuit_t *start, + const char *digest, uint8_t purpose) +{ + circuit_t *circ; + if (start == NULL) + circ = global_circuitlist; + else + circ = start->next; + + for( ; circ; circ = circ->next) { + if (circ->marked_for_close) + continue; + if (circ->purpose != purpose) + continue; + if (!memcmp(circ->rend_pk_digest, digest, DIGEST_LEN)) + return circ; + } + return NULL; +} + +/** Return the circuit waiting for a rendezvous with the provided cookie. + * Return NULL if no such circuit is found. + */ +circuit_t *circuit_get_rendezvous(const char *cookie) +{ + circuit_t *circ; + for (circ = global_circuitlist; circ; circ = circ->next) { + if (! circ->marked_for_close && + circ->purpose == CIRCUIT_PURPOSE_REND_POINT_WAITING && + ! memcmp(circ->rend_cookie, cookie, REND_COOKIE_LEN) ) + return circ; + } + return NULL; +} + +/** Count the number of circs originating here that aren't open, and + * that have the specified purpose. */ +int circuit_count_building(uint8_t purpose) { + circuit_t *circ; + int num=0; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && + circ->state != CIRCUIT_STATE_OPEN && + circ->purpose == purpose && + !circ->marked_for_close) + num++; + } + return num; +} + +/** Return the circuit that is open, has specified purpose, + * has a timestamp_dirty value of 0, and was created most recently, + * or NULL if no circuit fits this description. + */ +circuit_t * +circuit_get_youngest_clean_open(uint8_t purpose) { + circuit_t *circ; + circuit_t *youngest=NULL; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && circ->state == CIRCUIT_STATE_OPEN && + !circ->marked_for_close && circ->purpose == purpose && + !circ->timestamp_dirty && + (!youngest || youngest->timestamp_created < circ->timestamp_created)) + youngest = circ; + } + return youngest; +} + +/** 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 case call circuit_rep_hist_note_result() + * to note stats. + * - If purpose is C_INTRODUCE_ACK_WAIT, remove the intro point we + * just tried from our list of intro points for that service + * descriptor. + * - 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. + */ +int _circuit_mark_for_close(circuit_t *circ) { + connection_t *conn; + + assert_circuit_ok(circ); + if (circ->marked_for_close) + return -1; + + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + onion_pending_remove(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)) + circuit_build_failed(circ); /* take actions if necessary */ + circuit_rep_hist_note_result(circ); + } + if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + tor_assert(circ->state == CIRCUIT_STATE_OPEN); + /* treat this like getting a nack from it */ + log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.", + circ->rend_query, circ->build_state->chosen_exit_name); + rend_client_remove_intro_point(circ->build_state->chosen_exit_name, circ->rend_query); + } + + if(circ->n_conn) + connection_send_destroy(circ->n_circ_id, circ->n_conn); + for(conn=circ->n_streams; conn; conn=conn->next_stream) + connection_edge_destroy(circ->n_circ_id, conn); + while(circ->resolving_streams) { + conn = circ->resolving_streams; + circ->resolving_streams = conn->next_stream; + connection_dns_remove(conn); /* remove it from resolve lists */ + log_fn(LOG_INFO,"Freeing resolving-conn."); + connection_free(conn); + } + if(circ->p_conn) + connection_send_destroy(circ->p_circ_id, circ->p_conn); + for(conn=circ->p_streams; conn; conn=conn->next_stream) + connection_edge_destroy(circ->p_circ_id, conn); + + circ->marked_for_close = 1; + + if (circ->rend_splice && !circ->rend_splice->marked_for_close) { + /* do this after marking this circuit, to avoid infinite recursion. */ + circuit_mark_for_close(circ->rend_splice); + circ->rend_splice = NULL; + } + return 0; +} + +/** 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); + switch(cp->state) + { + case CPATH_STATE_OPEN: + tor_assert(cp->f_crypto); + tor_assert(cp->b_crypto); + /* fall through */ + case CPATH_STATE_CLOSED: + tor_assert(!cp->handshake_state); + break; + case CPATH_STATE_AWAITING_KEYS: + tor_assert(cp->handshake_state); + break; + default: + 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) +{ + connection_t *conn; + + tor_assert(c); + tor_assert(c->magic == CIRCUIT_MAGIC); + tor_assert(c->purpose >= _CIRCUIT_PURPOSE_MIN && + c->purpose <= _CIRCUIT_PURPOSE_MAX); + + if (c->n_conn) { + tor_assert(c->n_conn->type == CONN_TYPE_OR); + tor_assert(!memcmp(c->n_conn->identity_digest, c->n_conn_id_digest, DIGEST_LEN)); + } + if (c->p_conn) + tor_assert(c->p_conn->type == CONN_TYPE_OR); + for (conn = c->p_streams; conn; conn = conn->next_stream) + tor_assert(conn->type == CONN_TYPE_AP); + for (conn = c->n_streams; conn; conn = conn->next_stream) + tor_assert(conn->type == CONN_TYPE_EXIT); + + tor_assert(c->deliver_window >= 0); + tor_assert(c->package_window >= 0); + if (c->state == CIRCUIT_STATE_OPEN) { + if (c->cpath) { + tor_assert(CIRCUIT_IS_ORIGIN(c)); + tor_assert(!c->n_crypto); + tor_assert(!c->p_crypto); + tor_assert(!c->n_digest); + tor_assert(!c->p_digest); + } else { + tor_assert(!CIRCUIT_IS_ORIGIN(c)); + tor_assert(c->n_crypto); + tor_assert(c->p_crypto); + tor_assert(c->n_digest); + tor_assert(c->p_digest); + } + } + if (c->cpath) { + assert_cpath_ok(c->cpath); + } + if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) { + if (!c->marked_for_close) { + tor_assert(c->rend_splice); + tor_assert(c->rend_splice->rend_splice == c); + } + tor_assert(c->rend_splice != c); + } else { + tor_assert(!c->rend_splice); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/circuituse.c b/tags/tor-0_0_8pre1/src/or/circuituse.c new file mode 100644 index 0000000000..e81005fd87 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/circuituse.c @@ -0,0 +1,859 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuituse.c + * \brief Launch the right sort of circuits, attach streams to them. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +extern circuit_t *global_circuitlist; /* from circuitlist.c */ +extern int has_fetched_directory; /* from main.c */ + +/********* END VARIABLES ************/ + +static void circuit_expire_old_circuits(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(circuit_t *circ, + connection_t *conn, + int must_be_open, + uint8_t purpose, + time_t now) +{ + routerinfo_t *exitrouter; + + if (!CIRCUIT_IS_ORIGIN(circ)) + return 0; /* this circ doesn't start at us */ + if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn)) + 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(purpose == CIRCUIT_PURPOSE_C_GENERAL) + if(circ->timestamp_dirty && + circ->timestamp_dirty+options.NewCircuitPeriod <= now) + return 0; + + if(conn) { + /* 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. + */ + exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest); + + if(!exitrouter) { + log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)"); + return 0; /* this circuit is screwed and doesn't know it yet */ + } + + if (conn->socks_request && + conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { + /* 0.0.7 servers and earlier don't support DNS resolution. There are no + * ORs running code before 0.0.7, so we only worry about 0.0.7. Once all + * servers are running 0.0.8, remove this check. */ + if (!strncmp(exitrouter->platform, "Tor 0.0.7", 9)) + return 0; + } else if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if(!connection_ap_can_use_exit(conn, exitrouter)) { + /* can't exit from this router */ + return 0; + } + } else { /* not general */ + if(rend_cmp_service_ids(conn->rend_query, circ->rend_query) && + (circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) { + /* this circ is not for this conn, and it's not suitable + * for cannibalizing either */ + return 0; + } + } + } + return 1; +} + +/* Return 1 if circuit a is better than circuit b for + * purpose, and return 0 otherwise. Used by circuit_get_best. + */ +static int circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose) +{ + 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 || + a->timestamp_created > b->timestamp_created) + 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; + } + 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 circuit_t * +circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose) +{ + circuit_t *circ, *best=NULL; + time_t now = time(NULL); + + tor_assert(conn); + + tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || + purpose == CIRCUIT_PURPOSE_C_REND_JOINED); + + for (circ=global_circuitlist;circ;circ = circ->next) { + if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,now)) + 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(circ,best,purpose)) + best = circ; + } + + return best; +} + +/** Circuits that were born at the end of their second might be expired + * after 30.1 seconds; circuits born at the beginning might be expired + * after closer to 31 seconds. + */ +#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30 + +/** Close all circuits that start at us, aren't open, and were born + * at least MIN_SECONDS_BEFORE_EXPIRING_CIRC seconds ago. + */ +void circuit_expire_building(time_t now) { + circuit_t *victim, *circ = global_circuitlist; + + while(circ) { + victim = circ; + circ = circ->next; + if(!CIRCUIT_IS_ORIGIN(victim)) + continue; /* didn't originate here */ + if(victim->marked_for_close) + continue; /* don't mess with marked circs */ + if(victim->timestamp_created + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now) + continue; /* it's young still, don't mess with it */ + + /* 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 purp %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 purp %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)); + } + + /* 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 || + victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || + victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING || + victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + + /* it's a rend_ready circ, but it's already picked a query */ + (victim->purpose == CIRCUIT_PURPOSE_C_REND_READY && + victim->rend_query[0]) || + + /* c_rend_ready circs measure age since timestamp_dirty, + * because that's set when they switch purposes + */ + /* rend and intro circs become dirty each time they + * make an introduction attempt. so timestamp_dirty + * will reflect the time since the last attempt. + */ + ((victim->purpose == CIRCUIT_PURPOSE_C_REND_READY || + victim->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED || + victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) && + victim->timestamp_dirty + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now)) { + if(victim->n_conn) + log_fn(LOG_INFO,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)", + victim->n_conn->address, victim->n_port, victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state], victim->purpose); + else + log_fn(LOG_INFO,"Abandoning circ %d (state %d:%s, purpose %d)", victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state], victim->purpose); + circuit_log_path(LOG_INFO,victim); + circuit_mark_for_close(victim); + } + } +} + +/** How many circuits do we want simultaneously in-progress to handle + * a given stream? + */ +#define MIN_CIRCUITS_HANDLING_STREAM 2 + +/** Return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open + * general-purpose circuits will have an acceptable exit node for + * conn. Else return 0. + */ +int circuit_stream_is_being_handled(connection_t *conn) { + circuit_t *circ; + routerinfo_t *exitrouter; + int num=0; + time_t now = time(NULL); + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && circ->state != CIRCUIT_STATE_OPEN && + !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && + (!circ->timestamp_dirty || + circ->timestamp_dirty + options.NewCircuitPeriod < now)) { + exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest); + if(exitrouter && connection_ap_can_use_exit(conn, exitrouter)) + if(++num >= MIN_CIRCUITS_HANDLING_STREAM) + return 1; + } + } + return 0; +} + +/** Build a new test circuit every 5 minutes */ +#define TESTING_CIRCUIT_INTERVAL 300 + +/** This function is called once a second. 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 long time_to_new_circuit = 0; + circuit_t *circ; + + /* 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(); + + circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL); + + if(time_to_new_circuit < now) { + circuit_reset_failure_count(); + time_to_new_circuit = now + options.NewCircuitPeriod; + if(proxy_mode()) + client_dns_clean(); + circuit_expire_old_circuits(); + + if(options.RunTesting && circ && + circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) { + log_fn(LOG_INFO,"Creating a new testing circuit."); + circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL); + } + } + +/** How many simultaneous in-progress general-purpose circuits do we + * want to be building at once, if there are no open general-purpose + * circuits? + */ +#define CIRCUIT_MIN_BUILDING_GENERAL 3 + /* if there's no open circ, and less than 3 are on the way, + * go ahead and try another. */ + if(!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL) + < CIRCUIT_MIN_BUILDING_GENERAL) { + circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL); + } + + /* XXX count idle rendezvous circs and build more */ +} + +/** 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, connection_t *conn) { + connection_t *prevconn; + + tor_assert(circ && conn); + + if(conn == circ->p_streams) { + circ->p_streams = conn->next_stream; + return; + } + if(conn == circ->n_streams) { + circ->n_streams = conn->next_stream; + return; + } + if(conn == circ->resolving_streams) { + circ->resolving_streams = conn->next_stream; + return; + } + + for(prevconn = 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; + } + + for(prevconn = 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 = 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_fn(LOG_ERR,"edge conn not in circuit's list?"); + tor_assert(0); /* should never get here */ +} + +/** Notify the global circuit list that conn is about to be + * removed and then freed. + * + * If it's an OR conn, then mark-for-close all the circuits that use + * that conn. + * + * If it's an edge conn, then detach it from its circ, so we don't + * try to reference it later. + */ +void circuit_about_to_close_connection(connection_t *conn) { + /* currently, we assume it's too late to flush conn's buf here. + * down the road, maybe we'll consider that eof doesn't mean can't-write + */ + circuit_t *circ; + + switch(conn->type) { + case CONN_TYPE_OR: + if(conn->state != OR_CONN_STATE_OPEN) { + /* Inform any pending (not attached) circs that they should give up. */ + circuit_n_conn_done(conn, 0); + } + /* Now close all the attached circuits on it. */ + while((circ = circuit_get_by_conn(conn))) { + if(circ->n_conn == conn) /* it's closing in front of us */ + circ->n_conn = NULL; + if(circ->p_conn == conn) /* it's closing behind us */ + circ->p_conn = NULL; + circuit_mark_for_close(circ); + } + return; + case CONN_TYPE_AP: + case CONN_TYPE_EXIT: + + /* It's an edge conn. Need to remove it from the linked list of + * conn's for this circuit. Confirm that 'end' relay command has + * been sent. But don't kill the circuit. + */ + + circ = circuit_get_by_conn(conn); + if(!circ) + return; + + circuit_detach_stream(circ, conn); + + } /* end switch */ +} + +/** Don't keep more than 10 unused open circuits around. */ +#define MAX_UNUSED_OPEN_CIRCUITS 10 + +/** Find each circuit that has been dirty for too long, and has + * no streams on it: mark it for close. + * + * Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and + * unused circuits, then mark the excess circs for close. + */ +static void +circuit_expire_old_circuits(void) +{ + circuit_t *circ; + time_t now = time(NULL); + smartlist_t *unused_open_circs; + int i; + + unused_open_circs = smartlist_create(); + + for (circ = global_circuitlist; circ; circ = circ->next) { + if (circ->marked_for_close) + 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 + options.NewCircuitPeriod < now && + !circ->p_conn && /* we're the origin */ + !circ->p_streams /* nothing attached */ ) { + log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id, + (int)(now - circ->timestamp_dirty), circ->purpose); + /* (only general and purpose_c circs can get dirty) */ + tor_assert(!circ->n_streams); + tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED); + circuit_mark_for_close(circ); + } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) && + circ->state == CIRCUIT_STATE_OPEN && + circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { + /* Also, gather a list of open unused general circuits that we created. + * Because we add elements to the front of global_circuitlist, + * the last elements of unused_open_circs will be the oldest + * ones. + */ + smartlist_add(unused_open_circs, circ); + } + } + for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) { + circuit_t *circ = smartlist_get(unused_open_circs, i); + log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)", + circ->n_circ_id, circ->purpose); + circuit_mark_for_close(circ); + } + smartlist_free(unused_open_circs); +} + +/** 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(circuit_t *circ) { + + switch(circ->purpose) { + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + rend_client_rendcirc_has_opened(circ); + 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. */ + connection_ap_attach_pending(); + 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; + default: + log_fn(LOG_ERR,"unhandled purpose %d",circ->purpose); + tor_assert(0); + } +} + +/*~ Called whenever a circuit could not be successfully built. + */ +void circuit_build_failed(circuit_t *circ) { + + /* 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; + } + + switch(circ->purpose) { + case CIRCUIT_PURPOSE_C_GENERAL: + if (circ->state != CIRCUIT_STATE_OPEN) { + /* If we never built the circuit, note it as a failure. */ + /* Note that we can't just check circ->cpath here, because if + * circuit-building failed immediately, it won't be set yet. */ + circuit_increment_failure_count(); + } + break; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + /* at Bob, waiting for introductions */ + if (circ->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 */ + if (circ->state != CIRCUIT_STATE_OPEN) { + 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 */ + if (failed_at_last_hop) { + log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s. Sucks to be Alice.", circ->build_state->chosen_exit_name); + } else { + log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s, because an earlier node failed.", + circ->build_state->chosen_exit_name); + rend_service_relaunch_rendezvous(circ); + } + break; + default: + /* Other cases are impossible, since this function is only called with + * unbuilt circuits. */ + tor_assert(0); + } +} + +/** Number of consecutive failures so far; should only be touched by + * circuit_launch_new and circuit_*_failure_count. + */ +static int n_circuit_failures = 0; + +/** Don't retry launching a new circuit if we try this many times with no + * success. */ +#define MAX_CIRCUIT_FAILURES 5 + +circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest) +{ + if (!has_fetched_directory) { + log_fn(LOG_DEBUG,"Haven't fetched directory yet; cancelling circuit launch."); + return NULL; + } + + if (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, exit_digest); +} + +/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */ +circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname) +{ + const char *digest = NULL; + + if (exit_nickname) { + routerinfo_t *r = router_get_by_nickname(exit_nickname); + if (!r) { + log_fn(LOG_WARN, "No such OR as '%s'", exit_nickname); + return NULL; + } + digest = r->identity_digest; + } + return circuit_launch_by_identity(purpose, digest); +} + +/** 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_fn(LOG_DEBUG,"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(void) { + n_circuit_failures = 0; +} + +/** Find an open circ that we're happy with: 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(connection_t *conn, + uint8_t desired_circuit_purpose, + circuit_t **circp) { + circuit_t *circ; + uint32_t addr; + int is_resolve; + + tor_assert(conn); + tor_assert(circp); + tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + is_resolve = conn->socks_request->command == SOCKS_COMMAND_RESOLVE; + + circ = circuit_get_best(conn, 1, desired_circuit_purpose); + + if(circ) { + *circp = circ; + return 1; /* we're happy */ + } + + /* Do we need to check exit policy? */ + if(!is_resolve && !connection_edge_is_rendezvous_stream(conn)) { + addr = client_dns_lookup_entry(conn->socks_request->address); + if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) { + log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.", + conn->socks_request->address, conn->socks_request->port); + return -1; + } + } + + /* is one already on the way? */ + circ = circuit_get_best(conn, 0, desired_circuit_purpose); + if(!circ) { + char *exitname=NULL; + uint8_t new_circ_purpose; + + if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + /* need to pick an intro point */ + exitname = rend_client_get_random_intro(conn->rend_query); + if(!exitname) { + log_fn(LOG_WARN,"Couldn't get an intro point for '%s'. Closing conn.", + conn->rend_query); + return -1; + } + if(!router_get_by_nickname(exitname)) { + log_fn(LOG_WARN,"Advertised intro point '%s' is not known. Closing.", exitname); + return -1; + } + /* XXX if we failed, then refetch the descriptor */ + log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query); + } + + 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; + + circ = circuit_launch_by_nickname(new_circ_purpose, exitname); + tor_free(exitname); + + if(circ && + (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)) { + /* then write the service_id into circ */ + strcpy(circ->rend_query, conn->rend_query); + } + } + if(!circ) + log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.", + desired_circuit_purpose); + *circp = circ; + return 0; +} + +/** Attach the AP stream apconn to circ's linked list of + * p_streams. Also set apconn's cpath_layer to the last hop in + * circ's cpath. + */ +static void link_apconn_to_circ(connection_t *apconn, circuit_t *circ) { + /* add it into the linked list of streams on this circuit */ + log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id); + apconn->next_stream = circ->p_streams; + /* assert_connection_ok(conn, time(NULL)); */ + circ->p_streams = apconn; + + tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath && circ->cpath->prev); + tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN); + apconn->cpath_layer = circ->cpath->prev; +} + +/** 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); + * 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. + */ +int connection_ap_handshake_attach_circuit(connection_t *conn) { + int retval; + int conn_age; + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP); + tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(conn->socks_request); + + conn_age = time(NULL) - conn->timestamp_created; + if(conn_age > 60) { + /* XXX make this cleaner than '60' */ + log_fn(LOG_WARN,"Giving up on unattached conn (%d sec old).", conn_age); + return -1; + } + + if(!connection_edge_is_rendezvous_stream(conn)) { /* we're a general conn */ + circuit_t *circ=NULL; + + /* 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) + return retval; + + /* We have found a suitable circuit for our conn. Hurray. */ + + log_fn(LOG_DEBUG,"Attaching apconn to general circ %d (stream %d sec old).", + circ->n_circ_id, conn_age); + /* here, print the circ's path. so people can figure out which circs are sucking. */ + circuit_log_path(LOG_INFO,circ); + + if(!circ->timestamp_dirty) + circ->timestamp_dirty = time(NULL); + + link_apconn_to_circ(conn, circ); + tor_assert(conn->socks_request); + if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) + connection_ap_handshake_send_begin(conn, circ); + else + connection_ap_handshake_send_resolve(conn, circ); + + return 1; + } else { /* we're a rendezvous conn */ + circuit_t *rendcirc=NULL, *introcirc=NULL; + + tor_assert(!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 */ + tor_assert(rendcirc); + + if(retval > 0) { + /* one is already established, attach */ + log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)", + rendcirc->n_circ_id, conn_age); + link_apconn_to_circ(conn, rendcirc); + if(connection_ap_handshake_send_begin(conn, rendcirc) < 0) + return 0; /* already marked, let them fade away */ + return 1; + } + + if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { + log_fn(LOG_INFO,"pending-join circ %d already here, with intro ack. Stalling. (stream %d sec old)", rendcirc->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 */ + tor_assert(introcirc); + + if(retval > 0) { + /* one has already sent the intro. keep waiting. */ + log_fn(LOG_INFO,"Intro circ %d present and awaiting ack (rend %d). Stalling. (stream %d sec old)", + introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + return 0; + } + + /* now both rendcirc and introcirc are defined, and neither is finished */ + + if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) { + log_fn(LOG_INFO,"ready rend circ %d already here (no intro-ack yet on intro %d). (stream %d sec old)", + rendcirc->n_circ_id, introcirc->n_circ_id, conn_age); + /* look around for any new intro circs that should introduce */ + + tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); + if(introcirc->state == CIRCUIT_STATE_OPEN) { + log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)", + introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + /* XXX here we should cannibalize the rend circ if it's a zero service id */ + if(rend_client_send_introduction(introcirc, rendcirc) < 0) { + return -1; + } + rendcirc->timestamp_dirty = time(NULL); + introcirc->timestamp_dirty = time(NULL); + assert_circuit_ok(rendcirc); + assert_circuit_ok(introcirc); + return 0; + } + } + + log_fn(LOG_INFO,"Intro (%d) and rend (%d) circs are not both ready. Stalling conn. (%d sec old)", introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + return 0; + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/command.c b/tags/tor-0_0_8pre1/src/or/command.c new file mode 100644 index 0000000000..284c4c6a9c --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/command.c @@ -0,0 +1,289 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file command.c + * \brief Functions for processing incoming cells + **/ + +/* In-points to command.c: + * + * - command_process_cell(), called from + * connection_or_process_cells_from_inbuf() in connection_or.c + */ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ +extern int shutting_down; /* whether we should refuse create cells */ + +/** Keep statistics about how many of each type of cell we've received. */ +unsigned long stats_n_padding_cells_processed = 0; +unsigned long stats_n_create_cells_processed = 0; +unsigned long stats_n_created_cells_processed = 0; +unsigned long stats_n_relay_cells_processed = 0; +unsigned long stats_n_destroy_cells_processed = 0; + +/** These are the main four functions for processing cells */ +static void command_process_create_cell(cell_t *cell, connection_t *conn); +static void command_process_created_cell(cell_t *cell, connection_t *conn); +static void command_process_relay_cell(cell_t *cell, connection_t *conn); +static void command_process_destroy_cell(cell_t *cell, connection_t *conn); + +/** 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, connection_t *conn, int *time, + void (*func)(cell_t *, connection_t *)) { + struct timeval start, end; + long time_passed; + + tor_gettimeofday(&start); + + (*func)(cell, conn); + + tor_gettimeofday(&end); + time_passed = tv_udiff(&start, &end) ; + + if (time_passed > 10000) { /* more than 10ms */ + log_fn(LOG_INFO,"That call just took %ld ms.",time_passed/1000); + } + *time += time_passed; +} + +/** Process a 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. + */ +void command_process_cell(cell_t *cell, connection_t *conn) { + /* 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(LOG_INFO,"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; + } + + switch(cell->command) { + case CELL_PADDING: + ++stats_n_padding_cells_processed; + /* do nothing */ + break; + case CELL_CREATE: + ++stats_n_create_cells_processed; + ++num_create; + command_time_process_cell(cell, conn, &create_time, + command_process_create_cell); + break; + case CELL_CREATED: + ++stats_n_created_cells_processed; + ++num_created; + command_time_process_cell(cell, conn, &created_time, + command_process_created_cell); + break; + case CELL_RELAY: + ++stats_n_relay_cells_processed; + ++num_relay; + command_time_process_cell(cell, conn, &relay_time, + command_process_relay_cell); + break; + case CELL_DESTROY: + ++stats_n_destroy_cells_processed; + ++num_destroy; + command_time_process_cell(cell, conn, &destroy_time, + command_process_destroy_cell); + break; + default: + log_fn(LOG_WARN,"Cell of unknown type (%d) received. Dropping.", cell->command); + break; + } +} + +/** Process a 'create' cell that just arrived from conn. 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, connection_t *conn) { + circuit_t *circ; + + if(shutting_down) { + log_fn(LOG_INFO,"Received create cell but we're shutting down. Sending back destroy."); + connection_send_destroy(cell->circ_id, conn); + return; + } + + circ = circuit_get_by_circ_id_conn(cell->circ_id, conn); + + if(circ) { + log_fn(LOG_WARN,"received CREATE cell (circID %d) for known circ. Dropping.", cell->circ_id); + return; + } + + circ = circuit_new(cell->circ_id, conn); + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + circ->purpose = CIRCUIT_PURPOSE_OR; + + memcpy(circ->onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN); + + /* hand it off to the cpuworkers, and then return */ + if(assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) { + log_fn(LOG_WARN,"Failed to hand off onionskin. Closing."); + circuit_mark_for_close(circ); + return; + } + log_fn(LOG_DEBUG,"success: handed off onionskin."); +} + +/** Process a 'created' cell that just arrived from conn. 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, connection_t *conn) { + circuit_t *circ; + + circ = circuit_get_by_circ_id_conn(cell->circ_id, conn); + + if(!circ) { + log_fn(LOG_INFO,"(circID %d) unknown circ (probably got a destroy earlier). Dropping.", cell->circ_id); + return; + } + + if(circ->n_circ_id != cell->circ_id) { + log_fn(LOG_WARN,"got created cell from OPward? Closing."); + circuit_mark_for_close(circ); + return; + } + + if(CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */ + log_fn(LOG_DEBUG,"at OP. Finishing handshake."); + if(circuit_finish_handshake(circ, cell->payload) < 0) { + log_fn(LOG_WARN,"circuit_finish_handshake failed."); + circuit_mark_for_close(circ); + return; + } + log_fn(LOG_DEBUG,"Moving to next skin."); + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"circuit_send_next_onion_skin failed."); + circuit_mark_for_close(circ); /* XXX push this circuit_close lower */ + return; + } + } else { /* pack it into an extended relay cell, and send it. */ + log_fn(LOG_DEBUG,"Converting created cell to extended relay cell, sending."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTENDED, + cell->payload, ONIONSKIN_REPLY_LEN, NULL); + } +} + +/** Process a 'relay' 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, connection_t *conn) { + circuit_t *circ; + + circ = circuit_get_by_circ_id_conn(cell->circ_id, conn); + + if(!circ) { + log_fn(LOG_INFO,"unknown circuit %d on connection to %s:%d. Dropping.", + cell->circ_id, conn->address, conn->port); + return; + } + + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + log_fn(LOG_WARN,"circuit in create_wait. Closing."); + circuit_mark_for_close(circ); + return; + } + + if(cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */ + if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_OUT) < 0) { + log_fn(LOG_WARN,"circuit_receive_relay_cell (forward) failed. Closing."); + circuit_mark_for_close(circ); + return; + } + } else { /* it's an ingoing cell */ + if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_IN) < 0) { + log_fn(LOG_WARN,"circuit_receive_relay_cell (backward) failed. Closing."); + circuit_mark_for_close(circ); + return; + } + } +} + +/** Process a 'destroy' cell that just arrived from + * conn. 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, connection_t *conn) { + circuit_t *circ; + + circ = circuit_get_by_circ_id_conn(cell->circ_id, conn); + + if(!circ) { + log_fn(LOG_INFO,"unknown circuit %d on connection to %s:%d. Dropping.", + cell->circ_id, conn->address, conn->port); + return; + } + + log_fn(LOG_INFO,"Received for circID %d.",cell->circ_id); + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + onion_pending_remove(circ); + } + + if(cell->circ_id == circ->p_circ_id) { + /* the destroy came from behind */ + circ->p_conn = NULL; + circuit_mark_for_close(circ); + } else { /* the destroy came from ahead */ + circ->n_conn = NULL; +#if 0 + if(!CIRCUIT_IS_ORIGIN(circ)) { + log_fn(LOG_DEBUG, "Delivering 'truncated' back."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + } +#endif + circuit_mark_for_close(circ); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/config.c b/tags/tor-0_0_8pre1/src/or/config.c new file mode 100644 index 0000000000..6ce47263bd --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/config.c @@ -0,0 +1,968 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * /file config.c + * + * /brief Code to parse and interpret configuration files. + * + **/ + +#include "or.h" + +/** Enumeration of types which option values can take */ +typedef enum config_type_t { + CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ + CONFIG_TYPE_INT, /**< An integer */ + CONFIG_TYPE_DOUBLE, /**< A floating-point value */ + CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ + CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ +} config_type_t; + +/** Largest allowed config line */ +#define CONFIG_LINE_T_MAXLEN 4096 + +static FILE *config_open(const unsigned char *filename); +static int config_close(FILE *f); +static struct config_line_t *config_get_commandlines(int argc, char **argv); +static struct config_line_t *config_get_lines(FILE *f); +static void config_free_lines(struct config_line_t *front); +static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg); +static int config_assign(or_options_t *options, struct config_line_t *list); + +/** Open a configuration file for reading */ +static FILE *config_open(const unsigned char *filename) { + tor_assert(filename); + if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) { + /* filename has illegal letters */ + return NULL; + } + return fopen(filename, "r"); +} + +/** Close the configuration file */ +static int config_close(FILE *f) { + tor_assert(f); + return fclose(f); +} + +/** Helper: Read a list of configuration options from the command line. */ +static struct config_line_t *config_get_commandlines(int argc, char **argv) { + struct config_line_t *new; + struct config_line_t *front = NULL; + char *s; + int i = 1; + + while(i < argc-1) { + if(!strcmp(argv[i],"-f")) { +// log(LOG_DEBUG,"Commandline: skipping over -f."); + i+=2; /* this is the config file option. ignore it. */ + continue; + } + + new = tor_malloc(sizeof(struct config_line_t)); + s = argv[i]; + while(*s == '-') + s++; + new->key = tor_strdup(s); + new->value = tor_strdup(argv[i+1]); + + log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'", + new->key, new->value); + new->next = front; + front = new; + i += 2; + } + return front; +} + +/** Helper: allocate a new configuration option mapping 'key' to 'val', + * prepend it to 'front', and return the newly allocated config_line_t */ +static struct config_line_t * +config_line_prepend(struct config_line_t *front, + const char *key, + const char *val) +{ + struct config_line_t *newline; + newline = tor_malloc(sizeof(struct config_line_t)); + newline->key = tor_strdup(key); + newline->value = tor_strdup(val); + newline->next = front; + return newline; +} + +/** Helper: parse the config file and strdup into key/value + * strings. Return list, or NULL if parsing the file failed. Warn and + * ignore any misformatted lines. */ +static struct config_line_t *config_get_lines(FILE *f) { + + struct config_line_t *front = NULL; + char line[CONFIG_LINE_T_MAXLEN]; + int result; + char *key, *value; + + while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) { + front = config_line_prepend(front, key, value); + } + if(result < 0) + return NULL; + return front; +} + +/** + * Free all the configuration lines on the linked list front. + */ +static void config_free_lines(struct config_line_t *front) { + struct config_line_t *tmp; + + while(front) { + tmp = front; + front = tmp->next; + + free(tmp->key); + free(tmp->value); + free(tmp); + } +} + +/** Search the linked list c for any option whose key is key. + * If such an option is found, interpret it as of type type, and store + * the result in arg. If the option is misformatted, log a warning and + * skip it. + */ +static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg) { + int i; + + if(strncasecmp(c->key,key,strlen(c->key))) + return 0; + + if(strcasecmp(c->key,key)) { + tor_free(c->key); + c->key = tor_strdup(key); + } + + /* it's a match. cast and assign. */ + log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value); + + switch(type) { + case CONFIG_TYPE_INT: + *(int *)arg = atoi(c->value); + break; + case CONFIG_TYPE_BOOL: + i = atoi(c->value); + if (i != 0 && i != 1) { + log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1", c->key); + return 0; + } + *(int *)arg = i; + break; + case CONFIG_TYPE_STRING: + tor_free(*(char **)arg); + *(char **)arg = tor_strdup(c->value); + break; + case CONFIG_TYPE_DOUBLE: + *(double *)arg = atof(c->value); + break; + case CONFIG_TYPE_LINELIST: + /* Note: this reverses the order that the lines appear in. That's + * just fine, since we build up the list of lines reversed in the + * first place. */ + *(struct config_line_t**)arg = + config_line_prepend(*(struct config_line_t**)arg, c->key, c->value); + break; + } + return 1; +} + +/** Iterate through the linked list of options list. + * For each item, convert as appropriate and assign to options. + * If an item is unrecognized, return -1 immediately, + * else return 0 for success. */ +static int config_assign(or_options_t *options, struct config_line_t *list) { + + while(list) { + if( + + /* order matters here! abbreviated arguments use the first match. */ + + /* string options */ + config_compare(list, "Address", CONFIG_TYPE_STRING, &options->Address) || + config_compare(list, "AuthoritativeDirectory",CONFIG_TYPE_BOOL, &options->AuthoritativeDir) || + + config_compare(list, "BandwidthRate", CONFIG_TYPE_INT, &options->BandwidthRate) || + config_compare(list, "BandwidthBurst", CONFIG_TYPE_INT, &options->BandwidthBurst) || + + config_compare(list, "ClientOnly", CONFIG_TYPE_BOOL, &options->ClientOnly) || + config_compare(list, "ContactInfo", CONFIG_TYPE_STRING, &options->ContactInfo) || + + config_compare(list, "DebugLogFile", CONFIG_TYPE_STRING, &options->DebugLogFile) || + config_compare(list, "DataDirectory", CONFIG_TYPE_STRING, &options->DataDirectory) || + config_compare(list, "DirPort", CONFIG_TYPE_INT, &options->DirPort) || + config_compare(list, "DirBindAddress", CONFIG_TYPE_LINELIST, &options->DirBindAddress) || + config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_INT, &options->DirFetchPostPeriod) || + + config_compare(list, "ExitNodes", CONFIG_TYPE_STRING, &options->ExitNodes) || + config_compare(list, "EntryNodes", CONFIG_TYPE_STRING, &options->EntryNodes) || + config_compare(list, "ExitPolicy", CONFIG_TYPE_LINELIST, &options->ExitPolicy) || + config_compare(list, "ExcludeNodes", CONFIG_TYPE_STRING, &options->ExcludeNodes) || + + config_compare(list, "Group", CONFIG_TYPE_STRING, &options->Group) || + + config_compare(list, "IgnoreVersion", CONFIG_TYPE_BOOL, &options->IgnoreVersion) || + + config_compare(list, "KeepalivePeriod",CONFIG_TYPE_INT, &options->KeepalivePeriod) || + + config_compare(list, "LogLevel", CONFIG_TYPE_LINELIST, &options->LogOptions) || + config_compare(list, "LogFile", CONFIG_TYPE_LINELIST, &options->LogOptions) || + config_compare(list, "LinkPadding", CONFIG_TYPE_BOOL, &options->LinkPadding) || + + config_compare(list, "MaxConn", CONFIG_TYPE_INT, &options->MaxConn) || + config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) || + + config_compare(list, "Nickname", CONFIG_TYPE_STRING, &options->Nickname) || + config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) || + config_compare(list, "NumCpus", CONFIG_TYPE_INT, &options->NumCpus) || + + config_compare(list, "ORPort", CONFIG_TYPE_INT, &options->ORPort) || + config_compare(list, "ORBindAddress", CONFIG_TYPE_LINELIST, &options->ORBindAddress) || + + config_compare(list, "PidFile", CONFIG_TYPE_STRING, &options->PidFile) || + config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) || + + config_compare(list, "RouterFile", CONFIG_TYPE_STRING, &options->RouterFile) || + config_compare(list, "RunAsDaemon", CONFIG_TYPE_BOOL, &options->RunAsDaemon) || + config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) || + config_compare(list, "RendNodes", CONFIG_TYPE_STRING, &options->RendNodes) || + config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) || + + config_compare(list, "SocksPort", CONFIG_TYPE_INT, &options->SocksPort) || + config_compare(list, "SocksBindAddress",CONFIG_TYPE_LINELIST,&options->SocksBindAddress) || + config_compare(list, "SocksPolicy", CONFIG_TYPE_LINELIST,&options->SocksPolicy) || + + config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) || + + config_compare(list, "User", CONFIG_TYPE_STRING, &options->User) || + config_compare(list, "RunTesting", CONFIG_TYPE_BOOL, &options->RunTesting) || + config_compare(list, "HiddenServiceDir", CONFIG_TYPE_LINELIST, &options->RendConfigLines)|| + config_compare(list, "HiddenServicePort", CONFIG_TYPE_LINELIST, &options->RendConfigLines)|| + config_compare(list, "HiddenServiceNodes", CONFIG_TYPE_LINELIST, &options->RendConfigLines)|| + config_compare(list, "HiddenServiceExcludeNodes", CONFIG_TYPE_LINELIST, &options->RendConfigLines) + ) { + /* then we're ok. it matched something. */ + } else { + log_fn(LOG_WARN,"Unknown keyword '%s'. Failing.",list->key); + return -1; + } + + list = list->next; + } + return 0; +} + +const char default_dirservers_string[] = +"router moria1 18.244.0.188 9001 9021 9031\n" +"platform Tor 0.0.6rc1 on Linux moria.mit.edu i686\n" +"published 2004-04-25 21:54:28\n" +"bandwidth 800000 10000000\n" +"onion-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBANoIvHieyHUTzIacbnWOnyTyzGrLOdXqbcjz2GGMxyHEd5K1bO1ZBNHP\n" +"9i5qLQpN5viFk2K2rEGuG8tFgDEzSWZEtBqv3NVfUdiumdERWMBwlaQ0MVK4C+jf\n" +"y5gZ8KI3o9ZictgPS1AQF+Kk932/vIHTuRIUKb4ILTnQilNvID0NAgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"signing-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBAMHa0ZC/jo2Q2DrwKYF/6ZbmZ27PFYG91u4gUzzmZ/VXLpZ8wNzEV3oW\n" +"nt+I61048fBiC1frT1/DZ351n2bLSk9zJbB6jyGZJn0380FPRX3+cXyXS0Gq8Ril\n" +"xkhMQf5XuNFUb8UmYPSOH4WErjvYjKvU+gfjbK/82Jo9SuHpYz+BAgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"reject 0.0.0.0/255.0.0.0:*\n" +"reject 169.254.0.0/255.255.0.0:*\n" +"reject 127.0.0.0/255.0.0.0:*\n" +"reject 192.168.0.0/255.255.0.0:*\n" +"reject 10.0.0.0/255.0.0.0:*\n" +"reject 172.16.0.0/255.240.0.0:*\n" +"accept *:20-22\n" +"accept *:53\n" +"accept *:79-80\n" +"accept *:110\n" +"accept *:143\n" +"accept *:443\n" +"accept *:873\n" +"accept *:993\n" +"accept *:995\n" +"accept *:1024-65535\n" +"reject *:*\n" +"router-signature\n" +"-----BEGIN SIGNATURE-----\n" +"o1eAoRHDAEAXsnh5wN++vIwrupd+DbAJ2p3wxHDrmqxTpygzxxCnyQyhMfX03ua2\n" +"4iplyNlwyFwzWcw0sk31otlO2HBYXT1V9G0YxGtKMOeOBMHjfGbUjGvEALHzWi4z\n" +"8DXGJp13zgnUyP4ZA6xaGROwcT6oB5e7UlztvvpGxTg=\n" +"-----END SIGNATURE-----\n" +"\n" +"router moria2 18.244.0.188 9002 9022 9032\n" +"platform Tor 0.0.6rc1 on Linux moria.mit.edu i686\n" +"published 2004-04-25 21:54:30\n" +"bandwidth 800000 10000000\n" +"onion-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBAM4Cc/npgYC54XrYLC+grVxJp7PDmNO2DRRJOxKttBBtvLpnR1UaueTi\n" +"kyknT5kmlx+ihgZF/jmye//2dDUp2+kK/kSkpRV4xnDLXZmed+sNSQxqmm9TtZQ9\n" +"/hjpxhp5J9HmUTYhntBs+4E4CUKokmrI6oRLoln4SA39AX9QLPcnAgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"signing-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48\n" +"rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0\n" +"nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"reject 0.0.0.0/255.0.0.0:*\n" +"reject 169.254.0.0/255.255.0.0:*\n" +"reject 127.0.0.0/255.0.0.0:*\n" +"reject 192.168.0.0/255.255.0.0:*\n" +"reject 10.0.0.0/255.0.0.0:*\n" +"reject 172.16.0.0/255.240.0.0:*\n" +"accept *:20-22\n" +"accept *:53\n" +"accept *:79-80\n" +"accept *:110\n" +"accept *:143\n" +"accept *:443\n" +"accept *:873\n" +"accept *:993\n" +"accept *:995\n" +"accept *:1024-65535\n" +"reject *:*\n" +"router-signature\n" +"-----BEGIN SIGNATURE-----\n" +"RKROLwP1ExjTZeg6wuN0pzYqed9IJUd5lAe9hp4ritbnmJAgS6qfww6jgx61CfUR\n" +"6SElhOLE7Q77jAdoL45Ji5pn/Y+Q+E+5lJm1E/ed9ha+YsOPaOc7z6GQ7E4mihCL\n" +"gI1vsw92+P1Ty4RHj6fyD9DhbV19nh2Qs+pvGJOS2FY=\n" +"-----END SIGNATURE-----\n" +"\n" +"router tor26 62.116.124.106 9001 9050 9030\n" +"platform Tor 0.0.6 on Linux seppia i686\n" +"published 2004-05-06 21:33:23\n" +"bandwidth 500000 10000000\n" +"onion-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBAMEHdDnpj3ik1AF1xe/VqjoguH2DbANifYqXXfempu0fS+tU9FGo6dU/\n" +"fnVHAZwL9Ek9k2rMzumShi1RduK9p035R/Gk+PBBcLfvwYJ/Nat+ZO/L8jn/3bZe\n" +"ieQd9CKj2LjNGKpRNry37vkwMGIOIlegwK+2us8aXJ7sIvlNts0TAgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"signing-key\n" +"-----BEGIN RSA PUBLIC KEY-----\n" +"MIGJAoGBAMQgV2gXLbXgesWgeAsj8P1Uvm/zibrFXqwDq27lLKNgWGYGX2ax3LyT\n" +"3nzI1Y5oLs4kPKTsMM5ft9aokwf417lKoCRlZc9ptfRbgxDx90c9GtWVmkrmDvCK\n" +"ae59TMoXIiGfZiwWT6KKq5Zm9/Fu2Il3B2vHGkKJYKixmiBJRKp/AgMBAAE=\n" +"-----END RSA PUBLIC KEY-----\n" +"accept 62.245.184.24:25\n" +"accept 62.116.124.106:6666-6670\n" +"accept *:48099\n" +"reject *:*\n" +"router-signature\n" +"-----BEGIN SIGNATURE-----\n" +"qh/xRoqfLNFzPaB8VdpbdMAwRyuk5qjx4LeLVQ2pDwTZ55PqmG99+VKUNte2WTTD\n" +"7dZEA7um2rueohGe4nYmvbhJWr20/I0ZxmWDRDvFy0b5nwzDMGvLvDw95Zu/XJQ2\n" +"md32NE3y9VZCfbCN+GlvETX3fdR3Svzcm8Kzesg2/s4=\n" +"-----END SIGNATURE-----\n" +; + +int config_assign_default_dirservers(void) { + if(router_load_routerlist_from_string(default_dirservers_string, 1) < 0) { + log_fn(LOG_WARN,"Bug: the default dirservers internal string is corrupt."); + return -1; + } + return 0; +} + +/** Set options to a reasonable default. + * + * Call this function when we can't find any torrc config file. + */ +static int config_assign_defaults(or_options_t *options) { + + /* set them up as a client only */ + options->SocksPort = 9050; + + config_free_lines(options->ExitPolicy); + options->ExitPolicy = config_line_prepend(NULL, "ExitPolicy", "reject *:*"); + + /* plus give them a dirservers file */ + if(config_assign_default_dirservers() < 0) + return -1; + return 0; +} + +/** Print a usage message for tor. */ +static void print_usage(void) { + printf("tor -f [args]\n" + "See man page for more options. This -h is probably obsolete.\n\n" + "-b \t\tbytes/second rate limiting\n" + "-d \t\tDebug file\n" +// "-m \t\tMax number of connections\n" + "-l \t\tLog level\n" + "-r \t\tList of known routers\n"); + printf("\nClient options:\n" + "-e \"nick1 nick2 ...\"\t\tExit nodes\n" + "-s \t\t\tPort to bind to for Socks\n" + ); + printf("\nServer options:\n" + "-n \t\tNickname of router\n" + "-o \t\tOR port to bind to\n" + "-p \t\tPID file\n" + ); +} + +/** + * Adjust options to contain a reasonable value for Address. + */ +static int resolve_my_address(or_options_t *options) { + struct in_addr in; + struct hostent *rent; + char localhostname[256]; + int explicit_ip=1; + + if(!options->Address) { /* then we need to guess our address */ + explicit_ip = 0; /* it's implicit */ + + if(gethostname(localhostname,sizeof(localhostname)) < 0) { + log_fn(LOG_WARN,"Error obtaining local hostname"); + return -1; + } +#if 0 /* don't worry about complaining, as long as it resolves */ + if(!strchr(localhostname,'.')) { + log_fn(LOG_WARN,"fqdn '%s' has only one element. Misconfigured machine?",address); + log_fn(LOG_WARN,"Try setting the Address line in your config file."); + return -1; + } +#endif + options->Address = tor_strdup(localhostname); + log_fn(LOG_DEBUG,"Guessed local host name as '%s'",options->Address); + } + + /* now we know options->Address is set. resolve it and keep only the IP */ + + if(tor_inet_aton(options->Address, &in) == 0) { + /* then we have to resolve it */ + explicit_ip = 0; + rent = (struct hostent *)gethostbyname(options->Address); + if (!rent) { + log_fn(LOG_WARN,"Could not resolve Address %s. Failing.", options->Address); + return -1; + } + tor_assert(rent->h_length == 4); + memcpy(&in.s_addr, rent->h_addr,rent->h_length); + } + if(!explicit_ip && is_internal_IP(htonl(in.s_addr))) { + log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. " + "Please set the Address config option to be the IP you want to use.", + options->Address, inet_ntoa(in)); + return -1; + } + tor_free(options->Address); + options->Address = tor_strdup(inet_ntoa(in)); + log_fn(LOG_DEBUG,"Resolved Address to %s.", options->Address); + return 0; +} + +static char *get_default_nickname(void) +{ + char localhostname[256]; + char *cp, *out, *outp; + + if(gethostname(localhostname,sizeof(localhostname)) < 0) { + log_fn(LOG_WARN,"Error obtaining local hostname"); + return NULL; + } + /* Put it in lowercase; stop at the first dot. */ + for(cp = localhostname; *cp; ++cp) { + if (*cp == '.') { + *cp = '\0'; + break; + } + *cp = tolower(*cp); + } + /* Strip invalid characters. */ + cp = localhostname; + out = outp = tor_malloc(strlen(localhostname)+1); + while (*cp) { + if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp)) + *outp++ = *cp++; + else + cp++; + } + *outp = '\0'; + /* Enforce length. */ + if (strlen(out) > MAX_NICKNAME_LEN) + out[MAX_NICKNAME_LEN]='\0'; + + return out; +} + +/** Release storage held by options */ +static void free_options(or_options_t *options) { + config_free_lines(options->LogOptions); + tor_free(options->ContactInfo); + tor_free(options->DebugLogFile); + tor_free(options->DataDirectory); + tor_free(options->RouterFile); + tor_free(options->Nickname); + tor_free(options->Address); + tor_free(options->PidFile); + tor_free(options->ExitNodes); + tor_free(options->EntryNodes); + tor_free(options->ExcludeNodes); + tor_free(options->RendNodes); + tor_free(options->RendExcludeNodes); + tor_free(options->RecommendedVersions); + tor_free(options->User); + tor_free(options->Group); + config_free_lines(options->RendConfigLines); + config_free_lines(options->SocksBindAddress); + config_free_lines(options->ORBindAddress); + config_free_lines(options->DirBindAddress); + config_free_lines(options->ExitPolicy); + config_free_lines(options->SocksPolicy); +} + +/** Set options to hold reasonable defaults for most options. */ +static void init_options(or_options_t *options) { +/* give reasonable values for each option. Defaults to zero. */ + memset(options,0,sizeof(or_options_t)); + options->LogOptions = NULL; + options->ExitNodes = tor_strdup(""); + options->EntryNodes = tor_strdup(""); + options->ExcludeNodes = tor_strdup(""); + options->RendNodes = tor_strdup(""); + options->RendExcludeNodes = tor_strdup(""); + options->ExitPolicy = NULL; + options->SocksPolicy = NULL; + options->SocksBindAddress = NULL; + options->ORBindAddress = NULL; + options->DirBindAddress = NULL; + options->RecommendedVersions = NULL; + options->PidFile = NULL; // tor_strdup("tor.pid"); + options->DataDirectory = NULL; + options->PathlenCoinWeight = 0.3; + options->MaxConn = 900; + options->DirFetchPostPeriod = 600; + options->KeepalivePeriod = 300; + options->MaxOnionsPending = 100; + options->NewCircuitPeriod = 30; /* twice a minute */ + options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */ + options->BandwidthBurst = 10000000; /* max burst on the token bucket */ + options->NumCpus = 1; + options->RendConfigLines = NULL; +} + +/** Read a configuration file into options, finding the configuration + * file location based on the command line. After loading the options, + * validate them for consistency. Return 0 if success, <0 if failure. */ +int getconfig(int argc, char **argv, or_options_t *options) { + struct config_line_t *cl; + FILE *cf; + char *fname; + int i; + int result = 0; + static int first_load = 1; + static char **backup_argv; + static int backup_argc; + char *previous_pidfile = NULL; + int previous_runasdaemon = 0; + int previous_orport = -1; + int using_default_torrc; + + if(first_load) { /* first time we're called. save commandline args */ + backup_argv = argv; + backup_argc = argc; + first_load = 0; + } else { /* we're reloading. need to clean up old ones first. */ + argv = backup_argv; + argc = backup_argc; + + /* record some previous values, so we can fail if they change */ + if(options->PidFile) + previous_pidfile = tor_strdup(options->PidFile); + previous_runasdaemon = options->RunAsDaemon; + previous_orport = options->ORPort; + free_options(options); + } + init_options(options); + + if(argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) { + print_usage(); + exit(0); + } + + if(argc > 1 && (!strcmp(argv[1],"--version"))) { + printf("Tor version %s.\n",VERSION); + exit(0); + } + +/* learn config file name, get config lines, assign them */ + i = 1; + while(i < argc-1 && strcmp(argv[i],"-f")) { + i++; + } + if(i < argc-1) { /* we found one */ + fname = tor_strdup(argv[i+1]); + using_default_torrc = 0; + } else if (file_status(CONFDIR "/torrc")==FN_FILE) { + /* didn't find one, try CONFDIR */ + fname = tor_strdup(CONFDIR "/torrc"); + using_default_torrc = 1; + } else { + char *fn = expand_filename("~/.torrc"); + if (file_status(fn)==FN_FILE) { + fname = fn; + } else { + tor_free(fn); + fname = tor_strdup(CONFDIR "/torrc"); + } + using_default_torrc = 1; + } + log(LOG_DEBUG,"Opening config file '%s'",fname); + + cf = config_open(fname); + if(!cf) { + if(using_default_torrc == 1) { + log(LOG_NOTICE, "Configuration file '%s' not present, using reasonable defaults.",fname); + tor_free(fname); + if(config_assign_defaults(options) < 0) { + return -1; + } + } else { + log(LOG_WARN, "Unable to open configuration file '%s'.",fname); + tor_free(fname); + return -1; + } + } else { /* it opened successfully. use it. */ + tor_free(fname); + cl = config_get_lines(cf); + if(!cl) return -1; + if(config_assign(options,cl) < 0) + return -1; + config_free_lines(cl); + config_close(cf); + } + +/* go through command-line variables too */ + cl = config_get_commandlines(argc,argv); + if(config_assign(options,cl) < 0) + return -1; + config_free_lines(cl); + +/* Validate options */ + + /* first check if any of the previous options have changed but aren't allowed to */ + if(previous_pidfile && strcmp(previous_pidfile,options->PidFile)) { + log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.", + previous_pidfile, options->PidFile); + return -1; + } + tor_free(previous_pidfile); + + if(previous_runasdaemon && !options->RunAsDaemon) { + log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing."); + return -1; + } + + if(previous_orport == 0 && options->ORPort > 0) { + log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing."); + return -1; + } + + if(options->ORPort < 0) { + log(LOG_WARN,"ORPort option can't be negative."); + result = -1; + } + + if (options->Nickname == NULL) { + if (!(options->Nickname = get_default_nickname())) + return -1; + log_fn(LOG_INFO, "Choosing default nickname %s", options->Nickname); + } else { + if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) != + strlen(options->Nickname)) { + log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname); + result = -1; + } + if (strlen(options->Nickname) > MAX_NICKNAME_LEN) { + log_fn(LOG_WARN, "Nickname '%s' has more than %d characters.", + options->Nickname, MAX_NICKNAME_LEN); + result = -1; + } + } + + if(options->ORPort) { /* get an IP for ourselves */ + if(resolve_my_address(options) < 0) + result = -1; + } + + if(options->SocksPort < 0) { + log(LOG_WARN,"SocksPort option can't be negative."); + result = -1; + } + + if(options->SocksPort == 0 && options->ORPort == 0) { + log(LOG_WARN,"SocksPort and ORPort are both undefined? Quitting."); + result = -1; + } + + if(options->DirPort < 0) { + log(LOG_WARN,"DirPort option can't be negative."); + result = -1; + } + + if(options->AuthoritativeDir && options->RecommendedVersions == NULL) { + log(LOG_WARN,"Directory servers must configure RecommendedVersions."); + result = -1; + } + + if(options->AuthoritativeDir && !options->DirPort) { + log(LOG_WARN,"Running as authoritative directory, but no DirPort set."); + result = -1; + } + + if(options->AuthoritativeDir && !options->ORPort) { + log(LOG_WARN,"Running as authoritative directory, but no ORPort set."); + result = -1; + } + + if(options->AuthoritativeDir && options->ClientOnly) { + log(LOG_WARN,"Running as authoritative directory, but ClientOnly also set."); + result = -1; + } + + if(options->SocksPort >= 1 && + (options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) { + log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0."); + result = -1; + } + + if(options->MaxConn < 1) { + log(LOG_WARN,"MaxConn option must be a non-zero positive integer."); + result = -1; + } + + if(options->MaxConn >= MAXCONNECTIONS) { + log(LOG_WARN,"MaxConn option must be less than %d.", MAXCONNECTIONS); + result = -1; + } + + if(options->DirFetchPostPeriod < 1) { + log(LOG_WARN,"DirFetchPostPeriod option must be positive."); + result = -1; + } + if(options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME/2) { + log(LOG_WARN,"DirFetchPostPeriod is too large; clipping."); + options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME/2; + } + + if(options->KeepalivePeriod < 1) { + log(LOG_WARN,"KeepalivePeriod option must be positive."); + result = -1; + } + + /* XXX look at the various nicknamelists and make sure they're + * valid and don't have hostnames that are too long. + */ + + if (rend_config_services(options) < 0) { + result = -1; + } + + return result; +} + +static int add_single_log(struct config_line_t *level_opt, + struct config_line_t *file_opt, + int isDaemon) +{ + int levelMin=-1, levelMax=-1; + char *cp, *tmp_sev; + + if (level_opt) { + cp = strchr(level_opt->value, '-'); + if (cp) { + tmp_sev = tor_strndup(level_opt->value, cp - level_opt->value); + levelMin = parse_log_level(tmp_sev); + if (levelMin<0) { + log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", tmp_sev); + return -1; + } + tor_free(tmp_sev); + levelMax = parse_log_level(cp+1); + if (levelMax<0) { + log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", cp+1); + return -1; + } + } else { + levelMin = parse_log_level(level_opt->value); + if (levelMin<0) { + log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", level_opt->value); + return -1; + + } + } + } + if (levelMin < 0 && levelMax < 0) { + levelMin = LOG_NOTICE; + levelMax = LOG_ERR; + } else if (levelMin < 0) { + levelMin = levelMax; + } else { + levelMax = LOG_ERR; + } + if (file_opt) { + if (add_file_log(levelMin, levelMax, file_opt->value) < 0) { + log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value, + strerror(errno)); + return -1; + } + log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.", + file_opt->value); + } else if (!isDaemon) { + add_stream_log(levelMin, levelMax, "", stdout); + close_temp_logs(); + } + return 0; +} + +/** + * Initialize the logs based on the configuration file. + */ +int config_init_logs(or_options_t *options) +{ + /* The order of options is: Level? (File Level?)+ + */ + struct config_line_t *opt = options->LogOptions; + + /* Special case if no options are given. */ + if (!opt) { + add_stream_log(LOG_NOTICE, LOG_ERR, "", stdout); + close_temp_logs(); + /* don't return yet, in case we want to do a debuglogfile below */ + } + + /* Special case for if first option is LogLevel. */ + if (opt && !strcasecmp(opt->key, "LogLevel")) { + if (opt->next && !strcasecmp(opt->next->key, "LogFile")) { + if (add_single_log(opt, opt->next, options->RunAsDaemon)<0) + return -1; + opt = opt->next->next; + } else if (!opt->next) { + if (add_single_log(opt, NULL, options->RunAsDaemon)<0) + return -1; + opt = opt->next; + } else { + ; /* give warning below */ + } + } + + while (opt) { + if (!strcasecmp(opt->key, "LogLevel")) { + log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile"); + opt = opt->next; + } else { + tor_assert(!strcasecmp(opt->key, "LogFile")); + if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) { + /* LogFile followed by LogLevel */ + if (add_single_log(opt->next, opt, options->RunAsDaemon)<0) + return -1; + opt = opt->next->next; + } else { + /* LogFile followed by LogFile or end of list. */ + if (add_single_log(NULL, opt, options->RunAsDaemon)<0) + return -1; + opt = opt->next; + } + } + } + + if (options->DebugLogFile) { + log_fn(LOG_WARN, "DebugLogFile is deprecated; use LogFile and LogLevel instead"); + if (add_file_log(LOG_DEBUG, LOG_ERR, options->DebugLogFile)<0) + return -1; + } + return 0; +} + +void +config_parse_exit_policy(struct config_line_t *cfg, + struct exit_policy_t **dest) +{ + struct exit_policy_t **nextp; + char *e, *s; + int last=0; + char line[1024]; + + if (!cfg) + return; + nextp = dest; + while (*nextp) + nextp = &((*nextp)->next); + + for (; cfg; cfg = cfg->next) { + s = cfg->value; + for (;;) { + e = strchr(s,','); + if(!e) { + last = 1; + strncpy(line,s,1023); + } else { + memcpy(line,s, ((e-s)<1023)?(e-s):1023); + line[e-s] = 0; + } + line[1023]=0; + log_fn(LOG_DEBUG,"Adding new entry '%s'",line); + *nextp = router_parse_exit_policy_from_string(line); + if(*nextp) { + nextp = &((*nextp)->next); + } else { + log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line); + } + if (last) + break; + s = e+1; + } + } +} + +void exit_policy_free(struct exit_policy_t *p) { + struct exit_policy_t *e; + while (p) { + e = p; + p = p->next; + tor_free(e->string); + tor_free(e); + } +} + +const char *get_data_directory(or_options_t *options) { + const char *d; + if (options->DataDirectory) + d = options->DataDirectory; + else if (server_mode()) + d = "~/.tor"; + else + d = NULL; /* XXX008 don't create datadir until we have something + we'll be putting in it */ + + if (d && strncmp(d,"~/",2)==0) { + char *fn = expand_filename(d); + tor_free(options->DataDirectory); + options->DataDirectory = fn; + } + return options->DataDirectory; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/connection.c b/tags/tor-0_0_8pre1/src/or/connection.c new file mode 100644 index 0000000000..7cbdff9553 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/connection.c @@ -0,0 +1,1382 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file connection.c + * \brief General high-level functions to handle reading and writing + * on connections. + **/ + +#include "or.h" + +/********* START VARIABLES **********/ + +extern or_options_t options; /* command-line and config-file options */ +extern int shutting_down; /* whether we should refuse new connections */ + +/** Array of strings to make conn-\>type human-readable. */ +char *conn_type_to_string[] = { + "", /* 0 */ + "OP listener", /* 1 */ + "OP", /* 2 */ + "OR listener", /* 3 */ + "OR", /* 4 */ + "Exit", /* 5 */ + "App listener",/* 6 */ + "App", /* 7 */ + "Dir listener",/* 8 */ + "Dir", /* 9 */ + "DNS worker", /* 10 */ + "CPU worker", /* 11 */ +}; + +/** Array of string arrays to make {conn-\>type,conn-\>state} human-readable. */ +char *conn_state_to_string[][_CONN_TYPE_MAX+1] = { + { NULL }, /* no type associated with 0 */ + { NULL }, /* op listener, obsolete */ + { NULL }, /* op, obsolete */ + { "ready" }, /* or listener, 0 */ + { "", /* OR, 0 */ + "connect()ing", /* 1 */ + "handshaking", /* 2 */ + "open" }, /* 3 */ + { "", /* exit, 0 */ + "waiting for dest info", /* 1 */ + "connecting", /* 2 */ + "open", /* 3 */ + "resolve failed" }, /* 4 */ + { "ready" }, /* app listener, 0 */ + { "", /* 0 */ + "", /* 1 */ + "", /* 2 */ + "", /* 3 */ + "", /* 4 */ + "awaiting dest info", /* app, 5 */ + "waiting for rendezvous desc", /* 6 */ + "waiting for safe circuit", /* 7 */ + "waiting for connected", /* 8 */ + "open" }, /* 9 */ + { "ready" }, /* dir listener, 0 */ + { "", /* dir, 0 */ + "connecting", /* 1 */ + "client sending", /* 2 */ + "client reading", /* 3 */ + "awaiting command", /* 4 */ + "writing" }, /* 5 */ + { "", /* dns worker, 0 */ + "idle", /* 1 */ + "busy" }, /* 2 */ + { "", /* cpu worker, 0 */ + "idle", /* 1 */ + "busy with onion", /* 2 */ + "busy with handshake" }, /* 3 */ +}; + +/********* END VARIABLES ************/ + +static int connection_create_listener(const char *bindaddress, + uint16_t bindport, int type); +static int connection_init_accepted_conn(connection_t *conn); +static int connection_handle_listener_read(connection_t *conn, int new_type); +static int connection_receiver_bucket_should_increase(connection_t *conn); +static int connection_finished_flushing(connection_t *conn); +static int connection_finished_connecting(connection_t *conn); +static int connection_read_to_buf(connection_t *conn); +static int connection_process_inbuf(connection_t *conn); + +/**************************************************************/ + +/** Allocate space for a new connection_t. This function just initializes + * conn; you must call connection_add() to link it into the main array. + * + * Set conn-\>type to type. Set conn-\>s and conn-\>poll_index to + * -1 to signify they are not yet assigned. + * + * If conn is not a listener type, allocate buffers for it. If it's + * an AP type, allocate space to store the socks_request. + * + * Assign a pseudorandom next_circ_id between 0 and 2**15. + * + * Initialize conn's timestamps to now. + */ +connection_t *connection_new(int type) { + connection_t *conn; + time_t now = time(NULL); + + conn = tor_malloc_zero(sizeof(connection_t)); + conn->magic = CONNECTION_MAGIC; + conn->s = -1; /* give it a default of 'not used' */ + conn->poll_index = -1; /* also default to 'not used' */ + + conn->type = type; + if(!connection_is_listener(conn)) { /* listeners never use their buf */ + conn->inbuf = buf_new(); + conn->outbuf = buf_new(); + } + if (type == CONN_TYPE_AP) { + conn->socks_request = tor_malloc_zero(sizeof(socks_request_t)); + } + + conn->next_circ_id = crypto_pseudo_rand_int(1<<15); + + conn->timestamp_created = now; + conn->timestamp_lastread = now; + conn->timestamp_lastwritten = now; + + return conn; +} + +/** 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. + */ +void connection_free(connection_t *conn) { + tor_assert(conn); + tor_assert(conn->magic == CONNECTION_MAGIC); + + if(!connection_is_listener(conn)) { + buf_free(conn->inbuf); + buf_free(conn->outbuf); + } + tor_free(conn->address); + + if(connection_speaks_cells(conn)) { + directory_set_dirty(); /* XXX should only do this for an open OR conn */ + if (conn->tls) + tor_tls_free(conn->tls); + } + + if (conn->identity_pkey) + crypto_free_pk_env(conn->identity_pkey); + tor_free(conn->nickname); + tor_free(conn->socks_request); + + if(conn->s >= 0) { + log_fn(LOG_INFO,"closing fd %d.",conn->s); + tor_close_socket(conn->s); + } + memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */ + free(conn); +} + +/** Call connection_free() on every connection in our array. + * This is used by cpuworkers and dnsworkers when they fork, + * so they don't keep resources held open (especially sockets). + */ +void connection_free_all(void) { + int i, n; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;imarked_for_close); + + if(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT) { + if(!conn->has_sent_end) + log_fn(LOG_WARN,"Edge connection hasn't sent end yet? Bug."); + } + + switch(conn->type) { + case CONN_TYPE_DIR: + if(conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) + rend_client_desc_fetched(conn->rend_query, 0); + break; + case CONN_TYPE_OR: + /* Remember why we're closing this connection. */ + if (conn->state != OR_CONN_STATE_OPEN) { + /* XXX Nick: this still isn't right, because it might be + * dying even though we didn't initiate the connect. Can + * you look at this more? -RD XXXX008 -NM*/ + if(conn->nickname) + rep_hist_note_connect_failed(conn->identity_digest, time(NULL)); + } else if (0) { // XXX reason == CLOSE_REASON_UNUSED_OR_CONN) { + rep_hist_note_disconnect(conn->identity_digest, time(NULL)); + } else { + rep_hist_note_connection_died(conn->identity_digest, time(NULL)); + } + break; + case CONN_TYPE_AP: + if (conn->socks_request->has_finished == 0) { + log_fn(LOG_INFO,"Cleaning up AP -- sending socks reject."); + connection_ap_handshake_socks_reply(conn, NULL, 0, 0); + conn->socks_request->has_finished = 1; + conn->hold_open_until_flushed = 1; + } + break; + case CONN_TYPE_EXIT: + if (conn->state == EXIT_CONN_STATE_RESOLVING) { + circuit_detach_stream(circuit_get_by_conn(conn), conn); + connection_dns_remove(conn); + } + break; + case CONN_TYPE_DNSWORKER: + if (conn->state == DNSWORKER_STATE_BUSY) { + dns_cancel_pending_resolve(conn->address); + } + break; + } +} + +/** 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->s < 0) { + log_fn(LOG_WARN,"Attempt to close already-closed connection."); + return; + } + if (conn->outbuf_flushlen) { + log_fn(LOG_INFO,"fd %d, type %s, state %d, %d bytes on outbuf.", + conn->s, CONN_TYPE_TO_STRING(conn->type), + conn->state, conn->outbuf_flushlen); + } + tor_close_socket(conn->s); + conn->s = -1; + if(!connection_is_listener(conn)) { + buf_clear(conn->outbuf); + conn->outbuf_flushlen = 0; + } +} + +/** Mark conn to be closed next time we loop through + * conn_close_if_marked() in main.c. Do any cleanup needed: + * - Directory conns that fail 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. + */ +int +_connection_mark_for_close(connection_t *conn) +{ + assert_connection_ok(conn,0); + + if (conn->marked_for_close) { + log(LOG_WARN, "Double mark-for-close on connection."); + return -1; + } + + conn->marked_for_close = 1; + + /* 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); + + return 0; +} + +/** 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 main.c. + */ +void connection_expire_held_open(void) +{ + connection_t **carray, *conn; + int n, i; + time_t now; + + now = time(NULL); + + get_connection_array(&carray, &n); + for (i = 0; i < n; ++i) { + conn = carray[i]; + /* 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) { + log_fn(LOG_WARN,"Giving up on marked_for_close conn that's been flushing for 15s (fd %d, type %s, state %d).", + conn->s, CONN_TYPE_TO_STRING(conn->type), conn->state); + conn->hold_open_until_flushed = 0; + } + } + } +} + +/** Bind a new non-blocking socket listening to + * bindaddress:bindport, and add this new connection + * (of type type) to the connection array. + * + * If bindaddress includes a port, we bind on that port; otherwise, we + * use bindport. + */ +static int connection_create_listener(const char *bindaddress, uint16_t bindport, int type) { + struct sockaddr_in bindaddr; /* where to bind */ + connection_t *conn; + char *hostname, *cp; + int usePort; + int s; /* the socket we're going to make */ + int one=1; + + + cp = strchr(bindaddress, ':'); + if (cp) { + hostname = tor_strndup(bindaddress, cp-bindaddress); + usePort = atoi(cp+1); + } else { + hostname = tor_strdup(bindaddress); + usePort = bindport; + } + + memset(&bindaddr,0,sizeof(struct sockaddr_in)); + bindaddr.sin_family = AF_INET; + bindaddr.sin_port = htons((uint16_t) usePort); + if(tor_lookup_hostname(hostname, &(bindaddr.sin_addr.s_addr)) != 0) { + log_fn(LOG_WARN,"Can't resolve BindAddress %s",hostname); + tor_free(hostname); + return -1; + } + tor_free(hostname); + + s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) { + log_fn(LOG_WARN,"Socket creation failed."); + return -1; + } + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)); + + if(bind(s,(struct sockaddr *)&bindaddr,sizeof(bindaddr)) < 0) { + log_fn(LOG_WARN,"Could not bind to port %u: %s",usePort, + tor_socket_strerror(tor_socket_errno(s))); + return -1; + } + + if(listen(s,SOMAXCONN) < 0) { + log_fn(LOG_WARN,"Could not listen on port %u: %s",usePort, + tor_socket_strerror(tor_socket_errno(s))); + return -1; + } + + set_socket_nonblocking(s); + + conn = connection_new(type); + conn->s = s; + + if(connection_add(conn) < 0) { /* no space, forget it */ + log_fn(LOG_WARN,"connection_add failed. Giving up."); + connection_free(conn); + return -1; + } + + log_fn(LOG_DEBUG,"%s listening on port %u.",conn_type_to_string[type], usePort); + + conn->state = LISTENER_STATE_READY; + connection_start_reading(conn); + + 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) { + int news; /* the new socket */ + connection_t *newconn; + struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */ + int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */ + + news = accept(conn->s,(struct sockaddr *)&remote,&remotelen); + if (news == -1) { /* accept() error */ + if(ERRNO_IS_EAGAIN(tor_socket_errno(conn->s))) { + return 0; /* he hung up before we could accept(). that's fine. */ + } + /* else there was a real error. */ + log_fn(LOG_WARN,"accept() failed. Closing listener."); + connection_mark_for_close(conn); + return -1; + } + log(LOG_INFO,"Connection accepted on socket %d (child of fd %d).",news, conn->s); + + if(shutting_down && new_type != CONN_TYPE_DIR) { + /* allow directory connections even while we're shutting down */ + log(LOG_INFO,"But we're shutting down, so closing (type %d).", new_type); + tor_close_socket(news); + return 0; + } + + set_socket_nonblocking(news); + + newconn = connection_new(new_type); + newconn->s = news; + + newconn->address = tor_strdup(inet_ntoa(remote.sin_addr)); /* remember the remote address */ + newconn->addr = ntohl(remote.sin_addr.s_addr); + newconn->port = ntohs(remote.sin_port); + + 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) < 0) { + 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. + */ +static int connection_init_accepted_conn(connection_t *conn) { + + connection_start_reading(conn); + + switch(conn->type) { + case CONN_TYPE_OR: + return connection_tls_start_handshake(conn, 1); + case CONN_TYPE_AP: + /* check sockspolicy to see if we should accept it */ + if(socks_policy_permits_address(conn->addr) == 0) { + log_fn(LOG_WARN,"Denying socks connection from untrusted address %s.", conn->address); + return -1; + } + conn->state = AP_CONN_STATE_SOCKS_WAIT; + break; + case CONN_TYPE_DIR: + conn->purpose = DIR_PURPOSE_SERVER; + conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT; + break; + } + return 0; +} + +/** Take conn, make a nonblocking socket; try to connect to + * addr:port (they arrive in *host order*). If fail, return -1. 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, char *address, uint32_t addr, uint16_t port) { + int s; + struct sockaddr_in dest_addr; + + s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) { + log_fn(LOG_WARN,"Error creating network socket."); + return -1; + } + set_socket_nonblocking(s); + + memset(&dest_addr,0,sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + dest_addr.sin_addr.s_addr = htonl(addr); + + log_fn(LOG_DEBUG,"Connecting to %s:%u.",address,port); + + if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0) { + if(!ERRNO_IS_CONN_EINPROGRESS(tor_socket_errno(s))) { + /* yuck. kill it. */ + log_fn(LOG_INFO,"Connect() to %s:%u failed: %s",address,port, + tor_socket_strerror(tor_socket_errno(s))); + tor_close_socket(s); + return -1; + } else { + /* it's in progress. set state appropriately and return. */ + conn->s = s; + if(connection_add(conn) < 0) /* no space, forget it */ + return -1; + log_fn(LOG_DEBUG,"connect in progress, socket %d.",s); + return 0; + } + } + + /* it succeeded. we're connected. */ + log_fn(LOG_INFO,"Connection to %s:%u established.",address,port); + conn->s = s; + if(connection_add(conn) < 0) /* no space, forget it */ + return -1; + return 1; +} + +/** If there exist any listeners of type type in the connection + * array, mark them for close. + */ +static void listener_close_if_present(int type) { + connection_t *conn; + connection_t **carray; + int i,n; + tor_assert(type == CONN_TYPE_OR_LISTENER || + type == CONN_TYPE_AP_LISTENER || + type == CONN_TYPE_DIR_LISTENER); + get_connection_array(&carray,&n); + for(i=0;itype == type && !conn->marked_for_close) { + connection_close_immediate(conn); + connection_mark_for_close(conn); + } + } +} + +static int retry_listeners(int type, struct config_line_t *cfg, + int port_option, const char *default_addr) +{ + listener_close_if_present(type); + if (port_option) { + if (!cfg) { + if (connection_create_listener(default_addr, (uint16_t) port_option, + type)<0) + return -1; + } else { + for ( ; cfg; cfg = cfg->next) { + if (connection_create_listener(cfg->value, (uint16_t) port_option, + type)<0) + return -1; + } + } + } + return 0; +} + +/** (Re)launch listeners for each port you should have open. + */ +int retry_all_listeners(void) { + + if (retry_listeners(CONN_TYPE_OR_LISTENER, options.ORBindAddress, + options.ORPort, "0.0.0.0")<0) + return -1; + if (retry_listeners(CONN_TYPE_DIR_LISTENER, options.DirBindAddress, + options.DirPort, "0.0.0.0")<0) + return -1; + if (retry_listeners(CONN_TYPE_AP_LISTENER, options.SocksBindAddress, + options.SocksPort, "127.0.0.1")<0) + return -1; + + return 0; +} + +extern int global_read_bucket; + +/** How many bytes at most can we read onto this connection? */ +int connection_bucket_read_limit(connection_t *conn) { + int at_most; + + if(options.LinkPadding) { + at_most = global_read_bucket; + } else { + /* do a rudimentary round-robin so one circuit can't hog a connection */ + if(connection_speaks_cells(conn)) { + at_most = 32*(CELL_NETWORK_SIZE); + } else { + at_most = 32*(RELAY_PAYLOAD_SIZE); + } + + if(at_most > global_read_bucket) + at_most = global_read_bucket; + } + + if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) + if(at_most > conn->receiver_bucket) + at_most = conn->receiver_bucket; + + return at_most; +} + +/** We just read num_read onto conn. Decrement buckets appropriately. */ +void connection_bucket_decrement(connection_t *conn, int num_read) { + global_read_bucket -= num_read; tor_assert(global_read_bucket >= 0); + if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { + conn->receiver_bucket -= num_read; tor_assert(conn->receiver_bucket >= 0); + } + if(global_read_bucket == 0) { + log_fn(LOG_DEBUG,"global bucket exhausted. Pausing."); + conn->wants_to_read = 1; + connection_stop_reading(conn); + return; + } + if(connection_speaks_cells(conn) && + conn->state == OR_CONN_STATE_OPEN && + conn->receiver_bucket == 0) { + log_fn(LOG_DEBUG,"receiver bucket exhausted. Pausing."); + conn->wants_to_read = 1; + connection_stop_reading(conn); + } +} + +/** Keep a timeval to know when time has passed enough to refill buckets */ +static struct timeval current_time; + +/** Initiatialize the global read bucket to options.BandwidthBurst, + * and current_time to the current time. */ +void connection_bucket_init(void) { + tor_gettimeofday(¤t_time); + global_read_bucket = options.BandwidthBurst; /* start it at max traffic */ +} + +/** Some time has passed; increment buckets appropriately. */ +void connection_bucket_refill(struct timeval *now) { + int i, n; + connection_t *conn; + connection_t **carray; + + if(now->tv_sec <= current_time.tv_sec) + return; /* wait until the second has rolled over */ + + current_time.tv_sec = now->tv_sec; /* update current_time */ + /* (ignore usecs for now) */ + + /* refill the global bucket */ + if(global_read_bucket < options.BandwidthBurst) { + global_read_bucket += options.BandwidthRate; + log_fn(LOG_DEBUG,"global_read_bucket now %d.", global_read_bucket); + } + + /* refill the per-connection buckets */ + get_connection_array(&carray,&n); + for(i=0;ireceiver_bucket += conn->bandwidth; + //log_fn(LOG_DEBUG,"Receiver bucket %d now %d.", i, conn->receiver_bucket); + } + + if(conn->wants_to_read == 1 /* it's marked to turn reading back on now */ + && global_read_bucket > 0 /* and we're allowed to read */ + && (!connection_speaks_cells(conn) || + conn->state != OR_CONN_STATE_OPEN || + conn->receiver_bucket > 0)) { + /* and either a non-cell conn or a cell conn with non-empty bucket */ + log_fn(LOG_DEBUG,"waking up conn (fd %d)",conn->s); + conn->wants_to_read = 0; + connection_start_reading(conn); + if(conn->wants_to_write == 1) { + conn->wants_to_write = 0; + connection_start_writing(conn); + } + } + } +} + +/** Is the receiver bucket for connection conn low enough that we + * should add another pile of tokens to it? + */ +static int connection_receiver_bucket_should_increase(connection_t *conn) { + tor_assert(conn); + + if(!connection_speaks_cells(conn)) + return 0; /* edge connections don't use receiver_buckets */ + if(conn->state != OR_CONN_STATE_OPEN) + return 0; /* only open connections play the rate limiting game */ + + tor_assert(conn->bandwidth > 0); + if(conn->receiver_bucket > 9*conn->bandwidth) + return 0; + + return 1; +} + +/** Read bytes from conn->\s and process them. + * + * This function gets called from conn_read() in main.c, either + * when poll() has declared that conn wants to read, or (for OR conns) + * when there are pending TLS bytes. + * + * 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. + */ +int connection_handle_read(connection_t *conn) { + + conn->timestamp_lastread = time(NULL); + + switch(conn->type) { + case CONN_TYPE_OR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_OR); + case CONN_TYPE_AP_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_AP); + case CONN_TYPE_DIR_LISTENER: + return connection_handle_listener_read(conn, CONN_TYPE_DIR); + } + + if(connection_read_to_buf(conn) < 0) { + /* There's a read error; kill the connection.*/ + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + conn->has_sent_end = 1; /* XXX have we already sent the end? really? */ + connection_mark_for_close(conn); + if(conn->type == CONN_TYPE_DIR && + conn->state == DIR_CONN_STATE_CONNECTING) { + /* it's a directory server and connecting failed: forget about this router */ + /* XXX I suspect pollerr may make Windows not get to this point. :( */ + router_mark_as_down(conn->identity_digest); + if(conn->purpose == DIR_PURPOSE_FETCH_DIR && !all_directory_servers_down()) { + log_fn(LOG_INFO,"Giving up on dirserver %s; trying another.", conn->nickname); + directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0); + } + } + return -1; + } + if(connection_process_inbuf(conn) < 0) { +// log_fn(LOG_DEBUG,"connection_process_inbuf returned -1."); + return -1; + } + return 0; +} + +/** Pull in new bytes from conn-\>s onto conn-\>inbuf, either + * directly or via TLS. Reduce the token buckets by the number of + * bytes read. + * + * Return -1 if we want to break conn, else return 0. + */ +static int connection_read_to_buf(connection_t *conn) { + int result; + int at_most; + + /* how many bytes are we allowed to read? */ + at_most = connection_bucket_read_limit(conn); + + if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) { + if(conn->state == OR_CONN_STATE_HANDSHAKING) { + /* continue handshaking even if global token bucket is empty */ + return connection_tls_continue_handshake(conn); + } + + log_fn(LOG_DEBUG,"%d: starting, inbuf_datalen %d (%d pending in tls object). at_most %d.", + conn->s,(int)buf_datalen(conn->inbuf),tor_tls_get_pending_bytes(conn->tls), at_most); + + /* else open, or closing */ + result = read_to_buf_tls(conn->tls, at_most, conn->inbuf); + + switch(result) { + case TOR_TLS_ERROR: + case TOR_TLS_CLOSE: + log_fn(LOG_INFO,"tls error. breaking (nickname %s, address %s).", + conn->nickname ? conn->nickname : "not set", conn->address); + return -1; /* XXX deal with close better */ + case TOR_TLS_WANTWRITE: + connection_start_writing(conn); + return 0; + case TOR_TLS_WANTREAD: /* we're already reading */ + case TOR_TLS_DONE: /* no data read, so nothing to process */ + result = 0; + break; /* so we call bucket_decrement below */ + } + } else { + result = read_to_buf(conn->s, at_most, conn->inbuf, + &conn->inbuf_reached_eof); + +// log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result); + + if(result < 0) + return -1; + } + + if(result > 0 && !is_local_IP(conn->addr)) { /* remember it */ + rep_hist_note_bytes_read(result, time(NULL)); + } + + connection_bucket_decrement(conn, result); + return 0; +} + +/** A pass-through to fetch_from_buf. */ +int connection_fetch_from_buf(char *string, int len, connection_t *conn) { + return fetch_from_buf(string, len, conn->inbuf); +} + +/** 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; +} + +/** 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() in main.c + * when poll() has declared 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 no more + * more bytes on conn->outbuf, then call connection_finished_flushing + * on it too. + * + * Mark the connection and return -1 if you want to close it, else + * return 0. + */ +int connection_handle_write(connection_t *conn) { + int e, len=sizeof(e); + int result; + time_t now = time(NULL); + + tor_assert(!connection_is_listener(conn)); + + conn->timestamp_lastwritten = now; + + /* Sometimes, "writeable" means "connected". */ + if (connection_state_is_connecting(conn)) { + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { + log_fn(LOG_WARN,"getsockopt() syscall failed?! Please report to tor-ops."); + connection_close_immediate(conn); + connection_mark_for_close(conn); + return -1; + } + if(e) { + /* some sort of error, but maybe just inprogress still */ + errno = e; /* XXX008 this is a kludge. maybe we should rearrange + our error-hunting functions? E.g. pass errno to + tor_socket_errno(). */ + if(!ERRNO_IS_CONN_EINPROGRESS(tor_socket_errno(conn->s))) { + log_fn(LOG_INFO,"in-progress connect failed. Removing."); + connection_close_immediate(conn); + connection_mark_for_close(conn); + /* it's safe to pass OPs to router_mark_as_down(), since it just + * ignores unrecognized routers + */ + if (conn->type == CONN_TYPE_OR) + router_mark_as_down(conn->identity_digest); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* The connection is successful. */ + return connection_finished_connecting(conn); + } + + if (connection_speaks_cells(conn)) { + if (conn->state == OR_CONN_STATE_HANDSHAKING) { + connection_stop_writing(conn); + if(connection_tls_continue_handshake(conn) < 0) { + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + connection_mark_for_close(conn); + return -1; + } + return 0; + } + + /* else open, or closing */ + result = flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen); + switch(result) { + case TOR_TLS_ERROR: + case TOR_TLS_CLOSE: + log_fn(LOG_INFO,"tls error. breaking."); + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + connection_mark_for_close(conn); + return -1; /* XXX deal with close better */ + case TOR_TLS_WANTWRITE: + log_fn(LOG_DEBUG,"wanted write."); + /* we're already writing */ + return 0; + case TOR_TLS_WANTREAD: + /* Make sure to avoid a loop if the receive buckets are empty. */ + log_fn(LOG_DEBUG,"wanted read."); + if(!connection_is_reading(conn)) { + connection_stop_writing(conn); + conn->wants_to_write = 1; + /* we'll start reading again when the next second arrives, + * and then also start writing again. + */ + } + /* 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. + */ + } + } else { + result = flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); + if (result < 0) { + connection_close_immediate(conn); /* Don't flush; connection is dead. */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + return -1; + } + } + + if(result > 0 && !is_local_IP(conn->addr)) { /* remember it */ + rep_hist_note_bytes_written(result, now); + } + + if(!connection_wants_to_flush(conn)) { /* it's done flushing */ + if(connection_finished_flushing(conn) < 0) { + /* already marked */ + return -1; + } + } + + return 0; +} + +/** Append len bytes of string onto conn's + * outbuf, and ask it to start writing. + */ +void connection_write_to_buf(const char *string, int len, connection_t *conn) { + + if(!len || conn->marked_for_close) + return; + + if(write_to_buf(string, len, conn->outbuf) < 0) { + if(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT) { + /* if it failed, it means we have our package/delivery windows set + wrong compared to our max outbuf size. close the whole circuit. */ + log_fn(LOG_WARN,"write_to_buf failed. Closing circuit (fd %d).", conn->s); + circuit_mark_for_close(circuit_get_by_conn(conn)); + } else { + log_fn(LOG_WARN,"write_to_buf failed. Closing connection (fd %d).", conn->s); + connection_mark_for_close(conn); + } + return; + } + + connection_start_writing(conn); + conn->outbuf_flushlen += len; +} + +/** Return the conn to addr/port that has the most recent + * timestamp_created, or NULL if no such conn exists. */ +connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port) { + int i, n; + connection_t *conn, *best=NULL; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;iaddr == addr && conn->port == port && !conn->marked_for_close && + (!best || best->timestamp_created < conn->timestamp_created)) + best = conn; + } + return best; +} + +/** Find a connection to the router described by addr and port, + * or alternately any router with the same identity key. + * This connection must be in an "open" state. + * If not, return NULL. + */ +/* XXX this twin thing is busted, now that we're rotating onion + * keys. abandon/patch? */ +connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) { + int i, n; + connection_t *conn; + routerinfo_t *router; + connection_t **carray; + + /* first check if it's there exactly */ + conn = connection_exact_get_by_addr_port(addr,port); + if(conn && connection_state_is_open(conn)) { + log(LOG_DEBUG,"connection_twin_get_by_addr_port(): Found exact match."); + return conn; + } + + /* now check if any of the other open connections are a twin for this one */ + + router = router_get_by_addr_port(addr,port); + if(!router) + return NULL; + + get_connection_array(&carray,&n); + for(i=0;iidentity_pkey, router->identity_pkey)) { + log(LOG_DEBUG,"connection_twin_get_by_addr_port(): Found twin (%s).",conn->address); + return conn; + } + } + return NULL; +} + +connection_t *connection_get_by_identity_digest(const char *digest, int type) +{ + int i, n; + connection_t *conn, *best=NULL; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype != type) + continue; + if (!memcmp(conn->identity_digest, digest, DIGEST_LEN) + && !conn->marked_for_close + && (!best || best->timestamp_created < conn->timestamp_created)) + best = conn; + } + return best; +} + +/** Return a connection of type type that is not marked for + * close. + */ +connection_t *connection_get_by_type(int type) { + int i, n; + connection_t *conn; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype == 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) { + int i, n; + connection_t *conn; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype == type && conn->state == state && !conn->marked_for_close) + return conn; + } + return NULL; +} + +/** Return the connection of type type that is in state + * state, that was written to least recently, and that is not + * marked for close. + */ +connection_t *connection_get_by_type_state_lastwritten(int type, int state) { + int i, n; + connection_t *conn, *best=NULL; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype == type && conn->state == state && !conn->marked_for_close) + if(!best || conn->timestamp_lastwritten < best->timestamp_lastwritten) + best = conn; + } + return best; +} + +/** Return a connection of type type that has rendquery equal + * to rendquery, and that is not marked for close. + */ +connection_t *connection_get_by_type_rendquery(int type, const char *rendquery) { + int i, n; + connection_t *conn; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype == type && + !conn->marked_for_close && + !rend_cmp_service_ids(rendquery, conn->rend_query)) + 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_AP_LISTENER || + conn->type == CONN_TYPE_DIR_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_AP && conn->state == AP_CONN_STATE_OPEN) || + (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_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; +} + +/** Write a destroy cell with circ ID circ_id onto OR connection + * conn. + * + * Return 0. + */ +int connection_send_destroy(uint16_t circ_id, connection_t *conn) { + cell_t cell; + + tor_assert(conn); + tor_assert(connection_speaks_cells(conn)); + + memset(&cell, 0, sizeof(cell_t)); + cell.circ_id = circ_id; + cell.command = CELL_DESTROY; + log_fn(LOG_INFO,"Sending destroy (circID %d).", circ_id); + connection_or_write_cell_to_buf(&cell, conn); + return 0; +} + +/** Process new bytes that have arrived on conn-\>inbuf. + * + * This function just passes conn to the connection-specific + * connection_*_process_inbuf() function. + */ +static int connection_process_inbuf(connection_t *conn) { + + tor_assert(conn); + + switch(conn->type) { + case CONN_TYPE_OR: + return connection_or_process_inbuf(conn); + case CONN_TYPE_EXIT: + case CONN_TYPE_AP: + return connection_edge_process_inbuf(conn); + case CONN_TYPE_DIR: + return connection_dir_process_inbuf(conn); + case CONN_TYPE_DNSWORKER: + return connection_dns_process_inbuf(conn); + case CONN_TYPE_CPUWORKER: + return connection_cpu_process_inbuf(conn); + default: + log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type); + return -1; + } +} + +/** We just finished flushing bytes from conn-\>outbuf, and there + * are no more bytes remaining. + * + * This function just passes conn to the connection-specific + * connection_*_finished_flushing() function. + */ +static int connection_finished_flushing(connection_t *conn) { + + tor_assert(conn); + +// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s); + + switch(conn->type) { + case CONN_TYPE_OR: + return connection_or_finished_flushing(conn); + case CONN_TYPE_AP: + case CONN_TYPE_EXIT: + return connection_edge_finished_flushing(conn); + case CONN_TYPE_DIR: + return connection_dir_finished_flushing(conn); + case CONN_TYPE_DNSWORKER: + return connection_dns_finished_flushing(conn); + case CONN_TYPE_CPUWORKER: + return connection_cpu_finished_flushing(conn); + default: + log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type); + 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); + switch (conn->type) + { + case CONN_TYPE_OR: + return connection_or_finished_connecting(conn); + case CONN_TYPE_EXIT: + return connection_edge_finished_connecting(conn); + case CONN_TYPE_DIR: + return connection_dir_finished_connecting(conn); + default: + tor_assert(0); + return -1; + } +} + +/** 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) +{ + tor_assert(conn); + tor_assert(conn->magic == CONNECTION_MAGIC); + tor_assert(conn->type >= _CONN_TYPE_MIN); + tor_assert(conn->type <= _CONN_TYPE_MAX); + + if(conn->outbuf_flushlen > 0) { + tor_assert(connection_is_writing(conn) || conn->wants_to_write); + } + + if(conn->hold_open_until_flushed) + tor_assert(conn->marked_for_close); + + /* XXX check: wants_to_read, wants_to_write, s, poll_index, + * marked_for_close. */ + + /* buffers */ + if (!connection_is_listener(conn)) { + assert_buf_ok(conn->inbuf); + assert_buf_ok(conn->outbuf); + } + +#if 0 /* computers often go back in time; no way to know */ + tor_assert(!now || conn->timestamp_lastread <= now); + tor_assert(!now || conn->timestamp_lastwritten <= now); + tor_assert(conn->timestamp_created <= conn->timestamp_lastread); + tor_assert(conn->timestamp_created <= conn->timestamp_lastwritten); +#endif + + /* XXX Fix this; no longer so.*/ +#if 0 + if(conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) + tor_assert(!conn->pkey); + /* pkey is set if we're a dir client, or if we're an OR in state OPEN + * connected to another OR. + */ +#endif + + if (conn->type != CONN_TYPE_OR) { + tor_assert(!conn->tls); + } else { + 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->receiver_bucket >= 0); + } + tor_assert(conn->addr && conn->port); + tor_assert(conn->address); + if (conn->state != OR_CONN_STATE_CONNECTING) + tor_assert(conn->tls); + } + + if (conn->type != CONN_TYPE_EXIT && conn->type != CONN_TYPE_AP) { + tor_assert(!conn->stream_id); + tor_assert(!conn->next_stream); + tor_assert(!conn->cpath_layer); + tor_assert(!conn->package_window); + tor_assert(!conn->deliver_window); + tor_assert(!conn->done_sending); + tor_assert(!conn->done_receiving); + } else { + /* XXX unchecked: package window, deliver window. */ + } + if (conn->type == CONN_TYPE_AP) { + tor_assert(conn->socks_request); + if (conn->state == AP_CONN_STATE_OPEN) { + tor_assert(conn->socks_request->has_finished); + tor_assert(conn->cpath_layer); + assert_cpath_layer_ok(conn->cpath_layer); + } + } else { + tor_assert(!conn->socks_request); + } + 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) { + tor_assert(!conn->purpose); /* only used for dir types currently */ + } + + switch(conn->type) + { + case CONN_TYPE_OR_LISTENER: + case CONN_TYPE_AP_LISTENER: + case CONN_TYPE_DIR_LISTENER: + tor_assert(conn->state == LISTENER_STATE_READY); + break; + case CONN_TYPE_OR: + tor_assert(conn->state >= _OR_CONN_STATE_MIN && + conn->state <= _OR_CONN_STATE_MAX); + break; + case CONN_TYPE_EXIT: + tor_assert(conn->state >= _EXIT_CONN_STATE_MIN && + conn->state <= _EXIT_CONN_STATE_MAX); + break; + case CONN_TYPE_AP: + tor_assert(conn->state >= _AP_CONN_STATE_MIN && + conn->state <= _AP_CONN_STATE_MAX); + tor_assert(conn->socks_request); + break; + case CONN_TYPE_DIR: + tor_assert(conn->state >= _DIR_CONN_STATE_MIN && + conn->state <= _DIR_CONN_STATE_MAX); + tor_assert(conn->purpose >= _DIR_PURPOSE_MIN && + conn->purpose <= _DIR_PURPOSE_MAX); + break; + case CONN_TYPE_DNSWORKER: + tor_assert(conn->state == DNSWORKER_STATE_IDLE || + conn->state == DNSWORKER_STATE_BUSY); + break; + case CONN_TYPE_CPUWORKER: + tor_assert(conn->state >= _CPUWORKER_STATE_MIN && + conn->state <= _CPUWORKER_STATE_MAX); + break; + default: + tor_assert(0); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/connection_edge.c b/tags/tor-0_0_8pre1/src/or/connection_edge.c new file mode 100644 index 0000000000..52e578da8e --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/connection_edge.c @@ -0,0 +1,1152 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file connection_edge.c + * \brief Handle edge streams. + **/ + +#include "or.h" +#include "tree.h" + +extern or_options_t options; /* command-line and config-file options */ +extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; /* from connection.c */ + +static struct exit_policy_t *socks_policy = NULL; + +static int connection_ap_handshake_process_socks(connection_t *conn); +static void parse_socks_policy(void); + +/** Handle new bytes on conn->inbuf, or notification of eof. + * + * If there was an EOF, then send an end and mark the connection + * for close. + * + * Otherwise handle it 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 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(connection_t *conn) { + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT); + + if(conn->inbuf_reached_eof) { +#ifdef HALF_OPEN + /* eof reached; we're done reading, but we might want to write more. */ + conn->done_receiving = 1; + shutdown(conn->s, 0); /* XXX check return, refactor NM */ + if (conn->done_sending) { + connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer); + connection_mark_for_close(conn); + } else { + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_END, + NULL, 0, conn->cpath_layer); + } + return 0; +#else + /* eof reached, kill it. */ + log_fn(LOG_INFO,"conn (fd %d) reached eof. Closing.", conn->s); + connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer); + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; /* just because we shouldn't read + doesn't mean we shouldn't write */ + return 0; +#endif + } + + switch(conn->state) { + case AP_CONN_STATE_SOCKS_WAIT: + if(connection_ap_handshake_process_socks(conn) < 0) { + conn->has_sent_end = 1; /* no circ yet */ + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; + return -1; + } + return 0; + case AP_CONN_STATE_OPEN: + case EXIT_CONN_STATE_OPEN: + if(conn->package_window <= 0) { + /* XXX this is still getting called rarely :( */ + log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); + return 0; + } + if(connection_edge_package_raw_inbuf(conn) < 0) { + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return -1; + } + return 0; + case EXIT_CONN_STATE_CONNECTING: + case AP_CONN_STATE_RENDDESC_WAIT: + case AP_CONN_STATE_CIRCUIT_WAIT: + case AP_CONN_STATE_CONNECT_WAIT: + log_fn(LOG_INFO,"data from edge while in '%s' state. Leaving it on buffer.", + conn_state_to_string[conn->type][conn->state]); + return 0; + } + log_fn(LOG_WARN,"Got unexpected state %d. Closing.",conn->state); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(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(uint16_t circ_id, connection_t *conn) { + tor_assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT); + + if(conn->marked_for_close) + return 0; /* already marked; probably got an 'end' */ + log_fn(LOG_INFO,"CircID %d: At an edge. Marking connection for close.", + circ_id); + conn->has_sent_end = 1; /* we're closing the circuit, nothing to send to */ + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; + return 0; +} + +/** Send a relay end cell from stream conn to conn's circuit, + * with a destination of cpath_layer. (If cpath_layer is NULL, the + * destination is the circuit's origin.) Mark the relay end cell as + * closing because of reason. + * + * Return -1 if this function has already been called on this conn, + * else return 0. + */ +int +connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer) +{ + char payload[5]; + int payload_len=1; + circuit_t *circ; + + if(conn->has_sent_end) { + log_fn(LOG_WARN,"It appears I've already sent the end. Are you calling me twice?"); + return -1; + } + + payload[0] = reason; + if(reason == END_STREAM_REASON_EXITPOLICY) { + /* this is safe even for rend circs, because they never fail + * because of exitpolicy */ + set_uint32(payload+1, htonl(conn->addr)); + payload_len += 4; + } + + circ = circuit_get_by_conn(conn); + if(circ && !circ->marked_for_close) { + log_fn(LOG_DEBUG,"Marking conn (fd %d) and sending end.",conn->s); + connection_edge_send_command(conn, circ, RELAY_COMMAND_END, + payload, payload_len, cpath_layer); + } else { + log_fn(LOG_DEBUG,"Marking conn (fd %d); no circ to send end.",conn->s); + } + + conn->has_sent_end = 1; + 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(connection_t *conn) { + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT); + + switch(conn->state) { + case AP_CONN_STATE_OPEN: + case EXIT_CONN_STATE_OPEN: + connection_stop_writing(conn); + connection_edge_consider_sending_sendme(conn); + return 0; + case AP_CONN_STATE_SOCKS_WAIT: + case AP_CONN_STATE_RENDDESC_WAIT: + case AP_CONN_STATE_CIRCUIT_WAIT: + case AP_CONN_STATE_CONNECT_WAIT: + connection_stop_writing(conn); + return 0; + default: + log_fn(LOG_WARN,"BUG: called in unexpected state %d.", conn->state); + return -1; + } + return 0; +} + +/** 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(connection_t *conn) +{ + unsigned char connected_payload[4]; + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_EXIT); + tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING); + + + log_fn(LOG_INFO,"Exit connection to %s:%u established.", + conn->address,conn->port); + + conn->state = EXIT_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); /* stop writing, continue reading */ + if(connection_wants_to_flush(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(conn)) { + if(connection_edge_send_command(conn, circuit_get_by_conn(conn), + RELAY_COMMAND_CONNECTED, NULL, 0, conn->cpath_layer) < 0) + return 0; /* circuit is closed, don't continue */ + } else { + *(uint32_t*)connected_payload = htonl(conn->addr); + if(connection_edge_send_command(conn, circuit_get_by_conn(conn), + RELAY_COMMAND_CONNECTED, connected_payload, 4, conn->cpath_layer) < 0) + return 0; /* circuit is closed, don't continue */ + } + tor_assert(conn->package_window > 0); + return connection_edge_process_inbuf(conn); /* in case the server has written anything */ +} + +/** How many times do we retry a general-purpose stream (detach it from + * one circuit and try another, after we wait a while with no 'connected' + * cell) before giving up? + */ +#define MAX_STREAM_RETRIES 4 + +/** Find all general-purpose AP streams in state connect_wait that sent + * their begin cell >=15 seconds 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 45 seconds (with no + * retry attempt). + */ +void connection_ap_expire_beginning(void) { + connection_t **carray; + connection_t *conn; + circuit_t *circ; + int n, i; + time_t now = time(NULL); + + get_connection_array(&carray, &n); + + for (i = 0; i < n; ++i) { + conn = carray[i]; + if (conn->type != CONN_TYPE_AP || + conn->state != AP_CONN_STATE_CONNECT_WAIT) + continue; + if (now - conn->timestamp_lastread < 15) + continue; + conn->num_retries++; + circ = circuit_get_by_conn(conn); + if(!circ) { /* it's vanished? */ + log_fn(LOG_INFO,"Conn is in connect-wait, but lost its circ."); + connection_mark_for_close(conn); + continue; + } + if(circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { + if (now - conn->timestamp_lastread > 45) { + log_fn(LOG_WARN,"Rend stream is %d seconds late. Giving up.", + (int)(now - conn->timestamp_lastread)); + connection_edge_end(conn, END_STREAM_REASON_TIMEOUT, conn->cpath_layer); + connection_mark_for_close(conn); + } + continue; + } + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL); + if(conn->num_retries >= MAX_STREAM_RETRIES) { + log_fn(LOG_WARN,"Stream is %d seconds late. Giving up.", + 15*conn->num_retries); + circuit_log_path(LOG_WARN, circ); + connection_edge_end(conn, END_STREAM_REASON_TIMEOUT, conn->cpath_layer); + connection_mark_for_close(conn); + } else { + log_fn(LOG_WARN,"Stream is %d seconds late. Retrying.", + (int)(now - conn->timestamp_lastread)); + circuit_log_path(LOG_WARN, circ); + /* send an end down the circuit */ + connection_edge_end(conn, END_STREAM_REASON_TIMEOUT, conn->cpath_layer); + /* un-mark it as ending, since we're going to reuse it */ + conn->has_sent_end = 0; + /* move it back into 'pending' state. */ + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + circuit_detach_stream(circ, conn); + /* kludge to make us not try this circuit again, yet to allow + * current streams on it to survive if they can: make it + * unattractive to use for new streams */ + tor_assert(circ->timestamp_dirty); + circ->timestamp_dirty -= options.NewCircuitPeriod; + /* give our stream another 15 seconds to try */ + conn->timestamp_lastread += 15; + /* attaching to a dirty circuit is fine */ + if(connection_ap_handshake_attach_circuit(conn)<0) { + /* it will never work */ + /* Don't need to send end -- we're not connected */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + } + } /* end if max_retries */ + } /* end for */ +} + +/** Tell any AP streamss that are waiting for a new circuit that one is + * available. + */ +void connection_ap_attach_pending(void) +{ + connection_t **carray; + connection_t *conn; + int n, i; + + get_connection_array(&carray, &n); + + for (i = 0; i < n; ++i) { + conn = carray[i]; + if (conn->marked_for_close || + conn->type != CONN_TYPE_AP || + conn->state != AP_CONN_STATE_CIRCUIT_WAIT) + continue; + if(connection_ap_handshake_attach_circuit(conn) < 0) { + /* -1 means it will never work */ + /* Don't send end; there is no 'other side' yet */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + } + } +} + +/** 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, and it's for a general circuit, then + * try to attach it to a circuit (or launch one as needed). If it's for + * a rendezvous circuit, then fetch a rendezvous descriptor first (or + * attach/launch a circuit if the rendezvous descriptor is already here + * and fresh enough). + * + * Return -1 if an unexpected error with conn (and it should be marked + * for close), else return 0. + */ +static int connection_ap_handshake_process_socks(connection_t *conn) { + socks_request_t *socks; + int sockshere; + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP); + tor_assert(conn->state == AP_CONN_STATE_SOCKS_WAIT); + tor_assert(conn->socks_request); + socks = conn->socks_request; + + log_fn(LOG_DEBUG,"entered."); + + sockshere = fetch_from_buf_socks(conn->inbuf, socks); + if(sockshere == -1 || sockshere == 0) { + if(socks->replylen) { /* we should send reply back */ + log_fn(LOG_DEBUG,"reply is already set for us. Using it."); + connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen, 0); + } else if(sockshere == -1) { /* send normal reject */ + log_fn(LOG_WARN,"Fetching socks handshake failed. Closing."); + connection_ap_handshake_socks_reply(conn, NULL, 0, 0); + } else { + log_fn(LOG_DEBUG,"socks handshake not all here yet."); + } + if (sockshere == -1) + socks->has_finished = 1; + return sockshere; + } /* else socks handshake is done, continue processing */ + + if (socks->command == SOCKS_COMMAND_RESOLVE) { + uint32_t answer; + /* Reply to resolves immediately if we can. */ + if (strlen(socks->address) > RELAY_PAYLOAD_SIZE) { + connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,0,NULL); + conn->socks_request->has_finished = 1; + connection_mark_for_close(conn); + return 0; + } + answer = htonl(client_dns_lookup_entry(socks->address)); + if (answer) { + connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV4,4, + (char*)&answer); + conn->socks_request->has_finished = 1; + connection_mark_for_close(conn); + return 0; + } + } + + /* this call _modifies_ socks->address iff it's a hidden-service request */ + if (rend_parse_rendezvous_address(socks->address) < 0) { + /* normal request */ + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + return connection_ap_handshake_attach_circuit(conn); + } else { + /* it's a hidden-service request */ + /* XXX008 what does it mean to socks-resolve a hidden service? should + * we fail those right here? */ + rend_cache_entry_t *entry; + int r; + + strcpy(conn->rend_query, socks->address); /* this strcpy is safe -RD */ + log_fn(LOG_INFO,"Got a hidden service request for ID '%s'", conn->rend_query); + /* see if we already have it cached */ + r = rend_cache_lookup_entry(conn->rend_query, &entry); + if(r<0) { + log_fn(LOG_WARN,"Invalid service descriptor %s", conn->rend_query); + return -1; + } + if(r==0) { + conn->state = AP_CONN_STATE_RENDDESC_WAIT; + log_fn(LOG_INFO, "Unknown descriptor %s. Fetching.", conn->rend_query); + rend_client_refetch_renddesc(conn->rend_query); + return 0; + } + if(r>0) { +#define NUM_SECONDS_BEFORE_REFETCH (60*15) + if(time(NULL) - entry->received < NUM_SECONDS_BEFORE_REFETCH) { + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + log_fn(LOG_INFO, "Descriptor is here and fresh enough. Great."); + return connection_ap_handshake_attach_circuit(conn); + } else { + conn->state = AP_CONN_STATE_RENDDESC_WAIT; + log_fn(LOG_INFO, "Stale descriptor %s. Refetching.", conn->rend_query); + rend_client_refetch_renddesc(conn->rend_query); + return 0; + } + } + } + return 0; +} + +/** 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. + */ +static uint16_t get_unique_stream_id_by_circ(circuit_t *circ) { + connection_t *tmpconn; + uint16_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_fn(LOG_WARN,"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; +} + +/** 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(connection_t *ap_conn, circuit_t *circ) +{ + char payload[CELL_PAYLOAD_SIZE]; + int payload_len; + struct in_addr in; + const char *string_addr; + + tor_assert(ap_conn->type == CONN_TYPE_AP); + tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(ap_conn->socks_request); + + ap_conn->stream_id = get_unique_stream_id_by_circ(circ); + if (ap_conn->stream_id==0) { + /* Don't send end: there is no 'other side' yet */ + ap_conn->has_sent_end = 1; + connection_mark_for_close(ap_conn); + circuit_mark_for_close(circ); + return -1; + } + + if(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { + in.s_addr = htonl(client_dns_lookup_entry(ap_conn->socks_request->address)); + string_addr = in.s_addr ? inet_ntoa(in) : NULL; + + snprintf(payload,RELAY_PAYLOAD_SIZE, + "%s:%d", + string_addr ? string_addr : ap_conn->socks_request->address, + ap_conn->socks_request->port); + } else { + snprintf(payload,RELAY_PAYLOAD_SIZE, + ":%d", ap_conn->socks_request->port); + } + payload_len = strlen(payload)+1; + + log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",ap_conn->stream_id); + + if(connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_BEGIN, + payload, payload_len, ap_conn->cpath_layer) < 0) + return -1; /* circuit is closed, don't continue */ + + ap_conn->package_window = STREAMWINDOW_START; + ap_conn->deliver_window = STREAMWINDOW_START; + ap_conn->state = AP_CONN_STATE_CONNECT_WAIT; + log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id); + 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(connection_t *ap_conn, circuit_t *circ) +{ + int payload_len; + const char *string_addr; + + tor_assert(ap_conn->type == CONN_TYPE_AP); + tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(ap_conn->socks_request); + tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE); + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL); + + ap_conn->stream_id = get_unique_stream_id_by_circ(circ); + if (ap_conn->stream_id==0) { + /* Don't send end: there is no 'other side' yet */ + ap_conn->has_sent_end = 1; + connection_mark_for_close(ap_conn); + circuit_mark_for_close(circ); + return -1; + } + + string_addr = ap_conn->socks_request->address; + payload_len = strlen(string_addr); + tor_assert(strlen(string_addr) <= RELAY_PAYLOAD_SIZE); + + log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",ap_conn->stream_id); + + if(connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_RESOLVE, + string_addr, payload_len, ap_conn->cpath_layer) < 0) + return -1; /* circuit is closed, don't continue */ + + ap_conn->state = AP_CONN_STATE_RESOLVE_WAIT; + log_fn(LOG_INFO,"Address sent for resolve, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id); + return 0; +} + +/** Make an AP connection_t, do a socketpair 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 other end of the socketpair, or -1 if error. + */ +int connection_ap_make_bridge(char *address, uint16_t port) { + int fd[2]; + connection_t *conn; + + log_fn(LOG_INFO,"Making AP bridge to %s:%d ...",address,port); + + if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { + log(LOG_WARN,"Couldn't construct socketpair (%s). Network down? Delaying.", + tor_socket_strerror(tor_socket_errno(-1))); + return -1; + } + + set_socket_nonblocking(fd[0]); + set_socket_nonblocking(fd[1]); + + conn = connection_new(CONN_TYPE_AP); + conn->s = fd[0]; + + /* 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' */ + strcpy(conn->socks_request->address, address); + conn->socks_request->port = port; + conn->socks_request->command = SOCKS_COMMAND_CONNECT; + + conn->address = tor_strdup("(local bridge)"); + conn->addr = ntohs(0); + conn->port = 0; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); /* this closes fd[0] */ + tor_close_socket(fd[1]); + return -1; + } + + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + connection_start_reading(conn); + + /* attaching to a dirty circuit is fine */ + if (connection_ap_handshake_attach_circuit(conn) < 0) { + conn->has_sent_end = 1; /* no circ to send to */ + connection_mark_for_close(conn); + tor_close_socket(fd[1]); + return -1; + } + + log_fn(LOG_INFO,"... AP bridge created and connected."); + return fd[1]; +} + +void connection_ap_handshake_socks_resolved(connection_t *conn, + int answer_type, + int answer_len, + const char *answer) +{ + char buf[256]; + int replylen; + + if (answer_type == RESOLVED_TYPE_IPV4) { + uint32_t a = get_uint32(answer); + client_dns_set_entry(conn->socks_request->address, ntohl(a)); + } + + if (conn->socks_request->socks_version == 4) { + buf[0] = 0x00; /* version */ + if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { + buf[1] = 90; /* "Granted" */ + set_uint16(buf+2, 0); + memcpy(buf+4, answer, 4); /* address */ + replylen = SOCKS4_NETWORK_LEN; + } else { + buf[1] = 91; /* "error" */ + memset(buf+2, 0, 6); + replylen = SOCKS4_NETWORK_LEN; + } + } else { + /* SOCKS5 */ + buf[0] = 0x05; /* version */ + if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { + buf[1] = 0; /* 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] = 0; /* 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 { + buf[1] = 0x04; /* host unreachable */ + memset(buf+2, 0, 8); + replylen = 10; + } + } + connection_ap_handshake_socks_reply(conn, buf, replylen, + answer_type == RESOLVED_TYPE_IPV4 || + answer_type == RESOLVED_TYPE_IPV6); +} + +/** Send a socks reply to stream conn, using the appropriate + * socks version, etc. + * + * If reply is defined, then write replylen bytes of it + * to conn and return. + * + * Otherwise, send back a reply based on whether success is 1 or 0. + */ +void connection_ap_handshake_socks_reply(connection_t *conn, char *reply, + int replylen, char success) { + char buf[256]; + + if(replylen) { /* we already have a reply in mind */ + connection_write_to_buf(reply, replylen, conn); + return; + } + tor_assert(conn->socks_request); + if(conn->socks_request->socks_version == 4) { + memset(buf,0,SOCKS4_NETWORK_LEN); +#define SOCKS4_GRANTED 90 +#define SOCKS4_REJECT 91 + buf[1] = (success ? SOCKS4_GRANTED : SOCKS4_REJECT); + /* leave version, destport, destip zero */ + connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, conn); + } + if(conn->socks_request->socks_version == 5) { + buf[0] = 5; /* version 5 */ +#define SOCKS5_SUCCESS 0 +#define SOCKS5_GENERIC_ERROR 1 + buf[1] = success ? SOCKS5_SUCCESS : SOCKS5_GENERIC_ERROR; + 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,conn); + } + /* If socks_version isn't 4 or 5, don't send anything. + * This can happen in the case of AP bridges. */ + return; +} + +/** A relay 'begin' 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 -1 if we want to tear down circ. Else return 0. + */ +int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { + connection_t *n_stream; + relay_header_t rh; + char *colon; + + assert_circuit_ok(circ); + relay_header_unpack(&rh, cell->payload); + + /* XXX currently we don't send an end cell back if we drop the + * begin because it's malformed. + */ + + if(!memchr(cell->payload+RELAY_HEADER_SIZE, 0, rh.length)) { + log_fn(LOG_WARN,"relay begin cell has no \\0. Dropping."); + return 0; + } + colon = strchr(cell->payload+RELAY_HEADER_SIZE, ':'); + if(!colon) { + log_fn(LOG_WARN,"relay begin cell has no colon. Dropping."); + return 0; + } + *colon = 0; + + if(!atoi(colon+1)) { /* bad port */ + log_fn(LOG_WARN,"relay begin cell has invalid port. Dropping."); + return 0; + } + + log_fn(LOG_DEBUG,"Creating new exit connection."); + n_stream = connection_new(CONN_TYPE_EXIT); + n_stream->purpose = EXIT_PURPOSE_CONNECT; + + n_stream->stream_id = rh.stream_id; + n_stream->port = atoi(colon+1); + /* 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) { + log_fn(LOG_DEBUG,"begin is for rendezvous. configuring stream."); + n_stream->address = tor_strdup("(rendezvous)"); + n_stream->state = EXIT_CONN_STATE_CONNECTING; + strcpy(n_stream->rend_query, circ->rend_query); + tor_assert(n_stream->rend_query[0]); + assert_circuit_ok(circ); + if(rend_service_set_connection_addr_port(n_stream, circ) < 0) { + log_fn(LOG_INFO,"Didn't find rendezvous service (port %d)",n_stream->port); + connection_edge_end(n_stream, END_STREAM_REASON_EXITPOLICY, n_stream->cpath_layer); + connection_free(n_stream); + circuit_mark_for_close(circ); /* knock the whole thing down, somebody screwed up */ + return 0; + } + assert_circuit_ok(circ); + log_fn(LOG_DEBUG,"Finished assigning addr/port"); + n_stream->cpath_layer = circ->cpath->prev; /* link it */ + + /* add it into the linked list of n_streams on this circuit */ + n_stream->next_stream = circ->n_streams; + circ->n_streams = n_stream; + assert_circuit_ok(circ); + + connection_exit_connect(n_stream); + return 0; + } + n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE); + n_stream->state = EXIT_CONN_STATE_RESOLVEFAILED; + /* default to failed, change in dns_resolve if it turns out not to fail */ + + /* send it off to the gethostbyname farm */ + switch(dns_resolve(n_stream)) { + case 1: /* resolve worked */ + + /* add it into the linked list of n_streams on this circuit */ + n_stream->next_stream = circ->n_streams; + circ->n_streams = n_stream; + assert_circuit_ok(circ); + + connection_exit_connect(n_stream); + return 0; + case -1: /* resolve failed */ + log_fn(LOG_INFO,"Resolve failed (%s).", n_stream->address); + connection_edge_end(n_stream, END_STREAM_REASON_RESOLVEFAILED, n_stream->cpath_layer); + connection_free(n_stream); + break; + case 0: /* resolve added to pending list */ + /* add it into the linked list of resolving_streams on this circuit */ + n_stream->next_stream = circ->resolving_streams; + circ->resolving_streams = n_stream; + assert_circuit_ok(circ); + ; + } + return 0; +} + +/** + * Called when we receive a RELAY_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, circuit_t *circ) { + connection_t *dummy_conn; + relay_header_t rh; + + assert_circuit_ok(circ); + relay_header_unpack(&rh, cell->payload); + + + /* 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 = connection_new(CONN_TYPE_EXIT); + dummy_conn->stream_id = rh.stream_id; + dummy_conn->address = tor_strndup(cell->payload+RELAY_HEADER_SIZE, + rh.length); + dummy_conn->port = 0; + dummy_conn->state = EXIT_CONN_STATE_RESOLVEFAILED; + dummy_conn->purpose = EXIT_PURPOSE_RESOLVE; + + /* send it off to the gethostbyname farm */ + switch(dns_resolve(dummy_conn)) { + case 1: /* resolve worked; resolved cell was sent. */ + connection_free(dummy_conn); + return 0; + case -1: /* resolve failed; resolved cell was sent. */ + log_fn(LOG_INFO,"Resolve failed (%s).",dummy_conn->address); + connection_free(dummy_conn); + break; + case 0: /* resolve added to pending list */ + /* add it into the linked list of resolving_streams on this circuit */ + dummy_conn->next_stream = circ->resolving_streams; + circ->resolving_streams = dummy_conn; + assert_circuit_ok(circ); + ; + } + 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(connection_t *conn) { + unsigned char connected_payload[4]; + + if (!connection_edge_is_rendezvous_stream(conn) && + router_compare_to_my_exit_policy(conn) == ADDR_POLICY_REJECTED) { + log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port); + connection_edge_end(conn, END_STREAM_REASON_EXITPOLICY, conn->cpath_layer); + circuit_detach_stream(circuit_get_by_conn(conn), conn); + connection_free(conn); + return; + } + + log_fn(LOG_DEBUG,"about to try connecting"); + switch(connection_connect(conn, conn->address, conn->addr, conn->port)) { + case -1: + connection_edge_end(conn, END_STREAM_REASON_CONNECTFAILED, conn->cpath_layer); + circuit_detach_stream(circuit_get_by_conn(conn), conn); + connection_free(conn); + return; + case 0: + conn->state = EXIT_CONN_STATE_CONNECTING; + + connection_watch_events(conn, POLLOUT | POLLIN | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link in windowsland. */ + return; + /* case 1: fall through */ + } + + conn->state = EXIT_CONN_STATE_OPEN; + if(connection_wants_to_flush(conn)) { /* in case there are any queued data cells */ + log_fn(LOG_WARN,"tell roger: newly connected conn had data waiting!"); +// connection_start_writing(conn); + } +// connection_process_inbuf(conn); + connection_watch_events(conn, POLLIN); + + /* also, deliver a 'connected' cell back through the circuit. */ + if(connection_edge_is_rendezvous_stream(conn)) { /* rendezvous stream */ + /* don't send an address back! */ + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, + NULL, 0, conn->cpath_layer); + } else { /* normal stream */ + *(uint32_t*)connected_payload = htonl(conn->addr); + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, + connected_payload, 4, conn->cpath_layer); + } +} + +/** Return 1 if conn is a rendezvous stream, or 0 if + * it is a general stream. + */ +int connection_edge_is_rendezvous_stream(connection_t *conn) { + tor_assert(conn); + if(*conn->rend_query) /* XXX */ + return 1; + return 0; +} + +/** Return 1 if router exit might allow stream conn + * to exit from it, or 0 if it definitely will not allow it. + * (We might be uncertain if conn's destination address has not yet been + * resolved.) + */ +int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit) +{ + uint32_t addr; + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP); + tor_assert(conn->socks_request); + + log_fn(LOG_DEBUG,"considering nickname %s, for address %s / port %d:", + exit->nickname, conn->socks_request->address, + conn->socks_request->port); + if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { + /* 0.0.7 servers and earlier don't support DNS resolution. There are no + * ORs running code before 0.0.7, so we only worry about 0.0.7. Once all + * servers are running 0.0.8, remove this check. XXX */ + return strncmp(exit->platform, "Tor 0.0.7", 9) ? 1 : 0; + } + addr = client_dns_lookup_entry(conn->socks_request->address); + if(router_compare_addr_to_exit_policy(addr, + conn->socks_request->port, exit->exit_policy) < 0) + return 0; + return 1; +} + +/** A helper function for socks_policy_permits_address() below. + * + * Parse options.SocksPolicy in the same way that the exit policy + * is parsed, and put the processed version in &socks_policy. + * Ignore port specifiers. + */ +static void parse_socks_policy(void) +{ + struct exit_policy_t *n; + if (socks_policy) { + exit_policy_free(socks_policy); + socks_policy = NULL; + } + config_parse_exit_policy(options.SocksPolicy, &socks_policy); + /* ports aren't used. */ + for (n=socks_policy; n; n = n->next) { + n->prt_min = 1; + n->prt_max = 65535; + } +} + +/** Return 1 if addr is permitted to connect to our socks port, + * based on socks_policy. Else return 0. + */ +int socks_policy_permits_address(uint32_t addr) +{ + int a; + if (options.SocksPolicy && !socks_policy) + parse_socks_policy(); + + a = router_compare_addr_to_exit_policy(addr, 1, socks_policy); + if (a==-1) + return 0; + else if (a==0) + return 1; + tor_assert(a==1); + log_fn(LOG_WARN, "Got unexpected 'maybe' answer from socks policy"); + return 0; +} + +/* ***** Client DNS code ***** */ + +/* XXX Perhaps this should get merged with the dns.c code somehow. */ +/* XXX But we can't just merge them, because then nodes that act as + * both OR and OP could be attacked: people could rig the dns cache + * by answering funny things to stream begin requests, and later + * other clients would reuse those funny addr's. Hm. + */ + +/** A client-side struct to remember the resolved IP (addr) for + * a given address. These structs make up a tree, with client_dns_map + * below as its root. + */ +struct client_dns_entry { + uint32_t addr; /**< The resolved IP of this entry. */ + time_t expires; /**< At what second does addr expire? */ + int n_failures; /**< How many times has this entry failed to resolve so far? */ +}; + +/** How many elements are in the client dns cache currently? */ +static int client_dns_size = 0; +/** The tree of client-side cached DNS resolves. */ +static strmap_t *client_dns_map = NULL; + +/** Initialize client_dns_map and client_dns_size. */ +void client_dns_init(void) { + client_dns_map = strmap_new(); + client_dns_size = 0; +} + +/** Return the client_dns_entry that corresponds to address. + * If it's not there, allocate and return a new entry for address. + */ +static struct client_dns_entry * +_get_or_create_ent(const char *address) +{ + struct client_dns_entry *ent; + ent = strmap_get_lc(client_dns_map,address); + if (!ent) { + ent = tor_malloc_zero(sizeof(struct client_dns_entry)); + ent->expires = time(NULL)+MAX_DNS_ENTRY_AGE; + strmap_set_lc(client_dns_map,address,ent); + ++client_dns_size; + } + return ent; +} + +/** Return the IP associated with address, if we know it + * and it's still fresh enough. Otherwise return 0. + */ +uint32_t client_dns_lookup_entry(const char *address) +{ + struct client_dns_entry *ent; + struct in_addr in; + time_t now; + + tor_assert(address); + + if (tor_inet_aton(address, &in)) { + log_fn(LOG_DEBUG, "Using static address %s (%08lX)", address, + (unsigned long)ntohl(in.s_addr)); + return ntohl(in.s_addr); + } + ent = strmap_get_lc(client_dns_map,address); + if (!ent || !ent->addr) { + log_fn(LOG_DEBUG, "No entry found for address %s", address); + return 0; + } else { + now = time(NULL); + if (ent->expires < now) { + log_fn(LOG_DEBUG, "Expired entry found for address %s", address); + strmap_remove_lc(client_dns_map,address); + tor_free(ent); + --client_dns_size; + return 0; + } + in.s_addr = htonl(ent->addr); + log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address, + inet_ntoa(in)); + return ent->addr; + } +} + +/** 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) +{ + struct client_dns_entry *ent; + ent = _get_or_create_ent(address); + ++ent->n_failures; + log_fn(LOG_DEBUG,"Address %s now has %d resolve failures.", + address, ent->n_failures); + return ent->n_failures; +} + +/** Record the fact that address resolved to val. + * We can now use this in subsequent streams in client_dns_lookup_entry(), + * so we can more correctly choose a router that will allow address + * to exit from him. + */ +void client_dns_set_entry(const char *address, uint32_t val) +{ + struct client_dns_entry *ent; + struct in_addr in; + time_t now; + + tor_assert(address); + tor_assert(val); + + if (tor_inet_aton(address, &in)) + return; + now = time(NULL); + ent = _get_or_create_ent(address); + in.s_addr = htonl(val); + log_fn(LOG_DEBUG, "Updating entry for address %s: %s", address, + inet_ntoa(in)); + ent->addr = val; + ent->expires = now+MAX_DNS_ENTRY_AGE; + ent->n_failures = 0; +} + +/** A helper function for client_dns_clean() below. If ent is too old, + * then remove it from the tree and return NULL, else return ent. + */ +static void* _remove_if_expired(const char *addr, + struct client_dns_entry *ent, + time_t *nowp) +{ + if (ent->expires < *nowp) { + --client_dns_size; + tor_free(ent); + return NULL; + } else { + return ent; + } +} + +/** Clean out entries from the client-side DNS cache that were + * resolved long enough ago that they are no longer valid. + */ +void client_dns_clean(void) +{ + time_t now; + + if(!client_dns_size) + return; + now = time(NULL); + strmap_foreach(client_dns_map, (strmap_foreach_fn)_remove_if_expired, &now); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/connection_or.c b/tags/tor-0_0_8pre1/src/or/connection_or.c new file mode 100644 index 0000000000..1648b02ebe --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/connection_or.c @@ -0,0 +1,433 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file connection_or.c + * \brief Functions to handle OR connections, TLS handshaking, and + * cells on the network. + **/ + +#include "or.h" + +extern or_options_t options; /**< command-line and config-file options */ + +static int connection_tls_finish_handshake(connection_t *conn); +static int connection_or_process_cells_from_inbuf(connection_t *conn); + +/**************************************************************/ + +/** 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. + */ +static void cell_pack(char *dest, const cell_t *src) { + *(uint16_t*)dest = htons(src->circ_id); + *(uint8_t*)(dest+2) = src->command; + memcpy(dest+3, 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) { + dest->circ_id = ntohs(*(uint16_t*)(src)); + dest->command = *(uint8_t*)(src+2); + memcpy(dest->payload, src+3, CELL_PAYLOAD_SIZE); +} + +/** 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(connection_t *conn) { + + tor_assert(conn && conn->type == CONN_TYPE_OR); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_INFO,"OR connection reached EOF. Closing."); + connection_mark_for_close(conn); + return 0; + } + + if(conn->state != OR_CONN_STATE_OPEN) + return 0; /* don't do anything */ + return connection_or_process_cells_from_inbuf(conn); +} + +/** 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(connection_t *conn) { + tor_assert(conn && conn->type == CONN_TYPE_OR); + + assert_connection_ok(conn,0); + + if (conn->state != OR_CONN_STATE_OPEN) { + log_fn(LOG_WARN,"BUG: called in unexpected state %d",conn->state); + return -1; + } + + connection_stop_writing(conn); + return 0; +} + +/** Connected handler for OR connections: begin the TLS handshake. + */ +int connection_or_finished_connecting(connection_t *conn) +{ + tor_assert(conn && conn->type == CONN_TYPE_OR); + tor_assert(conn->state == OR_CONN_STATE_CONNECTING); + + log_fn(LOG_INFO,"OR connect() to router %s:%u finished.", + conn->address,conn->port); + + if(connection_tls_start_handshake(conn, 0) < 0) { + /* TLS handshaking error of some kind. */ + connection_mark_for_close(conn); + return -1; + } + return 0; +} + +/** Initialize conn to include all the relevant data from router. + * This function is called either from connection_or_connect(), if + * we initiated the connect, or from connection_tls_finish_handshake() + * if the other side initiated it. + */ +static void +connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router) { + conn->addr = router->addr; + conn->port = router->or_port; + conn->receiver_bucket = conn->bandwidth = router->bandwidthburst; + conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); + conn->nickname = tor_strdup(router->nickname); + tor_free(conn->address); + conn->address = tor_strdup(router->address); +} + +static void +connection_or_init_conn_from_address(connection_t *conn, + uint32_t addr, uint16_t port, + const char *id_digest) +{ + routerinfo_t *r; + struct in_addr in; + r = router_get_by_digest(id_digest); + if (r) { + connection_or_init_conn_from_router(conn,r); + return; + } + conn->addr = addr; + conn->port = port; + /* This next part isn't really right, but it's good enough for now. */ + conn->receiver_bucket = conn->bandwidth = options.BandwidthBurst; + memcpy(conn->identity_digest, id_digest, DIGEST_LEN); + conn->nickname = tor_malloc(HEX_DIGEST_LEN+1); + base16_encode(conn->nickname, HEX_DIGEST_LEN+1, + conn->identity_digest, DIGEST_LEN); + tor_free(conn->address); + in.s_addr = htonl(addr); + conn->address = tor_strdup(inet_ntoa(in)); +} + +/** Launch a new OR connection to addr:port and expect to + * handshake with an OR with identity digest id_digest. + * + * 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. + */ +connection_t *connection_or_connect(uint32_t addr, uint16_t port, + const char *id_digest) { + connection_t *conn; + routerinfo_t *me; + + tor_assert(id_digest); + + if(server_mode() && (me=router_get_my_routerinfo()) && + !memcmp(me->identity_digest, id_digest,DIGEST_LEN)) { + log_fn(LOG_WARN,"Request to connect to myself! Failing."); + return NULL; + } + + /* this function should never be called if we're already connected to + * id_digest, but check first to be sure */ + conn = connection_get_by_identity_digest(id_digest, CONN_TYPE_OR); + if(conn) + return conn; + + conn = connection_new(CONN_TYPE_OR); + + /* set up conn so it's got all the data we need to remember */ + connection_or_init_conn_from_address(conn, addr, port, id_digest); + conn->state = OR_CONN_STATE_CONNECTING; + + switch(connection_connect(conn, conn->address, addr, port)) { + case -1: + router_mark_as_down(conn->identity_digest); + connection_free(conn); + return NULL; + case 0: + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link on windows */ + return conn; + /* case 1: fall through */ + } + + if(connection_tls_start_handshake(conn, 0) >= 0) + return conn; + + /* failure */ + connection_mark_for_close(conn); + return NULL; +} + +/** 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. + */ +int connection_tls_start_handshake(connection_t *conn, int receiving) { + int use_no_cert=0; + conn->state = OR_CONN_STATE_HANDSHAKING; + if(receiving) { /* check if he's 0.0.7 and I'm unverified */ + routerinfo_t *him, *me; + him = router_get_by_digest(conn->identity_digest); + me = router_get_my_routerinfo(); + + if(him && !strncmp(him->platform, "Tor 0.0.7", 9) && + (!me || !me->is_verified)) { + log_fn(LOG_INFO,"He's running 0.0.7, and I'm unverified. Acting like OP."); + use_no_cert = 1; + } + } + conn->tls = tor_tls_new(conn->s, receiving, use_no_cert); + if(!conn->tls) { + log_fn(LOG_WARN,"tor_tls_new failed. Closing."); + return -1; + } + connection_start_reading(conn); + log_fn(LOG_DEBUG,"starting the handshake"); + if(connection_tls_continue_handshake(conn) < 0) { + return -1; + } + return 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(connection_t *conn) { + switch(tor_tls_handshake(conn->tls)) { + case TOR_TLS_ERROR: + case TOR_TLS_CLOSE: + log_fn(LOG_INFO,"tls error. breaking."); + return -1; + case TOR_TLS_DONE: + return connection_tls_finish_handshake(conn); + case TOR_TLS_WANTWRITE: + connection_start_writing(conn); + log_fn(LOG_DEBUG,"wanted write"); + return 0; + case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */ + log_fn(LOG_DEBUG,"wanted read"); + return 0; + } + return 0; +} + +static int digest_is_zero(const char *id) { + char ZERO_DIGEST[DIGEST_LEN]; + memset(ZERO_DIGEST, 0, DIGEST_LEN); + return !memcmp(ZERO_DIGEST, id, DIGEST_LEN); +} + +/** The tls handshake is finished. + * + * Make sure we are happy with the person we just handshaked with: + * If it's an OP (that is, it has no certificate), make sure I'm an OR. + * If it's an OR (it has a certificate), make sure it has a recognized + * nickname, and its cert is signed by the identity key of that nickname. + * If I initiated the connection, make sure it's the right guy; and if + * he initiated the connection, make sure he's not already connected. + * + * If he initiated the conn, also initialize conn from the information + * in router. + * + * If either of us is an OP, set bandwidth to the default OP bandwidth. + * + * If all is successful and he's an OR, then call circuit_n_conn_done() + * to handle events that have been pending on the tls handshake + * completion, and set the directory to be dirty (only matters if I'm + * an authdirserver). + */ +static int +connection_tls_finish_handshake(connection_t *conn) { + routerinfo_t *router; + char nickname[MAX_NICKNAME_LEN+1]; + connection_t *c; + crypto_pk_env_t *identity_rcvd=NULL; + char digest_rcvd[DIGEST_LEN]; + + conn->state = OR_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + log_fn(LOG_DEBUG,"tls handshake done. verifying."); + if (! tor_tls_peer_has_cert(conn->tls)) { /* It's an OP. */ + if (server_mode()) { /* I'm an OR; good. */ + conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; + return 0; + } else { /* Neither side sent a certificate: ouch. */ + log_fn(LOG_WARN,"Neither peer sent a cert! Closing."); + return -1; + } + } + /* Okay; the other side is an OR. */ + if (tor_tls_get_peer_cert_nickname(conn->tls, nickname, MAX_NICKNAME_LEN)) { + log_fn(LOG_WARN,"Other side (%s:%d) has a cert without a valid nickname. Closing.", + conn->address, conn->port); + return -1; + } + log_fn(LOG_DEBUG, "Other side (%s:%d) claims to be '%s'", conn->address, + conn->port, nickname); + + if(tor_tls_verify(conn->tls, &identity_rcvd) < 0) { + log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.", + nickname, conn->address, conn->port); + return -1; + } + log_fn(LOG_DEBUG,"The router's cert is valid."); + crypto_pk_get_digest(identity_rcvd, digest_rcvd); + crypto_free_pk_env(identity_rcvd); + + router = router_get_by_nickname(nickname); + if(router && /* we know this nickname */ + router->is_verified && /* make sure it's the right guy */ + memcmp(digest_rcvd, router->identity_digest, DIGEST_LEN) != 0) { + log_fn(LOG_WARN, "Identity key not as expected for %s", nickname); + return -1; + } + + if (!digest_is_zero(conn->identity_digest)) { + /* I initiated this connection. */ + if (strcasecmp(conn->nickname, nickname)) { + log_fn(options.DirPort ? LOG_WARN : LOG_INFO, + "Other side (%s:%d) is '%s', but we tried to connect to '%s'", + conn->address, conn->port, nickname, conn->nickname); + return -1; + } + } else { + if((c=connection_get_by_identity_digest(digest_rcvd, CONN_TYPE_OR))) { + log_fn(LOG_INFO,"Router %s is already connected on fd %d. Dropping fd %d.", nickname, c->s, conn->s); + return -1; + } + connection_or_init_conn_from_address(conn,conn->addr,conn->port,digest_rcvd); + } + + if (!server_mode()) { /* If I'm an OP... */ + conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; + } + directory_set_dirty(); + circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */ + /* Note the success */ + rep_hist_note_connect_succeeded(conn->identity_digest, time(NULL)); + return 0; +} + +/** Pack cell into wire-format, and write it onto conn's + * outbuf. + * + * (Commented out) If it's an OR conn, and an entire TLS record is + * ready, then try to flush the record now. + */ +void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn) { + char networkcell[CELL_NETWORK_SIZE]; + char *n = networkcell; + + tor_assert(cell && conn); + tor_assert(connection_speaks_cells(conn)); + + cell_pack(n, cell); + + connection_write_to_buf(n, CELL_NETWORK_SIZE, conn); + +#if 0 /* commented out -- can we get away with not doing this, + * because we're already round-robining in handle_read? + */ +#define MIN_TLS_FLUSHLEN 15872 +/* openssl tls record size is 16383, this is close. The goal here is to + * push data out as soon as we know there's enough for a tls record, so + * during periods of high load we won't read the entire megabyte from + * input before pushing any data out. */ + if(conn->outbuf_flushlen-CELL_NETWORK_SIZE < MIN_TLS_FLUSHLEN && + conn->outbuf_flushlen >= MIN_TLS_FLUSHLEN) { + int extra = conn->outbuf_flushlen - MIN_TLS_FLUSHLEN; + conn->outbuf_flushlen = MIN_TLS_FLUSHLEN; + if(connection_handle_write(conn) < 0) { + log_fn(LOG_WARN,"flushing failed."); + return; + } + if(extra) { + conn->outbuf_flushlen += extra; + connection_start_writing(conn); + } + } +#endif + +} + +/** 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(connection_t *conn) { + char buf[CELL_NETWORK_SIZE]; + cell_t cell; + +loop: + log_fn(LOG_DEBUG,"%d: starting, inbuf_datalen %d (%d pending in tls object).", + conn->s,(int)buf_datalen(conn->inbuf),tor_tls_get_pending_bytes(conn->tls)); + if(buf_datalen(conn->inbuf) < CELL_NETWORK_SIZE) /* entire response available? */ + return 0; /* not yet */ + + connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, conn); + + /* retrieve cell info from buf (create the host-order struct from the + * network-order string) */ + cell_unpack(&cell, buf); + + command_process_cell(&cell, conn); + + goto loop; /* process the remainder of the buffer */ +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/cpuworker.c b/tags/tor-0_0_8pre1/src/or/cpuworker.c new file mode 100644 index 0000000000..4e66a40f49 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/cpuworker.c @@ -0,0 +1,384 @@ +/* Copyright 2003-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file cpuworker.c + * \brief Run computation-intensive tasks (generally for crypto) in + * a separate execution context. [OR only.] + * + * Right now, we only use this for processing onionskins. + **/ + +#include "or.h" +extern or_options_t options; /* command-line and config-file options */ + +/** 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 8 +/** How many bytes are sent from tor to the cpuworker? */ +#define LEN_ONION_QUESTION (1+TAG_LEN+ONIONSKIN_CHALLENGE_LEN) +/** How many bytes are sent from the cpuworker back to tor? */ +#define LEN_ONION_RESPONSE (1+TAG_LEN+ONIONSKIN_REPLY_LEN+40+32) + +/** 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 int cpuworker_main(void *data); +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) { + last_rotation_time=time(NULL); + spawn_enough_cpuworkers(); +} + +/** Called when we're done sending a request to a cpuworker. */ +int connection_cpu_finished_flushing(connection_t *conn) { + tor_assert(conn && conn->type == CONN_TYPE_CPUWORKER); + connection_stop_writing(conn); + return 0; +} + +/** Pack addr,port,and circ_id; set *tag to the result. (See note on + * cpuworker_main for wire format.) */ +static void tag_pack(char *tag, uint32_t addr, uint16_t port, uint16_t circ_id) { + *(uint32_t *)tag = addr; + *(uint16_t *)(tag+4) = port; + *(uint16_t *)(tag+6) = circ_id; +} + +/** Unpack tag into addr, port, and circ_id. + */ +static void tag_unpack(const char *tag, uint32_t *addr, uint16_t *port, uint16_t *circ_id) { + struct in_addr in; + + *addr = *(const uint32_t *)tag; + *port = *(const uint16_t *)(tag+4); + *circ_id = *(const uint16_t *)(tag+6); + + in.s_addr = htonl(*addr); + log_fn(LOG_DEBUG,"onion was from %s:%d, circ_id %d.", inet_ntoa(in), *port, *circ_id); +} + +/** 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); + spawn_enough_cpuworkers(); +} + +/** Called when we get data from a cpuworker. If the answer is not complete, + * wait for a complete answer. If the cpuworker closes the connection, + * mark it as closed and spawn a new one as needed. If the answer is complete, + * process it as appropriate. + */ +int connection_cpu_process_inbuf(connection_t *conn) { + char success; + unsigned char buf[LEN_ONION_RESPONSE]; + uint32_t addr; + uint16_t port; + uint16_t circ_id; + connection_t *p_conn; + circuit_t *circ; + + tor_assert(conn && conn->type == CONN_TYPE_CPUWORKER); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_WARN,"Read eof. 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_fn(LOG_WARN,"...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; + } + + if(conn->state == CPUWORKER_STATE_BUSY_ONION) { + if(buf_datalen(conn->inbuf) < LEN_ONION_RESPONSE) /* entire answer available? */ + return 0; /* not yet */ + tor_assert(buf_datalen(conn->inbuf) == LEN_ONION_RESPONSE); + + connection_fetch_from_buf(&success,1,conn); + connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn); + + /* parse out the circ it was talking about */ + tag_unpack(buf, &addr, &port, &circ_id); + circ = NULL; + /* XXXX This is actually right: we want a specific port here in + * case there are multiple connections. */ + p_conn = connection_exact_get_by_addr_port(addr,port); + if(p_conn) + circ = circuit_get_by_circ_id_conn(circ_id, p_conn); + + if(success == 0) { + log_fn(LOG_WARN,"decoding onionskin failed. Closing."); + if(circ) + circuit_mark_for_close(circ); + goto done_processing; + } + if(!circ) { + log_fn(LOG_INFO,"processed onion for a circ that's gone. Dropping."); + goto done_processing; + } + tor_assert(circ->p_conn); + if(onionskin_answer(circ, buf+TAG_LEN, buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) { + log_fn(LOG_WARN,"onionskin_answer failed. Closing."); + circuit_mark_for_close(circ); + goto done_processing; + } + log_fn(LOG_DEBUG,"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: + * Task type [1 byte, always CPUWORKER_TASK_ONION] + * Opaque tag TAG_LEN + * Onionskin challenge ONIONSKIN_CHALLENGE_LEN + * Response format: + * Success/failure [1 byte, boolean.] + * Opaque tag TAG_LEN + * Onionskin challenge ONIONSKIN_REPLY_LEN + * Negotiated keys KEY_LEN*2+DIGEST_LEN*2 + * + * (Note: this _should_ be by addr/port, since we're concerned with specific + * connections, not with routers (where we'd use identity). Also, using + * identity would break pre-0.0.8 OPs, which don't have identity keys.) + */ +static int cpuworker_main(void *data) { + unsigned char question[ONIONSKIN_CHALLENGE_LEN]; + unsigned char question_type; + int *fdarray = data; + int fd; + + /* variables for onion processing */ + unsigned char keys[40+32]; + unsigned char reply_to_proxy[ONIONSKIN_REPLY_LEN]; + unsigned char buf[LEN_ONION_RESPONSE]; + char tag[TAG_LEN]; + crypto_pk_env_t *onion_key = NULL, *last_onion_key = NULL; + + tor_close_socket(fdarray[0]); /* this is the side of the socketpair the parent uses */ + fd = fdarray[1]; /* this side is ours */ +#ifndef MS_WINDOWS + connection_free_all(); /* so the child doesn't hold the parent's fd's open */ +#endif + + dup_onion_keys(&onion_key, &last_onion_key); + + for(;;) { + + if(recv(fd, &question_type, 1, 0) != 1) { +// log_fn(LOG_ERR,"read type failed. Exiting."); + log_fn(LOG_INFO,"cpuworker exiting because tor process closed connection (either rotated keys or died)."); + goto end; + } + tor_assert(question_type == CPUWORKER_TASK_ONION); + + if(read_all(fd, tag, TAG_LEN, 1) != TAG_LEN) { + log_fn(LOG_ERR,"read tag failed. Exiting."); + goto end; + } + + if(read_all(fd, question, ONIONSKIN_CHALLENGE_LEN, 1) != ONIONSKIN_CHALLENGE_LEN) { + log_fn(LOG_ERR,"read question failed. Exiting."); + goto end; + } + + if(question_type == CPUWORKER_TASK_ONION) { + if(onion_skin_server_handshake(question, onion_key, last_onion_key, + reply_to_proxy, keys, 40+32) < 0) { + /* failure */ + log_fn(LOG_WARN,"onion_skin_server_handshake failed."); + memset(buf,0,LEN_ONION_RESPONSE); /* send all zeros for failure */ + } else { + /* success */ + log_fn(LOG_DEBUG,"onion_skin_server_handshake succeeded."); + buf[0] = 1; /* 1 means success */ + memcpy(buf+1,tag,TAG_LEN); + memcpy(buf+1+TAG_LEN,reply_to_proxy,ONIONSKIN_REPLY_LEN); + memcpy(buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN,keys,40+32); + } + if(write_all(fd, buf, LEN_ONION_RESPONSE, 1) != LEN_ONION_RESPONSE) { + log_fn(LOG_ERR,"writing response buf failed. Exiting."); + spawn_exit(); + } + log_fn(LOG_DEBUG,"finished writing response."); + } + } + end: + if (onion_key) + crypto_free_pk_env(onion_key); + if (last_onion_key) + crypto_free_pk_env(last_onion_key); + spawn_exit(); + return 0; /* windows wants this function to return an int */ +} + +/** Launch a new cpuworker. + */ +static int spawn_cpuworker(void) { + int fd[2]; + connection_t *conn; + + if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { + log(LOG_ERR, "Couldn't construct socketpair: %s", + tor_socket_strerror(tor_socket_errno(-1))); + tor_cleanup(); + exit(1); + } + + spawn_func(cpuworker_main, (void*)fd); + log_fn(LOG_DEBUG,"just spawned a worker."); + tor_close_socket(fd[1]); /* we don't need the worker's side of the pipe */ + + conn = connection_new(CONN_TYPE_CPUWORKER); + + set_socket_nonblocking(fd[0]); + + /* set up conn so it's got all the data we need to remember */ + conn->s = fd[0]; + conn->address = tor_strdup("localhost"); + + if(connection_add(conn) < 0) { /* no space, forget it */ + log_fn(LOG_WARN,"connection_add failed. Giving up."); + connection_free(conn); /* this closes fd[0] */ + 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 = options.NumCpus; + + 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_fn(LOG_WARN,"spawn failed!"); + return; + } + num_cpuworkers++; + } +} + +/** Take a pending task from the queue and assign it to 'cpuworker'. */ +static void process_pending_task(connection_t *cpuworker) { + circuit_t *circ; + + tor_assert(cpuworker); + + /* for now only process onion tasks */ + + circ = onion_next_task(); + if(!circ) + return; + if(assign_to_cpuworker(cpuworker, CPUWORKER_TASK_ONION, circ) < 0) + log_fn(LOG_WARN,"assign_to_cpuworker failed. Ignoring."); +} + +/** 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. + * If question_type is CPUWORKER_TASK_ONION then task is a circ. + * No other question_types are allowed. + */ +int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type, + void *task) { + circuit_t *circ; + char tag[TAG_LEN]; + + tor_assert(question_type == CPUWORKER_TASK_ONION); + + if(question_type == CPUWORKER_TASK_ONION) { + circ = task; + + if(num_cpuworkers_busy == num_cpuworkers) { + log_fn(LOG_DEBUG,"No idle cpuworkers. Queuing."); + if(onion_pending_add(circ) < 0) + return -1; + return 0; + } + + if (!cpuworker) + cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, CPUWORKER_STATE_IDLE); + + tor_assert(cpuworker); + + if(!circ->p_conn) { + log_fn(LOG_INFO,"circ->p_conn gone. Failing circ."); + return -1; + } + tag_pack(tag, circ->p_conn->addr, circ->p_conn->port, circ->p_circ_id); + + cpuworker->state = CPUWORKER_STATE_BUSY_ONION; + num_cpuworkers_busy++; + + connection_write_to_buf(&question_type, 1, cpuworker); + connection_write_to_buf(tag, sizeof(tag), cpuworker); + connection_write_to_buf(circ->onionskin, ONIONSKIN_CHALLENGE_LEN, cpuworker); + } + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/directory.c b/tags/tor-0_0_8pre1/src/or/directory.c new file mode 100644 index 0000000000..4ab1b93809 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/directory.c @@ -0,0 +1,729 @@ +/* Copyright 2001,2002,2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/** + * \file directory.c + * \brief Implement directory HTTP protocol. + **/ + +/* 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 main.c + * do_hup() in 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_initiate_command(routerinfo_t *router, uint8_t purpose, + const char *payload, int payload_len); +static void directory_send_command(connection_t *conn, int purpose, + const char *payload, int payload_len); +static int directory_handle_command(connection_t *conn); + +/********* START VARIABLES **********/ + +extern or_options_t options; /* command-line and config-file options */ + +/** URL for publishing rendezvous descriptors. */ +char rend_publish_string[] = "/rendezvous/publish"; +/** Prefix for downloading rendezvous descriptors. */ +char rend_fetch_url[] = "/rendezvous/"; + +#define MAX_HEADERS_SIZE 50000 +#define MAX_BODY_SIZE 500000 + +/********* END VARIABLES ************/ + +/** Start a connection to every known directory server, using + * connection purpose 'purpose' and uploading the payload 'payload' + * (length 'payload_len'). The purpose should be one of + * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'. + */ +void +directory_post_to_dirservers(uint8_t purpose, const char *payload, + int payload_len) +{ + int i; + routerinfo_t *router; + routerlist_t *rl; + + router_get_routerlist(&rl); + if(!rl) + return; + + for(i=0; i < smartlist_len(rl->routers); i++) { + router = smartlist_get(rl->routers, i); + /* Note: this posts our descriptor to ourselves, if we're an + * authdirserver. But I think that's ok. */ + if(router->is_trusted_dir) + directory_initiate_command(router, purpose, payload, payload_len); + } +} + +/** Start a connection to a random running directory server, using + * connection purpose 'purpose' requesting 'payload' (length + * 'payload_len'). The purpose should be one of + * 'DIR_PURPOSE_FETCH_DIR' or 'DIR_PURPOSE_FETCH_RENDDESC'. + */ +void +directory_get_from_dirserver(uint8_t purpose, const char *payload, + int payload_len) +{ + routerinfo_t *ds; + + if (purpose == DIR_PURPOSE_FETCH_DIR) { + if (advertised_server_mode()) { + /* only ask authdirservers, and don't ask myself */ + ds = router_pick_directory_server(1, 1); + } else { + /* anybody with a non-zero dirport will do */ + ds = router_pick_directory_server(0, 1); + } + } else { // (purpose == DIR_PURPOSE_FETCH_RENDDESC) + /* only ask authdirservers, any of them will do */ + ds = router_pick_directory_server(1, 0); + } + + if (!ds) { /* no viable dirserver found */ + log_fn(LOG_WARN,"No running dirservers known. Not trying. (purpose %d)", purpose); + return; + } + + directory_initiate_command(ds, purpose, payload, payload_len); +} + +/** Launch a new connection to the directory server router to upload or + * download a service or rendezvous descriptor. purpose determines what + * kind of directory connection we're launching, and must be one of + * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}. + * + * When uploading, payload and payload_len determine the content + * of the HTTP post. When fetching a rendezvous descriptor, payload + * and payload_len are the service ID we want to fetch. + */ +static void +directory_initiate_command(routerinfo_t *router, uint8_t purpose, + const char *payload, int payload_len) +{ + connection_t *conn; + + tor_assert(router); + tor_assert(router->dir_port); + + switch (purpose) + { + case DIR_PURPOSE_FETCH_DIR: + log_fn(LOG_DEBUG,"initiating directory fetch"); + break; + case DIR_PURPOSE_FETCH_RENDDESC: + log_fn(LOG_DEBUG,"initiating hidden-service descriptor fetch"); + break; + case DIR_PURPOSE_UPLOAD_DIR: + log_fn(LOG_DEBUG,"initiating server descriptor upload"); + break; + case DIR_PURPOSE_UPLOAD_RENDDESC: + log_fn(LOG_DEBUG,"initiating hidden-service descriptor upload"); + break; + default: + log_fn(LOG_ERR, "Unrecognized directory connection purpose."); + tor_assert(0); + } + + conn = connection_new(CONN_TYPE_DIR); + + /* set up conn so it's got all the data we need to remember */ + conn->addr = router->addr; + conn->port = router->dir_port; + conn->address = tor_strdup(router->address); + conn->nickname = tor_strdup(router->nickname); + tor_assert(router->identity_pkey); + conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); + + conn->purpose = purpose; + + /* give it an initial state */ + conn->state = DIR_CONN_STATE_CONNECTING; + + if(purpose == DIR_PURPOSE_FETCH_DIR || + purpose == DIR_PURPOSE_UPLOAD_DIR) { + /* then we want to connect directly */ + switch(connection_connect(conn, conn->address, conn->addr, conn->port)) { + case -1: + router_mark_as_down(conn->identity_digest); /* don't try him again */ + if(purpose == DIR_PURPOSE_FETCH_DIR && !all_directory_servers_down()) { + log_fn(LOG_INFO,"Giving up on dirserver %s; trying another.", conn->nickname); + directory_get_from_dirserver(purpose, payload, payload_len); + } + connection_free(conn); + return; + case 1: + conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ + /* fall through */ + case 0: + /* queue the command on the outbuf */ + directory_send_command(conn, purpose, payload, payload_len); + + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link in windowsland. */ + } + } else { /* we want to connect via tor */ + /* make an AP connection + * populate it and add it at the right state + * socketpair and hook up both sides + */ + conn->s = connection_ap_make_bridge(conn->address, conn->port); + if(conn->s < 0) { + log_fn(LOG_WARN,"Making AP bridge to dirserver failed."); + connection_mark_for_close(conn); + return; + } + + conn->state = DIR_CONN_STATE_CLIENT_SENDING; + connection_add(conn); + /* queue the command on the outbuf */ + directory_send_command(conn, purpose, payload, payload_len); + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + } +} + +/** Queue an appropriate HTTP command on conn-\>outbuf. The args + * purpose, payload, and payload_len are as in + * directory_initiate_command. + */ +static void directory_send_command(connection_t *conn, int purpose, + const char *payload, int payload_len) { + char fetchwholedir[] = "GET / HTTP/1.0\r\n\r\n"; + char fetchrunninglist[] = "GET /running-routers HTTP/1.0\r\n\r\n"; + char tmp[8192]; + + tor_assert(conn && conn->type == CONN_TYPE_DIR); + + switch(purpose) { + case DIR_PURPOSE_FETCH_DIR: + tor_assert(payload == NULL); + connection_write_to_buf(fetchwholedir, strlen(fetchwholedir), conn); + break; + case DIR_PURPOSE_FETCH_RUNNING_LIST: + tor_assert(payload == NULL); + connection_write_to_buf(fetchrunninglist, strlen(fetchrunninglist), conn); + break; + case DIR_PURPOSE_UPLOAD_DIR: + tor_assert(payload); + snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n", + payload_len); + connection_write_to_buf(tmp, strlen(tmp), conn); + connection_write_to_buf(payload, payload_len, conn); + break; + case DIR_PURPOSE_FETCH_RENDDESC: + tor_assert(payload); + + /* this must be true or we wouldn't be doing the lookup */ + tor_assert(payload_len <= REND_SERVICE_ID_LEN); + /* This breaks the function abstraction. */ + memcpy(conn->rend_query, payload, payload_len); + conn->rend_query[payload_len] = 0; + + snprintf(tmp, sizeof(tmp), "GET %s%s HTTP/1.0\r\n\r\n", rend_fetch_url, payload); + connection_write_to_buf(tmp, strlen(tmp), conn); + break; + case DIR_PURPOSE_UPLOAD_RENDDESC: + tor_assert(payload); + snprintf(tmp, sizeof(tmp), + "POST %s HTTP/1.0\r\nContent-Length: %d\r\n\r\n", rend_publish_string, payload_len); + connection_write_to_buf(tmp, strlen(tmp), conn); + /* could include nuls, need to write it separately */ + connection_write_to_buf(payload, payload_len, conn); + break; + } +} + +/** Parse an HTTP request string headers of the form "\%s \%s HTTP/1..." + * If it's well-formed, point *url to the second \%s, + * null-terminate it (this modifies headers!) and return 0. + * Otherwise, return -1. + */ +static int +parse_http_url(char *headers, char **url) +{ + char *s, *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; + tmp = s; /* this is it, assuming it's valid */ + s = (char *)find_whitespace(s); + if (!*s) return -1; + *s = 0; + *url = tmp; + return 0; +} + +/** Parse an HTTP response string headers of the form + * "HTTP/1.\%d \%d\%s\r\n...". + * If it's well-formed, assign *code, point *message to the first + * non-space character after code if there is one and message is non-NULL + * (else leave it alone), and return 0. + * Otherwise, return -1. + */ +static int +parse_http_response(char *headers, int *code, char **message) +{ + int n1, n2; + tor_assert(headers && code); + + while(isspace((int)*headers)) headers++; /* tolerate leading whitespace */ + + if(sscanf(headers, "HTTP/1.%d %d", &n1, &n2) < 2 || + (n1 != 0 && n1 != 1) || + (n2 < 100 || n2 >= 600)) { + log_fn(LOG_WARN,"Failed to parse header '%s'",headers); + return -1; + } + *code = n2; + if(message) { + /* XXX should set *message correctly */ + } + return 0; +} + +/** We are a client, and we've finished reading the server's + * response. Parse and it and act appropriately. + * + * Return -1 if an error has occurred, or 0 normally. The caller + * will take care of marking the connection for close. + */ +static int +connection_dir_client_reached_eof(connection_t *conn) +{ + char *body; + char *headers; + int body_len=0; + int status_code; + + switch(fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, + &body, &body_len, MAX_DIR_SIZE)) { + case -1: /* overflow */ + log_fn(LOG_WARN,"'fetch' response too large. Failing."); + return -1; + case 0: + log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing."); + return -1; + /* case 1, fall through */ + } + + if(parse_http_response(headers, &status_code, NULL) < 0) { + log_fn(LOG_WARN,"Unparseable headers. Closing."); + free(body); free(headers); + return -1; + } + + if(conn->purpose == DIR_PURPOSE_FETCH_DIR) { + /* fetch/process the directory to learn about new routers. */ + log_fn(LOG_INFO,"Received directory (size %d):\n%s", body_len, body); + if(status_code == 503 || body_len == 0) { + log_fn(LOG_INFO,"Empty directory. Ignoring."); + free(body); free(headers); + return 0; + } + if(status_code != 200) { + log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.", + status_code); + free(body); free(headers); + return -1; + } + if(router_load_routerlist_from_directory(body, NULL) < 0){ + log_fn(LOG_INFO,"...but parsing failed. Ignoring."); + } else { + log_fn(LOG_INFO,"updated routers."); + } + directory_has_arrived(); /* do things we've been waiting to do */ + } + + if(conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) { + running_routers_t *rrs; + routerlist_t *rl; + /* just update our list of running routers, if this list is new info */ + log_fn(LOG_INFO,"Received running-routers list (size %d):\n%s", body_len, body); + if(status_code != 200) { + log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.", + status_code); + free(body); free(headers); + return -1; + } + if (!(rrs = router_parse_runningrouters(body))) { + log_fn(LOG_WARN, "Can't parse runningrouters list"); + free(body); free(headers); + return -1; + } + router_get_routerlist(&rl); + routerlist_update_from_runningrouters(rl,rrs); + running_routers_free(rrs); + } + + if(conn->purpose == DIR_PURPOSE_UPLOAD_DIR) { + switch(status_code) { + case 200: + log_fn(LOG_INFO,"eof (status 200) after uploading server descriptor: finished."); + break; + case 400: + log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed server descriptor?"); + break; + case 403: + log_fn(LOG_WARN,"http status 403 (unapproved server) response from dirserver. Is your clock skewed? Have you mailed us your identity fingerprint? Are you using the right key? See README."); + break; + default: + log_fn(LOG_WARN,"http status %d response unrecognized.", status_code); + break; + } + } + + if(conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) { + log_fn(LOG_INFO,"Received rendezvous descriptor (size %d, status code %d)", + body_len, status_code); + switch(status_code) { + case 200: + if(rend_cache_store(body, body_len) < 0) { + log_fn(LOG_WARN,"Failed to store rendezvous descriptor."); + /* alice's ap_stream will notice when connection_mark_for_close + * cleans it up */ + } else { + /* success. notify pending connections about this. */ + rend_client_desc_fetched(conn->rend_query, 1); + conn->purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC; + } + break; + case 404: + /* not there. pending connections will be notified when + * connection_mark_for_close cleans it up. */ + break; + case 400: + log_fn(LOG_WARN,"http status 400 (bad request). Dirserver didn't like our rendezvous query?"); + break; + } + } + + if(conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC) { + switch(status_code) { + case 200: + log_fn(LOG_INFO,"eof (status 200) after uploading rendezvous descriptor: finished."); + break; + case 400: + log_fn(LOG_WARN,"http status 400 (bad request) response from dirserver. Malformed rendezvous descriptor?"); + break; + default: + log_fn(LOG_WARN,"http status %d response unrecognized.", status_code); + break; + } + } + free(body); free(headers); + return 0; +} + +/** Read handler for directory connections. (That's connections to + * directory servers and connections at directory servers.) + */ +int connection_dir_process_inbuf(connection_t *conn) { + int retval; + + tor_assert(conn && conn->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(conn->inbuf_reached_eof) { + if(conn->state != DIR_CONN_STATE_CLIENT_READING) { + log_fn(LOG_INFO,"conn reached eof, not reading. Closing."); + connection_close_immediate(conn); /* it was an error; give up on flushing */ + connection_mark_for_close(conn); + return -1; + } + + retval = connection_dir_client_reached_eof(conn); + connection_mark_for_close(conn); + return retval; + } /* endif 'reached eof' */ + + /* If we're on the dirserver side, look for a command. */ + if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) { + if (directory_handle_command(conn) < 0) { + connection_mark_for_close(conn); + return -1; + } + return 0; + } + + /* XXX for READ states, might want to make sure inbuf isn't too big */ + + log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf."); + return 0; +} + +static char answer200[] = "HTTP/1.0 200 OK\r\n\r\n"; +static char answer400[] = "HTTP/1.0 400 Bad request\r\n\r\n"; +static char answer403[] = "HTTP/1.0 403 Unapproved server\r\n\r\n"; +static char answer404[] = "HTTP/1.0 404 Not found\r\n\r\n"; +static char answer503[] = "HTTP/1.0 503 Directory unavailable\r\n\r\n"; + +/** 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 404. + * Always return 0. */ +static int +directory_handle_command_get(connection_t *conn, char *headers, + char *body, int body_len) +{ + size_t dlen; + const char *cp; + char *url; + char tmp[8192]; + + log_fn(LOG_DEBUG,"Received GET command."); + + conn->state = DIR_CONN_STATE_SERVER_WRITING; + + if (parse_http_url(headers, &url) < 0) { + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + + if(!strcmp(url,"/")) { /* directory fetch */ + dlen = dirserv_get_directory(&cp); + + if(dlen == 0) { + log_fn(LOG_WARN,"My directory is empty. Closing."); + connection_write_to_buf(answer503, strlen(answer503), conn); + return 0; + } + + log_fn(LOG_DEBUG,"Dumping directory to client."); + snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n", + (int)dlen); + connection_write_to_buf(tmp, strlen(tmp), conn); + connection_write_to_buf(cp, strlen(cp), conn); + return 0; + } + + if(!strcmp(url,"/running-routers")) { /* running-routers fetch */ + if(!authdir_mode()) { + /* XXX008 for now, we don't cache running-routers. Reject. */ + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + dlen = dirserv_get_runningrouters(&cp); + if(dlen < 0) { /* we failed to create cp */ + connection_write_to_buf(answer503, strlen(answer503), conn); + return 0; + } + + snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/plain\r\n\r\n", + (int)dlen); + connection_write_to_buf(tmp, strlen(tmp), conn); + connection_write_to_buf(cp, strlen(cp), conn); + return 0; + } + + if(!strncmp(url,rend_fetch_url,strlen(rend_fetch_url))) { + /* rendezvous descriptor fetch */ + const char *descp; + int desc_len; + + if(!authdir_mode()) { + /* We don't hand out rend descs. In fact, it could be a security + * risk, since rend_cache_lookup_desc() below would provide it + * if we're gone to the site recently, and 404 if we haven't. + * + * Reject. */ + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + switch(rend_cache_lookup_desc(url+strlen(rend_fetch_url), &descp, &desc_len)) { + case 1: /* valid */ + snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n", + desc_len); /* can't include descp here, because it's got nuls */ + connection_write_to_buf(tmp, strlen(tmp), conn); + connection_write_to_buf(descp, desc_len, conn); + break; + case 0: /* well-formed but not present */ + connection_write_to_buf(answer404, strlen(answer404), conn); + break; + case -1: /* not well-formed */ + connection_write_to_buf(answer400, strlen(answer400), conn); + break; + } + return 0; + } + + /* we didn't recognize the url */ + connection_write_to_buf(answer404, strlen(answer404), conn); + 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 + * 404. Always return 0. */ +static int +directory_handle_command_post(connection_t *conn, char *headers, + char *body, int body_len) +{ + const char *cp; + char *url; + + log_fn(LOG_DEBUG,"Received POST command."); + + conn->state = DIR_CONN_STATE_SERVER_WRITING; + + if(!authdir_mode()) { + /* we just provide cached directories; we don't want to + * receive anything. */ + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + + if (parse_http_url(headers, &url) < 0) { + connection_write_to_buf(answer400, strlen(answer400), conn); + return 0; + } + log_fn(LOG_INFO,"url '%s' posted to us.", url); + + if(!strcmp(url,"/")) { /* server descriptor post */ + cp = body; + switch(dirserv_add_descriptor(&cp)) { + case -1: + /* malformed descriptor, or something wrong */ + connection_write_to_buf(answer400, strlen(answer400), conn); + break; + case 0: + /* descriptor was well-formed but server has not been approved */ + connection_write_to_buf(answer403, strlen(answer403), conn); + break; + case 1: + dirserv_get_directory(&cp); /* rebuild and write to disk */ + connection_write_to_buf(answer200, strlen(answer200), conn); + break; + } + return 0; + } + + if(!strncmp(url,rend_publish_string,strlen(rend_publish_string))) { + /* rendezvous descriptor post */ + if(rend_cache_store(body, body_len) < 0) + connection_write_to_buf(answer400, strlen(answer400), conn); + else + connection_write_to_buf(answer200, strlen(answer200), conn); + return 0; + } + + /* we didn't recognize the url */ + connection_write_to_buf(answer404, strlen(answer404), conn); + 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(connection_t *conn) { + char *headers=NULL, *body=NULL; + int body_len=0; + int r; + + tor_assert(conn && conn->type == CONN_TYPE_DIR); + + switch(fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, + &body, &body_len, MAX_BODY_SIZE)) { + case -1: /* overflow */ + log_fn(LOG_WARN,"input too large. Failing."); + return -1; + case 0: + log_fn(LOG_DEBUG,"command not all here yet."); + return 0; + /* case 1, fall through */ + } + + log_fn(LOG_DEBUG,"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_WARN,"Got headers '%s' with unknown command. Closing.", 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(connection_t *conn) { + + tor_assert(conn && conn->type == CONN_TYPE_DIR); + + switch(conn->state) { + case DIR_CONN_STATE_CLIENT_SENDING: + log_fn(LOG_DEBUG,"client finished sending command."); + conn->state = DIR_CONN_STATE_CLIENT_READING; + connection_stop_writing(conn); + return 0; + case DIR_CONN_STATE_SERVER_WRITING: + log_fn(LOG_INFO,"Finished writing server response. Closing."); + connection_mark_for_close(conn); + return 0; + default: + log_fn(LOG_WARN,"BUG: called in unexpected state %d.", conn->state); + return -1; + } + return 0; +} + +/** Connected handler for directory connections: begin sending data to the + * server */ +int connection_dir_finished_connecting(connection_t *conn) +{ + tor_assert(conn && conn->type == CONN_TYPE_DIR); + tor_assert(conn->state == DIR_CONN_STATE_CONNECTING); + + log_fn(LOG_INFO,"Dir connection to router %s:%u established.", + conn->address,conn->port); + + conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/dirserv.c b/tags/tor-0_0_8pre1/src/or/dirserv.c new file mode 100644 index 0000000000..5316fe900a --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/dirserv.c @@ -0,0 +1,762 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/** + * \file dirserv.c + * \brief Directory server core implementation. + **/ + +/** How far in the future do we allow a router to get? (seconds) */ +#define ROUTER_ALLOW_SKEW (30*60) + +extern or_options_t options; /**< command-line and config-file options */ + +/** Do we need to regenerate the directory when someone asks for it? */ +static int the_directory_is_dirty = 1; +static int runningrouters_is_dirty = 1; + +static int list_running_servers(char **nicknames_out); +static void directory_remove_unrecognized(void); + +/************** Fingerprint handling code ************/ + +typedef struct fingerprint_entry_t { + char *nickname; + char *fingerprint; +} fingerprint_entry_t; + +/** List of nickname-\>identity fingerprint mappings for all the routers + * that we recognize. Used to prevent Sybil attacks. */ +static smartlist_t *fingerprint_list = NULL; + +/** Add the fingerprint fp for the nickname nickname to + * the global list of recognized identity key fingerprints. + */ +void /* Should be static; exposed for testing */ +add_fingerprint_to_dir(const char *nickname, const char *fp) +{ + int i; + fingerprint_entry_t *ent; + if (!fingerprint_list) + fingerprint_list = smartlist_create(); + + for (i = 0; i < smartlist_len(fingerprint_list); ++i) { + ent = smartlist_get(fingerprint_list, i); + if (!strcasecmp(ent->nickname,nickname)) { + tor_free(ent->fingerprint); + ent->fingerprint = tor_strdup(fp); + return; + } + } + ent = tor_malloc(sizeof(fingerprint_entry_t)); + ent->nickname = tor_strdup(nickname); + ent->fingerprint = tor_strdup(fp); + smartlist_add(fingerprint_list, ent); +} + +/** Add the nickname and fingerprint for this OR to the recognized list. + */ +int +dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk) +{ + char fp[FINGERPRINT_LEN+1]; + if (crypto_pk_get_fingerprint(pk, fp)<0) { + log_fn(LOG_ERR, "Error computing fingerprint"); + return -1; + } + add_fingerprint_to_dir(nickname, fp); + return 0; +} + +/** Parse the nickname-\>fingerprint mappings stored in the file named + * fname. 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 contents of fname and return 0. On failure, leave the + * current fingerprint list untouched, and return -1. */ +int +dirserv_parse_fingerprint_file(const char *fname) +{ + FILE *file; + char line[FINGERPRINT_LEN+MAX_NICKNAME_LEN+20+1]; + char *nickname, *fingerprint; + smartlist_t *fingerprint_list_new; + int i, result; + fingerprint_entry_t *ent; + + if(!(file = fopen(fname, "r"))) { + log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname); + return -1; + } + fingerprint_list_new = smartlist_create(); + while( (result=parse_line_from_file(line, sizeof(line),file,&nickname,&fingerprint)) > 0) { + if (strlen(nickname) > MAX_NICKNAME_LEN) { + log(LOG_WARN, "Nickname %s too long in fingerprint file. Skipping.", nickname); + continue; + } + if(strlen(fingerprint) != FINGERPRINT_LEN || + !crypto_pk_check_fingerprint_syntax(fingerprint)) { + log_fn(LOG_WARN, "Invalid fingerprint (nickname %s, fingerprint %s). Skipping.", + nickname, fingerprint); + continue; + } + for (i = 0; i < smartlist_len(fingerprint_list_new); ++i) { + ent = smartlist_get(fingerprint_list_new, i); + if (0==strcasecmp(ent->nickname, nickname)) { + log(LOG_WARN, "Duplicate nickname %s. Skipping.",nickname); + break; /* out of the for. the 'if' below means skip to the next line. */ + } + } + if(i == smartlist_len(fingerprint_list_new)) { /* not a duplicate */ + ent = tor_malloc(sizeof(fingerprint_entry_t)); + ent->nickname = tor_strdup(nickname); + ent->fingerprint = tor_strdup(fingerprint); + smartlist_add(fingerprint_list_new, ent); + } + } + fclose(file); + if(result == 0) { /* eof; replace the global fingerprints list. */ + dirserv_free_fingerprint_list(); + fingerprint_list = fingerprint_list_new; + /* Delete any routers whose fingerprints we no longer recognize */ + directory_remove_unrecognized(); + return 0; + } + /* error */ + log_fn(LOG_WARN, "Error reading from fingerprint file"); + for (i = 0; i < smartlist_len(fingerprint_list_new); ++i) { + ent = smartlist_get(fingerprint_list_new, i); + tor_free(ent->nickname); + tor_free(ent->fingerprint); + tor_free(ent); + } + smartlist_free(fingerprint_list_new); + return -1; +} + +/** Check whether router has a nickname/identity key combination that + * we recognize from the fingerprint list. Return 1 if router's + * identity and nickname match, -1 if we recognize the nickname but + * the identity key is wrong, and 0 if the nickname is not known. */ +int +dirserv_router_fingerprint_is_known(const routerinfo_t *router) +{ + int i, found=0; + fingerprint_entry_t *ent =NULL; + char fp[FINGERPRINT_LEN+1]; + + if (!fingerprint_list) + fingerprint_list = smartlist_create(); + + log_fn(LOG_DEBUG, "%d fingerprints known.", smartlist_len(fingerprint_list)); + for (i=0;inickname, ent->nickname); + if (!strcasecmp(router->nickname,ent->nickname)) { + found = 1; + break; + } + } + + if (!found) { /* No such server known */ + log_fn(LOG_INFO,"no fingerprint found for %s",router->nickname); + return 0; + } + if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) { + log_fn(LOG_WARN,"error computing fingerprint"); + return -1; + } + if (0==strcasecmp(ent->fingerprint, fp)) { + log_fn(LOG_DEBUG,"good fingerprint for %s",router->nickname); + return 1; /* Right fingerprint. */ + } else { + log_fn(LOG_WARN,"mismatched fingerprint for %s",router->nickname); + return -1; /* Wrong fingerprint. */ + } +} + +/** Return true iff any router named nickname with digest + * is in the verified fingerprint list. */ +static int +router_nickname_is_approved(const char *nickname, const char *digest) +{ + int i,j; + fingerprint_entry_t *ent; + char fp[FINGERPRINT_LEN+1]; + char hexdigest[HEX_DIGEST_LEN+1]; + if (!fingerprint_list) + return 0; + + base16_encode(hexdigest, sizeof(hexdigest), digest, DIGEST_LEN); + for (i=j=0;jnickname) && + !strcasecmp(fp,ent->fingerprint)) { + return 1; + } + } + return 0; +} + +/** Clear the current fingerprint list. */ +void +dirserv_free_fingerprint_list() +{ + int i; + fingerprint_entry_t *ent; + if (!fingerprint_list) + return; + + for (i = 0; i < smartlist_len(fingerprint_list); ++i) { + ent = smartlist_get(fingerprint_list, i); + tor_free(ent->nickname); + tor_free(ent->fingerprint); + tor_free(ent); + } + smartlist_free(fingerprint_list); + fingerprint_list = NULL; +} + +/* + * Descriptor list + */ + +/** A directory server's view of a server descriptor. Contains both + * parsed and unparsed versions. */ +typedef struct descriptor_entry_t { + char *nickname; + time_t published; + size_t desc_len; + char *descriptor; + int verified; + routerinfo_t *router; +} descriptor_entry_t; + +/** List of all server descriptors that this dirserv is holding. */ +static smartlist_t *descriptor_list = NULL; + +/** Release the storage held by desc */ +static void free_descriptor_entry(descriptor_entry_t *desc) +{ + tor_free(desc->descriptor); + tor_free(desc->nickname); + routerinfo_free(desc->router); + free(desc); +} + +/** Release all storage that the dirserv is holding for server + * descriptors. */ +void +dirserv_free_descriptors() +{ + if (!descriptor_list) + return; + SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d, + free_descriptor_entry(d)); + smartlist_clear(descriptor_list); +} + +/** Parse the server descriptor at *desc and maybe insert it into the + * list of service descriptors, and (if the descriptor is well-formed) + * advance *desc immediately past the descriptor's end. + * + * Return 1 if descriptor is well-formed and accepted; + * 0 if well-formed and server is unapproved; + * -1 if not well-formed or other error. + */ +int +dirserv_add_descriptor(const char **desc) +{ + descriptor_entry_t *ent = NULL; + routerinfo_t *ri = NULL; + int i, r, found=-1; + char *start, *end; + char *desc_tmp = NULL; + const char *cp; + size_t desc_len; + time_t now; + int verified=1; /* whether we knew its fingerprint already */ + + if (!descriptor_list) + descriptor_list = smartlist_create(); + + start = strstr(*desc, "router "); + if (!start) { + log_fn(LOG_WARN, "no 'router' line found. This is not a descriptor."); + return -1; + } + if ((end = strstr(start+6, "\nrouter "))) { + ++end; /* Include NL. */ + } else if ((end = strstr(start+6, "\ndirectory-signature"))) { + ++end; + } else { + end = start+strlen(start); + } + desc_len = end-start; + cp = desc_tmp = tor_strndup(start, desc_len); + + /* Check: is the descriptor syntactically valid? */ + ri = router_parse_entry_from_string(cp, NULL); + tor_free(desc_tmp); + if (!ri) { + log(LOG_WARN, "Couldn't parse descriptor"); + return -1; + } + /* Okay. Now check whether the fingerprint is recognized. */ + r = dirserv_router_fingerprint_is_known(ri); + if(r==-1) { + log_fn(LOG_WARN, "Known nickname %s, wrong fingerprint. Not adding.", ri->nickname); + routerinfo_free(ri); + *desc = end; + return 0; + } + if(r==0) { + char fp[FINGERPRINT_LEN+1]; + log_fn(LOG_INFO, "Unknown nickname %s (%s:%d). Adding.", + ri->nickname, ri->address, ri->or_port); + if (crypto_pk_get_fingerprint(ri->identity_pkey, fp) < 0) { + log_fn(LOG_WARN, "Error computing fingerprint for %s", ri->nickname); + } else { + log_fn(LOG_INFO, "Fingerprint line: %s %s", ri->nickname, fp); + } + verified = 0; + } + /* Is there too much clock skew? */ + now = time(NULL); + if (ri->published_on > now+ROUTER_ALLOW_SKEW) { + log_fn(LOG_WARN, "Publication time for nickname %s is too far in the future; possible clock skew. Not adding.", ri->nickname); + routerinfo_free(ri); + *desc = end; + return 0; + } + if (ri->published_on < now-ROUTER_MAX_AGE) { + log_fn(LOG_WARN, "Publication time for router with nickname %s is too far in the past. Not adding.", ri->nickname); + routerinfo_free(ri); + *desc = end; + return 0; + } + + /* Do we already have an entry for this router? */ + for (i = 0; i < smartlist_len(descriptor_list); ++i) { + ent = smartlist_get(descriptor_list, i); + if (!strcasecmp(ri->nickname, ent->nickname)) { + found = i; + break; + } + } + if (found >= 0) { + /* if so, decide whether to update it. */ + if (ent->published > ri->published_on) { + /* We already have a newer descriptor */ + log_fn(LOG_INFO,"We already have a newer desc for nickname %s. Not adding.",ri->nickname); + /* This isn't really an error; return success. */ + routerinfo_free(ri); + *desc = end; + return 1; + } + /* We don't have a newer one; we'll update this one. */ + log_fn(LOG_INFO,"Dirserv updating desc for nickname %s",ri->nickname); + free_descriptor_entry(ent); + smartlist_del_keeporder(descriptor_list, found); + } else { + /* Add at the end. */ + log_fn(LOG_INFO,"Dirserv adding desc for nickname %s",ri->nickname); + } + + ent = tor_malloc(sizeof(descriptor_entry_t)); + ent->nickname = tor_strdup(ri->nickname); + ent->published = ri->published_on; + ent->desc_len = desc_len; + ent->descriptor = tor_malloc(desc_len+1); + strncpy(ent->descriptor, start, desc_len); + ent->descriptor[desc_len] = '\0'; + ent->router = ri; + /* XXX008 is ent->verified useful/used for anything? */ + ent->verified = verified; /* XXXX008 support other possibilities. */ + smartlist_add(descriptor_list, ent); + + *desc = end; + directory_set_dirty(); + + return 1; +} + +/** Remove all descriptors whose nicknames or fingerprints we don't + * recognize. (Descriptors that used to be good can become + * unrecognized when we reload the fingerprint list.) + */ +static void +directory_remove_unrecognized(void) +{ + int i; + descriptor_entry_t *ent; + if (!descriptor_list) + descriptor_list = smartlist_create(); + + for (i = 0; i < smartlist_len(descriptor_list); ++i) { + ent = smartlist_get(descriptor_list, i); + if (dirserv_router_fingerprint_is_known(ent->router)<=0) { + log(LOG_INFO, "Router %s is no longer recognized", + ent->nickname); + free_descriptor_entry(ent); + smartlist_del(descriptor_list, i--); + } + } +} + +/** 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() +{ + the_directory_is_dirty = 1; + runningrouters_is_dirty = 1; +} + +/** Load all descriptors from a directory stored in the string + * dir. + */ +int +dirserv_load_from_directory_string(const char *dir) +{ + const char *cp = dir; + while(1) { + cp = strstr(cp, "\nrouter "); + if (!cp) break; + ++cp; + if (dirserv_add_descriptor(&cp) < 0) { + return -1; + } + --cp; /*Back up to newline.*/ + } + return 0; +} + +/** Set *nicknames_out to a comma-separated list of all the ORs that we + * believe are currently running (because we have open connections to + * them). Return 0 on success; -1 on error. + */ +static int +list_running_servers(char **nicknames_out) +{ + connection_t **connection_array; + int n_conns; + connection_t *conn; + char *cp; + int i; + int length; + smartlist_t *nicknames_up, *nicknames_down; + + *nicknames_out = NULL; + nicknames_up = smartlist_create(); + nicknames_down = smartlist_create(); + smartlist_add(nicknames_up, tor_strdup(options.Nickname)); + + get_connection_array(&connection_array, &n_conns); + for (i = 0; itype != CONN_TYPE_OR || !conn->nickname) + continue; /* only list ORs. */ + if (router_nickname_is_approved(conn->nickname, conn->identity_digest)) { + name = tor_strdup(conn->nickname); + } else { + name = tor_malloc(HEX_DIGEST_LEN+2); + *name = '$'; + base16_encode(name+1, HEX_DIGEST_LEN+1, conn->identity_digest, DIGEST_LEN); + } + + if(conn->state == OR_CONN_STATE_OPEN) + smartlist_add(nicknames_up, name); + else + smartlist_add(nicknames_down, name); + } + length = smartlist_len(nicknames_up) + + 2*smartlist_len(nicknames_down) + 1; + /* spaces + EOS + !'s + 1. */ + SMARTLIST_FOREACH(nicknames_up, char *, c, length += strlen(c)); + SMARTLIST_FOREACH(nicknames_down, char *, c, length += strlen(c)); + *nicknames_out = tor_malloc_zero(length); + cp = *nicknames_out; + for (i = 0; ipublished < cutoff) { + /* descriptor_list[i] is too old. Remove it. */ + free_descriptor_entry(ent); + smartlist_del(descriptor_list, i--); + directory_set_dirty(); + } + } +} + +/** Dump all routers currently in the directory into the string + * s, using at most maxlen characters, and signing the + * directory with private_key. Return 0 on success, -1 on + * failure. + */ +int +dirserv_dump_directory_to_string(char *s, unsigned int maxlen, + crypto_pk_env_t *private_key) +{ + char *cp, *eos; + char digest[20]; + char signature[128]; + char published[33]; + time_t published_on; + int i; + eos = s+maxlen; + + if (!descriptor_list) + descriptor_list = smartlist_create(); + + if (list_running_servers(&cp)) + return -1; + dirserv_remove_old_servers(); + published_on = time(NULL); + strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on)); + snprintf(s, maxlen, + "signed-directory\n" + "published %s\n" + "recommended-software %s\n" + "running-routers %s\n\n", + published, options.RecommendedVersions, cp); + + free(cp); + i = strlen(s); + cp = s+i; + + SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d, + if (strlcat(s, d->descriptor, maxlen) >= maxlen) + goto truncated); + + /* These multiple strlcat calls are inefficient, but dwarfed by the RSA + signature. + */ + if (strlcat(s, "directory-signature ", maxlen) >= maxlen) + goto truncated; + if (strlcat(s, options.Nickname, maxlen) >= maxlen) + goto truncated; + if (strlcat(s, "\n", maxlen) >= maxlen) + goto truncated; + + if (router_get_dir_hash(s,digest)) { + log_fn(LOG_WARN,"couldn't compute digest"); + return -1; + } + if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) { + log_fn(LOG_WARN,"couldn't sign digest"); + return -1; + } + log(LOG_DEBUG,"generated directory digest begins with %s",hex_str(digest,4)); + + if (strlcat(cp, "-----BEGIN SIGNATURE-----\n", maxlen) >= maxlen) + goto truncated; + + i = strlen(s); + cp = s+i; + if (base64_encode(cp, maxlen-i, signature, 128) < 0) { + log_fn(LOG_WARN,"couldn't base64-encode signature"); + return -1; + } + + if (strlcat(s, "-----END SIGNATURE-----\n", maxlen) >= maxlen) + goto truncated; + + return 0; + truncated: + log_fn(LOG_WARN,"tried to exceed string length."); + return -1; +} + +/** Most recently generated encoded signed directory. */ +static char *the_directory = NULL; +static int the_directory_len = -1; +static char *cached_directory = NULL; /* used only by non-auth dirservers */ +static time_t cached_directory_published = 0; +static int cached_directory_len = -1; + +void dirserv_set_cached_directory(const char *directory, time_t when) +{ + time_t now; + tor_assert(!options.AuthoritativeDir); + now = time(NULL); + if (when>cached_directory_published && + whendirectory to the most recently generated encoded signed + * directory, generating a new one as necessary. */ +size_t dirserv_get_directory(const char **directory) +{ + char *new_directory; + char filename[512]; + if (!options.AuthoritativeDir) { + if (cached_directory) { + *directory = cached_directory; + return (size_t) cached_directory_len; + } else { + /* no directory yet retrieved */ + return 0; + } + } + if (the_directory_is_dirty) { + new_directory = tor_malloc(MAX_DIR_SIZE); + if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE, + get_identity_key())) { + log(LOG_WARN, "Error creating directory."); + free(new_directory); + return 0; + } + tor_free(the_directory); + the_directory = new_directory; + the_directory_len = strlen(the_directory); + log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len, + the_directory); + the_directory_is_dirty = 0; + /* Now read the directory we just made in order to update our own + * router lists. This does more signature checking than is strictly + * necessary, but safe is better than sorry. */ + new_directory = tor_strdup(the_directory); + /* use a new copy of the dir, since get_dir_from_string scribbles on it */ + if (router_load_routerlist_from_directory(new_directory, get_identity_key())) { + log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying."); + tor_cleanup(); + exit(0); + } + free(new_directory); + if(get_data_directory(&options)) { + sprintf(filename,"%s/cached-directory", get_data_directory(&options)); + if(write_str_to_file(filename,the_directory) < 0) { + log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring."); + } + } + } else { + log(LOG_INFO,"Directory still clean, reusing."); + } + *directory = the_directory; + return the_directory_len; +} + +static char *runningrouters_string=NULL; +static size_t runningrouters_len=0; + +/** Replace the current running-routers list with a newly generated one. */ +static int generate_runningrouters(crypto_pk_env_t *private_key) +{ + char *s, *cp; + char digest[DIGEST_LEN]; + char signature[PK_BYTES]; + int i, len; + char published[33]; + time_t published_on; + + len = 1024+(MAX_HEX_NICKNAME_LEN+2)*smartlist_len(descriptor_list); + s = tor_malloc_zero(len); + if (list_running_servers(&cp)) + return -1; + published_on = time(NULL); + strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on)); + sprintf(s, "network-status\n" + "published %s\n" + "running-routers %s\n" + "directory-signature %s\n" + "-----BEGIN SIGNATURE-----\n", + published, cp, options.Nickname); + tor_free(cp); + if (router_get_runningrouters_hash(s,digest)) { + log_fn(LOG_WARN,"couldn't compute digest"); + return -1; + } + if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) { + log_fn(LOG_WARN,"couldn't sign digest"); + return -1; + } + + i = strlen(s); + cp = s+i; + if (base64_encode(cp, len-i, signature, 128) < 0) { + log_fn(LOG_WARN,"couldn't base64-encode signature"); + return -1; + } + if (strlcat(s, "-----END SIGNATURE-----\n", len) >= len) { + return -1; + } + + tor_free(runningrouters_string); + runningrouters_string = s; + runningrouters_len = strlen(s); + runningrouters_is_dirty = 0; + return 0; +} + +/** Set *rr to the most recently generated encoded signed + * running-routers list, generating a new one as necessary. */ +size_t dirserv_get_runningrouters(const char **rr) +{ + if (runningrouters_is_dirty) { + if(generate_runningrouters(get_identity_key())) { + log_fn(LOG_ERR, "Couldn't generate running-routers list?"); + return -1; + } + } + *rr = runningrouters_string; + return runningrouters_len; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/dns.c b/tags/tor-0_0_8pre1/src/or/dns.c new file mode 100644 index 0000000000..01b5bd46ee --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/dns.c @@ -0,0 +1,783 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file dns.c + * \brief Resolve hostnames in separate processes. + **/ + +/* See http://elvin.dstc.com/ListArchive/elvin-dev/archive/2001/09/msg00027.html + * for some approaches to asynchronous dns. We will want to switch once one of + * them becomes more commonly available. + */ + +#include "or.h" +#include "tree.h" + +extern or_options_t options; /* command-line and config-file options */ + +/** Longest hostname we're willing to resolve. */ +#define MAX_ADDRESSLEN 256 + +/** Maximum DNS processes to spawn. */ +#define MAX_DNSWORKERS 50 +/** Minimum DNS processes to spawn. */ +#define MIN_DNSWORKERS 3 + +/** If more than this many processes are idle, shut down the extras. */ +#define MAX_IDLE_DNSWORKERS 10 + +/** Possible outcomes from hostname lookup: permanent failure, + * transient (retryable) failure, and success. */ +#define DNS_RESOLVE_FAILED_TRANSIENT 1 +#define DNS_RESOLVE_FAILED_PERMANENT 2 +#define DNS_RESOLVE_SUCCEEDED 3 + +/** How many dnsworkers we have running right now. */ +static int num_dnsworkers=0; +/** How many of the running dnsworkers have an assigned task right now. */ +static int num_dnsworkers_busy=0; +/** When did we last rotate the dnsworkers? */ +static time_t last_rotation_time=0; + +/** Linked list of connections waiting for a DNS answer. */ +struct pending_connection_t { + struct connection_t *conn; + struct pending_connection_t *next; +}; + +/** A DNS request: possibly completed, possibly pending; cached_resolve + * structs are stored at the OR side in a splay tree, and as a linked + * list from oldest to newest. + */ +struct cached_resolve { + SPLAY_ENTRY(cached_resolve) node; + char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ + uint32_t addr; /**< IPv4 addr for address. */ + char state; /**< 0 is pending; 1 means answer is valid; 2 means resolve failed. */ +#define CACHE_STATE_PENDING 0 +#define CACHE_STATE_VALID 1 +#define CACHE_STATE_FAILED 2 + uint32_t expire; /**< Remove items from cache after this time. */ + struct pending_connection_t *pending_connections; + struct cached_resolve *next; +}; + +static void purge_expired_resolves(uint32_t now); +static int assign_to_dnsworker(connection_t *exitconn); +static void dns_purge_resolve(struct cached_resolve *resolve); +static void dns_found_answer(char *address, uint32_t addr, char outcome); +static int dnsworker_main(void *data); +static int spawn_dnsworker(void); +static void spawn_enough_dnsworkers(void); +static void send_resolved_cell(connection_t *conn, uint8_t answer_type); + +/** Splay tree of cached_resolve objects. */ +static SPLAY_HEAD(cache_tree, cached_resolve) cache_root; + +/** Function to compare hashed resolves on their addresses; used to + * implement splay trees. */ +static int compare_cached_resolves(struct cached_resolve *a, + struct cached_resolve *b) { + /* make this smarter one day? */ + return strncasecmp(a->address, b->address, MAX_ADDRESSLEN); +} + +SPLAY_PROTOTYPE(cache_tree, cached_resolve, node, compare_cached_resolves); +SPLAY_GENERATE(cache_tree, cached_resolve, node, compare_cached_resolves); + +/** Initialize the DNS cache. */ +static void init_cache_tree(void) { + SPLAY_INIT(&cache_root); +} + +/** Initialize the DNS subsystem; called by the OR process. */ +void dns_init(void) { + init_cache_tree(); + last_rotation_time=time(NULL); + spawn_enough_dnsworkers(); +} + +/** Linked list of resolved addresses, oldest to newest. */ +static struct cached_resolve *oldest_cached_resolve = NULL; +static struct cached_resolve *newest_cached_resolve = NULL; + +/** Remove every cached_resolve whose expire time is before now + * from the cache. */ +static void purge_expired_resolves(uint32_t now) { + struct cached_resolve *resolve; + struct pending_connection_t *pend; + connection_t *pendconn; + + /* this is fast because the linked list + * oldest_cached_resolve is ordered by when they came in. + */ + while(oldest_cached_resolve && (oldest_cached_resolve->expire < now)) { + resolve = oldest_cached_resolve; + log(LOG_DEBUG,"Forgetting old cached resolve (expires %lu)", (unsigned long)resolve->expire); + if(resolve->state == CACHE_STATE_PENDING) { + log_fn(LOG_WARN,"Expiring a dns resolve that's still pending. Forgot to cull it?"); + } + if (resolve->pending_connections) { + log_fn(LOG_WARN, "Closing pending connections on expiring 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(pend->conn->s == -1); + pendconn = pend->conn; + connection_edge_end(pendconn, END_STREAM_REASON_MISC, + pendconn->cpath_layer); + circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn); + connection_free(pendconn); + tor_free(pend); + } + } + oldest_cached_resolve = resolve->next; + if(!oldest_cached_resolve) /* if there are no more, */ + newest_cached_resolve = NULL; /* then make sure the list's tail knows that too */ + SPLAY_REMOVE(cache_tree, &cache_root, resolve); + tor_free(resolve); + } +} + +static void send_resolved_cell(connection_t *conn, uint8_t answer_type) +{ + char buf[RELAY_PAYLOAD_SIZE]; + int buflen; + + buf[0] = answer_type; + + switch (answer_type) + { + case RESOLVED_TYPE_IPV4: + buf[1] = 4; + set_uint32(buf+2, htonl(conn->addr)); + buflen = 6; + break; + case RESOLVED_TYPE_ERROR_TRANSIENT: + case RESOLVED_TYPE_ERROR: + buf[1] = 24; /* length of "error resolving hostname" */ + strcpy(buf+2, "error resolving hostname"); + buflen = 26; + break; + default: + tor_assert(0); + } + connection_edge_send_command(conn, circuit_get_by_conn(conn), + RELAY_COMMAND_RESOLVED, buf, buflen, + conn->cpath_layer); +} + +/** 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, return -1. + * + * 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. + */ +int dns_resolve(connection_t *exitconn) { + struct cached_resolve *resolve; + struct cached_resolve search; + struct pending_connection_t *pending_connection; + struct in_addr in; + uint32_t now = time(NULL); + assert_connection_ok(exitconn, 0); + tor_assert(exitconn->s == -1); + + /* first check if exitconn->address is an IP. If so, we already + * know the answer. */ + if (tor_inet_aton(exitconn->address, &in) != 0) { + exitconn->addr = ntohl(in.s_addr); + return 1; + } + + /* then take this opportunity to see if there are any expired + * resolves in the tree. */ + purge_expired_resolves(now); + + /* now check the tree to see if 'address' is already there. */ + strncpy(search.address, exitconn->address, MAX_ADDRESSLEN); + search.address[MAX_ADDRESSLEN-1] = 0; + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(resolve) { /* already there */ + switch(resolve->state) { + case CACHE_STATE_PENDING: + /* add us to the pending list */ + pending_connection = tor_malloc_zero( + sizeof(struct pending_connection_t)); + pending_connection->conn = exitconn; + pending_connection->next = resolve->pending_connections; + resolve->pending_connections = pending_connection; + log_fn(LOG_DEBUG,"Connection (fd %d) waiting for pending DNS resolve of '%s'", + exitconn->s, exitconn->address); + exitconn->state = EXIT_CONN_STATE_RESOLVING; + return 0; + case CACHE_STATE_VALID: + exitconn->addr = resolve->addr; + log_fn(LOG_DEBUG,"Connection (fd %d) found cached answer for '%s'", + exitconn->s, exitconn->address); + if (exitconn->purpose == EXIT_PURPOSE_RESOLVE) + send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4); + return 1; + case CACHE_STATE_FAILED: + if (exitconn->purpose == EXIT_PURPOSE_RESOLVE) + send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR); + return -1; + } + tor_assert(0); + } + /* not there, need to add it */ + resolve = tor_malloc_zero(sizeof(struct cached_resolve)); + resolve->state = CACHE_STATE_PENDING; + resolve->expire = now + MAX_DNS_ENTRY_AGE; + strncpy(resolve->address, exitconn->address, MAX_ADDRESSLEN); + resolve->address[MAX_ADDRESSLEN-1] = 0; + + /* add us to the pending list */ + pending_connection = tor_malloc_zero(sizeof(struct pending_connection_t)); + pending_connection->conn = exitconn; + pending_connection->next = NULL; + resolve->pending_connections = pending_connection; + exitconn->state = EXIT_CONN_STATE_RESOLVING; + + /* add us to the linked list of resolves */ + if (!oldest_cached_resolve) { + oldest_cached_resolve = resolve; + } else { + newest_cached_resolve->next = resolve; + } + newest_cached_resolve = resolve; + + SPLAY_INSERT(cache_tree, &cache_root, resolve); + return assign_to_dnsworker(exitconn); +} + +/** Find or spawn a dns worker process to handle resolving + * exitconn-\>address; tell that dns worker to begin resolving. + */ +static int assign_to_dnsworker(connection_t *exitconn) { + connection_t *dnsconn; + unsigned char len; + + tor_assert(exitconn->state == EXIT_CONN_STATE_RESOLVING); + tor_assert(exitconn->s == -1); + + spawn_enough_dnsworkers(); /* respawn here, to be sure there are enough */ + + dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE); + + if(!dnsconn) { + log_fn(LOG_WARN,"no idle dns workers. Failing."); + dns_cancel_pending_resolve(exitconn->address); + send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT); + return -1; + } + + log_fn(LOG_DEBUG, "Connection (fd %d) needs to resolve '%s'; assigning to DNSWorker (fd %d)", + exitconn->s, exitconn->address, dnsconn->s); + + tor_free(dnsconn->address); + dnsconn->address = tor_strdup(exitconn->address); + dnsconn->state = DNSWORKER_STATE_BUSY; + num_dnsworkers_busy++; + + len = strlen(dnsconn->address); + connection_write_to_buf(&len, 1, dnsconn); + connection_write_to_buf(dnsconn->address, len, dnsconn); + +// log_fn(LOG_DEBUG,"submitted '%s'", exitconn->address); + return 0; +} + +/** Remove conn from the list of connections waiting for conn-\>address. + */ +void connection_dns_remove(connection_t *conn) +{ + struct pending_connection_t *pend, *victim; + struct cached_resolve search; + struct cached_resolve *resolve; + + tor_assert(conn->type == CONN_TYPE_EXIT); + tor_assert(conn->state == EXIT_CONN_STATE_RESOLVING); + + strncpy(search.address, conn->address, MAX_ADDRESSLEN); + search.address[MAX_ADDRESSLEN-1] = 0; + + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(!resolve) { + log_fn(LOG_WARN,"Address '%s' is not pending. Dropping.", conn->address); + return; + } + + tor_assert(resolve->pending_connections); + assert_connection_ok(conn,0); + + pend = resolve->pending_connections; + + if(pend->conn == conn) { + resolve->pending_connections = pend->next; + tor_free(pend); + log_fn(LOG_DEBUG, "First connection (fd %d) no longer waiting for resolve of '%s'", + conn->s, conn->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_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'", + conn->s, conn->address); + return; /* more are pending */ + } + } + tor_assert(0); /* not reachable unless onlyconn not in pending list */ + } +} + +/** Log an error and abort if conn is waiting for a DNS resolve. + */ +void assert_connection_edge_not_dns_pending(connection_t *conn) { + struct pending_connection_t *pend; + struct cached_resolve *resolve; + + SPLAY_FOREACH(resolve, cache_tree, &cache_root) { + for(pend = resolve->pending_connections; + pend; + pend = pend->next) { + tor_assert(pend->conn != conn); + } + } +} + +/** Log an error and abort if any connection waiting for a DNS resolve is + * corrupted. */ +void assert_all_pending_dns_resolves_ok(void) { + struct pending_connection_t *pend; + struct cached_resolve *resolve; + + SPLAY_FOREACH(resolve, cache_tree, &cache_root) { + for(pend = resolve->pending_connections; + pend; + pend = pend->next) { + assert_connection_ok(pend->conn, 0); + tor_assert(pend->conn->s == -1); + tor_assert(!connection_in_array(pend->conn)); + } + } +} + +/** 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(char *address) { + struct pending_connection_t *pend; + struct cached_resolve search; + struct cached_resolve *resolve; + connection_t *pendconn; + + strncpy(search.address, address, MAX_ADDRESSLEN); + search.address[MAX_ADDRESSLEN-1] = 0; + + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(!resolve) { + log_fn(LOG_WARN,"Address '%s' is not pending. Dropping.", address); + return; + } + + tor_assert(resolve->pending_connections); + + /* mark all pending connections to fail */ + log_fn(LOG_DEBUG, "Failing all connections waiting on DNS resolve of '%s'", + address); + while(resolve->pending_connections) { + pend = resolve->pending_connections; + pend->conn->state = EXIT_CONN_STATE_RESOLVEFAILED; + pendconn = pend->conn; + tor_assert(pendconn->s == -1); + if(!pendconn->marked_for_close) { + connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer); + } + circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn); + connection_free(pendconn); + resolve->pending_connections = pend->next; + tor_free(pend); + } + + dns_purge_resolve(resolve); +} + +/** Remove resolve from the cache. + */ +static void dns_purge_resolve(struct cached_resolve *resolve) { + struct cached_resolve *tmp; + + /* remove resolve from the linked list */ + if(resolve == oldest_cached_resolve) { + oldest_cached_resolve = resolve->next; + if(oldest_cached_resolve == NULL) + newest_cached_resolve = NULL; + } else { + /* FFFF make it a doubly linked list if this becomes too slow */ + for(tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next) ; + tor_assert(tmp); /* it's got to be in the list, or we screwed up somewhere else */ + tmp->next = resolve->next; /* unlink it */ + + if(newest_cached_resolve == resolve) + newest_cached_resolve = tmp; + } + + /* remove resolve from the tree */ + SPLAY_REMOVE(cache_tree, &cache_root, resolve); + + tor_free(resolve); +} + +/** Called on the OR side when a DNS worker tells us the outcome of a DNS + * resolve: tell all pending connections about the result of the lookup, and + * cache the value. (address is a NUL-terminated string containing the + * address to look up; addr is an IPv4 address in host order; + * outcome is one of + * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}. + */ +static void dns_found_answer(char *address, uint32_t addr, char outcome) { + struct pending_connection_t *pend; + struct cached_resolve search; + struct cached_resolve *resolve; + connection_t *pendconn; + circuit_t *circ; + + strncpy(search.address, address, MAX_ADDRESSLEN); + search.address[MAX_ADDRESSLEN-1] = 0; + + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(!resolve) { + log_fn(LOG_INFO,"Resolved unasked address '%s'? Dropping.", address); + /* XXX Why drop? Just because we don't care now doesn't mean we shouldn't + * XXX cache the result for later. */ + return; + } + + if (resolve->state != CACHE_STATE_PENDING) { + /* XXXX Maybe update addr? or check addr for consistency? Or let + * VALID replace FAILED? */ + log_fn(LOG_WARN, "Resolved '%s' which was already resolved; ignoring", + address); + tor_assert(resolve->pending_connections == NULL); + return; + } + /* Removed this assertion: in fact, we'll sometimes get a double answer + * to the same question. This can happen when we ask one worker to resolve + * X.Y.Z., then we cancel the request, and then we ask another worker to + * resolve X.Y.Z. */ + /* tor_assert(resolve->state == CACHE_STATE_PENDING); */ + + resolve->addr = ntohl(addr); + if(outcome == DNS_RESOLVE_SUCCEEDED) + resolve->state = CACHE_STATE_VALID; + else + resolve->state = CACHE_STATE_FAILED; + + while(resolve->pending_connections) { + pend = resolve->pending_connections; + assert_connection_ok(pend->conn,time(NULL)); + pend->conn->addr = resolve->addr; + pendconn = pend->conn; /* don't pass complex things to the + connection_mark_for_close macro */ + + if(resolve->state == CACHE_STATE_FAILED) { + /* prevent double-remove. */ + pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED; + circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn); + if (pendconn->purpose == EXIT_PURPOSE_CONNECT) + connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer); + else + send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR); + connection_free(pendconn); + } else { + if (pendconn->purpose == EXIT_PURPOSE_CONNECT) { + /* prevent double-remove. */ + pend->conn->state = EXIT_CONN_STATE_CONNECTING; + + circ = circuit_get_by_conn(pend->conn); + tor_assert(circ); + /* unlink pend->conn from resolving_streams, */ + circuit_detach_stream(circ, pend->conn); + /* and link it to n_streams */ + pend->conn->next_stream = circ->n_streams; + 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->state = EXIT_CONN_STATE_RESOLVEFAILED; + send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4); + circ = circuit_get_by_conn(pendconn); + tor_assert(circ); + circuit_detach_stream(circ, pendconn); + connection_free(pendconn); + } + } + resolve->pending_connections = pend->next; + tor_free(pend); + } + + if(outcome == DNS_RESOLVE_FAILED_TRANSIENT) { /* remove from cache */ + dns_purge_resolve(resolve); + } +} + +/******************************************************************/ + +/* + * Connection between OR and dnsworker + */ + +/** Write handler: called when we've pushed a request to a dnsworker. */ +int connection_dns_finished_flushing(connection_t *conn) { + tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER); + connection_stop_writing(conn); + return 0; +} + +/** Read handler: called when we get data from a dnsworker. If the + * connection is closed, mark the dnsworker as dead. Otherwise, see + * if we have a complete answer. If so, call dns_found_answer on the + * result. If not, wait. Returns 0. */ +int connection_dns_process_inbuf(connection_t *conn) { + char success; + uint32_t addr; + + tor_assert(conn && conn->type == CONN_TYPE_DNSWORKER); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_WARN,"Read eof. Worker died unexpectedly."); + if(conn->state == DNSWORKER_STATE_BUSY) { + dns_cancel_pending_resolve(conn->address); + num_dnsworkers_busy--; + } + num_dnsworkers--; + connection_mark_for_close(conn); + return 0; + } + + tor_assert(conn->state == DNSWORKER_STATE_BUSY); + if(buf_datalen(conn->inbuf) < 5) /* entire answer available? */ + return 0; /* not yet */ + tor_assert(buf_datalen(conn->inbuf) == 5); + + connection_fetch_from_buf(&success,1,conn); + connection_fetch_from_buf((char *)&addr,sizeof(uint32_t),conn); + + log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'", + conn->s, conn->address); + + tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT); + tor_assert(success <= DNS_RESOLVE_SUCCEEDED); + dns_found_answer(conn->address, addr, success); + + tor_free(conn->address); + conn->address = tor_strdup(""); + conn->state = DNSWORKER_STATE_IDLE; + num_dnsworkers_busy--; + if (conn->timestamp_created < last_rotation_time) { + connection_mark_for_close(conn); + num_dnsworkers--; + spawn_enough_dnsworkers(); + } + return 0; +} + +/** Close and re-open all idle dnsworkers; schedule busy ones to be closed + * and re-opened once they're no longer busy. + **/ +void dnsworkers_rotate(void) +{ + connection_t *dnsconn; + while ((dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, + DNSWORKER_STATE_IDLE))) { + connection_mark_for_close(dnsconn); + num_dnsworkers--; + } + last_rotation_time = time(NULL); + spawn_enough_dnsworkers(); +} + +/** Implementation for DNS workers; this code runs in a separate + * execution context. It takes as its argument an fdarray as returned + * by socketpair(), and communicates via fdarray[1]. The protocol is + * as follows: + * - The OR says: + * - ADDRESSLEN [1 byte] + * - ADDRESS [ADDRESSLEN bytes] + * - The DNS worker does the lookup, and replies: + * - OUTCOME [1 byte] + * - IP [4 bytes] + * + * OUTCOME is one of DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}. + * IP is in host order. + * + * The dnsworker runs indefinitely, until its connection is closed or an error + * occurs. + */ +static int dnsworker_main(void *data) { + char address[MAX_ADDRESSLEN]; + unsigned char address_len; + char answer[5]; + uint32_t ip; + int *fdarray = data; + int fd; + int result; + + tor_close_socket(fdarray[0]); /* this is the side of the socketpair the parent uses */ + fd = fdarray[1]; /* this side is ours */ +#ifndef MS_WINDOWS + connection_free_all(); /* so the child doesn't hold the parent's fd's open */ +#endif + + for(;;) { + + if(recv(fd, &address_len, 1, 0) != 1) { + log_fn(LOG_INFO,"dnsworker exiting because tor process died."); + spawn_exit(); + } + + if(address_len && read_all(fd, address, address_len, 1) != address_len) { + log_fn(LOG_ERR,"read hostname failed. Child exiting."); + spawn_exit(); + } + address[address_len] = 0; /* null terminate it */ + + result = tor_lookup_hostname(address, &ip); + /* Make 0.0.0.0 an error, so that we can use "0" to mean "no addr") */ + if (!ip) + result = -1; + switch (result) { + case 1: + log_fn(LOG_INFO,"Could not resolve dest addr %s (transient).",address); + answer[0] = DNS_RESOLVE_FAILED_TRANSIENT; + break; + case -1: + log_fn(LOG_INFO,"Could not resolve dest addr %s (permanent).",address); + answer[0] = DNS_RESOLVE_FAILED_PERMANENT; + break; + case 0: + log_fn(LOG_INFO,"Resolved address '%s'.",address); + answer[0] = DNS_RESOLVE_SUCCEEDED; + break; + } + set_uint32(answer+1, ip); + if(write_all(fd, answer, 5, 1) != 5) { + log_fn(LOG_ERR,"writing answer failed. Child exiting."); + spawn_exit(); + } + } + return 0; /* windows wants this function to return an int */ +} + +/** Launch a new DNS worker; return 0 on success, -1 on failure. + */ +static int spawn_dnsworker(void) { + int fd[2]; + connection_t *conn; + + if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { + log(LOG_ERR, "Couldn't construct socketpair: %s", + tor_socket_strerror(tor_socket_errno(-1))); + tor_cleanup(); + exit(1); + } + + spawn_func(dnsworker_main, (void*)fd); + log_fn(LOG_DEBUG,"just spawned a worker."); + tor_close_socket(fd[1]); /* we don't need the worker's side of the pipe */ + + conn = connection_new(CONN_TYPE_DNSWORKER); + + set_socket_nonblocking(fd[0]); + + /* set up conn so it's got all the data we need to remember */ + conn->s = fd[0]; + conn->address = tor_strdup(""); + + if(connection_add(conn) < 0) { /* no space, forget it */ + log_fn(LOG_WARN,"connection_add failed. Giving up."); + connection_free(conn); /* this closes fd[0] */ + return -1; + } + + conn->state = DNSWORKER_STATE_IDLE; + connection_start_reading(conn); + + return 0; /* success */ +} + +/** If we have too many or too few DNS workers, spawn or kill some. + */ +static void spawn_enough_dnsworkers(void) { + int num_dnsworkers_needed; /* aim to have 1 more than needed, + * but no less than min and no more than max */ + connection_t *dnsconn; + + /* XXX This may not be the best strategy. Maybe we should queue pending + * requests until the old ones finish or time out: otherwise, if + * the connection requests come fast enough, we never get any DNS done. -NM + * XXX But if we queue them, then the adversary can pile even more + * queries onto us, blocking legitimate requests for even longer. + * Maybe we should compromise and only kill if it's been at it for + * more than, e.g., 2 seconds. -RD + */ + if(num_dnsworkers_busy == MAX_DNSWORKERS) { + /* We always want at least one worker idle. + * So find the oldest busy worker and kill it. + */ + dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER, + DNSWORKER_STATE_BUSY); + tor_assert(dnsconn); + + log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.", + MAX_DNSWORKERS); + + connection_mark_for_close(dnsconn); + num_dnsworkers_busy--; + num_dnsworkers--; + } + + if(num_dnsworkers_busy >= MIN_DNSWORKERS) + num_dnsworkers_needed = num_dnsworkers_busy+1; + else + num_dnsworkers_needed = MIN_DNSWORKERS; + + while(num_dnsworkers < num_dnsworkers_needed) { + if(spawn_dnsworker() < 0) { + log(LOG_WARN,"spawn_enough_dnsworkers(): spawn failed!"); + return; + } + num_dnsworkers++; + } + + while(num_dnsworkers > num_dnsworkers_busy+MAX_IDLE_DNSWORKERS) { /* too many idle? */ + /* cull excess workers */ + log_fn(LOG_WARN,"%d of %d dnsworkers are idle. Killing one.", + num_dnsworkers-num_dnsworkers_needed, num_dnsworkers); + dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE); + tor_assert(dnsconn); + connection_mark_for_close(dnsconn); + num_dnsworkers--; + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/main.c b/tags/tor-0_0_8pre1/src/or/main.c new file mode 100644 index 0000000000..67188167e5 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/main.c @@ -0,0 +1,1142 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file main.c + * \brief Tor main loop and startup functions. + **/ + +#include "or.h" + +/********* PROTOTYPES **********/ + +static void dumpstats(int severity); /* log stats */ +static int init_from_config(int argc, char **argv); + +/********* START VARIABLES **********/ + +/* declared in connection.c */ +extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; + +or_options_t options; /**< Command-line and config-file options. */ +int global_read_bucket; /**< Max number of bytes I can read this second. */ + +/** What was the read bucket before the last call to prepare_for_pool? + * (used to determine how many bytes we've read). */ +static int stats_prev_global_read_bucket; +/** How many bytes have we read since we started the process? */ +static uint64_t stats_n_bytes_read = 0; +/** How many seconds have we been running? */ +long stats_n_seconds_uptime = 0; + +/** Array of all open connections; each element corresponds to the element of + * poll_array in the same position. The first nfds elements are valid. */ +static connection_t *connection_array[MAXCONNECTIONS] = + { NULL }; + +/** Array of pollfd objects for calls to poll(). */ +static struct pollfd poll_array[MAXCONNECTIONS]; + +static int nfds=0; /**< Number of connections currently active. */ + +#ifndef MS_WINDOWS /* do signal stuff only on unix */ +static int please_dumpstats=0; /**< Whether we should dump stats during the loop. */ +static int please_reset=0; /**< Whether we just got a sighup. */ +static int please_reap_children=0; /**< Whether we should waitpid for exited children. */ +static int please_shutdown=0; /**< Whether we should shut down Tor. */ +#endif /* signal stuff */ + +/** We should exit if shutting_down != 0 and now <= shutting_down. + * If it's non-zero, don't accept any new circuits or connections. + * This gets assigned when we receive a sig_int, and if we receive a + * second one we exit immediately. */ +int shutting_down=0; +#define SHUTDOWN_WAIT_LENGTH 30 /* seconds */ + +/** We set this to 1 when we've fetched a dir, to know whether to complain + * yet about unrecognized nicknames in entrynodes, exitnodes, etc. + * Also, we don't try building circuits unless this is 1. */ +int has_fetched_directory=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. */ +int has_completed_circuit=0; + +#ifdef MS_WINDOWS +SERVICE_STATUS service_status; +SERVICE_STATUS_HANDLE hStatus; +#endif + +/********* END VARIABLES ************/ + +/**************************************************************************** +* +* This section contains accessors and other methods on the connection_array +* and poll_array variables (which are global within this file and unavailable +* outside it). +* +****************************************************************************/ + +/** 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(connection_t *conn) { + tor_assert(conn); + tor_assert(conn->s >= 0); + + if(nfds >= options.MaxConn-1) { + log_fn(LOG_WARN,"failing because nfds is too high."); + return -1; + } + + tor_assert(conn->poll_index == -1); /* can only connection_add once */ + conn->poll_index = nfds; + connection_array[nfds] = conn; + + poll_array[nfds].fd = conn->s; + + /* zero these out here, because otherwise we'll inherit values from the previously freed one */ + poll_array[nfds].events = 0; + poll_array[nfds].revents = 0; + + nfds++; + + log_fn(LOG_INFO,"new conn type %s, socket %d, nfds %d.", + CONN_TYPE_TO_STRING(conn->type), conn->s, nfds); + + return 0; +} + +/** 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; + + tor_assert(conn); + tor_assert(nfds>0); + + log_fn(LOG_INFO,"removing socket %d (type %s), nfds now %d", + conn->s, CONN_TYPE_TO_STRING(conn->type), nfds-1); + + tor_assert(conn->poll_index >= 0); + current_index = conn->poll_index; + if(current_index == nfds-1) { /* this is the end */ + nfds--; + return 0; + } + + /* replace this one with the one at the end */ + nfds--; + poll_array[current_index].fd = poll_array[nfds].fd; + poll_array[current_index].events = poll_array[nfds].events; + poll_array[current_index].revents = poll_array[nfds].revents; + connection_array[current_index] = connection_array[nfds]; + connection_array[current_index]->poll_index = current_index; + + return 0; +} + +/** Return true iff conn is in the current poll array. */ +int connection_in_array(connection_t *conn) { + int i; + for (i=0; i*array to an array of all connections, and *n + * to the length of the array. *array and *n must not + * be modified. + */ +void get_connection_array(connection_t ***array, int *n) { + *array = connection_array; + *n = nfds; +} + +/** Set the event mask on conn to events. (The form of +* the event mask is as for poll().) + */ +void connection_watch_events(connection_t *conn, short events) { + + tor_assert(conn && conn->poll_index >= 0 && conn->poll_index < nfds); + + poll_array[conn->poll_index].events = events; +} + +/** Return true iff conn is listening for read events. */ +int connection_is_reading(connection_t *conn) { + tor_assert(conn && conn->poll_index >= 0); + return poll_array[conn->poll_index].events & POLLIN; +} + +/** Tell the main loop to stop notifying conn of any read events. */ +void connection_stop_reading(connection_t *conn) { + tor_assert(conn && conn->poll_index >= 0 && conn->poll_index < nfds); + + log(LOG_DEBUG,"connection_stop_reading() called."); + if(poll_array[conn->poll_index].events & POLLIN) + poll_array[conn->poll_index].events -= POLLIN; +} + +/** Tell the main loop to start notifying conn of any read events. */ +void connection_start_reading(connection_t *conn) { + tor_assert(conn && conn->poll_index >= 0 && conn->poll_index < nfds); + poll_array[conn->poll_index].events |= POLLIN; +} + +/** Return true iff conn is listening for write events. */ +int connection_is_writing(connection_t *conn) { + return poll_array[conn->poll_index].events & POLLOUT; +} + +/** Tell the main loop to stop notifying conn of any write events. */ +void connection_stop_writing(connection_t *conn) { + tor_assert(conn && conn->poll_index >= 0 && conn->poll_index < nfds); + if(poll_array[conn->poll_index].events & POLLOUT) + poll_array[conn->poll_index].events -= POLLOUT; +} + +/** Tell the main loop to start notifying conn of any write events. */ +void connection_start_writing(connection_t *conn) { + tor_assert(conn && conn->poll_index >= 0 && conn->poll_index < nfds); + poll_array[conn->poll_index].events |= POLLOUT; +} + +/** Called when the connection at connection_array[i] has a read event, + * or it has pending tls data waiting to be read: checks for validity, + * catches numerous errors, and dispatches to connection_handle_read. + */ +static void conn_read(int i) { + connection_t *conn = connection_array[i]; + + if (conn->marked_for_close) + return; + + /* see http://www.greenend.org.uk/rjk/2001/06/poll.html for + * discussion of POLLIN vs POLLHUP */ + if(!(poll_array[i].revents & (POLLIN|POLLHUP|POLLERR))) + if(!connection_is_reading(conn) || + !connection_has_pending_tls_data(conn)) + return; /* this conn should not read */ + + log_fn(LOG_DEBUG,"socket %d wants to read.",conn->s); + + assert_connection_ok(conn, time(NULL)); + assert_all_pending_dns_resolves_ok(); + + if( + /* XXX does POLLHUP also mean it's definitely broken? */ +#ifdef MS_WINDOWS + (poll_array[i].revents & POLLERR) || +#endif + connection_handle_read(conn) < 0) { + if (!conn->marked_for_close) { + /* this connection is broken. remove it */ + log_fn(LOG_WARN,"Unhandled error on read for %s connection (fd %d); removing", + CONN_TYPE_TO_STRING(conn->type), conn->s); + connection_mark_for_close(conn); + } + } + assert_connection_ok(conn, time(NULL)); + assert_all_pending_dns_resolves_ok(); +} + +/** Called when the connection at connection_array[i] has a write event: + * checks for validity, catches numerous errors, and dispatches to + * connection_handle_write. + */ +static void conn_write(int i) { + connection_t *conn; + + if(!(poll_array[i].revents & POLLOUT)) + return; /* this conn doesn't want to write */ + + conn = connection_array[i]; + log_fn(LOG_DEBUG,"socket %d wants to write.",conn->s); + if (conn->marked_for_close) + return; + + assert_connection_ok(conn, time(NULL)); + assert_all_pending_dns_resolves_ok(); + + if (connection_handle_write(conn) < 0) { + if (!conn->marked_for_close) { + /* this connection is broken. remove it. */ + log_fn(LOG_WARN,"Unhandled error on read for %s connection (fd %d); removing", + CONN_TYPE_TO_STRING(conn->type), conn->s); + conn->has_sent_end = 1; /* otherwise we cry wolf about duplicate close */ + /* XXX do we need a close-immediate here, so we don't try to flush? */ + connection_mark_for_close(conn); + } + } + assert_connection_ok(conn, time(NULL)); + assert_all_pending_dns_resolves_ok(); +} + +/** 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. + * If we remove the connection, then call conn_closed_if_marked at the new + * connection at position i. + */ +static void conn_close_if_marked(int i) { + connection_t *conn; + int retval; + + conn = connection_array[i]; + assert_connection_ok(conn, time(NULL)); + assert_all_pending_dns_resolves_ok(); + if(!conn->marked_for_close) + return; /* nothing to see here, move along */ + + log_fn(LOG_INFO,"Cleaning up connection (fd %d).",conn->s); + if(conn->s >= 0 && connection_wants_to_flush(conn)) { + /* -1 means it's an incomplete edge connection, or that the socket + * has already been closed as unflushable. */ + if(!conn->hold_open_until_flushed) + log_fn(LOG_WARN, + "Conn (fd %d, type %s, state %d) marked, but wants to flush %d bytes. " + "(Marked at %s:%d)", + conn->s, CONN_TYPE_TO_STRING(conn->type), conn->state, + conn->outbuf_flushlen, conn->marked_for_close_file, conn->marked_for_close); + if(connection_speaks_cells(conn)) { + if(conn->state == OR_CONN_STATE_OPEN) { + retval = flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen); + } else + retval = -1; /* never flush non-open broken tls connections */ + } else { + retval = flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); + } + if(retval >= 0 && + conn->hold_open_until_flushed && connection_wants_to_flush(conn)) { + log_fn(LOG_INFO,"Holding conn (fd %d) open for more flushing.",conn->s); + /* XXX should we reset timestamp_lastwritten here? */ + return; + } + if(connection_wants_to_flush(conn)) { + log_fn(LOG_WARN,"Conn (fd %d, type %s, state %d) still wants to flush. Losing %d bytes! (Marked at %s:%d)", + conn->s, CONN_TYPE_TO_STRING(conn->type), conn->state, + (int)buf_datalen(conn->outbuf), conn->marked_for_close_file, + conn->marked_for_close); + } + } + /* if it's 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 + */ + circuit_about_to_close_connection(conn); + connection_about_to_close_connection(conn); + connection_remove(conn); + if(conn->type == CONN_TYPE_EXIT) { + assert_connection_edge_not_dns_pending(conn); + } + connection_free(conn); + if(itype == CONN_TYPE_DIR && + !conn->marked_for_close && + conn->timestamp_lastwritten + 5*60 < now) { + log_fn(LOG_INFO,"Expiring wedged directory conn (fd %d, purpose %d)", conn->s, conn->purpose); + connection_mark_for_close(conn); + return; + } + + /* If we haven't written to an OR connection for a while, then either nuke + the connection or send a keepalive, depending. */ + if(connection_speaks_cells(conn) && + now >= conn->timestamp_lastwritten + options.KeepalivePeriod) { + routerinfo_t *router = router_get_by_digest(conn->identity_digest); + if((!connection_state_is_open(conn)) || + (!clique_mode() && !circuit_get_by_conn(conn) && + (!router || !server_mode() || !router_is_clique_mode(router)))) { + /* our handshake has expired; + * or we're not an authdirserver, we have no circuits, and + * either he's an OP, we're an OP, or we're both ORs and he's + * running 0.0.8 and he's not an authdirserver, + * then kill it. */ + log_fn(LOG_INFO,"Expiring connection to %d (%s:%d).", + i,conn->address, conn->port); + /* flush anything waiting, e.g. a destroy for a just-expired circ */ + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; + } else { + /* either in clique mode, or we've got a circuit. send a padding cell. */ + log_fn(LOG_DEBUG,"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, conn); + } + } +} + +#define MIN_BW_TO_PUBLISH_DESC 5000 /* 5000 bytes/s sustained */ +#define MIN_UPTIME_TO_PUBLISH_DESC (30*60) /* half an hour */ + +/** Decide if we're a publishable server or just a client. We are a server if: + * - We have the AuthoritativeDirectory option set. + * or + * - We don't have the ClientOnly option set; and + * - We have ORPort set; and + * - We have been up for at least MIN_UPTIME_TO_PUBLISH_DESC seconds; and + * - We have processed some suitable minimum bandwidth recently; and + * - We believe we are reachable from the outside. + */ +static int decide_if_publishable_server(time_t now) { + int bw; + + bw = rep_hist_bandwidth_assess(now); + router_set_advertised_bandwidth(bw); + + if(options.ClientOnly) + return 0; + if(!options.ORPort) + return 0; + + + /* XXX008 for now, you're only a server if you're a server */ + return server_mode(); + + + /* here, determine if we're reachable */ + if(0) { /* we've recently failed to reach our IP/ORPort from the outside */ + return 0; + } + + + + + if(bw < MIN_BW_TO_PUBLISH_DESC) + return 0; + if(options.AuthoritativeDir) + return 1; + if(stats_n_seconds_uptime < MIN_UPTIME_TO_PUBLISH_DESC) + return 0; + + return 1; +} + +/** Return true iff we believe ourselves to be an authoritative + * directory server. + */ +int authdir_mode(void) { + return (options.AuthoritativeDir != 0); +} + +/** Return true iff we try to stay connected to all ORs at once. + */ +int clique_mode(void) { + return authdir_mode(); +} + +/** Return true iff we are trying to be a server. + */ +int server_mode(void) { + return (options.ORPort != 0); +} + +/** Remember if we've advertised ourselves to the dirservers. */ +static int server_is_advertised=0; + +/** Return true iff we have published our descriptor lately. + */ +int advertised_server_mode(void) { + return server_is_advertised; +} + +/** Return true iff we are trying to be a socks proxy. */ +int proxy_mode(void) { + return (options.SocksPort != 0); +} + +/** Perform regular maintenance tasks. This function gets run once per + * second by prepare_for_poll. + */ +static void run_scheduled_events(time_t now) { + static long time_to_fetch_directory = 0; + static time_t last_uploaded_services = 0; + static time_t last_rotated_certificate = 0; + int i; + + /** 0. See if we've been asked to shut down and our timeout has + * expired. If so, exit now. + */ + if(shutting_down && shutting_down <= now) { + log(LOG_NOTICE,"Clean shutdown finished. Exiting."); + tor_cleanup(); + exit(0); + } + + /** 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys, + * shut down and restart all cpuworkers, and update the directory if + * necessary. + */ + if (server_mode() && get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) { + log_fn(LOG_INFO,"Rotating onion key."); + rotate_onion_key(); + cpuworkers_rotate(); + if (router_rebuild_descriptor()<0) { + log_fn(LOG_WARN, "Couldn't rebuild router descriptor"); + } + if(advertised_server_mode()) + router_upload_dir_desc_to_dirservers(); + } + + /** 1b. Every MAX_SSL_KEY_LIFETIME seconds, we change our TLS context. */ + if (!last_rotated_certificate) + last_rotated_certificate = now; + /*XXXX008 we should remove the server_mode() check once OPs also use + * identity keys (which they can't do until the known-router check in + * connection_or.c is removed. */ + if (server_mode() && last_rotated_certificate+MAX_SSL_KEY_LIFETIME < now) { + log_fn(LOG_INFO,"Rotating tls context."); + if (tor_tls_context_new(get_identity_key(), 1, options.Nickname, + MAX_SSL_KEY_LIFETIME) < 0) { + log_fn(LOG_WARN, "Error reinitializing TLS context"); + } + last_rotated_certificate = now; + /* XXXX We should rotate TLS connections as well; this code doesn't change + * XXXX them at all. */ + } + + /** 2. Every DirFetchPostPeriod seconds, we get a new directory and upload + * our descriptor (if we've passed our internal checks). */ + if(time_to_fetch_directory < now) { + + if(decide_if_publishable_server(now)) { + server_is_advertised = 1; + router_rebuild_descriptor(); + router_upload_dir_desc_to_dirservers(); + } else { + server_is_advertised = 0; + } + + routerlist_remove_old_routers(); /* purge obsolete entries */ + + if(authdir_mode()) { + /* We're a directory; dump any old descriptors. */ + dirserv_remove_old_servers(); + } + if(server_mode()) { + /* dirservers try to reconnect, in case connections have failed; + * and normal servers try to reconnect to dirservers */ + router_retry_connections(); + } + + directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0); + + /* Force an upload of our rend descriptors every DirFetchPostPeriod seconds. */ + rend_services_upload(1); + last_uploaded_services = now; + rend_cache_clean(); /* should this go elsewhere? */ + + time_to_fetch_directory = now + options.DirFetchPostPeriod; + } + + /** 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 3, so it can try building more if + * it's not comfortable with the number of available circuits. + */ + circuit_expire_building(now); + + /** 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 3, 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(); + + /** 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 NewCircuitPeriod seconds ago, + * and we make a new circ if there are no clean circuits. + */ + if(has_fetched_directory) + circuit_build_needed_circs(now); + + /** 5. We do housekeeping for each connection... */ + for(i=0;i current_second) { /* the second has rolled over. check more stuff. */ + + if(current_second) + stats_n_seconds_uptime += (now.tv_sec - current_second); + assert_all_pending_dns_resolves_ok(); + run_scheduled_events(now.tv_sec); + assert_all_pending_dns_resolves_ok(); + + current_second = now.tv_sec; /* remember which second it is, for next time */ + } + + for(i=0;is); + return 0; /* has pending bytes to read; don't let poll wait. */ + } + } + + return (1000 - (now.tv_usec / 1000)); /* how many milliseconds til the next second? */ +} + +/** Configure the Tor process from the command line arguments and from the + * configuration file. + */ +static int init_from_config(int argc, char **argv) { + /* read the configuration file. */ + if(getconfig(argc,argv,&options)) { + log_fn(LOG_ERR,"Reading config failed. For usage, try -h."); + return -1; + } + + /* Setuid/setgid as appropriate */ + if(options.User || options.Group) { + if(switch_id(options.User, options.Group) != 0) { + return -1; + } + } + + /* Ensure data directory is private; create if possible. */ + if (get_data_directory(&options) && + check_private_dir(get_data_directory(&options), 1) != 0) { + log_fn(LOG_ERR, "Couldn't access/create private data directory %s", + get_data_directory(&options)); + return -1; + } + + /* Start backgrounding the process, if requested. */ + if (options.RunAsDaemon) { + start_daemon(get_data_directory(&options)); + } + + /* Configure the log(s) */ + if (config_init_logs(&options)<0) + return -1; + /* Close the temporary log we used while starting up, if it isn't already + * gone. */ + close_temp_logs(); + + /* Set up our buckets */ + connection_bucket_init(); + stats_prev_global_read_bucket = global_read_bucket; + + /* Finish backgrounding the process */ + if(options.RunAsDaemon) { + /* XXXX Can we delay this any more? */ + finish_daemon(); + } + + /* 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); + + return 0; +} + +/** Called when we get a SIGHUP: reload configuration files and keys, + * retry all connections, re-upload all descriptors, and so on. */ +static int do_hup(void) { + char keydir[512]; + + log_fn(LOG_NOTICE,"Received sighup. Reloading config."); + has_completed_circuit=0; + mark_logs_temp(); /* Close current logs once new logs are open. */ + /* first, reload config variables, in case they've changed */ + /* no need to provide argc/v, they've been cached inside init_from_config */ + if (init_from_config(0, NULL) < 0) { + tor_cleanup(); + exit(1); + } + /* reload keys as needed for rendezvous services. */ + if (rend_service_load_keys()<0) { + log_fn(LOG_ERR,"Error reloading rendezvous service keys"); + tor_cleanup(); + exit(1); + } + if(retry_all_listeners() < 0) { + log_fn(LOG_ERR,"Failed to bind one of the listener ports."); + return -1; + } + if(authdir_mode()) { + /* reload the approved-routers file */ + sprintf(keydir,"%s/approved-routers", get_data_directory(&options)); + log_fn(LOG_INFO,"Reloading approved fingerprints from %s...",keydir); + if(dirserv_parse_fingerprint_file(keydir) < 0) { + log_fn(LOG_WARN, "Error reloading fingerprints. Continuing with old list."); + } + /* Since we aren't fetching a directory, we won't retry rendezvous points + * when it gets in. Try again now. */ + rend_services_introduce(); + } + /* Fetch a new directory. Even authdirservers do this. */ + directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0); + if(server_mode()) { + /* Restart cpuworker and dnsworker processes, so they get up-to-date + * configuration options. */ + cpuworkers_rotate(); + if (server_mode()) + dnsworkers_rotate(); + /* Rebuild fresh descriptor as needed. */ + router_rebuild_descriptor(); + sprintf(keydir,"%s/router.desc", get_data_directory(&options)); + log_fn(LOG_INFO,"Dumping descriptor to %s...",keydir); + if (write_str_to_file(keydir, router_get_my_descriptor())) { + return -1; + } + } + return 0; +} + +/** Tor main loop. */ +static int do_main_loop(void) { + int i; + int timeout; + int poll_result; + + /* Initialize the history structures. */ + rep_hist_init(); + /* Intialize the service cache. */ + rend_cache_init(); + + /* load the private keys, if we're supposed to have them, and set up the + * TLS context. */ + if (init_keys() < 0 || rend_service_load_keys() < 0) { + log_fn(LOG_ERR,"Error initializing keys; exiting"); + return -1; + } + + /* load the routers file */ + if(options.RouterFile) { + routerlist_clear_trusted_directories(); + if (router_load_routerlist_from_file(options.RouterFile, 1) < 0) { + log_fn(LOG_ERR,"Error loading router list."); + return -1; + } + } + + if(authdir_mode()) { + /* the directory is already here, run startup things */ + directory_has_arrived(); + } + + if(server_mode()) { + /* launch cpuworkers. Need to do this *after* we've read the onion key. */ + cpu_init(); + } + + /* start up the necessary listeners based on which ports are non-zero. */ + if(retry_all_listeners() < 0) { + log_fn(LOG_ERR,"Failed to bind one of the listener ports."); + return -1; + } + + for(;;) { +#ifdef MS_WINDOWS /* Do service stuff only on windows. */ + if (service_status.dwCurrentState != SERVICE_RUNNING) { + return 0; + } +#else /* do signal stuff only on unix */ + if(please_shutdown) { + if(!server_mode()) { /* do it now */ + log(LOG_NOTICE,"Interrupt: exiting cleanly."); + tor_cleanup(); + exit(0); + } + if(shutting_down) { /* we've already been asked. do it now. */ + log(LOG_NOTICE,"Second sigint received; exiting now."); + tor_cleanup(); + exit(0); + } else { + log(LOG_NOTICE,"Interrupt: will shut down in %d seconds. Interrupt again to exit now.", SHUTDOWN_WAIT_LENGTH); + shutting_down = time(NULL) + SHUTDOWN_WAIT_LENGTH; + } + please_shutdown = 0; + } + if(please_dumpstats) { + /* prefer to log it at INFO, but make sure we always see it */ + dumpstats(get_min_log_level()>LOG_INFO ? get_min_log_level() : LOG_INFO); + please_dumpstats = 0; + } + if(please_reset) { + do_hup(); + please_reset = 0; + } + if(please_reap_children) { + while(waitpid(-1,NULL,WNOHANG)) ; /* keep reaping until no more zombies */ + please_reap_children = 0; + } +#endif /* signal stuff */ + + timeout = prepare_for_poll(); + + /* poll until we have an event, or the second ends */ + poll_result = tor_poll(poll_array, nfds, timeout); + + /* let catch() handle things like ^c, and otherwise don't worry about it */ + if(poll_result < 0) { + /* let the program survive things like ^z */ + if(tor_socket_errno(-1) != EINTR) { + log_fn(LOG_ERR,"poll failed: %s [%d]", + tor_socket_strerror(tor_socket_errno(-1)), + tor_socket_errno(-1)); + return -1; + } else { + log_fn(LOG_DEBUG,"poll interrupted."); + } + } + + /* do all the reads and errors first, so we can detect closed sockets */ + for(i=0;is, 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)) { + log(severity,"Conn %d is to '%s:%d'.",i,conn->address, conn->port); + log(severity,"Conn %d: %d bytes waiting on inbuf (last read %d secs ago)",i, + (int)buf_datalen(conn->inbuf), + (int)(now - conn->timestamp_lastread)); + log(severity,"Conn %d: %d bytes waiting on outbuf (last written %d secs ago)",i, + (int)buf_datalen(conn->outbuf), (int)(now - conn->timestamp_lastwritten)); + } + circuit_dump_by_conn(conn, severity); /* dump info about all the circuits using this conn */ + } + log(severity, + "Cells processed: %10lu padding\n" + " %10lu create\n" + " %10lu created\n" + " %10lu relay\n" + " (%10lu relayed)\n" + " (%10lu delivered)\n" + " %10lu destroy", + stats_n_padding_cells_processed, + stats_n_create_cells_processed, + stats_n_created_cells_processed, + stats_n_relay_cells_processed, + stats_n_relay_cells_relayed, + stats_n_relay_cells_delivered, + stats_n_destroy_cells_processed); + if (stats_n_data_cells_packaged) + log(severity,"Average packaged cell fullness: %2.3f%%", + 100*(((double)stats_n_data_bytes_packaged) / + (stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) ); + if (stats_n_data_cells_received) + log(severity,"Average delivered cell fullness: %2.3f%%", + 100*(((double)stats_n_data_bytes_received) / + (stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) ); + + if (stats_n_seconds_uptime) + log(severity,"Average bandwidth used: %d bytes/sec", + (int) (stats_n_bytes_read/stats_n_seconds_uptime)); + + rep_hist_dump_stats(now,severity); + rend_service_dump_stats(severity); +} + +/** Called before we make any calls to network-related functions. + * (Some operating systems require their network libraries to be + * initialized.) */ +static int network_init(void) +{ +#ifdef MS_WINDOWS + /* This silly exercise is necessary before windows will allow gethostbyname to work. + */ + WSADATA WSAData; + int r; + r = WSAStartup(0x101,&WSAData); + if (r) { + log_fn(LOG_WARN,"Error initializing windows network layer: code was %d",r); + return -1; + } + /* XXXX We should call WSACleanup on exit, I think. */ +#endif + return 0; +} + +/** Called by exit() as we shut down the process. + */ +void exit_function(void) +{ +/* XXX if we ever daemonize, this gets called immediately */ +#ifdef MS_WINDOWS + WSACleanup(); +#endif +} + +/** Main entry point for the Tor command-line client. + */ +static int tor_init(int argc, char *argv[]) { + + /* give it somewhere to log to initially */ + add_temp_log(); + log_fn(LOG_NOTICE,"Tor v%s. This is experimental software. Do not use it if you need anonymity.",VERSION); + + if (network_init()<0) { + log_fn(LOG_ERR,"Error initializing network; exiting."); + return -1; + } + atexit(exit_function); + + if (init_from_config(argc,argv) < 0) + return -1; + +#ifndef MS_WINDOWS + if(geteuid()==0) + log_fn(LOG_WARN,"You are running Tor as root. You don't need to, and you probably shouldn't."); +#endif + + if(server_mode()) { /* only spawn dns handlers if we're a router */ + dns_init(); /* initialize the dns resolve tree, and spawn workers */ + } + if(proxy_mode()) { + client_dns_init(); /* init the client dns cache */ + } + +#ifndef MS_WINDOWS /* do signal stuff only on unix */ +{ + struct sigaction action; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + + action.sa_handler = catch; + sigaction(SIGINT, &action, NULL); /* do a controlled slow shutdown */ + sigaction(SIGTERM, &action, NULL); /* to terminate now */ + sigaction(SIGPIPE, &action, NULL); /* otherwise sigpipe kills us */ + sigaction(SIGUSR1, &action, NULL); /* dump stats */ + sigaction(SIGHUP, &action, NULL); /* to reload config, retry conns, etc */ + sigaction(SIGCHLD, &action, NULL); /* handle dns/cpu workers that exit */ +} +#endif /* signal stuff */ + + crypto_global_init(); + crypto_seed_rng(); + return 0; +} + +/** Do whatever cleanup is necessary before shutting Tor down. */ +void tor_cleanup(void) { + /* 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); + crypto_global_cleanup(); +} + +#ifdef MS_WINDOWS +void nt_service_control(DWORD request) +{ + switch (request) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + log(LOG_ERR, "Got stop/shutdown request; shutting down cleanly."); + service_status.dwWin32ExitCode = 0; + service_status.dwCurrentState = SERVICE_STOPPED; + return; + } + SetServiceStatus(hStatus, &service_status); +} + +void nt_service_body(int argc, char **argv) +{ + int err; + FILE *f; + f = fopen("d:\\foo.txt", "w"); + fprintf(f, "POINT 1\n"); + fclose(f); + service_status.dwServiceType = SERVICE_WIN32; + service_status.dwCurrentState = SERVICE_START_PENDING; + service_status.dwControlsAccepted = + SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + service_status.dwWin32ExitCode = 0; + service_status.dwServiceSpecificExitCode = 0; + service_status.dwCheckPoint = 0; + service_status.dwWaitHint = 0; + hStatus = RegisterServiceCtrlHandler("Tor", (LPHANDLER_FUNCTION) nt_service_control); + if (hStatus == 0) { + // failed; + return; + } + err = tor_init(argc, argv); // refactor this part out of tor_main and do_main_loop + if (err) { + // failed. + service_status.dwCurrentState = SERVICE_STOPPED; + service_status.dwWin32ExitCode = -1; + SetServiceStatus(hStatus, &service_status); + return; + } + service_status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(hStatus, &service_status); + do_main_loop(); + tor_cleanup(); + return; +} + +void nt_service_main(void) +{ + SERVICE_TABLE_ENTRY table[2]; + table[0].lpServiceName = "Tor"; + table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)nt_service_body; + table[1].lpServiceName = NULL; + table[1].lpServiceProc = NULL; + if (!StartServiceCtrlDispatcher(table)) + printf("Error was %d\n",GetLastError()); +} +#endif + +int tor_main(int argc, char *argv[]) { +#ifdef MS_WINDOWS_SERVICE + nt_service_main(); + return 0; +#else + if (tor_init(argc, argv)<0) + return -1; + do_main_loop(); + tor_cleanup(); + return -1; +#endif +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/onion.c b/tags/tor-0_0_8pre1/src/or/onion.c new file mode 100644 index 0000000000..8cf0073540 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/onion.c @@ -0,0 +1,326 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file onion.c + * \brief Functions to queue create cells, and handle onionskin + * parsing and creation. + **/ + +#include "or.h" + +extern or_options_t options; /**< command-line and config-file options */ + +struct onion_queue_t { + circuit_t *circ; + struct onion_queue_t *next; +}; + +/** global (within this file) variables used by the next few functions */ +static struct onion_queue_t *ol_list=NULL; +static struct onion_queue_t *ol_tail=NULL; +/** length of ol_list */ +static int ol_length=0; + +/** 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(circuit_t *circ) { + struct onion_queue_t *tmp; + + tmp = tor_malloc(sizeof(struct onion_queue_t)); + tmp->circ = circ; + tmp->next = NULL; + + if(!ol_tail) { + tor_assert(!ol_list); + tor_assert(!ol_length); + ol_list = tmp; + ol_tail = tmp; + ol_length++; + return 0; + } + + tor_assert(ol_list); + tor_assert(!ol_tail->next); + + if(ol_length >= options.MaxOnionsPending) { + log_fn(LOG_WARN,"Already have %d onions queued. Closing.", ol_length); + free(tmp); + return -1; + } + + ol_length++; + ol_tail->next = tmp; + ol_tail = tmp; + return 0; + +} + +/** Remove the first item from ol_list and return it, or return + * NULL if the list is empty. + */ +circuit_t *onion_next_task(void) { + circuit_t *circ; + + if(!ol_list) + return NULL; /* no onions pending, we're done */ + + tor_assert(ol_list->circ); + tor_assert(ol_list->circ->p_conn); /* make sure it's still valid */ + tor_assert(ol_length > 0); + circ = ol_list->circ; + onion_pending_remove(ol_list->circ); + return circ; +} + +/** 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(circuit_t *circ) { + struct onion_queue_t *tmpo, *victim; + + if(!ol_list) + return; /* nothing here. */ + + /* first check to see if it's the first entry */ + tmpo = ol_list; + if(tmpo->circ == circ) { + /* it's the first one. remove it from the list. */ + ol_list = tmpo->next; + if(!ol_list) + ol_tail = NULL; + ol_length--; + victim = tmpo; + } else { /* we need to hunt through the rest of the list */ + for( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ; + if(!tmpo->next) { + log_fn(LOG_DEBUG,"circ (p_circ_id %d) not in list, probably at cpuworker.",circ->p_circ_id); + return; + } + /* now we know tmpo->next->circ == circ */ + victim = tmpo->next; + tmpo->next = victim->next; + if(ol_tail == victim) + ol_tail = tmpo; + ol_length--; + } + + /* now victim points to the element that needs to be removed */ + + free(victim); +} + +/*----------------------------------------------------------------------*/ + +/** 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_create(crypto_pk_env_t *dest_router_key, + crypto_dh_env_t **handshake_state_out, + char *onion_skin_out) /* Must be ONIONSKIN_CHALLENGE_LEN bytes */ +{ + char *challenge = NULL; + crypto_dh_env_t *dh = NULL; + int dhbytes, pkbytes; + + *handshake_state_out = NULL; + memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN); + + if (!(dh = crypto_dh_new())) + goto err; + + dhbytes = crypto_dh_get_bytes(dh); + pkbytes = crypto_pk_keysize(dest_router_key); + tor_assert(dhbytes == 128); + tor_assert(pkbytes == 128); + challenge = tor_malloc_zero(DH_KEY_LEN); + + if (crypto_dh_get_public(dh, challenge, dhbytes)) + goto err; + +#ifdef DEBUG_ONION_SKINS +#define PA(a,n) \ + { int _i; for (_i = 0; _i0) + break; + } + if (len<0) { + log_fn(LOG_WARN, "Couldn't decrypt onionskin: client may be using old onion key"); + goto err; + } else if (len != DH_KEY_LEN) { + log_fn(LOG_WARN, "Unexpected onionskin length after decryption: %d", + len); + goto err; + } + + dh = crypto_dh_new(); + if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) + goto err; + +#ifdef DEBUG_ONION_SKINS + printf("Server: server g^y:"); + PA(handshake_reply_out+0,3); + printf("..."); + PA(handshake_reply_out+125,3); + puts(""); +#endif + + key_material = tor_malloc(DIGEST_LEN+key_out_len); + len = crypto_dh_compute_secret(dh, challenge, DH_KEY_LEN, + key_material, DIGEST_LEN+key_out_len); + if (len < 0) + 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); + +#ifdef DEBUG_ONION_SKINS + printf("Server: key material:"); + PA(buf, DH_KEY_LEN); + puts(""); + printf("Server: keys out:"); + PA(key_out, key_out_len); + puts(""); +#endif + + tor_free(key_material); + crypto_dh_free(dh); + return 0; + err: + 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_client_handshake(crypto_dh_env_t *handshake_state, + char *handshake_reply, /* Must be ONIONSKIN_REPLY_LEN bytes */ + char *key_out, + int key_out_len) +{ + int len; + char *key_material=NULL; + tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN); + +#ifdef DEBUG_ONION_SKINS + printf("Client: server g^y:"); + PA(handshake_reply+0,3); + printf("..."); + PA(handshake_reply+125,3); + puts(""); +#endif + + key_material = tor_malloc(20+key_out_len); + len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN, + key_material, 20+key_out_len); + if (len < 0) + return -1; + + if(memcmp(key_material, handshake_reply+DH_KEY_LEN, 20)) { + /* H(K) does *not* match. Something fishy. */ + tor_free(key_material); + log_fn(LOG_WARN,"Digest DOES NOT MATCH on onion handshake. Bug or attack."); + return -1; + } + + /* use the rest of the key material for our shared keys, digests, etc */ + memcpy(key_out, key_material+20, key_out_len); + +#ifdef DEBUG_ONION_SKINS + printf("Client: keys out:"); + PA(key_out, key_out_len); + puts(""); +#endif + + tor_free(key_material); + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/or.h b/tags/tor-0_0_8pre1/src/or/or.h new file mode 100644 index 0000000000..8bdfb1f80e --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/or.h @@ -0,0 +1,1443 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file or.h + * + * \brief Master header file for Tor-specific functionality. + */ + +#ifndef __OR_H +#define __OR_H + +#include "orconfig.h" +#ifdef MS_WINDOWS +#define WIN32_WINNT 0x400 +#define _WIN32_WINNT 0x400 +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif +#include "../common/torint.h" +#include "../common/fakepoll.h" +#ifdef HAVE_INTTYPES_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include /* FreeBSD needs this to know what version it is */ +#endif +#ifdef HAVE_SYS_LIMITS_H +#include +#endif +#ifdef HAVE_MACHINE_LIMITS_H +#ifndef __FreeBSD__ + /* FreeBSD has a bug where it complains that this file is obsolete, + and I should migrate to using sys/limits. It complains even when + I include both. */ +#include +#endif +#endif +#ifdef HAVE_SYS_TYPES_H +#include /* Must be included before sys/stat.h for Ultrix */ +#endif +#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_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_TIME_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 HAVE_WINSOCK_H +#include +#endif +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif + +#ifdef MS_WINDOWS +#include +#include +#include +#include +#define snprintf _snprintf +#endif + +#include "../common/crypto.h" +#include "../common/tortls.h" +#include "../common/log.h" +#include "../common/util.h" + +/** Upper bound on maximum simultaneous connections; can be lowered by + * config file. */ +#define MAXCONNECTIONS 10000 + +#define DEFAULT_BANDWIDTH_OP (1024 * 1000) +#define MAX_NICKNAME_LEN 19 +/* Hex digest plus dollar sign. */ +#define MAX_HEX_NICKNAME_LEN HEX_DIGEST_LEN+1 +#define MAX_DIR_SIZE 500000 + +#ifdef TOR_PERF +/** How long do we keep DNS cache entries before purging them? */ +#define MAX_DNS_ENTRY_AGE (150*60) +#else +#define MAX_DNS_ENTRY_AGE (15*60) +#endif + +/** How often do we rotate onion keys? */ +#define MIN_ONION_KEY_LIFETIME (120*60) +/** How often do we rotate TLS contexts? */ +#define MAX_SSL_KEY_LIFETIME (120*60) + +/** How old do we allow a router to get before removing it, either + * from the descriptor list (for dirservers) or the router list (for others)? + * In seconds. */ +#define ROUTER_MAX_AGE (60*60*24) + +#define CIRC_ID_TYPE_LOWER 0 +#define CIRC_ID_TYPE_HIGHER 1 + +#define _CONN_TYPE_MIN 3 +/** Type for sockets listening for OR connections. */ +#define CONN_TYPE_OR_LISTENER 3 +/** Type for OR-to-OR or OP-to-OR connections. */ +#define CONN_TYPE_OR 4 +/** Type for connections from final OR to chosen destination. */ +#define CONN_TYPE_EXIT 5 +/** Type for sockets listening for SOCKS connections. */ +#define CONN_TYPE_AP_LISTENER 6 +/** Type for SOCKS connections to OP. */ +#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 +/** Type for connections to local dnsworker processes. */ +#define CONN_TYPE_DNSWORKER 10 +/** Type for connections to local cpuworker processes. */ +#define CONN_TYPE_CPUWORKER 11 +#define _CONN_TYPE_MAX 11 + +/** State for any listener connection. */ +#define LISTENER_STATE_READY 0 + +#define _DNSWORKER_STATE_MIN 1 +/** State for a connection to a dnsworker process that's idle. */ +#define DNSWORKER_STATE_IDLE 1 +/** State for a connection to a dnsworker process that's resolving a hostname. */ +#define DNSWORKER_STATE_BUSY 2 +#define _DNSWORKER_STATE_MAX 2 + +#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 _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: SSL is handshaking, not done yet. */ +#define OR_CONN_STATE_HANDSHAKING 2 +/** State for a connection to an OR: Ready to send/receive cells. */ +#define OR_CONN_STATE_OPEN 3 +#define _OR_CONN_STATE_MAX 3 + +#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 +#if 0 +#define EXIT_CONN_STATE_CLOSE 3 /* flushing the buffer, then will close */ +#define EXIT_CONN_STATE_CLOSE_WAIT 4 /* have sent a destroy, awaiting a confirmation */ +#endif + +/* 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 rescriptor. */ +#define AP_CONN_STATE_RENDDESC_WAIT 6 +/** State for a SOCKS connection: waiting for a completed circuit. */ +#define AP_CONN_STATE_CIRCUIT_WAIT 7 +/** State for a SOCKS connection: sent BEGIN, waiting for CONNECTED. */ +#define AP_CONN_STATE_CONNECT_WAIT 8 +/** State for a SOCKS connection: send RESOLVE, waiting for RESOLVED. */ +#define AP_CONN_STATE_RESOLVE_WAIT 9 +/** State for a SOCKS connection: ready to send and receive. */ +#define AP_CONN_STATE_OPEN 10 +#define _AP_CONN_STATE_MAX 10 + +#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 at directory server: waiting for HTTP request. */ +#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 4 +/** State for connection at directory server: sending HTTP response. */ +#define DIR_CONN_STATE_SERVER_WRITING 5 +#define _DIR_CONN_STATE_MAX 5 + +#define _DIR_PURPOSE_MIN 1 +/** Purpose for connection to directory server: download a directory. */ +#define DIR_PURPOSE_FETCH_DIR 1 +/** Purpose for connection to directory server: download just the list + * of running routers. */ +#define DIR_PURPOSE_FETCH_RUNNING_LIST 2 +/** Purpose for connection to directory server: download a rendezvous + * descriptor. */ +#define DIR_PURPOSE_FETCH_RENDDESC 3 +/** Purpose for connection to directory server: set after a rendezvous + * descriptor is downloaded. */ +#define DIR_PURPOSE_HAS_FETCHED_RENDDESC 4 +/** Purpose for connection to directory server: upload a server descriptor. */ +#define DIR_PURPOSE_UPLOAD_DIR 5 +/** Purpose for connection to directory server: upload a rendezvous + * descriptor. */ +#define DIR_PURPOSE_UPLOAD_RENDDESC 6 +/** Purpose for connection at a directory server. */ +#define DIR_PURPOSE_SERVER 7 +#define _DIR_PURPOSE_MAX 7 + +#define _EXIT_PURPOSE_MIN 1 +#define EXIT_PURPOSE_CONNECT 1 +#define EXIT_PURPOSE_RESOLVE 2 +#define _EXIT_PURPOSE_MAX 2 + +/** Circuit state: I'm the OP, 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_conn is still connecting. */ +#define CIRCUIT_STATE_OR_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 + +/** Hidden-service-side circuit purpose: at Bob, waiting for introductions. */ +#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 13 +/** Hidden-service-side circuit purpose: at Bob, successfully established + * intro. */ +#define CIRCUIT_PURPOSE_S_INTRO 14 +/** Hidden-service-side circuit purpose: at Bob, connecting to rend point. */ +#define CIRCUIT_PURPOSE_S_CONNECT_REND 15 +/** Hidden-service-side circuit purpose: at Bob, rendezvous established. */ +#define CIRCUIT_PURPOSE_S_REND_JOINED 16 +#define _CIRCUIT_PURPOSE_MAX 16 + +/** True iff the circuit purpose p is for a circuit at the OP + * that this OP has originated. */ +#define CIRCUIT_PURPOSE_IS_ORIGIN(p) ((p)>_CIRCUIT_PURPOSE_OR_MAX) +#define CIRCUIT_IS_ORIGIN(c) (CIRCUIT_PURPOSE_IS_ORIGIN((c)->purpose)) + +#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_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 + +#define _MIN_END_STREAM_REASON 1 +#define END_STREAM_REASON_MISC 1 +#define END_STREAM_REASON_RESOLVEFAILED 2 +#define END_STREAM_REASON_CONNECTFAILED 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 _MAX_END_STREAM_REASON 7 + +#define RESOLVED_TYPE_IPV4 4 +#define RESOLVED_TYPE_IPV6 6 +#define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0 +#define RESOLVED_TYPE_ERROR 0xF1 + +/** Length of 'y' portion of 'y.onion' URL. */ +#define REND_SERVICE_ID_LEN 16 + +#define CELL_DIRECTION_IN 1 +#define CELL_DIRECTION_OUT 2 + +#ifdef TOR_PERF +#define CIRCWINDOW_START 10000 +#define CIRCWINDOW_INCREMENT 1000 +#define STREAMWINDOW_START 5000 +#define STREAMWINDOW_INCREMENT 500 +#else +#define CIRCWINDOW_START 1000 +#define CIRCWINDOW_INCREMENT 100 +#define STREAMWINDOW_START 500 +#define STREAMWINDOW_INCREMENT 50 +#endif + +/* cell commands */ +#define CELL_PADDING 0 +#define CELL_CREATE 1 +#define CELL_CREATED 2 +#define CELL_RELAY 3 +#define CELL_DESTROY 4 + +/* legal characters in a nickname */ +#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +#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] + */ + +#define CELL_PAYLOAD_SIZE 509 +#define CELL_NETWORK_SIZE 512 + +#define RELAY_HEADER_SIZE (1+2+2+4+2) +#define RELAY_PAYLOAD_SIZE (CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE) + +/** Parsed onion routing cell. All communication from OP-to-OR, or from + * OR-to-OR, is via cells. */ +typedef struct { + uint16_t circ_id; /**< Circuit which received the cell. */ + unsigned char command; /**< Type of the cell: one of PADDING, CREATE, RELAY, + * or DESTROY. */ + unsigned char payload[CELL_PAYLOAD_SIZE]; /**< Cell body. */ +} cell_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. */ + uint16_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; + +#define CONNECTION_MAGIC 0x7C3C304Eu +/** Description of a connection to another host or process, and associated + * data. */ +struct connection_t { + uint32_t magic; /**< For memory debugging: must equal CONNECTION_MAGIC. */ + + uint8_t type; /**< What kind of connection is this? */ + uint8_t state; /**< Current state of this connection. */ + uint8_t purpose; /**< Only used for DIR types currently. */ + uint8_t wants_to_read; /**< Boolean: should we start reading again once + * the bandwidth throttler allows it? + */ + uint8_t wants_to_write; /**< Boolean: should we start writing again once + * the bandwidth throttler allows reads? + */ + int s; /**< Our socket; -1 if this connection is closed. */ + int poll_index; /**< Index of this conn into the poll_array. */ + int marked_for_close; /**< Boolean: should we close this conn on the next + * iteration of the main loop? + */ + char *marked_for_close_file; /**< For debugging: in which file were we marked + * for close? */ + int hold_open_until_flushed; /**< Despite this connection's being marked + * for close, do we flush it before closing it? + */ + + buf_t *inbuf; /**< Buffer holding data read over this connection. */ + int inbuf_reached_eof; /**< Boolean: did read() return 0 on this conn? */ + time_t timestamp_lastread; /**< When was the last time poll() said we could read? */ + + buf_t *outbuf; /**< Buffer holding data to write over this connection. */ + int outbuf_flushlen; /**< How much data should we try to flush from the + * outbuf? */ + time_t timestamp_lastwritten; /**< When was the last time poll() said we could write? */ + + time_t timestamp_created; /**< When was this connection_t created? */ + + uint32_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. */ + char *address; /**< FQDN (or IP) of the guy on the other end. + * strdup into this, because free_connection frees it. + */ + crypto_pk_env_t *identity_pkey; /**< Public RSA key for the other side's + * signing key. */ + char identity_digest[DIGEST_LEN]; /**< Hash of identity_pkey */ + char *nickname; /**< Nickname of OR on other side (if any). */ + +/* Used only by OR connections: */ + tor_tls *tls; /**< TLS connection state (OR only.) */ + uint16_t next_circ_id; /**< Which circ_id do we try to use next on + * this connection? This is always in the + * range 0..1<<15-1. (OR only.)*/ + + /* bandwidth and receiver_bucket only used by ORs in OPEN state: */ + int bandwidth; /**< Connection bandwidth. (OPEN ORs only.) */ + int receiver_bucket; /**< When this hits 0, stop receiving. Every second we + * add 'bandwidth' to this, capping it at 10*bandwidth. + * (OPEN ORs only) + */ + +/* Used only by DIR and AP connections: */ + char rend_query[REND_SERVICE_ID_LEN+1]; /**< What rendezvous service are we + * querying for? (DIR/AP only) */ + +/* Used only by edge connections: */ + uint16_t stream_id; + struct connection_t *next_stream; /**< Points to the next stream at this + * edge, if any (Edge only). */ + struct crypt_path_t *cpath_layer; /**< A pointer to which node in the circ + * this conn exits at. (Edge only.) */ + int package_window; /**< How many more relay cells can i send into the + * circuit? (Edge only.) */ + int deliver_window; /**< How many more relay cells can end at me? (Edge + * only.) */ + + int done_sending; /**< For half-open connections; not used currently. */ + int done_receiving; /**< For half-open connections; not used currently. */ + char has_sent_end; /**< For debugging: set once we've set the stream end, + and check in circuit_about_to_close_connection(). */ + char num_retries; /**< How many times have we re-tried beginning this stream? (Edge only) */ + + /* Used only by AP connections */ + socks_request_t *socks_request; /**< SOCKS structure describing request (AP + * only.) */ +}; + +typedef struct connection_t connection_t; + +#define EXIT_POLICY_ACCEPT 1 +#define EXIT_POLICY_REJECT 2 + +/** A linked list of exit policy rules */ +struct exit_policy_t { + char policy_type; /**< One of EXIT_POLICY_ACCEPT or EXIT_POLICY_REJECT. */ + char *string; /**< String representation of this rule. */ + uint32_t addr; /**< Base address to accept or reject. */ + uint32_t msk; /**< Accept/reject all addresses a such that a & msk == + * addr & msk . */ + uint16_t prt_min; /**< Lowest port number to accept/reject. */ + uint16_t prt_max; /**< Highest port number to accept/reject. */ + + struct exit_policy_t *next; /**< Next rule in list. */ +}; + +/** Information about another onion router in the network. */ +typedef struct { + 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 OR-to-OR and OP-to-OR connections. */ + uint16_t socks_port; /**< Port for SOCKS connections. */ + uint16_t dir_port; /**< Port for HTTP directory connections. */ + + time_t published_on; /**< When was the information in this routerinfo_t + * published? */ + + crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */ + crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */ + char identity_digest[DIGEST_LEN]; /**< Digest of identity key */ + + 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 advertisedbandwidth; + struct exit_policy_t *exit_policy; /**< What streams will this OR permit + * to exit? */ + /* local info */ + int is_running; /**< As far as we know, is this OR currently running? */ + time_t status_set_at; /**< When did we last update is_running? */ + int is_verified; /**< Has a trusted dirserver validated this OR? */ + int is_trusted_dir; /**< Do we trust this OR as a directory server? */ + +} routerinfo_t; + +/** Contents of a directory of onion routers. */ +typedef struct { + /** List of routerinfo_t */ + smartlist_t *routers; + /** Which versions of tor are recommended by this directory? */ + char *software_versions; + /** When was the most recent directory that contributed to this list + * published? + */ + time_t published_on; + time_t running_routers_updated_on; + /** Which router is claimed to have signed it? */ + char *signing_router; +} routerlist_t; + +/** Contents of a running-routers list */ +typedef struct running_routers_t { + time_t published_on; /**< When was the list marked as published? */ + /** Which ORs are on the list? Entries may be prefixed with ! and $. */ + smartlist_t *running_routers; +} running_routers_t; + +/** Holds accounting information for a single step in the layered encryption + * performed by a circuit. Used only at the client edge of a circuit. */ +struct crypt_path_t { + + /* crypto environments */ + /** Encryption key and counter for cells heading towards the OR at this + * step. */ + crypto_cipher_env_t *f_crypto; + /** Encryption key and counter for cells heading back from the OR at this + * step. */ + crypto_cipher_env_t *b_crypto; + + /** Digest state for cells heading towards the OR at this step. */ + crypto_digest_env_t *f_digest; /* for integrity checking */ + /** Digest state for cells heading away from the OR at this step. */ + crypto_digest_env_t *b_digest; + + /** Current state of Diffie-Hellman key negotiation with the OR at this + * step. */ + crypto_dh_env_t *handshake_state; + /** Negotiated key material shared with the OR at this step. */ + char handshake_digest[DIGEST_LEN];/* KH in tor-spec.txt */ + + /** IP4 address of the OR at this step. */ + uint32_t addr; + /** Port of the OR at this step. */ + uint16_t port; + /** Identity key digest of the OR at this step. */ + char identity_digest[DIGEST_LEN]; + + /** Is the circuit built to this step? Must be one of: + * - CPATH_STATE_CLOSED (The circuit has not been extended to this step) + * - CPATH_STATE_AWAITING_KEYS (We have sent an EXTEND/CREATE to this step + * and not received an EXTENDED/CREATED) + * - CPATH_STATE_OPEN (The circuit has been extended to this step) */ + uint8_t state; +#define CPATH_STATE_CLOSED 0 +#define CPATH_STATE_AWAITING_KEYS 1 +#define CPATH_STATE_OPEN 2 + struct crypt_path_t *next; /**< Link to next crypt_path_t in the circuit. + * (The list is circular, so the last node + * links to the first.) */ + struct crypt_path_t *prev; /**< Link to previous crypt_path_t in the + * circuit. */ + + int package_window; /**< How many bytes are we allowed to originate ending + * at this step? */ + int deliver_window; /**< How many bytes are we willing to deliver originating + * at this step? */ +}; + +#define CPATH_KEY_MATERIAL_LEN (20*2+16*2) + +#define DH_KEY_LEN DH_BYTES +#define ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\ + CIPHER_KEY_LEN+\ + DH_KEY_LEN) +#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN) +#define REND_COOKIE_LEN DIGEST_LEN + +typedef struct crypt_path_t crypt_path_t; + +/** Information used to build a circuit. */ +typedef struct { + /** Intended length of the final circuit. */ + int desired_path_len; + /** Nickname of planned exit node. */ + char *chosen_exit_name; + /** Identity of planned exit node. */ + char chosen_exit_digest[DIGEST_LEN]; + /** The crypt_path_t to append after rendezvous: used for rendezvous. */ + struct crypt_path_t *pending_final_cpath; + /** How many times has building a circuit for this task failed? */ + int failure_count; +} cpath_build_state_t; + + +#define CIRCUIT_MAGIC 0x35315243u +/** Struct for a path (circuit) through the onion routing network. */ +struct circuit_t { + uint32_t magic; /**< For memory debugging: must equal CIRCUIT_MAGIC. */ + + int marked_for_close; /**< Should we close this circuit at the end of the + * main loop? */ + char *marked_for_close_file; /**< For debugging: in which file was this + * circuit marked for close? */ + + /** The IPv4 address of the OR that is next in this circuit. */ + uint32_t n_addr; + /** The port for the OR that is next in this circuit. */ + uint16_t n_port; + /** The OR connection that is previous in this circuit. */ + connection_t *p_conn; + /** The OR connection that is next in this circuit. */ + connection_t *n_conn; + /** The identity hash of n_conn. */ + char n_conn_id_digest[DIGEST_LEN]; + /** Linked list of AP streams associated with this circuit. */ + connection_t *p_streams; + /** Linked list of Exit streams associated with this circuit. */ + connection_t *n_streams; + /** Linked list of Exit streams associated with this circuit that are + * still being resolved. */ + connection_t *resolving_streams; + /** The next stream_id that will be tried when we're attempting to + * construct a new AP stream originating at this circuit. */ + uint16_t next_stream_id; + /** How many relay data cells can we package (read from edge streams) + * on this circuit before we receive a circuit-level sendme cell asking + * for more? */ + int package_window; + /** How many relay data cells will we deliver (write to edge streams) + * on this circuit? When deliver_window gets low, we send some + * circuit-level sendme cells to indicate that we're willing to accept + * more. */ + int deliver_window; + + /** The circuit_id used in the previous (backward) hop of this circuit. */ + uint16_t p_circ_id; + /** The circuit_id used in the next (forward) hop of this circuit. */ + uint16_t n_circ_id; + + /** The cipher used by intermediate hops for cells heading toward the + * OP. */ + crypto_cipher_env_t *p_crypto; + /** The cipher used by intermediate hops for cells heading away from + * the OP. */ + crypto_cipher_env_t *n_crypto; + + /** The integrity-checking digest used by intermediate hops, for + * cells packaged here and heading towards the OP. + */ + crypto_digest_env_t *p_digest; + /** The integrity-checking digest used by intermediate hops, for + * cells packaged at the OP and arriving here. + */ + crypto_digest_env_t *n_digest; + + /** Build state for this circuit. It includes the intended path + * length, the chosen exit router, rendezvous information, etc. + */ + cpath_build_state_t *build_state; + /** The doubly-linked list of crypt_path_t entries, one per hop, + * for this circuit. This includes ciphers for each hop, + * integrity-checking digests for each hop, and package/delivery + * windows for each hop. + * + * The cpath field is defined only when we are the circuit's origin. + */ + crypt_path_t *cpath; + + /** For storage while passing to cpuworker, or while n_conn is pending. */ + char onionskin[ONIONSKIN_CHALLENGE_LEN]; + + char handshake_digest[DIGEST_LEN]; /**< Stores KH for intermediate hops. */ + + time_t timestamp_created; /**< When was this circuit created? */ + time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the + * circuit is clean. */ + + uint8_t state; /**< Current status of this circuit. */ + uint8_t purpose; /**< Why are we creating this circuit? */ + + /** + * The rend_query field holds y portion of y.onion (nul-terminated) + * if purpose is C_INTRODUCING or C_ESTABLISH_REND, or is a C_GENERAL + * for a hidden service, or is S_*. + */ + char rend_query[REND_SERVICE_ID_LEN+1]; + + /** The rend_pk_digest field holds a hash of location-hidden service's + * PK if purpose is INTRO_POINT or S_ESTABLISH_INTRO or S_RENDEZVOUSING. + */ + char rend_pk_digest[DIGEST_LEN]; + + /** Holds rendezvous cookie if purpose is REND_POINT_WAITING or + * C_ESTABLISH_REND. Filled with zeroes otherwise. + */ + char rend_cookie[REND_COOKIE_LEN]; + + /** Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit + * is not marked for close. */ + struct circuit_t *rend_splice; + + struct circuit_t *next; /**< Next circuit in linked list. */ +}; + +typedef struct circuit_t circuit_t; + +/** Configuration options for a Tor process */ +typedef struct { + struct config_line_t *LogOptions; /**< List of configuration lines + * for logfiles */ + + char *DebugLogFile; /**< Where to send verbose log messages. */ + char *DataDirectory; /**< OR only: where to store long-term data. */ + char *RouterFile; /**< Where to find starting list of ORs. */ + char *Nickname; /**< OR only: nickname of this onion router. */ + char *Address; /**< OR only: configured address for this onion router. */ + char *PidFile; /**< Where to store PID of Tor process. */ + + char *ExitNodes; /**< Comma-separated list of nicknames of ORs to consider + * as exits. */ + char *EntryNodes; /**< Comma-separated list of nicknames of ORs to consider + * as entry points. */ + char *ExcludeNodes; /**< Comma-separated list of nicknames of ORs not to + * use in circuits. */ + + char *RendNodes; /**< Comma-separated list of nicknames used as introduction + * points. */ + char *RendExcludeNodes; /**< Comma-separated list of nicknames not to use + * as introduction points. */ + + struct config_line_t *ExitPolicy; /**< Lists of exit policy components. */ + struct config_line_t *SocksPolicy; /**< Lists of socks policy components */ + struct config_line_t *SocksBindAddress; + /**< Addresses to bind for listening for SOCKS connections. */ + struct config_line_t *ORBindAddress; + /**< Addresses to bind for listening for OR connections. */ + struct config_line_t *DirBindAddress; + /**< Addresses to bind for listening for directory connections. */ + char *RecommendedVersions; /**< Directory server only: which versions of + * Tor should we tell users to run? */ + char *User; /**< Name of user to run Tor as. */ + char *Group; /**< Name of group to run Tor as. */ + double PathlenCoinWeight; /**< Parameter used to configure average path + * length (alpha in geometric distribution). */ + int ORPort; /**< Port to listen on for OR connections. */ + int SocksPort; /**< Port to listen on for SOCKS connections. */ + int DirPort; /**< Port to listen on for directory connections. */ + int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ + int ClientOnly; /**< Boolean: should we never evolve into a server role? */ + int MaxConn; /**< Maximum number of simultaneous connections. */ + int TrafficShaping; /**< Unused. */ + int LinkPadding; /**< Unused. */ + int IgnoreVersion; /**< If true, run no matter what versions of Tor the + * directory recommends. */ + int RunAsDaemon; /**< If true, run in the background. (Unix only) */ + int DirFetchPostPeriod; /**< How often do we fetch new directories + * and post server descriptros to the directory + * server? */ + int KeepalivePeriod; /**< How often do we send padding cells to keep + * connections alive? */ + int MaxOnionsPending; /**< How many circuit CREATE requests do we allow + * to wait simultaneously before we start dropping + * them? */ + int NewCircuitPeriod; /**< How long do we use a circuit before building + * a new one? */ + int BandwidthRate; /**< How much bandwidth, on average, are we willing to + * use in a second? */ + int BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to + * use in a second? */ + int NumCpus; /**< How many CPUs should we try to use? */ + int RunTesting; /**< If true, create testing circuits to measure how well the + * other ORs are running. */ + struct config_line_t *RendConfigLines; /**< List of configuration lines + * for rendezvous services. */ + char *ContactInfo; /** Contact info to be published in the directory */ +} or_options_t; + +/* XXX are these good enough defaults? */ +#define MAX_SOCKS_REPLY_LEN 1024 +#define MAX_SOCKS_ADDR_LEN 256 +#define SOCKS_COMMAND_CONNECT 0x01 +#define SOCKS_COMMAND_RESOLVE 0xF0 +/** State of a SOCKS request from a user to an OP */ +struct socks_request_t { + char socks_version; /**< Which version of SOCKS did the client use? */ + int command; /**< What has the user requested? One of CONNECT or RESOLVE. */ + int replylen; /**< Length of reply. */ + char reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if + * we want to specify our own socks reply, + * rather than using the default socks4 or + * socks5 socks reply. We use this for the + * two-stage socks5 handshake. + */ + int has_finished; /**< Has the SOCKS handshake finished? */ + char address[MAX_SOCKS_ADDR_LEN]; /**< What address did the client ask to connect to? */ + uint16_t port; /**< What port did the client ask to connect to? */ +}; + +/* all the function prototypes go here */ + +/********************************* buffers.c ***************************/ + +buf_t *buf_new(); +buf_t *buf_new_with_capacity(size_t size); +void buf_free(buf_t *buf); +void buf_clear(buf_t *buf); + +size_t buf_datalen(const buf_t *buf); +size_t buf_capacity(const buf_t *buf); +const char *_buf_peek_raw_buffer(const buf_t *buf); + +int read_to_buf(int s, size_t at_most, buf_t *buf, int *reached_eof); +int read_to_buf_tls(tor_tls *tls, size_t at_most, buf_t *buf); + +int flush_buf(int s, buf_t *buf, int *buf_flushlen); +int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen); + +int write_to_buf(const char *string, int string_len, buf_t *buf); +int fetch_from_buf(char *string, size_t string_len, buf_t *buf); +int fetch_from_buf_http(buf_t *buf, + char **headers_out, int max_headerlen, + char **body_out, int *body_used, int max_bodylen); +int fetch_from_buf_socks(buf_t *buf, socks_request_t *req); + +void assert_buf_ok(buf_t *buf); + +/********************************* circuitbuild.c **********************/ + +void circuit_log_path(int severity, circuit_t *circ); +void circuit_rep_hist_note_result(circuit_t *circ); +void circuit_dump_by_conn(connection_t *conn, int severity); +circuit_t *circuit_establish_circuit(uint8_t purpose, + const char *exit_digest); +void circuit_n_conn_done(connection_t *or_conn, int success); +int circuit_send_next_onion_skin(circuit_t *circ); +int circuit_extend(cell_t *cell, circuit_t *circ); +int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse); +int circuit_finish_handshake(circuit_t *circ, char *reply); +int circuit_truncated(circuit_t *circ, crypt_path_t *layer); +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys); +void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); + +/********************************* circuitlist.c ***********************/ + +extern char *circuit_state_to_string[]; +void circuit_close_all_marked(void); +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn); +void circuit_free_cpath_node(crypt_path_t *victim); +circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn); +circuit_t *circuit_get_by_conn(connection_t *conn); +circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose); +circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start, + const char *digest, uint8_t purpose); +circuit_t *circuit_get_rendezvous(const char *cookie); +int circuit_count_building(uint8_t purpose); +circuit_t *circuit_get_youngest_clean_open(uint8_t purpose); +int _circuit_mark_for_close(circuit_t *circ); + +#define circuit_mark_for_close(c) \ + do { \ + if (_circuit_mark_for_close(c)<0) { \ + log(LOG_WARN,"Duplicate call to circuit_mark_for_close at %s:%d (first at %s:%d)", \ + __FILE__,__LINE__,c->marked_for_close_file,c->marked_for_close); \ + } else { \ + c->marked_for_close_file = __FILE__; \ + c->marked_for_close = __LINE__; \ + } \ + } while (0) + +void assert_cpath_layer_ok(const crypt_path_t *cp); +void assert_circuit_ok(const circuit_t *c); + +/********************************* circuituse.c ************************/ + +void circuit_expire_building(time_t now); +int circuit_stream_is_being_handled(connection_t *conn); +void circuit_build_needed_circs(time_t now); +void circuit_detach_stream(circuit_t *circ, connection_t *conn); +void circuit_about_to_close_connection(connection_t *conn); +void circuit_has_opened(circuit_t *circ); +void circuit_build_failed(circuit_t *circ); +circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname); +circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest); +void circuit_reset_failure_count(void); +int connection_ap_handshake_attach_circuit(connection_t *conn); + +int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data,int reverse); +int circuit_finish_handshake(circuit_t *circ, char *reply); +int circuit_truncated(circuit_t *circ, crypt_path_t *layer); + +void assert_cpath_layer_ok(const crypt_path_t *c); +void assert_circuit_ok(const circuit_t *c); + +/********************************* command.c ***************************/ + +void command_process_cell(cell_t *cell, connection_t *conn); + +extern unsigned long stats_n_padding_cells_processed; +extern unsigned long stats_n_create_cells_processed; +extern unsigned long stats_n_created_cells_processed; +extern unsigned long stats_n_relay_cells_processed; +extern unsigned long stats_n_destroy_cells_processed; + +/********************************* config.c ***************************/ + +struct config_line_t { + char *key; + char *value; + struct config_line_t *next; +}; + +int config_assign_default_dirservers(void); +int getconfig(int argc, char **argv, or_options_t *options); +int config_init_logs(or_options_t *options); +void config_parse_exit_policy(struct config_line_t *cfg, + struct exit_policy_t **dest); +void exit_policy_free(struct exit_policy_t *p); +const char *get_data_directory(or_options_t *options); + +/********************************* connection.c ***************************/ + +#define CONN_TYPE_TO_STRING(t) (((t) < _CONN_TYPE_MIN || (t) > _CONN_TYPE_MAX) ? \ + "Unknown" : conn_type_to_string[(t)]) + +extern char *conn_type_to_string[]; +extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; + +connection_t *connection_new(int type); +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); +int _connection_mark_for_close(connection_t *conn); + +#define connection_mark_for_close(c) \ + do { \ + if (_connection_mark_for_close(c)<0) { \ + log(LOG_WARN,"Duplicate call to connection_mark_for_close at %s:%d (first at %s:%d)", \ + __FILE__,__LINE__,c->marked_for_close_file,c->marked_for_close); \ + } else { \ + c->marked_for_close_file = __FILE__; \ + c->marked_for_close = __LINE__; \ + } \ + } while (0) + +void connection_expire_held_open(void); + +int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port); +int retry_all_listeners(void); + +void connection_bucket_init(void); +void connection_bucket_refill(struct timeval *now); + +int connection_handle_read(connection_t *conn); + +int connection_fetch_from_buf(char *string, int len, connection_t *conn); + +int connection_wants_to_flush(connection_t *conn); +int connection_outbuf_too_full(connection_t *conn); +int connection_handle_write(connection_t *conn); +void connection_write_to_buf(const char *string, int len, connection_t *conn); + +connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port); +connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port); +connection_t *connection_get_by_identity_digest(const char *digest, int type); + +connection_t *connection_get_by_type(int type); +connection_t *connection_get_by_type_state(int type, int state); +connection_t *connection_get_by_type_state_lastwritten(int type, int state); +connection_t *connection_get_by_type_rendquery(int type, const char *rendquery); + +#define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR) +#define connection_has_pending_tls_data(conn) \ + ((conn)->type == CONN_TYPE_OR && \ + (conn)->state == OR_CONN_STATE_OPEN && \ + tor_tls_get_pending_bytes((conn)->tls)) +int connection_is_listener(connection_t *conn); +int connection_state_is_open(connection_t *conn); +int connection_state_is_connecting(connection_t *conn); + +int connection_send_destroy(uint16_t circ_id, connection_t *conn); + +void assert_connection_ok(connection_t *conn, time_t now); + +/********************************* connection_edge.c ***************************/ + +int connection_edge_process_inbuf(connection_t *conn); +int connection_edge_destroy(uint16_t circ_id, connection_t *conn); +int connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer); +int connection_edge_finished_flushing(connection_t *conn); +int connection_edge_finished_connecting(connection_t *conn); + +int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ); +int connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ); + +int connection_ap_make_bridge(char *address, uint16_t port); +void connection_ap_handshake_socks_reply(connection_t *conn, char *reply, + int replylen, char success); +void connection_ap_handshake_socks_resolved(connection_t *conn, + int answer_type, + int answer_len, + const char *answer); + +int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); +int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ); +void connection_exit_connect(connection_t *conn); +int connection_edge_is_rendezvous_stream(connection_t *conn); +int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit); +void connection_ap_expire_beginning(void); +void connection_ap_attach_pending(void); + +int socks_policy_permits_address(uint32_t addr); + +void client_dns_init(void); +uint32_t client_dns_lookup_entry(const char *address); +int client_dns_incr_failures(const char *address); +void client_dns_set_entry(const char *address, uint32_t val); +void client_dns_clean(void); + +/********************************* connection_or.c ***************************/ + +int connection_or_process_inbuf(connection_t *conn); +int connection_or_finished_flushing(connection_t *conn); +int connection_or_finished_connecting(connection_t *conn); + +connection_t *connection_or_connect(uint32_t addr, uint16_t port, + const char *id_digest); + +int connection_tls_start_handshake(connection_t *conn, int receiving); +int connection_tls_continue_handshake(connection_t *conn); + +void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn); + +/********************************* cpuworker.c *****************************/ + +void cpu_init(void); +void cpuworkers_rotate(void); +int connection_cpu_finished_flushing(connection_t *conn); +int connection_cpu_process_inbuf(connection_t *conn); +int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type, + void *task); + +/********************************* directory.c ***************************/ + +void directory_post_to_dirservers(uint8_t purpose, const char *payload, + int payload_len); +void directory_get_from_dirserver(uint8_t purpose, const char *payload, + int payload_len); +int connection_dir_process_inbuf(connection_t *conn); +int connection_dir_finished_flushing(connection_t *conn); +int connection_dir_finished_connecting(connection_t *conn); + +/********************************* dirserv.c ***************************/ + +int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk); +int dirserv_parse_fingerprint_file(const char *fname); +int dirserv_router_fingerprint_is_known(const routerinfo_t *router); +void dirserv_free_fingerprint_list(); +int dirserv_add_descriptor(const char **desc); +int dirserv_load_from_directory_string(const char *dir); +void dirserv_free_descriptors(); +void dirserv_remove_old_servers(void); +int dirserv_dump_directory_to_string(char *s, unsigned int maxlen, + crypto_pk_env_t *private_key); +void directory_set_dirty(void); +size_t dirserv_get_directory(const char **cp); +size_t dirserv_get_runningrouters(const char **rr); +void dirserv_set_cached_directory(const char *directory, time_t when); + +/********************************* dns.c ***************************/ + +void dns_init(void); +int connection_dns_finished_flushing(connection_t *conn); +int connection_dns_process_inbuf(connection_t *conn); +void dnsworkers_rotate(void); +void connection_dns_remove(connection_t *conn); +void assert_connection_edge_not_dns_pending(connection_t *conn); +void assert_all_pending_dns_resolves_ok(void); +void dns_cancel_pending_resolve(char *question); +int dns_resolve(connection_t *exitconn); + +/********************************* main.c ***************************/ + +int connection_add(connection_t *conn); +int connection_remove(connection_t *conn); +int connection_in_array(connection_t *conn); + +void get_connection_array(connection_t ***array, int *n); + +void connection_watch_events(connection_t *conn, short events); +int connection_is_reading(connection_t *conn); +void connection_stop_reading(connection_t *conn); +void connection_start_reading(connection_t *conn); + +int connection_is_writing(connection_t *conn); +void connection_stop_writing(connection_t *conn); +void connection_start_writing(connection_t *conn); + +void directory_has_arrived(void); +int authdir_mode(void); +int clique_mode(void); +int server_mode(void); +int advertised_server_mode(void); +int proxy_mode(void); + +void tor_cleanup(void); + +/********************************* onion.c ***************************/ + +int onion_pending_add(circuit_t *circ); +circuit_t *onion_next_task(void); +void onion_pending_remove(circuit_t *circ); + +int onion_skin_create(crypto_pk_env_t *router_key, + crypto_dh_env_t **handshake_state_out, + char *onion_skin_out); + +int onion_skin_server_handshake(char *onion_skin, + crypto_pk_env_t *private_key, + crypto_pk_env_t *prev_private_key, + char *handshake_reply_out, + char *key_out, + int key_out_len); + +int onion_skin_client_handshake(crypto_dh_env_t *handshake_state, + char *handshake_reply, + char *key_out, + int key_out_len); + +/********************************* relay.c ***************************/ + +extern unsigned long stats_n_relay_cells_relayed; +extern unsigned long stats_n_relay_cells_delivered; + +int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction); + +void relay_header_pack(char *dest, const relay_header_t *src); +void relay_header_unpack(relay_header_t *dest, const char *src); +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, + int relay_command, const char *payload, + int payload_len, crypt_path_t *cpath_layer); +int connection_edge_package_raw_inbuf(connection_t *conn); +void connection_edge_consider_sending_sendme(connection_t *conn); + +extern uint64_t stats_n_data_cells_packaged; +extern uint64_t stats_n_data_bytes_packaged; +extern uint64_t stats_n_data_cells_received; +extern uint64_t stats_n_data_bytes_received; + +/********************************* rephist.c ***************************/ + +void rep_hist_init(void); +void rep_hist_note_connect_failed(const char* nickname, time_t when); +void rep_hist_note_connect_succeeded(const char* nickname, time_t when); +void rep_hist_note_disconnect(const char* nickname, time_t when); +void rep_hist_note_connection_died(const char* nickname, time_t when); +void rep_hist_note_extend_succeeded(const char *from_name, + const char *to_name); +void rep_hist_note_extend_failed(const char *from_name, const char *to_name); +void rep_hist_dump_stats(time_t now, int severity); +void rep_hist_note_bytes_read(int num_bytes, time_t when); +void rep_hist_note_bytes_written(int num_bytes, time_t when); +int rep_hist_bandwidth_assess(time_t when); + +/********************************* rendclient.c ***************************/ + +void rend_client_introcirc_has_opened(circuit_t *circ); +void rend_client_rendcirc_has_opened(circuit_t *circ); +int rend_client_introduction_acked(circuit_t *circ, const char *request, int request_len); +void rend_client_refetch_renddesc(const char *query); +int rend_client_remove_intro_point(char *failed_intro, const char *query); +int rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len); +int rend_client_receive_rendezvous(circuit_t *circ, const char *request, int request_len); +void rend_client_desc_fetched(char *query, int success); + +char *rend_client_get_random_intro(char *query); +int rend_parse_rendezvous_address(char *address); + +int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc); + +/********************************* rendcommon.c ***************************/ + +typedef struct rend_service_descriptor_t { + crypto_pk_env_t *pk; + time_t timestamp; + int n_intro_points; + char **intro_points; +} rend_service_descriptor_t; + +int rend_cmp_service_ids(const char *one, const char *two); + +void rend_process_relay_cell(circuit_t *circ, int command, int length, + const char *payload); + +void rend_service_descriptor_free(rend_service_descriptor_t *desc); +int rend_encode_service_descriptor(rend_service_descriptor_t *desc, + crypto_pk_env_t *key, + char **str_out, + int *len_out); +rend_service_descriptor_t *rend_parse_service_descriptor(const char *str, int len); +int rend_get_service_id(crypto_pk_env_t *pk, char *out); + +typedef struct rend_cache_entry_t { + int len; /* Length of desc */ + time_t received; /* When did we get the descriptor? */ + char *desc; /* Service descriptor */ + rend_service_descriptor_t *parsed; /* Parsed value of 'desc' */ +} rend_cache_entry_t; + +void rend_cache_init(void); +void rend_cache_clean(void); +int rend_valid_service_id(const char *query); +int rend_cache_lookup_desc(const char *query, const char **desc, int *desc_len); +int rend_cache_lookup_entry(const char *query, rend_cache_entry_t **entry_out); +int rend_cache_store(const char *desc, int desc_len); + +/********************************* rendservice.c ***************************/ + +int rend_config_services(or_options_t *options); +int rend_service_load_keys(void); +void rend_services_init(void); +void rend_services_introduce(void); +void rend_services_upload(int force); + +void rend_service_intro_has_opened(circuit_t *circuit); +int rend_service_intro_established(circuit_t *circuit, const char *request, int request_len); +void rend_service_rendezvous_has_opened(circuit_t *circuit); +int rend_service_introduce(circuit_t *circuit, const char *request, int request_len); +void rend_service_relaunch_rendezvous(circuit_t *oldcirc); +int rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ); +void rend_service_dump_stats(int severity); + +/********************************* rendmid.c *******************************/ +int rend_mid_establish_intro(circuit_t *circ, const char *request, int request_len); +int rend_mid_introduce(circuit_t *circ, const char *request, int request_len); +int rend_mid_establish_rendezvous(circuit_t *circ, const char *request, int request_len); +int rend_mid_rendezvous(circuit_t *circ, const char *request, int request_len); + +/********************************* router.c ***************************/ + +void set_onion_key(crypto_pk_env_t *k); +crypto_pk_env_t *get_onion_key(void); +crypto_pk_env_t *get_previous_onion_key(void); +time_t get_onion_key_set_at(void); +void set_identity_key(crypto_pk_env_t *k); +crypto_pk_env_t *get_identity_key(void); +void dup_onion_keys(crypto_pk_env_t **key, crypto_pk_env_t **last); +int init_keys(void); +crypto_pk_env_t *init_key_from_file(const char *fname); +void rotate_onion_key(void); +void router_set_advertised_bandwidth(int bw); +int router_get_advertised_bandwidth(void); + +void router_retry_connections(void); +int router_is_clique_mode(routerinfo_t *router); +void router_upload_dir_desc_to_dirservers(void); +int router_compare_to_my_exit_policy(connection_t *conn); +routerinfo_t *router_get_my_routerinfo(void); +const char *router_get_my_descriptor(void); +int router_is_me(routerinfo_t *router); +int router_rebuild_descriptor(void); +int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, + crypto_pk_env_t *ident_key); + +/********************************* routerlist.c ***************************/ + +routerinfo_t *router_pick_directory_server(int requireauth, int requireothers); +int all_directory_servers_down(void); +struct smartlist_t; +void add_nickname_list_to_smartlist(struct smartlist_t *sl, const char *list); +void router_add_running_routers_to_smartlist(struct smartlist_t *sl); +int router_nickname_matches(routerinfo_t *router, const char *nickname); +routerinfo_t *router_choose_random_node(char *preferred, char *excluded, + struct smartlist_t *excludedsmartlist); +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); +routerinfo_t *router_get_by_nickname(const char *nickname); +routerinfo_t *router_get_by_hexdigest(const char *hexdigest); +routerinfo_t *router_get_by_digest(const char *digest); +int router_digest_is_trusted_dir(const char *digest); +void router_get_routerlist(routerlist_t **prouterlist); +void routerlist_free(routerlist_t *routerlist); +void routerlist_clear_trusted_directories(void); +void routerinfo_free(routerinfo_t *router); +routerinfo_t *routerinfo_copy(const routerinfo_t *router); +void router_mark_as_down(const char *digest); +void routerlist_remove_old_routers(void); +int router_load_routerlist_from_file(char *routerfile, int trusted); +int router_load_routerlist_from_string(const char *s, int trusted); +int router_load_routerlist_from_directory(const char *s,crypto_pk_env_t *pkey); +int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port, + struct exit_policy_t *policy); +#define ADDR_POLICY_ACCEPTED 0 +#define ADDR_POLICY_REJECTED -1 +#define ADDR_POLICY_UNKNOWN 1 +int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port); +int router_exit_policy_rejects_all(routerinfo_t *router); +void running_routers_free(running_routers_t *rr); +void routerlist_update_from_runningrouters(routerlist_t *list, + running_routers_t *rr); +void router_update_status_from_smartlist(routerinfo_t *r, + time_t list_time, + smartlist_t *running_list); + +/********************************* routerparse.c ************************/ + +typedef struct tor_version_t { + int major; + int minor; + int micro; + enum { VER_PRE=0, VER_RC=1, VER_RELEASE=2 } status; + int patchlevel; + enum { IS_CVS=0, IS_NOT_CVS=1} cvs; +} tor_version_t; + +int router_get_router_hash(const char *s, char *digest); +int router_get_dir_hash(const char *s, char *digest); +int router_get_runningrouters_hash(const char *s, char *digest); +int router_parse_list_from_string(const char **s, + routerlist_t **dest, + smartlist_t *good_nickname_list, + time_t published); +int router_parse_routerlist_from_directory(const char *s, + routerlist_t **dest, + crypto_pk_env_t *pkey); +running_routers_t *router_parse_runningrouters(const char *str); +routerinfo_t *router_parse_entry_from_string(const char *s, const char *end); +int router_add_exit_policy_from_string(routerinfo_t *router, const char *s); +struct exit_policy_t *router_parse_exit_policy_from_string(const char *s); +int check_software_version_against_directory(const char *directory, + int ignoreversion); +int tor_version_parse(const char *s, tor_version_t *out); +int tor_version_compare(tor_version_t *a, tor_version_t *b); +int tor_version_compare_to_mine(const char *s); + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/relay.c b/tags/tor-0_0_8pre1/src/or/relay.c new file mode 100644 index 0000000000..36c3896eb1 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/relay.c @@ -0,0 +1,1012 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file relay.c + * \brief Handle relay cell encryption/decryption, plus packaging and + * receiving from circuits. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction, + crypt_path_t **layer_hint, char *recognized); +static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction); + +static int +connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, + connection_t *conn, + crypt_path_t *layer_hint); +static void +circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint); +static void +circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); +static int +circuit_resume_edge_reading_helper(connection_t *conn, + circuit_t *circ, + crypt_path_t *layer_hint); +static int +circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); +void connection_edge_consider_sending_sendme(connection_t *conn); + + + +/** Stats: how many relay cells have originated at this hop, or have + * been relayed onward (not recognized at this hop)? + */ +unsigned long stats_n_relay_cells_relayed = 0; +/** Stats: how many relay cells have been delivered to streams at this + * hop? + */ +unsigned long stats_n_relay_cells_delivered = 0; + +/** Update digest from the payload of cell. Assign integrity part to + * cell. + */ +static void relay_set_digest(crypto_digest_env_t *digest, cell_t *cell) { + char integrity[4]; + relay_header_t rh; + + crypto_digest_add_bytes(digest, cell->payload, CELL_PAYLOAD_SIZE); + crypto_digest_get_digest(digest, integrity, 4); +// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.", +// integrity[0], integrity[1], integrity[2], integrity[3]); + relay_header_unpack(&rh, cell->payload); + memcpy(rh.integrity, integrity, 4); + relay_header_pack(cell->payload, &rh); +} + +/** Does the digest for this circuit indicate that this cell is for us? + * + * Update digest from the payload of cell (with the integrity part set + * to 0). If the integrity part is valid, return 1, else restore digest + * and cell to their original state and return 0. + */ +static int relay_digest_matches(crypto_digest_env_t *digest, cell_t *cell) { + char received_integrity[4], calculated_integrity[4]; + relay_header_t rh; + crypto_digest_env_t *backup_digest=NULL; + + backup_digest = crypto_digest_dup(digest); + + relay_header_unpack(&rh, cell->payload); + memcpy(received_integrity, rh.integrity, 4); + memset(rh.integrity, 0, 4); + relay_header_pack(cell->payload, &rh); + +// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.", +// received_integrity[0], received_integrity[1], +// received_integrity[2], received_integrity[3]); + + crypto_digest_add_bytes(digest, cell->payload, CELL_PAYLOAD_SIZE); + crypto_digest_get_digest(digest, calculated_integrity, 4); + + if(memcmp(received_integrity, calculated_integrity, 4)) { +// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing."); +// (%d vs %d).", received_integrity, calculated_integrity); + /* restore digest to its old form */ + crypto_digest_assign(digest, backup_digest); + /* restore the relay header */ + memcpy(rh.integrity, received_integrity, 4); + relay_header_pack(cell->payload, &rh); + crypto_free_digest_env(backup_digest); + return 0; + } + crypto_free_digest_env(backup_digest); + return 1; +} + +/** Apply cipher to CELL_PAYLOAD_SIZE bytes of in + * (in place). + * + * If encrypt_mode is 1 then encrypt, else decrypt. + * + * Return -1 if the crypto fails, else return 0. + */ +static int relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in, + int encrypt_mode) { + char out[CELL_PAYLOAD_SIZE]; /* 'in' must be this size too */ + relay_header_t rh; + + relay_header_unpack(&rh, in); +// log_fn(LOG_DEBUG,"before crypt: %d",rh.recognized); + if(( encrypt_mode && crypto_cipher_encrypt(cipher, in, CELL_PAYLOAD_SIZE, out)) || + (!encrypt_mode && crypto_cipher_decrypt(cipher, in, CELL_PAYLOAD_SIZE, out))) { + log_fn(LOG_WARN,"Error during relay encryption"); + return -1; + } + memcpy(in,out,CELL_PAYLOAD_SIZE); + relay_header_unpack(&rh, in); +// log_fn(LOG_DEBUG,"after crypt: %d",rh.recognized); + return 0; +} + +/** Receive a relay cell: + * - Crypt it (encrypt APward, decrypt at AP, decrypt exitward). + * - Check if recognized (if exitward). + * - If recognized and the digest checks out, then find if there's + * a conn that the cell is intended for, and deliver it to· + * connection_edge. + * - Else connection_or_write_cell_to_buf to the conn on the other + * side of the circuit. + */ +int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction) { + connection_t *conn=NULL; + crypt_path_t *layer_hint=NULL; + char recognized=0; + + tor_assert(cell && circ); + tor_assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN); + if (circ->marked_for_close) + return 0; + + if(relay_crypt(circ, cell, cell_direction, &layer_hint, &recognized) < 0) { + log_fn(LOG_WARN,"relay crypt failed. Dropping connection."); + return -1; + } + + if(recognized) { + conn = relay_lookup_conn(circ, cell, cell_direction); + if(cell_direction == CELL_DIRECTION_OUT) { + ++stats_n_relay_cells_delivered; + log_fn(LOG_DEBUG,"Sending away from origin."); + if (connection_edge_process_relay_cell(cell, circ, conn, NULL) < 0) { + log_fn(LOG_WARN,"connection_edge_process_relay_cell (away from origin) failed."); + return -1; + } + } + if(cell_direction == CELL_DIRECTION_IN) { + ++stats_n_relay_cells_delivered; + log_fn(LOG_DEBUG,"Sending to origin."); + if (connection_edge_process_relay_cell(cell, circ, conn, layer_hint) < 0) { + log_fn(LOG_WARN,"connection_edge_process_relay_cell (at origin) failed."); + return -1; + } + } + return 0; + } + + /* not recognized. pass it on. */ + if(cell_direction == CELL_DIRECTION_OUT) { + cell->circ_id = circ->n_circ_id; /* switch it */ + conn = circ->n_conn; + } else { + cell->circ_id = circ->p_circ_id; /* switch it */ + conn = circ->p_conn; + } + + if(!conn) { + if (circ->rend_splice && cell_direction == CELL_DIRECTION_OUT) { + tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED); + tor_assert(circ->rend_splice->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED); + cell->circ_id = circ->rend_splice->p_circ_id; + if (circuit_receive_relay_cell(cell, circ->rend_splice, CELL_DIRECTION_IN)<0) { + log_fn(LOG_WARN, "Error relaying cell across rendezvous; closing circuits"); + circuit_mark_for_close(circ); /* XXXX Do this here, or just return -1? */ + return -1; + } + return 0; + } + log_fn(LOG_WARN,"Didn't recognize cell, but circ stops here! Closing circ."); + return -1; + } + + log_fn(LOG_DEBUG,"Passing on unrecognized cell."); + ++stats_n_relay_cells_relayed; + connection_or_write_cell_to_buf(cell, conn); + return 0; +} + +/** Do the appropriate en/decryptions for cell arriving on + * circ in direction cell_direction. + * + * If cell_direction == CELL_DIRECTION_IN: + * - If we're at the origin (we're the OP), for hops 1..N, + * decrypt cell. If recognized, stop. + * - Else (we're not the OP), encrypt one hop. Cell is not recognized. + * + * If cell_direction == CELL_DIRECTION_OUT: + * - decrypt one hop. Check if recognized. + * + * If cell is recognized, set *recognized to 1, and set + * *layer_hint to the hop that recognized it. + * + * Return -1 to indicate that we should mark the circuit for close, + * else return 0. + */ +/* wrap this into receive_relay_cell one day */ +static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction, + crypt_path_t **layer_hint, char *recognized) { + crypt_path_t *thishop; + relay_header_t rh; + + tor_assert(circ && cell && recognized); + tor_assert(cell_direction == CELL_DIRECTION_IN || cell_direction == CELL_DIRECTION_OUT); + + if(cell_direction == CELL_DIRECTION_IN) { + if(CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit. + We'll want to do layered decrypts. */ + tor_assert(circ->cpath); + thishop = circ->cpath; + if(thishop->state != CPATH_STATE_OPEN) { + log_fn(LOG_WARN,"Relay cell before first created cell? Closing."); + return -1; + } + do { /* Remember: cpath is in forward order, that is, first hop first. */ + tor_assert(thishop); + + if(relay_crypt_one_payload(thishop->b_crypto, cell->payload, 0) < 0) + return -1; + + relay_header_unpack(&rh, cell->payload); + if(rh.recognized == 0) { + /* it's possibly recognized. have to check digest to be sure. */ + if(relay_digest_matches(thishop->b_digest, cell)) { + *recognized = 1; + *layer_hint = thishop; + return 0; + } + } + + thishop = thishop->next; + } while(thishop != circ->cpath && thishop->state == CPATH_STATE_OPEN); + log_fn(LOG_WARN,"in-cell at OP not recognized. Closing."); + return -1; + } else { /* we're in the middle. Just one crypt. */ + if(relay_crypt_one_payload(circ->p_crypto, cell->payload, 1) < 0) + return -1; +// log_fn(LOG_DEBUG,"Skipping recognized check, because we're not the OP."); + } + } else /* cell_direction == CELL_DIRECTION_OUT */ { + /* we're in the middle. Just one crypt. */ + + if(relay_crypt_one_payload(circ->n_crypto, cell->payload, 0) < 0) + return -1; + + relay_header_unpack(&rh, cell->payload); + if (rh.recognized == 0) { + /* it's possibly recognized. have to check digest to be sure. */ + if(relay_digest_matches(circ->n_digest, cell)) { + *recognized = 1; + return 0; + } + } + } + return 0; +} + +/** Package a relay cell: + * - Encrypt it to the right layer + * - connection_or_write_cell_to_buf to the right conn + */ +static int +circuit_package_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction, + crypt_path_t *layer_hint) +{ + connection_t *conn; /* where to send the cell */ + crypt_path_t *thishop; /* counter for repeated crypts */ + + if(cell_direction == CELL_DIRECTION_OUT) { + conn = circ->n_conn; + if(!conn) { + log_fn(LOG_WARN,"outgoing relay cell has n_conn==NULL. Dropping."); + return 0; /* just drop it */ + } + relay_set_digest(layer_hint->f_digest, cell); + + thishop = layer_hint; + /* moving from farthest to nearest hop */ + do { + tor_assert(thishop); + + log_fn(LOG_DEBUG,"crypting a layer of the relay cell."); + if(relay_crypt_one_payload(thishop->f_crypto, cell->payload, 1) < 0) { + return -1; + } + + thishop = thishop->prev; + } while (thishop != circ->cpath->prev); + + } else { /* incoming cell */ + conn = circ->p_conn; + if(!conn) { + log_fn(LOG_WARN,"incoming relay cell has p_conn==NULL. Dropping."); + return 0; /* just drop it */ + } + relay_set_digest(circ->p_digest, cell); + if(relay_crypt_one_payload(circ->p_crypto, cell->payload, 1) < 0) + return -1; + } + ++stats_n_relay_cells_relayed; + connection_or_write_cell_to_buf(cell, conn); + return 0; +} + +/** If cell's stream_id matches the stream_id of any conn that's + * attached to circ, return that conn, else return NULL. + */ +static connection_t * +relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction) +{ + connection_t *tmpconn; + relay_header_t rh; + + relay_header_unpack(&rh, cell->payload); + + if(!rh.stream_id) + return NULL; + + /* IN or OUT cells could have come from either direction, now + * that we allow rendezvous *to* an OP. + */ + + for(tmpconn = circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(rh.stream_id == tmpconn->stream_id) { + log_fn(LOG_DEBUG,"found conn for stream %d.", rh.stream_id); + if(cell_direction == CELL_DIRECTION_OUT || + connection_edge_is_rendezvous_stream(tmpconn)) + return tmpconn; + } + } + for(tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(rh.stream_id == tmpconn->stream_id) { + log_fn(LOG_DEBUG,"found conn for stream %d.", rh.stream_id); + return tmpconn; + } + } + for(tmpconn = circ->resolving_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(rh.stream_id == tmpconn->stream_id) { + log_fn(LOG_DEBUG,"found conn for stream %d.", rh.stream_id); + return tmpconn; + } + } + return NULL; /* probably a begin relay cell */ +} + +/** Pack the relay_header_t host-order structure src into + * network-order in the buffer dest. See tor-spec.txt for details + * about the wire format. + */ +void relay_header_pack(char *dest, const relay_header_t *src) { + *(uint8_t*)(dest) = src->command; + + set_uint16(dest+1, htons(src->recognized)); + set_uint16(dest+3, htons(src->stream_id)); + memcpy(dest+5, src->integrity, 4); + set_uint16(dest+9, htons(src->length)); +} + +/** Unpack the network-order buffer src into a host-order + * relay_header_t structure dest. + */ +void relay_header_unpack(relay_header_t *dest, const char *src) { + dest->command = *(uint8_t*)(src); + + dest->recognized = ntohs(get_uint16(src+1)); + dest->stream_id = ntohs(get_uint16(src+3)); + memcpy(dest->integrity, src+5, 4); + dest->length = ntohs(get_uint16(src+9)); +} + +/** Make a relay cell out of relay_command and payload, and + * send it onto the open circuit circ. fromconn is the stream + * that's sending the relay cell, or NULL if it's a control cell. + * cpath_layer is NULL for OR->OP cells, or the destination hop + * for OP->OR cells. + * + * If you can't send the cell, mark the circuit for close and + * return -1. Else return 0. + */ +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, + int relay_command, const char *payload, + int payload_len, crypt_path_t *cpath_layer) { + cell_t cell; + relay_header_t rh; + int cell_direction; + + if(!circ) { + log_fn(LOG_WARN,"no circ. Closing conn."); + tor_assert(fromconn); + fromconn->has_sent_end = 1; /* no circ to send to */ + connection_mark_for_close(fromconn); + return -1; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + if(cpath_layer) { + cell.circ_id = circ->n_circ_id; + cell_direction = CELL_DIRECTION_OUT; + } else { + cell.circ_id = circ->p_circ_id; + cell_direction = CELL_DIRECTION_IN; + } + + memset(&rh, 0, sizeof(rh)); + rh.command = relay_command; + if(fromconn) + rh.stream_id = fromconn->stream_id; /* else it's 0 */ + rh.length = payload_len; + relay_header_pack(cell.payload, &rh); + if(payload_len) + memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len); + + log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command, + cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward"); + + if(circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) { + log_fn(LOG_WARN,"circuit_package_relay_cell failed. Closing."); + circuit_mark_for_close(circ); + return -1; + } + return 0; +} + +/** Translate the payload of length length, which + * came from a relay 'end' cell, into a static const string describing + * why the stream is closing. + */ +static const char * +connection_edge_end_reason(char *payload, uint16_t length) { + if(length < 1) { + log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1."); + return "MALFORMED"; + } + if(*payload < _MIN_END_STREAM_REASON || *payload > _MAX_END_STREAM_REASON) { + log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload); + return "MALFORMED"; + } + switch(*payload) { + case END_STREAM_REASON_MISC: return "misc error"; + case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed"; + case END_STREAM_REASON_CONNECTFAILED: return "connect failed"; + case END_STREAM_REASON_EXITPOLICY: return "exit policy failed"; + case END_STREAM_REASON_DESTROY: return "destroyed"; + case END_STREAM_REASON_DONE: return "closed normally"; + case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)"; + } + tor_assert(0); + return ""; +} + +/** How many times will I retry a stream that fails due to DNS + * resolve failure? + */ +#define MAX_RESOLVE_FAILURES 3 + +/** An incoming relay cell has arrived from circuit circ to + * stream conn. + * + * The arguments here are the same as in + * connection_edge_process_relay_cell() below; this function is called + * from there when conn is defined and not in an open state. + */ +static int +connection_edge_process_relay_cell_not_open( + relay_header_t *rh, cell_t *cell, circuit_t *circ, + connection_t *conn, crypt_path_t *layer_hint) { + uint32_t addr; + int reason; + + if(rh->command == RELAY_COMMAND_END) { + reason = *(cell->payload+RELAY_HEADER_SIZE); + /* We have to check this here, since we aren't connected yet. */ + if (rh->length >= 5 && reason == END_STREAM_REASON_EXITPOLICY) { + log_fn(LOG_INFO,"Address %s refused due to exit policy. Retrying.", + conn->socks_request->address); + addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); + client_dns_set_entry(conn->socks_request->address, addr); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + circuit_detach_stream(circ,conn); + if(connection_ap_handshake_attach_circuit(conn) >= 0) + return 0; + log_fn(LOG_INFO,"Giving up on retrying (from exitpolicy); conn can't be handled."); + /* else, conn will get closed below */ + } else if (rh->length && reason == END_STREAM_REASON_RESOLVEFAILED) { + if (client_dns_incr_failures(conn->socks_request->address) + < MAX_RESOLVE_FAILURES) { + /* We haven't retried too many times; reattach the connection. */ + log_fn(LOG_INFO,"Resolve of '%s' failed, trying again.", + conn->socks_request->address); + circuit_log_path(LOG_INFO,circ); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + circuit_detach_stream(circ,conn); + tor_assert(circ->timestamp_dirty); + circ->timestamp_dirty -= options.NewCircuitPeriod; + /* make sure not to expire/retry the stream quite yet */ + conn->timestamp_lastread = time(NULL); + if(connection_ap_handshake_attach_circuit(conn) >= 0) + return 0; + /* else, conn will get closed below */ + log_fn(LOG_INFO,"Giving up on retrying (from resolvefailed); conn can't be handled."); + } else { + log_fn(LOG_WARN,"Have tried resolving address %s at %d different places. Giving up.", + conn->socks_request->address, MAX_RESOLVE_FAILURES); + } + } + log_fn(LOG_INFO,"Edge got end (%s) before we're connected. Marking for close.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh->length)); + if(CIRCUIT_IS_ORIGIN(circ)) + circuit_log_path(LOG_INFO,circ); + conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */ + connection_mark_for_close(conn); + return 0; + } + + if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) { + if(conn->state != AP_CONN_STATE_CONNECT_WAIT) { + log_fn(LOG_WARN,"Got 'connected' while not in state connect_wait. Dropping."); + return 0; + } +// log_fn(LOG_INFO,"Connected! Notifying application."); + conn->state = AP_CONN_STATE_OPEN; + if (rh->length >= 4) { + addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); + client_dns_set_entry(conn->socks_request->address, addr); + } + log_fn(LOG_INFO,"'connected' received after %d seconds.", + (int)(time(NULL) - conn->timestamp_lastread)); + circuit_log_path(LOG_INFO,circ); + connection_ap_handshake_socks_reply(conn, NULL, 0, 1); + conn->socks_request->has_finished = 1; + /* handle anything that might have queued */ + if (connection_edge_package_raw_inbuf(conn) < 0) { + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return 0; + } + return 0; + } + if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_RESOLVED) { + if (conn->state != AP_CONN_STATE_RESOLVE_WAIT) { + log_fn(LOG_WARN,"Got a 'resolved' cell while not in state resolve_wait. Dropping."); + return 0; + } + tor_assert(conn->socks_request->command == SOCKS_COMMAND_RESOLVE); + if (rh->length < 2 || cell->payload[RELAY_HEADER_SIZE+1]+2>rh->length) { + log_fn(LOG_WARN, "Dropping malformed 'resolved' cell"); + conn->has_sent_end = 1; + connection_mark_for_close(conn); + return 0; + } + connection_ap_handshake_socks_resolved(conn, + cell->payload[RELAY_HEADER_SIZE], /*answer_type*/ + cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/ + cell->payload+RELAY_HEADER_SIZE+2); /* answer */ + conn->socks_request->has_finished = 1; + conn->has_sent_end = 1; + connection_mark_for_close(conn); + return 0; + } + + log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.", + rh->command, conn->state, conn_state_to_string[conn->type][conn->state]); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return -1; +} + +/** An incoming relay cell has arrived on circuit circ. If + * conn is NULL this is a control cell, else cell is + * destined for conn. + * + * If layer_hint is defined, then we're the origin of the + * circuit, and it specifies the hop that packaged cell. + * + * Return -1 if you want to tear down the circuit, else 0. + */ +static int +connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, + connection_t *conn, + crypt_path_t *layer_hint) +{ + static int num_seen=0; + relay_header_t rh; + + tor_assert(cell && circ); + + relay_header_unpack(&rh, cell->payload); +// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id); + num_seen++; + log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen); + + /* either conn is NULL, in which case we've got a control cell, or else + * conn points to the recognized stream. */ + + if(conn && + conn->state != AP_CONN_STATE_OPEN && + conn->state != EXIT_CONN_STATE_OPEN) { + return connection_edge_process_relay_cell_not_open( + &rh, cell, circ, conn, layer_hint); + } + + switch(rh.command) { + case RELAY_COMMAND_DROP: + log_fn(LOG_INFO,"Got a relay-level padding cell. Dropping."); + return 0; + case RELAY_COMMAND_BEGIN: + if (layer_hint && + circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { + log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping."); + return 0; + } + if(conn) { + log_fn(LOG_WARN,"begin cell for known stream. Dropping."); + return 0; + } + connection_exit_begin_conn(cell, circ); + return 0; + case RELAY_COMMAND_DATA: + ++stats_n_data_cells_received; + if((layer_hint && --layer_hint->deliver_window < 0) || + (!layer_hint && --circ->deliver_window < 0)) { + log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing."); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return -1; + } + log_fn(LOG_DEBUG,"circ deliver_window now %d.", layer_hint ? + layer_hint->deliver_window : circ->deliver_window); + + circuit_consider_sending_sendme(circ, layer_hint); + + if(!conn) { + log_fn(LOG_INFO,"data cell dropped, unknown stream."); + return 0; + } + + if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */ + log_fn(LOG_WARN,"(relay data) conn deliver_window below 0. Killing."); + return -1; /* somebody's breaking protocol. kill the whole circuit. */ + } + + stats_n_data_bytes_received += rh.length; + connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE, + rh.length, conn); + connection_edge_consider_sending_sendme(conn); + return 0; + case RELAY_COMMAND_END: + if(!conn) { + log_fn(LOG_INFO,"end cell (%s) dropped, unknown stream.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length)); + return 0; + } +/* XXX add to this log_fn the exit node's nickname? */ + log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length), + conn->stream_id); + +#ifdef HALF_OPEN + conn->done_sending = 1; + shutdown(conn->s, 1); /* XXX check return; refactor NM */ + if (conn->done_receiving) { + /* We just *got* an end; no reason to send one. */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; + } +#else + /* We just *got* an end; no reason to send one. */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; +#endif + return 0; + case RELAY_COMMAND_EXTEND: + if(conn) { + log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping."); + return 0; + } + return circuit_extend(cell, circ); + case RELAY_COMMAND_EXTENDED: + if(!layer_hint) { + log_fn(LOG_WARN,"'extended' unsupported at non-origin. Dropping."); + return 0; + } + log_fn(LOG_DEBUG,"Got an extended cell! Yay."); + if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) { + log_fn(LOG_WARN,"circuit_finish_handshake failed."); + return -1; + } + if (circuit_send_next_onion_skin(circ)<0) { + log_fn(LOG_INFO,"circuit_send_next_onion_skin() failed."); + return -1; + } + return 0; + case RELAY_COMMAND_TRUNCATE: + if(layer_hint) { + log_fn(LOG_WARN,"'truncate' unsupported at origin. Dropping."); + return 0; + } + if(circ->n_conn) { + connection_send_destroy(circ->n_circ_id, circ->n_conn); + circ->n_conn = NULL; + } + log_fn(LOG_DEBUG, "Processed 'truncate', replying."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + return 0; + case RELAY_COMMAND_TRUNCATED: + if(!layer_hint) { + log_fn(LOG_WARN,"'truncated' unsupported at non-origin. Dropping."); + return 0; + } + circuit_truncated(circ, layer_hint); + return 0; + case RELAY_COMMAND_CONNECTED: + if(conn) { + log_fn(LOG_WARN,"'connected' unsupported while open. Closing circ."); + return -1; + } + log_fn(LOG_INFO,"'connected' received, no conn attached anymore. Ignoring."); + return 0; + case RELAY_COMMAND_SENDME: + if(!conn) { + if(layer_hint) { + layer_hint->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at origin, packagewindow %d.", + layer_hint->package_window); + circuit_resume_edge_reading(circ, layer_hint); + } else { + circ->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at non-origin, packagewindow %d.", + circ->package_window); + circuit_resume_edge_reading(circ, layer_hint); + } + return 0; + } + conn->package_window += STREAMWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window); + connection_start_reading(conn); + connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */ + return 0; + case RELAY_COMMAND_RESOLVE: + if (layer_hint) { + log_fn(LOG_WARN,"resolve request unsupported at AP; dropping."); + return 0; + } else if (conn) { + log_fn(LOG_WARN, "resolve request for known stream; dropping."); + return 0; + } else if (circ->purpose != CIRCUIT_PURPOSE_OR) { + log_fn(LOG_WARN, "resolve request on circ with purpose %d; dropping", + circ->purpose); + return 0; + } + connection_exit_begin_resolve(cell, circ); + return 0; + case RELAY_COMMAND_RESOLVED: + if(conn) { + log_fn(LOG_WARN,"'resolved' unsupported while open. Closing circ."); + return -1; + } + log_fn(LOG_INFO,"'resolved' received, no conn attached anymore. Ignoring."); + return 0; + case RELAY_COMMAND_ESTABLISH_INTRO: + case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: + case RELAY_COMMAND_INTRODUCE1: + case RELAY_COMMAND_INTRODUCE2: + case RELAY_COMMAND_INTRODUCE_ACK: + case RELAY_COMMAND_RENDEZVOUS1: + case RELAY_COMMAND_RENDEZVOUS2: + case RELAY_COMMAND_INTRO_ESTABLISHED: + case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: + rend_process_relay_cell(circ, rh.command, rh.length, + cell->payload+RELAY_HEADER_SIZE); + return 0; + } + log_fn(LOG_WARN,"unknown relay command %d.",rh.command); + return -1; +} + +uint64_t stats_n_data_cells_packaged = 0; +uint64_t stats_n_data_bytes_packaged = 0; +uint64_t stats_n_data_cells_received = 0; +uint64_t stats_n_data_bytes_received = 0; + +/** While conn->inbuf has an entire relay payload of bytes on it, + * and the appropriate package windows aren't empty, grab a cell + * and send it down the circuit. + * + * Return -1 if conn should be marked for close, else return 0. + */ +int connection_edge_package_raw_inbuf(connection_t *conn) { + int amount_to_process, length; + char payload[CELL_PAYLOAD_SIZE]; + circuit_t *circ; + + tor_assert(conn); + tor_assert(!connection_speaks_cells(conn)); + +repeat_connection_edge_package_raw_inbuf: + + circ = circuit_get_by_conn(conn); + if(!circ) { + log_fn(LOG_INFO,"conn has no circuits! Closing."); + return -1; + } + + if(circuit_consider_stop_edge_reading(circ, conn->cpath_layer)) + return 0; + + if(conn->package_window <= 0) { + log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); + connection_stop_reading(conn); + return 0; + } + + amount_to_process = buf_datalen(conn->inbuf); + + if(!amount_to_process) + return 0; + + if(amount_to_process > RELAY_PAYLOAD_SIZE) { + length = RELAY_PAYLOAD_SIZE; + } else { + length = amount_to_process; + } + stats_n_data_bytes_packaged += length; + stats_n_data_cells_packaged += 1; + + connection_fetch_from_buf(payload, length, conn); + + log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).", conn->s, length, + (int)buf_datalen(conn->inbuf)); + + if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA, + payload, length, conn->cpath_layer) < 0) + return 0; /* circuit is closed, don't continue */ + + if(!conn->cpath_layer) { /* non-rendezvous exit */ + tor_assert(circ->package_window > 0); + circ->package_window--; + } else { /* we're an AP, or an exit on a rendezvous circ */ + tor_assert(conn->cpath_layer->package_window > 0); + conn->cpath_layer->package_window--; + } + + if(--conn->package_window <= 0) { /* is it 0 after decrement? */ + connection_stop_reading(conn); + log_fn(LOG_DEBUG,"conn->package_window reached 0."); + circuit_consider_stop_edge_reading(circ, conn->cpath_layer); + return 0; /* don't process the inbuf any more */ + } + log_fn(LOG_DEBUG,"conn->package_window is now %d",conn->package_window); + + /* handle more if there's more, or return 0 if there isn't */ + goto repeat_connection_edge_package_raw_inbuf; +} + +/** Called when we've just received a relay data cell, or when + * we've just finished flushing all bytes to stream conn. + * + * If conn->outbuf is not too full, and our deliver window is + * low, send back a suitable number of stream-level sendme cells. + */ +void connection_edge_consider_sending_sendme(connection_t *conn) { + circuit_t *circ; + + if(connection_outbuf_too_full(conn)) + return; + + circ = circuit_get_by_conn(conn); + if(!circ) { + /* this can legitimately happen if the destroy has already + * arrived and torn down the circuit */ + log_fn(LOG_INFO,"No circuit associated with conn. Skipping."); + return; + } + + while(conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"Outbuf %d, Queueing stream sendme.", conn->outbuf_flushlen); + conn->deliver_window += STREAMWINDOW_INCREMENT; + if(connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME, + NULL, 0, conn->cpath_layer) < 0) { + log_fn(LOG_WARN,"connection_edge_send_command failed. Returning."); + return; /* the circuit's closed, don't continue */ + } + } +} + +/** The circuit circ has received a circuit-level sendme + * (on hop layer_hint, if we're the OP). Go through all the + * attached streams and let them resume reading and packaging, if + * their stream windows allow it. + */ +static void +circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) +{ + + log_fn(LOG_DEBUG,"resuming"); + + /* have to check both n_streams and p_streams, to handle rendezvous */ + if(circuit_resume_edge_reading_helper(circ->n_streams, circ, layer_hint) >= 0) + circuit_resume_edge_reading_helper(circ->p_streams, circ, layer_hint); +} + +/** A helper function for circuit_resume_edge_reading() above. + * The arguments are the same, except that conn is the head + * of a linked list of edge streams that should each be considered. + */ +static int +circuit_resume_edge_reading_helper(connection_t *conn, + circuit_t *circ, + crypt_path_t *layer_hint) { + + for( ; conn; conn=conn->next_stream) { + if((!layer_hint && conn->package_window > 0) || + (layer_hint && conn->package_window > 0 && conn->cpath_layer == layer_hint)) { + connection_start_reading(conn); + /* handle whatever might still be on the inbuf */ + connection_edge_package_raw_inbuf(conn); + + /* If the circuit won't accept any more data, return without looking + * at any more of the streams. Any connections that should be stopped + * have already been stopped by connection_edge_package_raw_inbuf. */ + if(circuit_consider_stop_edge_reading(circ, layer_hint)) + return -1; + } + } + return 0; +} + +/** Check if the package window for circ is empty (at + * hop layer_hint if it's defined). + * + * If yes, tell edge streams to stop reading and return -1. + * Else return 0. + */ +static int +circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) +{ + connection_t *conn = NULL; + + log_fn(LOG_DEBUG,"considering"); + if(!layer_hint && circ->package_window <= 0) { + log_fn(LOG_DEBUG,"yes, not-at-origin. stopped."); + for(conn = circ->n_streams; conn; conn=conn->next_stream) + connection_stop_reading(conn); + return -1; + } else if(layer_hint && layer_hint->package_window <= 0) { + log_fn(LOG_DEBUG,"yes, at-origin. stopped."); + for(conn = circ->n_streams; conn; conn=conn->next_stream) + if(conn->cpath_layer == layer_hint) + connection_stop_reading(conn); + for(conn = circ->p_streams; conn; conn=conn->next_stream) + if(conn->cpath_layer == layer_hint) + connection_stop_reading(conn); + return -1; + } + return 0; +} + +/** Check if the deliver_window for circuit circ (at hop + * layer_hint if it's defined) is low enough that we should + * send a circuit-level sendme back down the circuit. If so, send + * enough sendmes that the window would be overfull if we sent any + * more. + */ +static void +circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) +{ +// log_fn(LOG_INFO,"Considering: layer_hint is %s", +// layer_hint ? "defined" : "null"); + while((layer_hint ? layer_hint->deliver_window : circ->deliver_window) < + CIRCWINDOW_START - CIRCWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"Queueing circuit sendme."); + if(layer_hint) + layer_hint->deliver_window += CIRCWINDOW_INCREMENT; + else + circ->deliver_window += CIRCWINDOW_INCREMENT; + if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_SENDME, + NULL, 0, layer_hint) < 0) { + log_fn(LOG_WARN,"connection_edge_send_command failed. Circuit's closed."); + return; /* the circuit's closed, don't continue */ + } + } +} + diff --git a/tags/tor-0_0_8pre1/src/or/rendclient.c b/tags/tor-0_0_8pre1/src/or/rendclient.c new file mode 100644 index 0000000000..aaaf865037 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/rendclient.c @@ -0,0 +1,459 @@ +/* Copyright 2004 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file rendclient.c + * \brief Client code to access location-hiddenn services. + **/ + +#include "or.h" + +/** Called when we've established a circuit to an introduction point: + * send the introduction request. */ +void +rend_client_introcirc_has_opened(circuit_t *circ) +{ + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); + tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath); + + log_fn(LOG_INFO,"introcirc is open"); + connection_ap_attach_pending(); +} + +/** Send the establish-rendezvous cell along a rendezvous circuit. if + * it fails, mark the circ for close and return -1. else return 0. + */ +static int +rend_client_send_establish_rendezvous(circuit_t *circ) +{ + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); + log_fn(LOG_INFO, "Sending an ESTABLISH_RENDEZVOUS cell"); + + if (crypto_rand(REND_COOKIE_LEN, circ->rend_cookie)<0) { + log_fn(LOG_WARN, "Couldn't get random cookie"); + circuit_mark_for_close(circ); + return -1; + } + if (connection_edge_send_command(NULL,circ, + RELAY_COMMAND_ESTABLISH_RENDEZVOUS, + circ->rend_cookie, REND_COOKIE_LEN, + circ->cpath->prev)<0) { + /* circ is already marked for close */ + log_fn(LOG_WARN, "Couldn't send ESTABLISH_RENDEZVOUS cell"); + return -1; + } + + return 0; +} + +/** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell + * down introcirc if possible. + */ +int +rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) { + int payload_len, r; + char payload[RELAY_PAYLOAD_SIZE]; + char tmp[(MAX_NICKNAME_LEN+1)+REND_COOKIE_LEN+DH_KEY_LEN]; + rend_cache_entry_t *entry; + crypt_path_t *cpath; + + tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); + tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY); + tor_assert(!rend_cmp_service_ids(introcirc->rend_query, rendcirc->rend_query)); + + if(rend_cache_lookup_entry(introcirc->rend_query, &entry) < 1) { + log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.", + introcirc->rend_query); + goto err; + } + + /* first 20 bytes of payload are the hash of bob's pk */ + if (crypto_pk_get_digest(entry->parsed->pk, payload)<0) { + log_fn(LOG_WARN, "Couldn't hash public key."); + goto err; + } + + /* Initialize the pending_final_cpath and start the DH handshake. */ + cpath = rendcirc->build_state->pending_final_cpath; + if (!cpath) { + cpath = rendcirc->build_state->pending_final_cpath = + tor_malloc_zero(sizeof(crypt_path_t)); + if (!(cpath->handshake_state = crypto_dh_new())) { + log_fn(LOG_WARN, "Couldn't allocate DH"); + goto err; + } + if (crypto_dh_generate_public(cpath->handshake_state)<0) { + log_fn(LOG_WARN, "Couldn't generate g^x"); + goto err; + } + } + + /* write the remaining items into tmp */ + strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */ + memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN); + if (crypto_dh_get_public(cpath->handshake_state, + tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN, + DH_KEY_LEN)<0) { + log_fn(LOG_WARN, "Couldn't extract g^x"); + goto err; + } + + r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, tmp, + MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN, + payload+DIGEST_LEN, + PK_PKCS1_OAEP_PADDING, 0); + if (r<0) { + log_fn(LOG_WARN,"hybrid pk encrypt failed."); + goto err; + } + + tor_assert(DIGEST_LEN + r <= RELAY_PAYLOAD_SIZE); /* we overran something */ + payload_len = DIGEST_LEN + r; + + if (connection_edge_send_command(NULL, introcirc, + RELAY_COMMAND_INTRODUCE1, + payload, payload_len, + introcirc->cpath->prev)<0) { + /* introcirc is already marked for close. leave rendcirc alone. */ + log_fn(LOG_WARN, "Couldn't send INTRODUCE1 cell"); + return -1; + } + + /* Now, we wait for an ACK or NAK on this circuit. */ + introcirc->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; + + return 0; +err: + circuit_mark_for_close(introcirc); + circuit_mark_for_close(rendcirc); + return -1; +} + +/** Called when a rendezvous circuit is open; sends a establish + * rendezvous circuit as appropriate. */ +void +rend_client_rendcirc_has_opened(circuit_t *circ) +{ + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); + tor_assert(CIRCUIT_IS_ORIGIN(circ)); + + log_fn(LOG_INFO,"rendcirc is open"); + + /* generate a rendezvous cookie, store it in circ */ + if (rend_client_send_establish_rendezvous(circ) < 0) { + return; + } + + connection_ap_attach_pending(); +} + +/** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell. + */ +int +rend_client_introduction_acked(circuit_t *circ, + const char *request, int request_len) +{ + char *nickname; + circuit_t *rendcirc; + + if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + log_fn(LOG_WARN, "Received REND_INTRODUCE_ACK on unexpected circuit %d", + circ->n_circ_id); + circuit_mark_for_close(circ); + return -1; + } + + tor_assert(circ->build_state->chosen_exit_name); + + if (request_len == 0) { + /* It's an ACK; the introduction point relayed our introduction request. */ + /* Locate the rend circ which is waiting to hear about this ack, + * and tell it. + */ + log_fn(LOG_INFO,"Received ack. Telling rend circ."); + rendcirc = circuit_get_by_rend_query_and_purpose( + circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY); + if(rendcirc) { /* remember the ack */ + rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED; + } + /* close the circuit: we won't need it anymore. */ + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED; + circuit_mark_for_close(circ); + } else { + /* It's a NAK; the introduction point didn't relay our request. */ + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; + /* Remove this intro point from the set of viable introduction + * points. If any remain, extend to a new one and try again. + * If none remain, refetch the service descriptor. + */ + if(rend_client_remove_intro_point(circ->build_state->chosen_exit_name, + circ->rend_query) > 0) { + /* There are introduction points left. re-extend the circuit to + * another intro point and try again. */ + routerinfo_t *r; + nickname = rend_client_get_random_intro(circ->rend_query); + tor_assert(nickname); + log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", circ->rend_query, circ->build_state->chosen_exit_name, nickname); + if (!(r = router_get_by_nickname(nickname))) { + log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.", + nickname, circ->rend_query); + circuit_mark_for_close(circ); + return -1; + } + log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)", + nickname, circ->rend_query, circ->n_circ_id); + circ->state = CIRCUIT_STATE_BUILDING; + tor_free(circ->build_state->chosen_exit_name); + circ->build_state->chosen_exit_name = tor_strdup(nickname); + memcpy(circ->build_state->chosen_exit_digest, r->identity_digest, DIGEST_LEN); + ++circ->build_state->desired_path_len; + if (circuit_send_next_onion_skin(circ)<0) { + log_fn(LOG_WARN, "Couldn't extend circuit to new intro point."); + circuit_mark_for_close(circ); + return -1; + } + } + } + return 0; +} + + +/** If we are not currently fetching a rendezvous service descriptor + * for the service ID query, start a directory connection to fetch a + * new one. + */ +void +rend_client_refetch_renddesc(const char *query) +{ + if(connection_get_by_type_rendquery(CONN_TYPE_DIR, query)) { + log_fn(LOG_INFO,"Would fetch a new renddesc here (for %s), but one is already in progress.", query); + } else { + /* not one already; initiate a dir rend desc lookup */ + directory_get_from_dirserver(DIR_PURPOSE_FETCH_RENDDESC, + query, strlen(query)); + } +} + +/** remove failed_intro from ent. if ent now has no intro points, or + * service is unrecognized, then launch a new renddesc fetch. + * + * Return -1 if error, 0 if no intro points remain or service + * unrecognized, 1 if recognized and some intro points remain. + */ +int +rend_client_remove_intro_point(char *failed_intro, const char *query) +{ + int i, r; + rend_cache_entry_t *ent; + + r = rend_cache_lookup_entry(query, &ent); + if (r<0) { + log_fn(LOG_WARN, "Malformed service ID '%s'", query); + return -1; + } + if (r==0) { + log_fn(LOG_INFO, "Unknown service %s. Re-fetching descriptor.", query); + rend_client_refetch_renddesc(query); + return 0; + } + + for (i=0; i < ent->parsed->n_intro_points; ++i) { + if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) { + tor_free(ent->parsed->intro_points[i]); + ent->parsed->intro_points[i] = + ent->parsed->intro_points[--ent->parsed->n_intro_points]; + break; + } + } + + if(!ent->parsed->n_intro_points) { + log_fn(LOG_INFO,"No more intro points remain for %s. Re-fetching descriptor.", query); + rend_client_refetch_renddesc(query); + return 0; + } + log_fn(LOG_INFO,"%d options left for %s.", ent->parsed->n_intro_points, query); + return 1; +} + +/** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of + * the circuit to C_REND_READY. + */ +int +rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len) +{ + /* we just got an ack for our establish-rendezvous. switch purposes. */ + if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) { + log_fn(LOG_WARN,"Got a rendezvous ack when we weren't expecting one. Closing circ."); + circuit_mark_for_close(circ); + return -1; + } + log_fn(LOG_INFO,"Got rendezvous ack. This circuit is now ready for rendezvous."); + circ->purpose = CIRCUIT_PURPOSE_C_REND_READY; + return 0; +} + +/** Bob sent us a rendezvous cell; join the circuits. */ +int +rend_client_receive_rendezvous(circuit_t *circ, const char *request, int request_len) +{ + crypt_path_t *hop; + char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; + + if( (circ->purpose != CIRCUIT_PURPOSE_C_REND_READY && + circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) + || !circ->build_state->pending_final_cpath) { + log_fn(LOG_WARN,"Got rendezvous2 cell from Bob, but not expecting it. Closing."); + circuit_mark_for_close(circ); + return -1; + } + + if (request_len != DH_KEY_LEN+DIGEST_LEN) { + log_fn(LOG_WARN,"Incorrect length (%d) on RENDEZVOUS2 cell.",request_len); + goto err; + } + + /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/ + tor_assert(circ->build_state && circ->build_state->pending_final_cpath); + hop = circ->build_state->pending_final_cpath; + tor_assert(hop->handshake_state); + if (crypto_dh_compute_secret(hop->handshake_state, request, DH_KEY_LEN, + keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { + log_fn(LOG_WARN, "Couldn't complete DH handshake"); + goto err; + } + /* ... and set up cpath. */ + if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0) + goto err; + + /* Check whether the digest is right... */ + if (memcmp(keys, request+DH_KEY_LEN, DIGEST_LEN)) { + log_fn(LOG_WARN, "Incorrect digest of key material"); + goto err; + } + + crypto_dh_free(hop->handshake_state); + hop->handshake_state = NULL; + + /* All is well. Extend the circuit. */ + circ->purpose = CIRCUIT_PURPOSE_C_REND_JOINED; + hop->state = CPATH_STATE_OPEN; + /* set the windows to default. these are the windows + * that alice thinks bob has. + */ + hop->package_window = CIRCWINDOW_START; + hop->deliver_window = CIRCWINDOW_START; + + onion_append_to_cpath(&circ->cpath, hop); + circ->build_state->pending_final_cpath = NULL; /* prevent double-free */ + return 0; + err: + circuit_mark_for_close(circ); + return -1; +} + +/** Find all the apconns in state AP_CONN_STATE_RENDDESC_WAIT that + * are waiting on query. If success==1, move them to the next state. + * If success==0, fail them. + */ +void rend_client_desc_fetched(char *query, int success) { + connection_t **carray; + connection_t *conn; + int n, i; + rend_cache_entry_t *entry; + + get_connection_array(&carray, &n); + + for (i = 0; i < n; ++i) { + conn = carray[i]; + if (conn->type != CONN_TYPE_AP || + conn->state != AP_CONN_STATE_RENDDESC_WAIT) + continue; + if (rend_cmp_service_ids(conn->rend_query, query)) + continue; + /* great, this guy was waiting */ + if(success || + rend_cache_lookup_entry(conn->rend_query, &entry) == 1) { + /* either this fetch worked, or it failed but there was a + * valid entry from before which we should reuse */ + log_fn(LOG_INFO,"Rend desc retrieved. Launching circuits."); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + if (connection_ap_handshake_attach_circuit(conn) < 0) { + /* it will never work */ + log_fn(LOG_WARN,"attaching to a rend circ failed. Closing conn."); + conn->has_sent_end = 1; + connection_mark_for_close(conn); + } + } else { /* 404, or fetch didn't get that far */ + log_fn(LOG_WARN,"service id '%s' fetched failed, and not in cache. Closing conn.", query); + conn->has_sent_end = 1; + connection_mark_for_close(conn); + } + } +} + +/** strdup a nickname for a random introduction + * point of query. return NULL if error. + */ +char *rend_client_get_random_intro(char *query) { + int i; + smartlist_t *sl; + char *choice; + char *nickname; + rend_cache_entry_t *entry; + + if(rend_cache_lookup_entry(query, &entry) < 1) { + log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.", query); + return NULL; + } + + sl = smartlist_create(); + + /* add the intro point nicknames */ + for(i=0;iparsed->n_intro_points;i++) + smartlist_add(sl,entry->parsed->intro_points[i]); + + choice = smartlist_choose(sl); + if(!choice) { + smartlist_free(sl); + return NULL; + } + nickname = tor_strdup(choice); + smartlist_free(sl); + return nickname; +} + +/** If address is of the form "y.onion" with a well-formed handle y, + * then put a '\0' after y, lower-case it, and return 0. + * Else return -1 and change nothing. + */ +int rend_parse_rendezvous_address(char *address) { + char *s; + char query[REND_SERVICE_ID_LEN+1]; + + s = strrchr(address,'.'); + if(!s) return -1; /* no dot */ + if (strcasecmp(s+1,"onion")) + return -1; /* not .onion */ + + *s = 0; /* null terminate it */ + if(strlcpy(query, address, REND_SERVICE_ID_LEN+1) >= REND_SERVICE_ID_LEN+1) + goto failed; + tor_strlower(query); + if(rend_valid_service_id(query)) { + tor_strlower(address); + return 0; /* success */ + } +failed: + /* otherwise, return to previous state and return -1 */ + *s = '.'; + return -1; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/rendcommon.c b/tags/tor-0_0_8pre1/src/or/rendcommon.c new file mode 100644 index 0000000000..91caa18e39 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/rendcommon.c @@ -0,0 +1,345 @@ +/* Copyright 2004 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file rendcommon.c + * \brief Rendezvous implementation: shared code between + * introducers, services, clients, and rendezvous points. + **/ + +#include "or.h" + + +/** Return 0 if one and two are the same service ids, else -1 or 1 */ +int rend_cmp_service_ids(const char *one, const char *two) { + return strcasecmp(one,two); +} + +/** Free the storage held by the service descriptor desc. + */ +void rend_service_descriptor_free(rend_service_descriptor_t *desc) +{ + int i; + if (desc->pk) + crypto_free_pk_env(desc->pk); + if (desc->intro_points) { + for (i=0; i < desc->n_intro_points; ++i) { + tor_free(desc->intro_points[i]); + } + tor_free(desc->intro_points); + } + tor_free(desc); +} + +/** Encode a service descriptor for desc, and sign it with + * key. Store the descriptor in *str_out, and set + * *len_out to its length. + */ +int +rend_encode_service_descriptor(rend_service_descriptor_t *desc, + crypto_pk_env_t *key, + char **str_out, int *len_out) +{ + char *buf, *cp, *ipoint; + int i, keylen, asn1len; + keylen = crypto_pk_keysize(desc->pk); + buf = tor_malloc(keylen*2); /* Too long, but that's okay. */ + asn1len = crypto_pk_asn1_encode(desc->pk, buf, keylen*2); + if (asn1len<0) { + tor_free(buf); + return -1; + } + *len_out = 2 + asn1len + 4 + 2 + keylen; + for (i = 0; i < desc->n_intro_points; ++i) { + *len_out += strlen(desc->intro_points[i]) + 1; + } + cp = *str_out = tor_malloc(*len_out); + set_uint16(cp, htons((uint16_t)asn1len)); + cp += 2; + memcpy(cp, buf, asn1len); + tor_free(buf); + cp += asn1len; + set_uint32(cp, htonl((uint32_t)desc->timestamp)); + cp += 4; + set_uint16(cp, htons((uint16_t)desc->n_intro_points)); + cp += 2; + for (i=0; i < desc->n_intro_points; ++i) { + ipoint = (char*)desc->intro_points[i]; + strcpy(cp, ipoint); + cp += strlen(ipoint)+1; + } + i = crypto_pk_private_sign_digest(key, *str_out, cp-*str_out, cp); + if (i<0) { + tor_free(*str_out); + return -1; + } + cp += i; + tor_assert(*len_out == (cp-*str_out)); + return 0; +} + +/** Parse a service descriptor at str (len bytes). On + * success, return a newly alloced service_descriptor_t. On failure, + * return NULL. + */ +rend_service_descriptor_t *rend_parse_service_descriptor( + const char *str, int len) +{ + rend_service_descriptor_t *result = NULL; + int keylen, asn1len, i; + const char *end, *cp, *eos; + + result = tor_malloc_zero(sizeof(rend_service_descriptor_t)); + cp = str; + end = str+len; + if (end-cp < 2) goto truncated; + asn1len = ntohs(get_uint16(cp)); + cp += 2; + if (end-cp < asn1len) goto truncated; + result->pk = crypto_pk_asn1_decode(cp, asn1len); + if (!result->pk) goto truncated; + cp += asn1len; + if (end-cp < 4) goto truncated; + result->timestamp = (time_t) ntohl(get_uint32(cp)); + cp += 4; + if (end-cp < 2) goto truncated; + result->n_intro_points = ntohs(get_uint16(cp)); + result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points); + cp += 2; + for (i=0;in_intro_points;++i) { + if (end-cp < 2) goto truncated; + eos = (const char *)memchr(cp,'\0',end-cp); + if (!eos) goto truncated; + result->intro_points[i] = tor_strdup(cp); + cp = eos+1; + } + keylen = crypto_pk_keysize(result->pk); + if (end-cp < keylen) goto truncated; + if (end-cp > keylen) { + log_fn(LOG_WARN, "Signature too long on service descriptor"); + goto error; + } + if (crypto_pk_public_checksig_digest(result->pk, + (char*)str,cp-str, /* data */ + (char*)cp,end-cp /* signature*/ + )<0) { + log_fn(LOG_WARN, "Bad signature on service descriptor"); + goto error; + } + + return result; + truncated: + log_fn(LOG_WARN, "Truncated service descriptor"); + error: + rend_service_descriptor_free(result); + return NULL; +} + +/** Sets out to the first 10 bytes of the digest of pk, + * base32 encoded. NUL-terminates out. (We use this string to + * identify services in directory requests and .onion URLs.) + */ +int rend_get_service_id(crypto_pk_env_t *pk, char *out) +{ + char buf[DIGEST_LEN]; + tor_assert(pk); + if (crypto_pk_get_digest(pk, buf) < 0) + return -1; + base32_encode(out, REND_SERVICE_ID_LEN+1, buf, 10); + return 0; +} + +/* ==== Rendezvous service descriptor cache. */ + +#define REND_CACHE_MAX_AGE (24*60*60) +#define REND_CACHE_MAX_SKEW (90*60) + +/** Map from service id (as generated by rend_get_service_id) to + * rend_cache_entry_t. */ +static strmap_t *rend_cache = NULL; + +/** Initializes the service descriptor cache. + */ +void rend_cache_init(void) +{ + rend_cache = strmap_new(); +} + +/** Removes all old entries from the service descriptor cache. + */ +void rend_cache_clean(void) +{ + strmap_iter_t *iter; + const char *key; + void *val; + rend_cache_entry_t *ent; + time_t cutoff; + cutoff = time(NULL) - REND_CACHE_MAX_AGE; + for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) { + strmap_iter_get(iter, &key, &val); + ent = (rend_cache_entry_t*)val; + if (ent->parsed->timestamp < cutoff) { + iter = strmap_iter_next_rmv(rend_cache, iter); + rend_service_descriptor_free(ent->parsed); + tor_free(ent->desc); + tor_free(ent); + } else { + iter = strmap_iter_next(rend_cache, iter); + } + } +} + +/** Return true iff query is a syntactically valid service ID (as + * generated by rend_get_service_id). */ +int rend_valid_service_id(const char *query) { + if(strlen(query) != REND_SERVICE_ID_LEN) + return 0; + + if (strspn(query, BASE32_CHARS) != REND_SERVICE_ID_LEN) + return 0; + + return 1; +} + +/** If we have a cached rend_cache_entry_t for the service ID query, set + * *e to that entry and return 1. Else return 0. + */ +int rend_cache_lookup_entry(const char *query, rend_cache_entry_t **e) +{ + tor_assert(rend_cache); + if (!rend_valid_service_id(query)) + return -1; + *e = strmap_get_lc(rend_cache, query); + if (!*e) + return 0; + return 1; +} + +/** query is a base-32'ed service id. If it's malformed, return -1. + * Else look it up. + * - If it is found, point *desc to it, and write its length into + * *desc_len, and return 1. + * - If it is not found, return 0. + * Note: calls to rend_cache_clean or rend_cache_store may invalidate + * *desc. + */ +int rend_cache_lookup_desc(const char *query, const char **desc, int *desc_len) +{ + rend_cache_entry_t *e; + int r; + r = rend_cache_lookup_entry(query,&e); + if (r <= 0) return r; + *desc = e->desc; + *desc_len = e->len; + return 1; +} + +/** Parse *desc, calculate its service id, and store it in the cache. + * If we have a newer descriptor with the same ID, ignore this one. + * If we have an older descriptor with the same ID, replace it. + * Returns -1 if it's malformed or otherwise rejected, else return 0. + */ +int rend_cache_store(const char *desc, int desc_len) +{ + rend_cache_entry_t *e; + rend_service_descriptor_t *parsed; + char query[REND_SERVICE_ID_LEN+1]; + time_t now; + tor_assert(rend_cache); + parsed = rend_parse_service_descriptor(desc,desc_len); + if (!parsed) { + log_fn(LOG_WARN,"Couldn't parse service descriptor"); + return -1; + } + if (rend_get_service_id(parsed->pk, query)<0) { + log_fn(LOG_WARN,"Couldn't compute service ID"); + rend_service_descriptor_free(parsed); + return -1; + } + now = time(NULL); + if (parsed->timestamp < now-REND_CACHE_MAX_AGE) { + log_fn(LOG_WARN,"Service descriptor %s is too old", query); + rend_service_descriptor_free(parsed); + return -1; + } + if (parsed->timestamp > now+REND_CACHE_MAX_SKEW) { + log_fn(LOG_WARN,"Service descriptor %s is too far in the future", query); + rend_service_descriptor_free(parsed); + return -1; + } + e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, query); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_fn(LOG_INFO,"We already have a newer service descriptor %s with the same ID", query); + rend_service_descriptor_free(parsed); + return 0; + } + if (e && e->len == desc_len && !memcmp(desc,e->desc,desc_len)) { + log_fn(LOG_INFO,"We already have this service descriptor %s", query); + e->received = time(NULL); + rend_service_descriptor_free(parsed); + return 0; + } + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + strmap_set_lc(rend_cache, query, e); + } else { + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->received = time(NULL); + e->parsed = parsed; + e->len = desc_len; + e->desc = tor_malloc(desc_len); + memcpy(e->desc, desc, desc_len); + + log_fn(LOG_INFO,"Successfully stored rend desc '%s', len %d", query, desc_len); + return 0; +} + +/** Called when we get a rendezvous-related relay cell on circuit + * circ. Dispatch on rendezvous relay command. */ +void rend_process_relay_cell(circuit_t *circ, int command, int length, + const char *payload) +{ + int r; + switch(command) { + case RELAY_COMMAND_ESTABLISH_INTRO: + r = rend_mid_establish_intro(circ,payload,length); + break; + case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: + r = rend_mid_establish_rendezvous(circ,payload,length); + break; + case RELAY_COMMAND_INTRODUCE1: + r = rend_mid_introduce(circ,payload,length); + break; + case RELAY_COMMAND_INTRODUCE2: + r = rend_service_introduce(circ,payload,length); + break; + case RELAY_COMMAND_INTRODUCE_ACK: + r = rend_client_introduction_acked(circ,payload,length); + break; + case RELAY_COMMAND_RENDEZVOUS1: + r = rend_mid_rendezvous(circ,payload,length); + break; + case RELAY_COMMAND_RENDEZVOUS2: + r = rend_client_receive_rendezvous(circ,payload,length); + break; + case RELAY_COMMAND_INTRO_ESTABLISHED: + r = rend_service_intro_established(circ,payload,length); + break; + case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: + r = rend_client_rendezvous_acked(circ,payload,length); + break; + default: + tor_assert(0); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/rendmid.c b/tags/tor-0_0_8pre1/src/or/rendmid.c new file mode 100644 index 0000000000..6c2b372956 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/rendmid.c @@ -0,0 +1,294 @@ +/* Copyright 2004 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file rendmid.c + * \brief Implement introductions points and rendezvous points. + **/ + +#include "or.h" + +/** Respond to an ESTABLISH_INTRO cell by checking the signed data and + * setting the circuit's purpose and service pk digest. + */ +int +rend_mid_establish_intro(circuit_t *circ, const char *request, int request_len) +{ + crypto_pk_env_t *pk = NULL; + char buf[DIGEST_LEN+9]; + char expected_digest[DIGEST_LEN]; + char pk_digest[DIGEST_LEN]; + int asn1len; + circuit_t *c; + char serviceid[REND_SERVICE_ID_LEN+1]; + + log_fn(LOG_INFO, + "Received an ESTABLISH_INTRO request on circuit %d", circ->p_circ_id); + + if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) { + log_fn(LOG_WARN, "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit"); + goto err; + } + if (request_len < 2+DIGEST_LEN) + goto truncated; + /* First 2 bytes: length of asn1-encoded key. */ + asn1len = ntohs(get_uint16(request)); + + /* Next asn1len bytes: asn1-encoded key. */ + if (request_len < 2+DIGEST_LEN+asn1len) + goto truncated; + pk = crypto_pk_asn1_decode(request+2, asn1len); + if (!pk) { + log_fn(LOG_WARN, "Couldn't decode public key"); + goto err; + } + + /* Next 20 bytes: Hash of handshake_digest | "INTRODUCE" */ + memcpy(buf, circ->handshake_digest, DIGEST_LEN); + memcpy(buf+DIGEST_LEN, "INTRODUCE", 9); + if (crypto_digest(buf, DIGEST_LEN+9, expected_digest)<0) { + log_fn(LOG_WARN, "Error computing digest"); + goto err; + } + if (memcmp(expected_digest, request+2+asn1len, DIGEST_LEN)) { + log_fn(LOG_WARN, "Hash of session info was not as expected"); + goto err; + } + /* Rest of body: signature of previous data */ + if (crypto_pk_public_checksig_digest(pk, request, 2+asn1len+DIGEST_LEN, + request+2+DIGEST_LEN+asn1len, + request_len-(2+DIGEST_LEN+asn1len))<0) { + log_fn(LOG_WARN, "Incorrect signature on ESTABLISH_INTRO cell; rejecting"); + goto err; + } + + /* The request is valid. First, compute the hash of Bob's PK.*/ + if (crypto_pk_get_digest(pk, pk_digest)<0) { + log_fn(LOG_WARN, "Couldn't hash public key."); + goto err; + } + + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, pk_digest,10); + + /* Close any other intro circuits with the same pk. */ + c = NULL; + while ((c = circuit_get_next_by_pk_and_purpose( + c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) { + log_fn(LOG_INFO, "Replacing old circuit %d for service %s", + c->p_circ_id, serviceid); + circuit_mark_for_close(c); + } + + /* Acknlowedge the request. */ + if (connection_edge_send_command(NULL,circ, + RELAY_COMMAND_INTRO_ESTABLISHED, + "", 0, NULL)<0) { + log_fn(LOG_WARN, "Couldn't send INTRO_ESTABLISHED cell"); + goto err; + } + + /* Now, set up this circuit. */ + circ->purpose = CIRCUIT_PURPOSE_INTRO_POINT; + memcpy(circ->rend_pk_digest, pk_digest, DIGEST_LEN); + + log_fn(LOG_INFO, + "Established introduction point on circuit %d for service %s", + circ->p_circ_id, serviceid); + + return 0; + truncated: + log_fn(LOG_WARN, "Rejecting truncated ESTABLISH_INTRO cell"); + err: + if (pk) crypto_free_pk_env(pk); + circuit_mark_for_close(circ); + return -1; +} + +/** Process an INTRODUCE1 cell by finding the corresponding introduction + * circuit, and relaying the body of the INTRODUCE1 cell inside an + * INTRODUCE2 cell. + */ +int +rend_mid_introduce(circuit_t *circ, const char *request, int request_len) +{ + circuit_t *intro_circ; + char serviceid[REND_SERVICE_ID_LEN+1]; + char nak_body[1]; + + if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) { + log_fn(LOG_WARN, "Rejecting INTRODUCE1 on non-OR or non-edge circuit %d", + circ->p_circ_id); + goto err; + } + + if (request_len < (DIGEST_LEN+(MAX_NICKNAME_LEN+1)+REND_COOKIE_LEN+ + DH_KEY_LEN+CIPHER_KEY_LEN+PKCS1_OAEP_PADDING_OVERHEAD)) { + log_fn(LOG_WARN, + "Impossibly short INTRODUCE1 cell on circuit %d; responding with nack.", + circ->p_circ_id); + goto err; + } + + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request,10); + + /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */ + intro_circ = circuit_get_next_by_pk_and_purpose( + NULL, request, CIRCUIT_PURPOSE_INTRO_POINT); + if (!intro_circ) { + log_fn(LOG_WARN, + "No intro circ found for INTRODUCE1 cell (%s) from circuit %d; responding with nack", + serviceid, circ->p_circ_id); + goto err; + } + + log_fn(LOG_INFO, + "Sending introduction request for service %s from circ %d to circ %d", + serviceid, circ->p_circ_id, intro_circ->p_circ_id); + + /* Great. Now we just relay the cell down the circuit. */ + if (connection_edge_send_command(NULL, intro_circ, + RELAY_COMMAND_INTRODUCE2, + request, request_len, NULL)) { + log_fn(LOG_WARN, "Unable to send INTRODUCE2 cell to OP."); + goto err; + } + /* And sent an ack down the cirecuit. Empty body->succeeded. */ + if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK, + NULL,0,NULL)) { + log_fn(LOG_WARN, "Unable to send INTRODUCE_ACK cell to OP."); + circuit_mark_for_close(circ); + return -1; + } + + return 0; + err: + /* Send the client an NACK */ + nak_body[0] = 1; + if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK, + nak_body, 1, NULL)) { + log_fn(LOG_WARN, "Unable to send NAK to OP"); + circuit_mark_for_close(circ); /* Is this right? */ + } + return -1; +} + +/** Process an ESTABLISH_RENDEZVOUS cell by setting the circuit's purpose and + * rendezvous cookie. + */ +int +rend_mid_establish_rendezvous(circuit_t *circ, const char *request, int request_len) +{ + char hexid[9]; + + if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) { + log_fn(LOG_WARN, "Tried to establish rendezvous on non-OR or non-edge circuit"); + goto err; + } + + if (request_len != REND_COOKIE_LEN) { + log_fn(LOG_WARN, "Invalid length on ESTABLISH_RENDEZVOUS"); + goto err; + } + + if (circuit_get_rendezvous(request)) { + log_fn(LOG_WARN, "Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS"); + goto err; + } + + /* Acknlowedge the request. */ + if (connection_edge_send_command(NULL,circ, + RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, + "", 0, NULL)<0) { + log_fn(LOG_WARN, "Couldn't send RENDEZVOUS_ESTABLISHED cell"); + goto err; + } + + circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + memcpy(circ->rend_cookie, request, REND_COOKIE_LEN); + + + hex_encode(request,4,hexid); + + log_fn(LOG_INFO, "Established rendezvous point on circuit %d for cookie %s", + circ->p_circ_id, hexid); + + return 0; + err: + circuit_mark_for_close(circ); + return -1; +} + +/** Process a RENDEZVOUS1 cell by looking up the correct rendezvous + * circuit by its relaying the cell's body in a RENDEZVOUS2 cell, and + * connecting the two circuits. + */ +int +rend_mid_rendezvous(circuit_t *circ, const char *request, int request_len) +{ + circuit_t *rend_circ; + char hexid[9]; + + if (request_len>=4) { + hex_encode(request,4,hexid); + log_fn(LOG_INFO, "Got request for rendezvous from circuit %d to cookie %s", + circ->p_circ_id, hexid); + } + + if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) { + log_fn(LOG_WARN, + "Tried to complete rendezvous on non-OR or non-edge circuit %d", + circ->p_circ_id); + goto err; + } + + if (request_len != REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN) { + log_fn(LOG_WARN, + "Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %d", + request_len, circ->p_circ_id); + goto err; + } + + rend_circ = circuit_get_rendezvous(request); + if (!rend_circ) { + log_fn(LOG_WARN, + "Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s", + hexid); + goto err; + } + + /* Send the RENDEZVOUS2 cell to Alice. */ + if (connection_edge_send_command(NULL, rend_circ, + RELAY_COMMAND_RENDEZVOUS2, + request+REND_COOKIE_LEN, + request_len-REND_COOKIE_LEN, NULL)) { + log_fn(LOG_WARN, "Unable to send RENDEZVOUS2 cell to OP on circuit %d", + rend_circ->p_circ_id); + goto err; + } + + /* Join the circuits. */ + log_fn(LOG_INFO, + "Completing rendezvous: circuit %d joins circuit %d (cookie %s)", + circ->p_circ_id, rend_circ->p_circ_id, hexid); + + circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED; + rend_circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED; + memset(circ->rend_cookie, 0, REND_COOKIE_LEN); + + rend_circ->rend_splice = circ; + circ->rend_splice = rend_circ; + + return 0; + err: + circuit_mark_for_close(circ); + return -1; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/rendservice.c b/tags/tor-0_0_8pre1/src/or/rendservice.c new file mode 100644 index 0000000000..ff25704f3f --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/rendservice.c @@ -0,0 +1,955 @@ +/* Copyright 2004 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file rendservice.c + * \brief The hidden-service side of rendezvous functionality. + **/ + +#include "or.h" + +static circuit_t *find_intro_circuit(routerinfo_t *router, const char *pk_digest); + +/** Represents the mapping from a virtual port of a rendezvous service to + * a real port on some IP. + */ +typedef struct rend_service_port_config_t { + uint16_t virtual_port; + uint16_t real_port; + uint32_t real_address; +} rend_service_port_config_t; + +/** Try to maintain this many intro points per service if possible. */ +#define NUM_INTRO_POINTS 3 + +/** If we can't build our intro circuits, don't retry for this long. */ +#define INTRO_CIRC_RETRY_PERIOD 60*5 +/** Don't try to build more than this many circuits before giving up + * for a while.*/ +#define MAX_INTRO_CIRCS_PER_PERIOD 10 + +/** Represents a single hidden service running at this OP. */ +typedef struct rend_service_t { + /** Fields specified in config file */ + char *directory; /**< where in the filesystem it stores it */ + smartlist_t *ports; /**< List of rend_service_port_config_t */ + char *intro_prefer_nodes; /**< comma-separated list of nicknames */ + char *intro_exclude_nodes; /**< comma-separated list of nicknames */ + /* Other fields */ + crypto_pk_env_t *private_key; + char service_id[REND_SERVICE_ID_LEN+1]; + char pk_digest[DIGEST_LEN]; + smartlist_t *intro_nodes; /**< list of nicknames for intro points we have, + * or are trying to establish. */ + time_t intro_period_started; + int n_intro_circuits_launched; /**< count of intro circuits we have + * established in this period. */ + rend_service_descriptor_t *desc; + int desc_is_dirty; +} rend_service_t; + +/** A list of rend_service_t's for services run on this OP. + */ +static smartlist_t *rend_service_list = NULL; + +/** Release the storage held by service. + */ +static void rend_service_free(rend_service_t *service) +{ + if (!service) return; + tor_free(service->directory); + SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p)); + smartlist_free(service->ports); + if (service->private_key) + crypto_free_pk_env(service->private_key); + tor_free(service->intro_prefer_nodes); + tor_free(service->intro_exclude_nodes); + SMARTLIST_FOREACH(service->intro_nodes, void*, p, tor_free(p)); + smartlist_free(service->intro_nodes); + if (service->desc) + rend_service_descriptor_free(service->desc); + tor_free(service); +} + +/** Release all the storage held in rend_service_list, and allocate a new, + * empty rend_service_list. + */ +static void rend_service_free_all(void) +{ + if (!rend_service_list) { + rend_service_list = smartlist_create(); + return; + } + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, + rend_service_free(ptr)); + smartlist_free(rend_service_list); + rend_service_list = smartlist_create(); +} + +/** Validate service and add it to rend_service_list if possible. + */ +static void add_service(rend_service_t *service) +{ + int i; + rend_service_port_config_t *p; + struct in_addr addr; + + if (!service->intro_prefer_nodes) + service->intro_prefer_nodes = tor_strdup(""); + if (!service->intro_exclude_nodes) + service->intro_exclude_nodes = tor_strdup(""); + + if (!smartlist_len(service->ports)) { + log_fn(LOG_WARN, "Hidden service with no ports configured; ignoring."); + rend_service_free(service); + } else { + smartlist_set_capacity(service->ports, -1); + smartlist_add(rend_service_list, service); + log_fn(LOG_DEBUG,"Configuring service with directory %s",service->directory); + for (i = 0; i < smartlist_len(service->ports); ++i) { + p = smartlist_get(service->ports, i); + addr.s_addr = htonl(p->real_address); + log_fn(LOG_DEBUG,"Service maps port %d to %s:%d", + p->virtual_port, inet_ntoa(addr), p->real_port); + } + } +} + +/** Parses a real-port to virtual-port mapping and returns a new + * rend_service_port_config_t. + * + * The format is: VirtualPort (IP|RealPort|IP:RealPort)? + * + * IP defaults to 127.0.0.1; RealPort defaults to VirtualPort. + */ +static rend_service_port_config_t *parse_port_config(const char *string) +{ + int virtport, realport, r; + struct in_addr addr; + char *endptr, *colon, *addrstring; + rend_service_port_config_t *result; + + virtport = (int) strtol(string, &endptr, 10); + if (endptr == string) { + log_fn(LOG_WARN, "Missing port in hidden service port configuration"); + return NULL; + } + if (virtport < 1 || virtport > 65535) { + log_fn(LOG_WARN, "Port out of range in hidden service port configuration"); + return NULL; + } + string = endptr + strspn(endptr, " \t"); + if (!*string) { + /* No addr:port part; use default. */ + realport = virtport; + addr.s_addr = htonl(0x7F000001u); /* 127.0.0.1 */ + } else { + colon = strchr(string, ':'); + if (colon) { + /* Try to parse addr:port. */ + addrstring = tor_strndup(string, colon-string); + r = tor_inet_aton(addrstring, &addr); + tor_free(addrstring); + if (!r) { + log_fn(LOG_WARN,"Unparseable address in hidden service port configuration"); + return NULL; + } + realport = strtol(colon+1, &endptr, 10); + if (*endptr) { + log_fn(LOG_WARN,"Unparseable or missing port in hidden service port configuration."); + return NULL; + } + } else if (strchr(string, '.') && tor_inet_aton(string, &addr)) { + /* We have addr; use deafult port. */ + realport = virtport; + } else { + /* No addr:port, no addr -- must be port. */ + realport = strtol(string, &endptr, 10); + if (*endptr) { + log_fn(LOG_WARN, "Unparseable of missing port in hidden service port configuration."); + return NULL; + } + addr.s_addr = htonl(0x7F000001u); /* Default to 127.0.0.1 */ + } + } + if (realport < 1 || realport > 65535) { + log_fn(LOG_WARN, "Port out of range in hidden service port configuration."); + return NULL; + } + + result = tor_malloc(sizeof(rend_service_port_config_t)); + result->virtual_port = virtport; + result->real_port = realport; + result->real_address = (uint32_t) ntohl(addr.s_addr); + return result; +} + +/** Set up rend_service_list, based on the values of HiddenServiceDir and + * HiddenServicePort in options. Return 0 on success and -1 on + * failure. + */ +int rend_config_services(or_options_t *options) +{ + struct config_line_t *line; + rend_service_t *service = NULL; + rend_service_port_config_t *portcfg; + rend_service_free_all(); + + for (line = options->RendConfigLines; line; line = line->next) { + if (!strcasecmp(line->key, "HiddenServiceDir")) { + if (service) + add_service(service); + service = tor_malloc_zero(sizeof(rend_service_t)); + service->directory = tor_strdup(line->value); + service->ports = smartlist_create(); + service->intro_nodes = smartlist_create(); + service->intro_period_started = time(NULL); + continue; + } + if (!service) { + log_fn(LOG_WARN, "HiddenServicePort with no preceeding HiddenServiceDir directive"); + rend_service_free(service); + return -1; + } + if (!strcasecmp(line->key, "HiddenServicePort")) { + portcfg = parse_port_config(line->value); + if (!portcfg) { + rend_service_free(service); + return -1; + } + smartlist_add(service->ports, portcfg); + } else if (!strcasecmp(line->key, "HiddenServiceNodes")) { + if (service->intro_prefer_nodes) { + log_fn(LOG_WARN, "Got multiple HiddenServiceNodes lines for a single service"); + return -1; + } + service->intro_prefer_nodes = tor_strdup(line->value); + } else { + tor_assert(!strcasecmp(line->key, "HiddenServiceExcludeNodes")); + if (service->intro_exclude_nodes) { + log_fn(LOG_WARN, "Got multiple HiddenServiceExcludedNodes lines for a single service"); + return -1; + } + service->intro_exclude_nodes = tor_strdup(line->value); + } + } + if (service) + add_service(service); + + return 0; +} + +/** Replace the old value of service-\>desc with one that reflects + * the other fields in service. + */ +static void rend_service_update_descriptor(rend_service_t *service) +{ + rend_service_descriptor_t *d; + circuit_t *circ; + int i,n; + routerinfo_t *router; + + if (service->desc) { + rend_service_descriptor_free(service->desc); + service->desc = NULL; + } + d = service->desc = tor_malloc(sizeof(rend_service_descriptor_t)); + d->pk = crypto_pk_dup_key(service->private_key); + d->timestamp = time(NULL); + n = smartlist_len(service->intro_nodes); + d->n_intro_points = 0; + d->intro_points = tor_malloc(sizeof(char*)*n); + for (i=0; i < n; ++i) { + router = router_get_by_nickname(smartlist_get(service->intro_nodes, i)); + circ = find_intro_circuit(router, service->pk_digest); + if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) { + /* We have an entirely established intro circuit. */ + d->intro_points[d->n_intro_points++] = tor_strdup(router->nickname); + } + } +} + +/** Load and/or generate private keys for all hidden services. Return 0 on + * success, -1 on failure. + */ +int rend_service_load_keys(void) +{ + int i; + rend_service_t *s; + char fname[512]; + char buf[128]; + + for (i=0; i < smartlist_len(rend_service_list); ++i) { + s = smartlist_get(rend_service_list,i); + if (s->private_key) + continue; + log_fn(LOG_INFO, "Loading hidden-service keys from '%s'", s->directory); + + /* Check/create directory */ + if (check_private_dir(s->directory, 1) < 0) + return -1; + + /* Load key */ + if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || + strlcat(fname,"/private_key",sizeof(fname)) >= sizeof(fname)) { + log_fn(LOG_WARN, "Directory name too long: '%s'", s->directory); + return -1; + } + s->private_key = init_key_from_file(fname); + if (!s->private_key) + return -1; + + /* Create service file */ + if (rend_get_service_id(s->private_key, s->service_id)<0) { + log_fn(LOG_WARN, "Couldn't encode service ID"); + return -1; + } + if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { + log_fn(LOG_WARN, "Couldn't compute hash of public key"); + return -1; + } + if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || + strlcat(fname,"/hostname",sizeof(fname)) >= sizeof(fname)) { + log_fn(LOG_WARN, "Directory name too long: '%s'", s->directory); + return -1; + } + sprintf(buf, "%s.onion\n", s->service_id); + if (write_str_to_file(fname,buf)<0) + return -1; + } + return 0; +} + +/** Return the service whose public key has a digest of digest. Return + * NULL if no such service exists. + */ +static rend_service_t * +rend_service_get_by_pk_digest(const char* digest) +{ + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, + if (!memcmp(s->pk_digest,digest,DIGEST_LEN)) return s); + return NULL; +} + +/****** + * Handle cells + ******/ + +/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen + * rendezvous points. + */ +int +rend_service_introduce(circuit_t *circuit, const char *request, int request_len) +{ + char *ptr, *rp_nickname, *r_cookie; + char buf[RELAY_PAYLOAD_SIZE]; + char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ + rend_service_t *service; + int len, keylen; + crypto_dh_env_t *dh = NULL; + circuit_t *launched = NULL; + crypt_path_t *cpath = NULL; + char serviceid[REND_SERVICE_ID_LEN+1]; + char hexcookie[9]; + + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, + circuit->rend_pk_digest,10); + log_fn(LOG_INFO, "Received INTRODUCE2 cell for service %s on circ %d", + serviceid, circuit->n_circ_id); + + if (circuit->purpose != CIRCUIT_PURPOSE_S_INTRO) { + log_fn(LOG_WARN, "Got an INTRODUCE2 over a non-introduction circuit %d", + circuit->n_circ_id); + return -1; + } + + /* min key length plus digest length plus nickname length */ + if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+ + DH_KEY_LEN+42){ + log_fn(LOG_WARN, "Got a truncated INTRODUCE2 cell on circ %d", + circuit->n_circ_id); + return -1; + } + + /* first DIGEST_LEN bytes of request is service pk digest */ + service = rend_service_get_by_pk_digest(request); + if (!service) { + log_fn(LOG_WARN, "Got an INTRODUCE2 cell for an unrecognized service %s", + serviceid); + return -1; + } + if (memcmp(circuit->rend_pk_digest, request, DIGEST_LEN)) { + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request, 10); + log_fn(LOG_WARN, "Got an INTRODUCE2 cell for the wrong service (%s)", + serviceid); + return -1; + } + + keylen = crypto_pk_keysize(service->private_key); + if (request_len < keylen+DIGEST_LEN) { + log_fn(LOG_WARN, "PK-encrypted portion of INTRODUCE2 cell was truncated"); + return -1; + } + /* Next N bytes is encrypted with service key */ + len = crypto_pk_private_hybrid_decrypt( + service->private_key,request+DIGEST_LEN,request_len-DIGEST_LEN,buf, + PK_PKCS1_OAEP_PADDING,1); + if (len<0) { + log_fn(LOG_WARN, "Couldn't decrypt INTRODUCE2 cell"); + return -1; + } + ptr=memchr(buf,0,MAX_NICKNAME_LEN+1); + if (!ptr || ptr == buf) { + log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell"); + return -1; + } + if ((int)strspn(buf,LEGAL_NICKNAME_CHARACTERS) != ptr-buf) { + log_fn(LOG_WARN, "Nickname in INTRODUCE2 cell contains illegal character."); + return -1; + } + /* Okay, now we know that the nickname is at the start of the buffer. */ + rp_nickname = buf; + ptr = buf+(MAX_NICKNAME_LEN+1); + len -= (MAX_NICKNAME_LEN+1); + if (len != REND_COOKIE_LEN+DH_KEY_LEN) { + log_fn(LOG_WARN, "Bad length for INTRODUCE2 cell."); + return -1; + } + r_cookie = ptr; + hex_encode(r_cookie,4,hexcookie); + + /* Try DH handshake... */ + dh = crypto_dh_new(); + if (!dh || crypto_dh_generate_public(dh)<0) { + log_fn(LOG_WARN, "Couldn't build DH state or generate public key"); + goto err; + } + if (crypto_dh_compute_secret(dh, ptr+REND_COOKIE_LEN, DH_KEY_LEN, keys, + DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { + log_fn(LOG_WARN, "Couldn't complete DH handshake"); + goto err; + } + + /* Launch a circuit to alice's chosen rendezvous point. + */ + launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname); + log_fn(LOG_INFO, + "Accepted intro; launching circuit to '%s' (cookie %s) for service %s", + rp_nickname, hexcookie, serviceid); + if (!launched) { + log_fn(LOG_WARN, + "Can't launch circuit to rendezvous point '%s' for service %s", + rp_nickname, serviceid); + return -1; + } + tor_assert(launched->build_state); + /* Fill in the circuit's state. */ + memcpy(launched->rend_pk_digest, circuit->rend_pk_digest, + DIGEST_LEN); + memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN); + strcpy(launched->rend_query, service->service_id); + launched->build_state->pending_final_cpath = cpath = + tor_malloc_zero(sizeof(crypt_path_t)); + + cpath->handshake_state = dh; + dh = NULL; + if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0) + goto err; + memcpy(cpath->handshake_digest, keys, DIGEST_LEN); + + return 0; + err: + if (dh) crypto_dh_free(dh); + if (launched) circuit_mark_for_close(launched); + return -1; +} + +/** How many times will a hidden service operator attempt to connect to + * a requested rendezvous point before giving up? */ +#define MAX_REND_FAILURES 3 + +/** Called when we fail building a rendezvous circuit at some point other + * than the last hop: launches a new circuit to the same rendezvous point. + */ +void +rend_service_relaunch_rendezvous(circuit_t *oldcirc) +{ + circuit_t *newcirc; + cpath_build_state_t *newstate, *oldstate; + + tor_assert(oldcirc->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); + + if (!oldcirc->build_state || + oldcirc->build_state->failure_count > MAX_REND_FAILURES) { + log_fn(LOG_INFO,"Attempt to build circuit to %s for rendezvous has failed too many times; giving up.", + oldcirc->build_state->chosen_exit_name); + return; + } + + log_fn(LOG_INFO,"Reattempting rendezvous circuit to %s", + oldcirc->build_state->chosen_exit_name); + + newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, + oldcirc->build_state->chosen_exit_name); + if (!newcirc) { + log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s", + oldcirc->build_state->chosen_exit_name); + return; + } + oldstate = oldcirc->build_state; + newstate = newcirc->build_state; + tor_assert(newstate && oldstate); + newstate->failure_count = oldstate->failure_count+1; + newstate->pending_final_cpath = oldstate->pending_final_cpath; + oldstate->pending_final_cpath = NULL; + + memcpy(newcirc->rend_query, oldcirc->rend_query, REND_SERVICE_ID_LEN+1); + memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest, DIGEST_LEN); + memcpy(newcirc->rend_cookie, oldcirc->rend_cookie, REND_COOKIE_LEN); +} + +/** Launch a circuit to serve as an introduction point for the service + * service at the introduction point nickname + */ +static int +rend_service_launch_establish_intro(rend_service_t *service, const char *nickname) +{ + circuit_t *launched; + + log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s", + nickname, service->service_id); + + ++service->n_intro_circuits_launched; + launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname); + if (!launched) { + log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'", + nickname); + return -1; + } + strcpy(launched->rend_query, service->service_id); + memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN); + + return 0; +} + +/** Called when we're done building a circuit to an introduction point: + * sends a RELAY_ESTABLISH_INTRO cell. + */ +void +rend_service_intro_has_opened(circuit_t *circuit) +{ + rend_service_t *service; + int len, r; + char buf[RELAY_PAYLOAD_SIZE]; + char auth[DIGEST_LEN + 9]; + char serviceid[REND_SERVICE_ID_LEN+1]; + + tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); + tor_assert(CIRCUIT_IS_ORIGIN(circuit) && circuit->cpath); + + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, + circuit->rend_pk_digest,10); + + service = rend_service_get_by_pk_digest(circuit->rend_pk_digest); + if (!service) { + log_fn(LOG_WARN, "Unrecognized service ID %s on introduction circuit %d", + serviceid, circuit->n_circ_id); + goto err; + } + + log_fn(LOG_INFO, + "Established circuit %d as introduction point for service %s", + circuit->n_circ_id, serviceid); + + /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */ + len = crypto_pk_asn1_encode(service->private_key, buf+2, + RELAY_PAYLOAD_SIZE-2); + set_uint16(buf, htons((uint16_t)len)); + len += 2; + memcpy(auth, circuit->cpath->prev->handshake_digest, DIGEST_LEN); + memcpy(auth+DIGEST_LEN, "INTRODUCE", 9); + if (crypto_digest(auth, DIGEST_LEN+9, buf+len)) + goto err; + len += 20; + r = crypto_pk_private_sign_digest(service->private_key, buf, len, buf+len); + if (r<0) { + log_fn(LOG_WARN, "Couldn't sign introduction request"); + goto err; + } + len += r; + + if (connection_edge_send_command(NULL, circuit,RELAY_COMMAND_ESTABLISH_INTRO, + buf, len, circuit->cpath->prev)<0) { + log_fn(LOG_WARN, + "Couldn't send introduction request for service %s on circuit %d", + serviceid, circuit->n_circ_id); + goto err; + } + + return; + err: + circuit_mark_for_close(circuit); +} + +/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a + * live introduction point, and note that the service descriptor is + * now out-of-date.*/ +int +rend_service_intro_established(circuit_t *circuit, const char *request, int request_len) +{ + rend_service_t *service; + + if (circuit->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) { + log_fn(LOG_WARN, "received INTRO_ESTABLISHED cell on non-intro circuit"); + goto err; + } + service = rend_service_get_by_pk_digest(circuit->rend_pk_digest); + if (!service) { + log_fn(LOG_WARN, "Unknown service on introduction circuit %d", + circuit->n_circ_id); + goto err; + } + service->desc_is_dirty = 1; + circuit->purpose = CIRCUIT_PURPOSE_S_INTRO; + + return 0; + err: + circuit_mark_for_close(circuit); + return -1; +} + +/** Called once a circuit to a rendezvous point is established: sends a + * RELAY_COMMAND_RENDEZVOUS1 cell. + */ +void +rend_service_rendezvous_has_opened(circuit_t *circuit) +{ + rend_service_t *service; + char buf[RELAY_PAYLOAD_SIZE]; + crypt_path_t *hop; + char serviceid[REND_SERVICE_ID_LEN+1]; + char hexcookie[9]; + + tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); + tor_assert(circuit->cpath); + tor_assert(circuit->build_state); + hop = circuit->build_state->pending_final_cpath; + tor_assert(hop); + + hex_encode(circuit->rend_cookie, 4, hexcookie); + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, + circuit->rend_pk_digest,10); + + log_fn(LOG_INFO, + "Done building circuit %d to rendezvous with cookie %s for service %s", + circuit->n_circ_id, hexcookie, serviceid); + + service = rend_service_get_by_pk_digest(circuit->rend_pk_digest); + if (!service) { + log_fn(LOG_WARN, "Internal error: unrecognized service ID on introduction circuit"); + goto err; + } + + /* All we need to do is send a RELAY_RENDEZVOUS1 cell... */ + memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN); + if (crypto_dh_get_public(hop->handshake_state, + buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) { + log_fn(LOG_WARN,"Couldn't get DH public key"); + goto err; + } + memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest, + DIGEST_LEN); + + /* Send the cell */ + if (connection_edge_send_command(NULL, circuit, RELAY_COMMAND_RENDEZVOUS1, + buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN, + circuit->cpath->prev)<0) { + log_fn(LOG_WARN, "Couldn't send RENDEZVOUS1 cell"); + goto err; + } + + crypto_dh_free(hop->handshake_state); + hop->handshake_state = NULL; + + /* Append the cpath entry. */ + hop->state = CPATH_STATE_OPEN; + /* set the windows to default. these are the windows + * that bob thinks alice has. + */ + hop->package_window = CIRCWINDOW_START; + hop->deliver_window = CIRCWINDOW_START; + + onion_append_to_cpath(&circuit->cpath, hop); + circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */ + + /* Change the circuit purpose. */ + circuit->purpose = CIRCUIT_PURPOSE_S_REND_JOINED; + + return; + err: + circuit_mark_for_close(circuit); +} + +/* + * Manage introduction points + */ + +/** Return the (possibly non-open) introduction circuit ending at + * router for the service whose public key is pk_digest. Return + * NULL if no such service is found. + */ +static circuit_t * +find_intro_circuit(routerinfo_t *router, const char *pk_digest) +{ + circuit_t *circ = NULL; + + while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest, + CIRCUIT_PURPOSE_S_INTRO))) { + tor_assert(circ->cpath); + if (circ->build_state->chosen_exit_name && + !strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) { + return circ; + } + } + + circ = NULL; + while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest, + CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) { + tor_assert(circ->cpath); + if (circ->build_state->chosen_exit_name && + !strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) { + return circ; + } + } + return NULL; +} + +/** If the directory servers don't have an up-to-date descriptor for + * service, encode and sign the service descriptor for service, + * and upload it to all the dirservers. + */ +static void +upload_service_descriptor(rend_service_t *service) +{ + char *desc; + int desc_len; + if (!service->desc_is_dirty) + return; + + /* Update the descriptor. */ + rend_service_update_descriptor(service); + if (rend_encode_service_descriptor(service->desc, + service->private_key, + &desc, &desc_len)<0) { + log_fn(LOG_WARN, "Couldn't encode service descriptor; not uploading"); + return; + } + + /* Post it to the dirservers */ + directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC, desc, desc_len); + tor_free(desc); + + service->desc_is_dirty = 0; +} + +/* XXXX Make this longer once directories remember service descriptors across + * restarts.*/ +#define MAX_SERVICE_PUBLICATION_INTERVAL (15*60) + +/** For every service, check how many intro points it currently has, and: + * - Pick new intro points as necessary. + * - Launch circuits to any new intro points. + */ +void rend_services_introduce(void) { + int i,j,r; + routerinfo_t *router; + rend_service_t *service; + char *intro; + int changed, prev_intro_nodes; + smartlist_t *intro_routers, *exclude_routers; + time_t now; + + intro_routers = smartlist_create(); + exclude_routers = smartlist_create(); + now = time(NULL); + + for (i=0; i< smartlist_len(rend_service_list); ++i) { + smartlist_clear(intro_routers); + service = smartlist_get(rend_service_list, i); + + tor_assert(service); + changed = 0; + if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) { + /* One period has elapsed; we can try building circuits again. */ + service->intro_period_started = now; + service->n_intro_circuits_launched = 0; + } else if (service->n_intro_circuits_launched>=MAX_INTRO_CIRCS_PER_PERIOD){ + /* We have failed too many times in this period; wait for the next + * one before we try again. */ + continue; + } + + /* Find out which introduction points we have in progress for this service. */ + for (j=0;j< smartlist_len(service->intro_nodes); ++j) { + intro = smartlist_get(service->intro_nodes, j); + router = router_get_by_nickname(intro); + if (!router || !find_intro_circuit(router,service->pk_digest)) { + log_fn(LOG_INFO,"Giving up on %s as intro point for %s.", + intro, service->service_id); + smartlist_del(service->intro_nodes,j--); + changed = service->desc_is_dirty = 1; + } + smartlist_add(intro_routers, router); + } + + /* We have enough intro points, and the intro points we thought we had were + * all connected. + */ + if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) { + /* We have all our intro points! Start a fresh period and reset the + * circuit count. */ + service->intro_period_started = now; + service->n_intro_circuits_launched = 0; + continue; + } + + /* Remember how many introduction circuits we started with. */ + prev_intro_nodes = smartlist_len(service->intro_nodes); + + smartlist_add_all(exclude_routers, intro_routers); + /* The directory is now here. Pick three ORs as intro points. */ + for (j=prev_intro_nodes; j < NUM_INTRO_POINTS; ++j) { + router = router_choose_random_node(service->intro_prefer_nodes, + service->intro_exclude_nodes, + exclude_routers); + if (!router) { + log_fn(LOG_WARN, "Could only establish %d introduction points for %s", + smartlist_len(service->intro_nodes), service->service_id); + break; + } + changed = 1; + smartlist_add(intro_routers, router); + smartlist_add(exclude_routers, router); + smartlist_add(service->intro_nodes, tor_strdup(router->nickname)); + log_fn(LOG_INFO,"Picked router %s as an intro point for %s.", router->nickname, + service->service_id); + } + + /* Reset exclude_routers, for the next time around the loop. */ + smartlist_clear(exclude_routers); + + /* If there's no need to launch new circuits, stop here. */ + if (!changed) + continue; + + /* Establish new introduction points. */ + for (j=prev_intro_nodes; j < smartlist_len(service->intro_nodes); ++j) { + intro = smartlist_get(service->intro_nodes, j); + r = rend_service_launch_establish_intro(service, intro); + if (r<0) { + log_fn(LOG_WARN, "Error launching circuit to node %s for service %s", + intro, service->service_id); + } + } + } + smartlist_free(intro_routers); + smartlist_free(exclude_routers); +} + +/** Regenerate and upload rendezvous service descriptors for all + * services. If force is false, skip services where we've already + * uploaded an up-to-date copy; if force is true, regenerate and + * upload everything. + */ +void +rend_services_upload(int force) +{ + int i; + rend_service_t *service; + + for (i=0; i< smartlist_len(rend_service_list); ++i) { + service = smartlist_get(rend_service_list, i); + if (force) + service->desc_is_dirty = 1; + if (service->desc_is_dirty) + upload_service_descriptor(service); + } +} + +/** Log the status of introduction points for all rendezvous services + * at log severity serverity. + */ +void +rend_service_dump_stats(int severity) +{ + int i,j; + routerinfo_t *router; + rend_service_t *service; + char *nickname; + circuit_t *circ; + + for (i=0; i < smartlist_len(rend_service_list); ++i) { + service = smartlist_get(rend_service_list, i); + log(severity, "Service configured in %s:", service->directory); + for (j=0; j < smartlist_len(service->intro_nodes); ++j) { + nickname = smartlist_get(service->intro_nodes, j); + router = router_get_by_nickname(smartlist_get(service->intro_nodes,j)); + if (!router) { + log(severity, " Intro point at %s: unrecognized router",nickname); + continue; + } + circ = find_intro_circuit(router, service->pk_digest); + if (!circ) { + log(severity, " Intro point at %s: no circuit",nickname); + continue; + } + log(severity, " Intro point at %s: circuit is %s",nickname, + circuit_state_to_string[circ->state]); + } + } +} + +/** Given conn, a rendezvous exit stream, look up the hidden service for + * 'circ', and look up the port and address based on conn-\>port. + * Assign the actual conn-\>addr and conn-\>port. Return -1 if failure, + * or 0 for success. + */ +int +rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ) +{ + rend_service_t *service; + int i; + rend_service_port_config_t *p; + char serviceid[REND_SERVICE_ID_LEN+1]; + + tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED); + log_fn(LOG_DEBUG,"beginning to hunt for addr/port"); + base32_encode(serviceid, REND_SERVICE_ID_LEN+1, + circ->rend_pk_digest,10); + service = rend_service_get_by_pk_digest(circ->rend_pk_digest); + if (!service) { + log_fn(LOG_WARN, "Couldn't find any service associated with pk %s on rendezvous circuit %d; closing", + serviceid, circ->n_circ_id); + return -1; + } + for (i = 0; i < smartlist_len(service->ports); ++i) { + p = smartlist_get(service->ports, i); + if (conn->port == p->virtual_port) { + conn->addr = p->real_address; + conn->port = p->real_port; + return 0; + } + } + log_fn(LOG_INFO, "No virtual port mapping exists for port %d on service %s", + conn->port,serviceid); + return -1; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/rephist.c b/tags/tor-0_0_8pre1/src/or/rephist.c new file mode 100644 index 0000000000..0cca2097dc --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/rephist.c @@ -0,0 +1,484 @@ +/* Copyright 2004 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file rephist.c + * \brief Basic history functionality for reputation module. + **/ + +#include "or.h" + +static void bw_arrays_init(void); + +/** History of an OR-\>OR link. */ +typedef struct link_history_t { + /** When did we start tracking this list? */ + time_t since; + /** How many times did extending from OR1 to OR2 succeeed? */ + unsigned long n_extend_ok; + /** How many times did extending from OR1 to OR2 fail? */ + unsigned long n_extend_fail; +} link_history_t; + +/** History of an OR. */ +typedef struct or_history_t { + /** When did we start tracking this OR? */ + time_t since; + /** How many times did we successfully connect? */ + unsigned long n_conn_ok; + /** How many times did we try to connect and fail?*/ + unsigned long n_conn_fail; + /** How many seconds have we been connected to this OR before + * 'up_since'? */ + unsigned long uptime; + /** How many seconds have we been unable to connect to this OR before + * 'down_since'? */ + unsigned long downtime; + /** If nonzero, we have been connected since this time. */ + time_t up_since; + /** If nonzero, we have been unable to connect since this time. */ + time_t down_since; + /** Map from hex OR2 identity digest to a link_history_t for the link + * from this OR to OR2. */ + strmap_t *link_history_map; +} or_history_t; + +/** Map from hex OR identity digest to or_history_t. */ +static strmap_t *history_map = NULL; + +/** Return the or_history_t for the named OR, creating it if necessary. + */ +static or_history_t *get_or_history(const char* id) +{ + or_history_t *hist; + char hexid[HEX_DIGEST_LEN+1]; + base16_encode(hexid, HEX_DIGEST_LEN+1, id, DIGEST_LEN); + + hist = (or_history_t*) strmap_get(history_map, hexid); + if (!hist) { + hist = tor_malloc_zero(sizeof(or_history_t)); + hist->link_history_map = strmap_new(); + hist->since = time(NULL); + strmap_set(history_map, hexid, hist); + } + return hist; +} + +/** Return the link_history_t for the link from the first named OR to + * the second, creating it if necessary. (ORs are identified by + * identity digest) + */ +static link_history_t *get_link_history(const char *from_id, + const char *to_id) +{ + or_history_t *orhist; + link_history_t *lhist; + char to_hexid[HEX_DIGEST_LEN+1]; + orhist = get_or_history(from_id); + base16_encode(to_hexid, HEX_DIGEST_LEN+1, to_id, DIGEST_LEN); + lhist = (link_history_t*) strmap_get(orhist->link_history_map, to_hexid); + if (!lhist) { + lhist = tor_malloc_zero(sizeof(link_history_t)); + lhist->since = time(NULL); + strmap_set(orhist->link_history_map, to_hexid, lhist); + } + return lhist; +} + +/** Update an or_history_t object hist so that its uptime/downtime + * count is up-to-date as of when. + */ +static void update_or_history(or_history_t *hist, time_t when) +{ + tor_assert(hist); + if (hist->up_since) { + tor_assert(!hist->down_since); + hist->uptime += (when - hist->up_since); + hist->up_since = when; + } else if (hist->down_since) { + hist->downtime += (when - hist->down_since); + hist->down_since = when; + } +} + +/** Initialize the static data structures for tracking history. + */ +void rep_hist_init(void) +{ + history_map = strmap_new(); + bw_arrays_init(); +} + +/** Remember that an attempt to connect to the OR with identity digest + * id failed at when. + */ +void rep_hist_note_connect_failed(const char* id, time_t when) +{ + or_history_t *hist; + hist = get_or_history(id); + ++hist->n_conn_fail; + if (hist->up_since) { + hist->uptime += (when - hist->up_since); + hist->up_since = 0; + } + if (!hist->down_since) + hist->down_since = when; +} + +/** Remember that an attempt to connect to the OR with identity digest + * id succeeded at when. + */ +void rep_hist_note_connect_succeeded(const char* id, time_t when) +{ + or_history_t *hist; + hist = get_or_history(id); + ++hist->n_conn_ok; + if (hist->down_since) { + hist->downtime += (when - hist->down_since); + hist->down_since = 0; + } + if (!hist->up_since) + hist->up_since = when; +} + +/** Remember that we intentionally closed our connection to the OR + * with identity digest id at when. + */ +void rep_hist_note_disconnect(const char* id, time_t when) +{ + or_history_t *hist; + hist = get_or_history(id); + ++hist->n_conn_ok; + if (hist->up_since) { + hist->uptime += (when - hist->up_since); + hist->up_since = 0; + } +} + +/** Remember that our connection to the OR with identity digest + * id had an error and stopped working at when. + */ +void rep_hist_note_connection_died(const char* id, time_t when) +{ + or_history_t *hist; + if(!id) { + /* XXXX008 not so. */ + /* If conn has no nickname, it's either an OP, or it is an OR + * which didn't complete its handshake (or did and was unapproved). + * Ignore it. + */ + return; + } + hist = get_or_history(id); + if (hist->up_since) { + hist->uptime += (when - hist->up_since); + hist->up_since = 0; + } + if (!hist->down_since) + hist->down_since = when; +} + +/** Remember that we successfully extended from the OR with identity + * digest from_id to the OR with identity digest + * to_name. + */ +void rep_hist_note_extend_succeeded(const char *from_id, + const char *to_id) +{ + link_history_t *hist; + /* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */ + hist = get_link_history(from_id, to_id); + ++hist->n_extend_ok; +} + +/** Remember that we tried to extend from the OR with identity digest + * from_id to the OR with identity digest to_name, but + * failed. + */ +void rep_hist_note_extend_failed(const char *from_id, const char *to_id) +{ + link_history_t *hist; + /* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */ + hist = get_link_history(from_id, to_id); + ++hist->n_extend_fail; +} + +/** Log all the reliability data we have rememberred, with the chosen + * severity. + */ +void rep_hist_dump_stats(time_t now, int severity) +{ + strmap_iter_t *lhist_it; + strmap_iter_t *orhist_it; + const char *name1, *name2, *hexdigest1, *hexdigest2; + or_history_t *or_history; + link_history_t *link_history; + void *or_history_p, *link_history_p; + double uptime; + char buffer[2048]; + int len; + unsigned long upt, downt; + routerinfo_t *r; + + log(severity, "--------------- Dumping history information:"); + + for (orhist_it = strmap_iter_init(history_map); !strmap_iter_done(orhist_it); + orhist_it = strmap_iter_next(history_map,orhist_it)) { + strmap_iter_get(orhist_it, &hexdigest1, &or_history_p); + or_history = (or_history_t*) or_history_p; + + if ((r = router_get_by_hexdigest(hexdigest1))) + name1 = r->nickname; + else + name1 = "(unknown)"; + + update_or_history(or_history, now); + upt = or_history->uptime; + downt = or_history->downtime; + if (upt+downt) { + uptime = ((double)upt) / (upt+downt); + } else { + uptime=1.0; + } + log(severity, + "OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%)", + name1, hexdigest1, + or_history->n_conn_ok, or_history->n_conn_fail+or_history->n_conn_ok, + upt, upt+downt, uptime*100.0); + + strcpy(buffer, " Good extend attempts: "); + len = strlen(buffer); + for (lhist_it = strmap_iter_init(or_history->link_history_map); + !strmap_iter_done(lhist_it); + lhist_it = strmap_iter_next(or_history->link_history_map, lhist_it)) { + strmap_iter_get(lhist_it, &hexdigest2, &link_history_p); + if ((r = router_get_by_hexdigest(hexdigest2))) + name2 = r->nickname; + else + name2 = "(unknown)"; + + link_history = (link_history_t*) link_history_p; + len += snprintf(buffer+len, 2048-len, "%s(%ld/%ld); ", name2, + link_history->n_extend_ok, + link_history->n_extend_ok+link_history->n_extend_fail); + if (len >= 2048) { + buffer[2047]='\0'; + break; + } + } + log(severity, buffer); + } +} + +#if 0 +void write_rep_history(const char *filename) +{ + FILE *f = NULL; + char *tmpfile; + int completed = 0; + or_history_t *or_history; + link_history_t *link_history; + strmap_iter_t *lhist_it; + strmap_iter_t *orhist_it; + void *or_history_p, *link_history_p; + const char *name1; + + tmpfile = tor_malloc(strlen(filename)+5); + strcpy(tmpfile, filename); + strcat(tmpfile, "_tmp"); + + f = fopen(tmpfile, "w"); + if (!f) goto done; + for (orhist_it = strmap_iter_init(history_map); !strmap_iter_done(orhist_it); + orhist_it = strmap_iter_next(history_map,orhist_it)) { + strmap_iter_get(orhist_it, &name1, &or_history_p); + or_history = (or_history_t*) or_history_p; + fprintf(f, "link %s connected:u%ld failed:%uld uptime:%uld", + name1, or_history->since1, + } + + done: + if (f) + fclose(f); + if (completed) + replace_file(filename, tmpfile); + else + unlink(tmpfile); + tor_free(tmpfile); +} +#endif + +#define NUM_SECS_ROLLING_MEASURE 10 +#define NUM_SECS_BW_SUM_IS_VALID (12*60*60) /* half a day */ +#define NUM_SECS_BW_SUM_INTERVAL (15*60) +#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL) + +/** + * Structure to track bandwidth use, and remember the maxima for a given + * time period. + */ +typedef struct bw_array_t { + /** Observation array: Total number of bytes transferred in each of the last + * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */ + int obs[NUM_SECS_ROLLING_MEASURE]; + int cur_obs_idx; /**< Current position in obs. */ + time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */ + int total_obs; /**< Total for all members of obs except obs[cur_obs_idx] */ + int max_total; /**< Largest value that total_obs has taken on in the current + * period. */ + + /** When does the next period begin? */ + time_t next_period; + /** Where in 'maxima' should the maximum bandwidth usage for the current + * period be stored? */ + int next_max_idx; + /** Circular array of the maximum bandwidth usage for the last NUM_TOTALS + * periods */ + int maxima[NUM_TOTALS]; +} bw_array_t; + +/** Shift the current period of b forward by one. + */ +static void commit_max(bw_array_t *b) { + /* Store maximum from current period. */ + b->maxima[b->next_max_idx++] = b->max_total; + /* Advance next_period and next_max_idx */ + b->next_period += NUM_SECS_BW_SUM_INTERVAL; + if (b->next_max_idx == NUM_TOTALS) + b->next_max_idx = 0; + /* Reset max_total. */ + b->max_total = 0; +} + +/** Shift the current observation time of 'b' forward by one second. + */ +static INLINE void advance_obs(bw_array_t *b) { + int nextidx; + int total; + + /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE + * seconds; adjust max_total as needed.*/ + total = b->total_obs + b->obs[b->cur_obs_idx]; + if (total > b->max_total) + b->max_total = total; + + nextidx = b->cur_obs_idx+1; + if (nextidx == NUM_SECS_ROLLING_MEASURE) + nextidx = 0; + + b->total_obs = total - b->obs[nextidx]; + b->obs[nextidx]=0; + b->cur_obs_idx = nextidx; + + if (++b->cur_obs_time >= b->next_period) + commit_max(b); +} + +/** Add 'n' bytes to the number of bytes in b for second 'when'. + */ +static INLINE void add_obs(bw_array_t *b, time_t when, int n) { + /* Don't record data in the past. */ + if (whencur_obs_time) + return; + /* If we're currently adding observations for an earlier second than + * 'when', advance b->cur_obs_time and b->cur_obs_idx by an + * appropriate number of seconds, and do all the other housekeeping */ + while (when>b->cur_obs_time) + advance_obs(b); + + b->obs[b->cur_obs_idx] += n; +} + +/** Allocate, initialize, and return a new bw_array. + */ +static bw_array_t *bw_array_new(void) { + bw_array_t *b; + time_t start; + b = tor_malloc_zero(sizeof(bw_array_t)); + start = time(NULL); + b->cur_obs_time = start; + b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; + return b; +} + +static bw_array_t *read_array = NULL; +static bw_array_t *write_array = NULL; + +/** Set up read_array and write_array + */ +static void bw_arrays_init(void) +{ + read_array = bw_array_new(); + write_array = bw_array_new(); +} + +/** We read num_bytes more bytes in second when. + * + * Add num_bytes to the current running total for when. + * + * when can go back to time, but it's safe to ignore calls + * earlier than the latest when you've heard of. + */ +void rep_hist_note_bytes_written(int num_bytes, time_t when) { +/* Maybe a circular array for recent seconds, and step to a new point + * every time a new second shows up. Or simpler is to just to have + * a normal array and push down each item every second; it's short. + */ +/* When a new second has rolled over, compute the sum of the bytes we've + * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it + * somewhere. See rep_hist_bandwidth_assess() below. + */ + add_obs(write_array, when, num_bytes); +} + +/** We wrote num_bytes more bytes in second when. + * (like rep_hist_note_bytes_written() above) + */ +void rep_hist_note_bytes_read(int num_bytes, time_t when) { +/* if we're smart, we can make this func and the one above share code */ + + add_obs(read_array, when, num_bytes); +} + +/** Helper: Return the largest value in b->maxima. (This is equal to the + * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last + * NUM_SECS_BW_SUM_IS_VALID seconds.) + */ +static int find_largest_max(bw_array_t *b) +{ + int i,max; + max=0; + for (i=0; imaxima[i]>max) + max = b->maxima[i]; + } + return max; +} + +/** + * Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly) + * seconds. Find one sum for reading and one for writing. They don't have + * to be at the same time). + * + * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE. + */ +int rep_hist_bandwidth_assess(time_t when) { + int w,r; + r = find_largest_max(read_array); + w = find_largest_max(write_array); + if (r>w) + return w/(double)NUM_SECS_ROLLING_MEASURE; + else + return r/(double)NUM_SECS_ROLLING_MEASURE; + + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/router.c b/tags/tor-0_0_8pre1/src/or/router.c new file mode 100644 index 0000000000..937370ca14 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/router.c @@ -0,0 +1,767 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/** + * \file router.c + * \brief OR functionality, including key maintenance, generating + * and uploading server descriptors, retrying OR connections. + **/ + +extern or_options_t options; /* command-line and config-file options */ +extern long stats_n_seconds_uptime; + +/** Exposed for test.c. */ void get_platform_str(char *platform, int len); + +/************************************************************/ + +/***** + * Key management: ORs only. + *****/ + +/** Private keys for this OR. There is also an SSL key managed by tortls.c. + */ +static tor_mutex_t *key_lock=NULL; +static time_t onionkey_set_at=0; /* When was onionkey last changed? */ +static crypto_pk_env_t *onionkey=NULL; +static crypto_pk_env_t *lastonionkey=NULL; +static crypto_pk_env_t *identitykey=NULL; + +/** Replace the current onion key with k. Does not affect lastonionkey; + * to update onionkey correctly, call rotate_onion_key(). + */ +void set_onion_key(crypto_pk_env_t *k) { + tor_mutex_acquire(key_lock); + onionkey = k; + onionkey_set_at = time(NULL); + tor_mutex_release(key_lock); +} + +/** Return the current onion key. Requires that the onion key has been + * loaded or generated. */ +crypto_pk_env_t *get_onion_key(void) { + tor_assert(onionkey); + return onionkey; +} + +/** Return the onion key that was current before the most recent onion + * key rotation. If no rotation has been performed since this process + * started, return NULL. + */ +crypto_pk_env_t *get_previous_onion_key(void) { + return lastonionkey; +} + +void dup_onion_keys(crypto_pk_env_t **key, crypto_pk_env_t **last) +{ + tor_assert(key && last); + tor_mutex_acquire(key_lock); + *key = crypto_pk_dup_key(onionkey); + if (lastonionkey) + *last = crypto_pk_dup_key(lastonionkey); + else + *last = NULL; + tor_mutex_release(key_lock); +} + +/** Return the time when the onion key was last set. This is either the time + * when the process launched, or the time of the most recent key rotation since + * the process launched. + */ +time_t get_onion_key_set_at(void) { + return onionkey_set_at; +} + +/** Set the current identity key to k. + */ +void set_identity_key(crypto_pk_env_t *k) { + identitykey = k; +} + +/** Returns the current identity key; requires that the identity key has been + * set. + */ +crypto_pk_env_t *get_identity_key(void) { + tor_assert(identitykey); + return identitykey; +} + +/** Replace the previous onion key with the current onion key, and generate + * a new previous onion key. Immediately after calling this function, + * the OR should: + * - schedule all previous cpuworkers to shut down _after_ processing + * pending work. (This will cause fresh cpuworkers to be generated.) + * - generate and upload a fresh routerinfo. + */ +void rotate_onion_key(void) +{ + char fname[512]; + crypto_pk_env_t *prkey; + sprintf(fname,"%s/keys/onion.key",get_data_directory(&options)); + if (!(prkey = crypto_new_pk_env())) { + log(LOG_ERR, "Error creating crypto environment."); + goto error; + } + if (crypto_pk_generate_key(prkey)) { + log(LOG_ERR, "Error generating onion key"); + goto error; + } + if (crypto_pk_write_private_key_to_filename(prkey, fname)) { + log(LOG_ERR, "Couldn't write generated key to %s.", fname); + goto error; + } + tor_mutex_acquire(key_lock); + if (lastonionkey) + crypto_free_pk_env(lastonionkey); + log_fn(LOG_INFO, "Rotating onion key"); + lastonionkey = onionkey; + set_onion_key(prkey); + tor_mutex_release(key_lock); + return; + error: + log_fn(LOG_WARN, "Couldn't rotate onion key."); +} + +/** The last calculated bandwidth usage for our node. */ +static int advertised_bw = 0; +/** Tuck bw away so we can produce it when somebody + * calls router_get_advertised_bandwidth() below. + */ +void router_set_advertised_bandwidth(int bw) { + advertised_bw = bw; +} +/** Return the value we tucked away above, or zero by default. */ +int router_get_advertised_bandwidth(void) { + return advertised_bw; +} + +/* Read an RSA secret key key from a file that was once named fname_old, + * but is now named fname_new. Rename the file from old to new as needed. + */ +static crypto_pk_env_t * +init_key_from_file_name_changed(const char *fname_old, + const char *fname_new) +{ + int fs; + + fs = file_status(fname_new); + if (fs == FN_FILE) + /* The new filename is there. */ + return init_key_from_file(fname_new); + fs = file_status(fname_old); + if (fs != FN_FILE) + /* There is no key under either name. */ + return init_key_from_file(fname_new); + + /* The old filename exists, and the new one doesn't. Rename and load. */ + if (rename(fname_old, fname_new) < 0) { + log_fn(LOG_ERR, "Couldn't rename %s to %s: %s", fname_old, fname_new, + strerror(errno)); + return NULL; + } + return init_key_from_file(fname_new); +} + +/** Try to read an RSA key from fname. If fname doesn't exist, + * create a new RSA key and save it in fname. Return the read/created + * key, or NULL on error. + */ +crypto_pk_env_t *init_key_from_file(const char *fname) +{ + crypto_pk_env_t *prkey = NULL; + FILE *file = NULL; + + if (!(prkey = crypto_new_pk_env())) { + log(LOG_ERR, "Error creating crypto environment."); + goto error; + } + + switch(file_status(fname)) { + case FN_DIR: + case FN_ERROR: + log(LOG_ERR, "Can't read key from %s", fname); + goto error; + case FN_NOENT: + log(LOG_INFO, "No key found in %s; generating fresh key.", fname); + if (crypto_pk_generate_key(prkey)) { + log(LOG_ERR, "Error generating onion key"); + goto error; + } + if (crypto_pk_check_key(prkey) <= 0) { + log(LOG_ERR, "Generated key seems invalid"); + goto error; + } + log(LOG_INFO, "Generated key seems valid"); + if (crypto_pk_write_private_key_to_filename(prkey, fname)) { + log(LOG_ERR, "Couldn't write generated key to %s.", fname); + goto error; + } + return prkey; + case FN_FILE: + if (crypto_pk_read_private_key_from_filename(prkey, fname)) { + log(LOG_ERR, "Error loading private key."); + goto error; + } + return prkey; + default: + tor_assert(0); + } + + error: + if (prkey) + crypto_free_pk_env(prkey); + if (file) + fclose(file); + return NULL; +} + +/** Initialize all OR private keys, and the TLS context, as necessary. + * On OPs, this only initializes the tls context. + */ +int init_keys(void) { + char keydir[512]; + char keydir2[512]; + char fingerprint[FINGERPRINT_LEN+MAX_NICKNAME_LEN+3]; + char *cp; + const char *tmp, *mydesc, *datadir; + crypto_pk_env_t *prkey; + + if (!key_lock) + key_lock = tor_mutex_new(); + + /* OP's don't need persistant keys; just make up an identity and + * initialize the TLS context. */ + if (!server_mode()) { +#if 0 + /* XXXX008 enable this once we make ORs tolerate unknown routers. */ + if (!(prkey = crypto_new_pk_env())) + return -1; + if (crypto_pk_generate_key(prkey)) + return -1; + set_identity_key(prkey); + if (tor_tls_context_new(get_identity_key(), 1, options.Nickname, + MAX_SSL_KEY_LIFETIME) < 0) { + log_fn(LOG_ERR, "Error creating TLS context for OP."); + return -1; + } +#endif + if (tor_tls_context_new(NULL, 0, NULL, MAX_SSL_KEY_LIFETIME)<0) { + log_fn(LOG_ERR, "Error creating TLS context for OP."); + return -1; + } + return 0; + } + /* Make sure DataDirectory exists, and is private. */ + datadir = get_data_directory(&options); + tor_assert(datadir); + if (strlen(datadir) > (512-128)) { + log_fn(LOG_ERR, "DataDirectory is too long."); + return -1; + } + if (check_private_dir(datadir, 1)) { + return -1; + } + /* Check the key directory. */ + sprintf(keydir,"%s/keys", datadir); + if (check_private_dir(keydir, 1)) { + return -1; + } + cp = keydir + strlen(keydir); /* End of string. */ + + /* 1. Read identity key. Make it if none is found. */ + sprintf(keydir,"%s/keys/identity.key",datadir); + sprintf(keydir2,"%s/keys/secret_id_key",datadir); + log_fn(LOG_INFO,"Reading/making identity key %s...",keydir2); + prkey = init_key_from_file_name_changed(keydir,keydir2); + if (!prkey) return -1; + set_identity_key(prkey); + /* 2. Read onion key. Make it if none is found. */ + sprintf(keydir,"%s/keys/onion.key",datadir); + sprintf(keydir2,"%s/keys/secret_onion_key",datadir); + log_fn(LOG_INFO,"Reading/making onion key %s...",keydir2); + prkey = init_key_from_file_name_changed(keydir,keydir2); + if (!prkey) return -1; + set_onion_key(prkey); + + /* 3. Initialize link key and TLS context. */ + if (tor_tls_context_new(get_identity_key(), 1, options.Nickname, + MAX_SSL_KEY_LIFETIME) < 0) { + log_fn(LOG_ERR, "Error initializing TLS context"); + return -1; + } + /* 4. Dump router descriptor to 'router.desc' */ + /* Must be called after keys are initialized. */ + tmp = mydesc = router_get_my_descriptor(); + if (!mydesc) { + log_fn(LOG_ERR, "Error initializing descriptor."); + return -1; + } + if(authdir_mode()) { + /* We need to add our own fingerprint so it gets recognized. */ + if (dirserv_add_own_fingerprint(options.Nickname, get_identity_key())) { + log_fn(LOG_ERR, "Error adding own fingerprint to approved set"); + return -1; + } + if (dirserv_add_descriptor(&tmp) != 1) { + log(LOG_ERR, "Unable to add own descriptor to directory."); + return -1; + } + } + + sprintf(keydir,"%s/router.desc", datadir); + log_fn(LOG_INFO,"Dumping descriptor to %s...",keydir); + if (write_str_to_file(keydir, mydesc)) { + return -1; + } + /* 5. Dump fingerprint to 'fingerprint' */ + sprintf(keydir,"%s/fingerprint", datadir); + log_fn(LOG_INFO,"Dumping fingerprint to %s...",keydir); + tor_assert(strlen(options.Nickname) <= MAX_NICKNAME_LEN); + strcpy(fingerprint, options.Nickname); + strcat(fingerprint, " "); + if (crypto_pk_get_fingerprint(get_identity_key(), + fingerprint+strlen(fingerprint))<0) { + log_fn(LOG_ERR, "Error computing fingerprint"); + return -1; + } + strcat(fingerprint, "\n"); + if (write_str_to_file(keydir, fingerprint)) + return -1; + if(!authdir_mode()) + return 0; + /* 6. [authdirserver only] load approved-routers file */ + sprintf(keydir,"%s/approved-routers", datadir); + log_fn(LOG_INFO,"Loading approved fingerprints from %s...",keydir); + if(dirserv_parse_fingerprint_file(keydir) < 0) { + log_fn(LOG_ERR, "Error loading fingerprints"); + return -1; + } + /* 7. [authdirserver only] load old directory, if it's there */ + sprintf(keydir,"%s/cached-directory", datadir); + log_fn(LOG_INFO,"Loading cached directory from %s...",keydir); + cp = read_file_to_str(keydir); + if(!cp) { + log_fn(LOG_INFO,"Cached directory %s not present. Ok.",keydir); + } else { + if(dirserv_load_from_directory_string(cp) < 0){ + log_fn(LOG_ERR, "Cached directory %s is corrupt", keydir); + tor_free(cp); + return -1; + } + tor_free(cp); + } + /* success */ + return 0; +} + +/* + * Clique maintenance + */ + +/** OR only: if in clique mode, try to open connections to all of the + * other ORs we know about. Otherwise, open connections to those we + * think are in clique mode. + */ +void router_retry_connections(void) { + int i; + routerinfo_t *router; + routerlist_t *rl; + + tor_assert(server_mode()); + + router_get_routerlist(&rl); + for (i=0;i < smartlist_len(rl->routers);i++) { + router = smartlist_get(rl->routers, i); + if(router_is_me(router)) + continue; + if(!clique_mode() && !router_is_clique_mode(router)) + continue; + if(!connection_get_by_identity_digest(router->identity_digest, + CONN_TYPE_OR)) { + /* not in the list */ + log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port); + connection_or_connect(router->addr, router->or_port, router->identity_digest); + } + } +} + +int router_is_clique_mode(routerinfo_t *router) { + if(router->is_trusted_dir) + return 1; + if(!strncmp(router->platform, "Tor 0.0.7", 9)) + return 1; + return 0; +} + +/* + * OR descriptor generation. + */ + +/** My routerinfo. */ +static routerinfo_t *desc_routerinfo = NULL; +/** String representation of my descriptor, signed by me. */ +static char descriptor[8192]; + +/** OR only: try to upload our signed descriptor to all the directory servers + * we know about. + */ +void router_upload_dir_desc_to_dirservers(void) { + const char *s; + + s = router_get_my_descriptor(); + if (!s) { + log_fn(LOG_WARN, "No descriptor; skipping upload"); + return; + } + directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_DIR, s, strlen(s)); +} + +#define DEFAULT_EXIT_POLICY "reject 0.0.0.0/8,reject 169.254.0.0/16,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-81,accept *:110,accept *:143,accept *:443,accept *:873,accept *:993,accept *:995,accept *:1024-65535,reject *:*" + +/** Set the exit policy on router to match the exit policy in the + * current configuration file. If the exit policy doesn't have a catch-all + * rule, then append the default exit policy as well. + */ +static void router_add_exit_policy_from_config(routerinfo_t *router) { + struct exit_policy_t *ep; + struct config_line_t default_policy; + config_parse_exit_policy(options.ExitPolicy, &router->exit_policy); + + for (ep = router->exit_policy; ep; ep = ep->next) { + if (ep->msk == 0 && ep->prt_min <= 1 && ep->prt_max >= 65535) { + /* if exitpolicy includes a *:* line, then we're done. */ + return; + } + } + + /* Else, append the default exitpolicy. */ + default_policy.key = NULL; + default_policy.value = DEFAULT_EXIT_POLICY; + default_policy.next = NULL; + config_parse_exit_policy(&default_policy, &router->exit_policy); +} + +/** OR only: Return false if my exit policy says to allow connection to + * conn. Else return true. + */ +int router_compare_to_my_exit_policy(connection_t *conn) +{ + tor_assert(desc_routerinfo); + + /* make sure it's resolved to something. this way we can't get a + 'maybe' below. */ + if (!conn->addr) + return -1; + + return router_compare_addr_to_exit_policy(conn->addr, conn->port, + desc_routerinfo->exit_policy); + +} + +/** Return true iff router has the same nickname as this OR. (For an + * OP, always returns false.) + */ +int router_is_me(routerinfo_t *router) +{ + routerinfo_t *me = router_get_my_routerinfo(); + tor_assert(router); + if(!me || memcmp(me->identity_digest, router->identity_digest, DIGEST_LEN)) + return 0; + return 1; +} + +/** Return a routerinfo for this OR, rebuilding a fresh one if + * necessary. Return NULL on error, or if called on an OP. */ +routerinfo_t *router_get_my_routerinfo(void) +{ + if (!server_mode()) + return NULL; + + if (!desc_routerinfo) { + if (router_rebuild_descriptor()) + return NULL; + } + return desc_routerinfo; +} + +/** OR only: Return a signed server descriptor for this OR, rebuilding a fresh + * one if necessary. Return NULL on error. + */ +const char *router_get_my_descriptor(void) { + if (!desc_routerinfo) { + if (router_rebuild_descriptor()) + return NULL; + } + log_fn(LOG_DEBUG,"my desc is '%s'",descriptor); + return descriptor; +} + +/** Rebuild a fresh routerinfo and signed server descriptor for this + * OR. Return 0 on success, -1 on error. + */ +int router_rebuild_descriptor(void) { + routerinfo_t *ri; + struct in_addr addr; + char platform[256]; + if (!tor_inet_aton(options.Address, &addr)) { + log_fn(LOG_ERR, "options.Address didn't hold an IP."); + return -1; + } + + ri = tor_malloc_zero(sizeof(routerinfo_t)); + ri->address = tor_strdup(options.Address); + ri->nickname = tor_strdup(options.Nickname); + ri->addr = (uint32_t) ntohl(addr.s_addr); + ri->or_port = options.ORPort; + ri->socks_port = options.SocksPort; + ri->dir_port = options.DirPort; + ri->published_on = time(NULL); + ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from main thread */ + ri->identity_pkey = crypto_pk_dup_key(get_identity_key()); + if (crypto_pk_get_digest(ri->identity_pkey, ri->identity_digest)<0) { + routerinfo_free(ri); + return -1; + } + get_platform_str(platform, sizeof(platform)); + ri->platform = tor_strdup(platform); + ri->bandwidthrate = options.BandwidthRate; + ri->bandwidthburst = options.BandwidthBurst; + ri->advertisedbandwidth = router_get_advertised_bandwidth(); + ri->exit_policy = NULL; /* zero it out first */ + router_add_exit_policy_from_config(ri); + ri->is_trusted_dir = authdir_mode(); + if(desc_routerinfo) /* inherit values */ + ri->is_verified = desc_routerinfo->is_verified; + + if (desc_routerinfo) + routerinfo_free(desc_routerinfo); + desc_routerinfo = ri; + if (router_dump_router_to_string(descriptor, 8192, ri, get_identity_key())<0) { + log_fn(LOG_WARN, "Couldn't dump router to string."); + return -1; + } + return 0; +} + +/** Set platform (max length len) to a NUL-terminated short + * string describing the version of Tor and the operating system we're + * currently running on. + */ +void get_platform_str(char *platform, int len) +{ + snprintf(platform, len-1, "Tor %s on %s", + VERSION, get_uname()); + platform[len-1] = '\0'; + return; +} + +/* XXX need to audit this thing and count fenceposts. maybe + * refactor so we don't have to keep asking if we're + * near the end of maxlen? + */ +#define DEBUG_ROUTER_DUMP_ROUTER_TO_STRING + +/** OR only: Given a routerinfo for this router, and an identity key to sign + * with, encode the routerinfo as a signed server descriptor and write the + * result into s, using at most maxlen bytes. Return -1 on + * failure, and the number of bytes used on success. + */ +int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, + crypto_pk_env_t *ident_key) { + char *onion_pkey; /* Onion key, PEM-encoded. */ + char *identity_pkey; /* Identity key, PEM-encoded. */ + char digest[20]; + char signature[128]; + char published[32]; + char fingerprint[FINGERPRINT_LEN+1]; + struct in_addr in; + int onion_pkeylen, identity_pkeylen; + int written; + int result=0; + struct exit_policy_t *tmpe; +#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING + char *s_tmp, *s_dup; + const char *cp; + routerinfo_t *ri_tmp; +#endif + + /* Make sure the identity key matches the one in the routerinfo. */ + if (crypto_pk_cmp_keys(ident_key, router->identity_pkey)) { + log_fn(LOG_WARN,"Tried to sign a router with a private key that didn't match router's public key!"); + return -1; + } + + /* record our fingerprint, so we can include it in the descriptor */ + if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint)<0) { + log_fn(LOG_ERR, "Error computing fingerprint"); + return -1; + } + + /* PEM-encode the onion key */ + if(crypto_pk_write_public_key_to_string(router->onion_pkey, + &onion_pkey,&onion_pkeylen)<0) { + log_fn(LOG_WARN,"write onion_pkey to string failed!"); + return -1; + } + + /* PEM-encode the identity key key */ + if(crypto_pk_write_public_key_to_string(router->identity_pkey, + &identity_pkey,&identity_pkeylen)<0) { + log_fn(LOG_WARN,"write identity_pkey to string failed!"); + tor_free(onion_pkey); + return -1; + } + + /* Encode the publication time. */ + strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on)); + + /* Generate the easy portion of the router descriptor. */ + result = snprintf(s, maxlen, + "router %s %s %d %d %d\n" + "platform %s\n" + "published %s\n" + "opt fingerprint %s\n" + "opt uptime %ld\n" + "bandwidth %d %d %d\n" + "onion-key\n%s" + "signing-key\n%s", + router->nickname, + router->address, + router->or_port, + router->socks_port, + /* Due to an 0.0.7 bug, we can't actually say that we have a dirport unles + * we're an authoritative directory. + */ + router->is_trusted_dir ? router->dir_port : 0, + router->platform, + published, + fingerprint, + stats_n_seconds_uptime, + (int) router->bandwidthrate, + (int) router->bandwidthburst, + (int) router->advertisedbandwidth, + onion_pkey, identity_pkey); + + tor_free(onion_pkey); + tor_free(identity_pkey); + + if(result < 0 || result >= maxlen) { + /* apparently different glibcs do different things on snprintf error.. so check both */ + return -1; + } + /* From now on, we use 'written' to remember the current length of 's'. */ + written = result; + + if (router->dir_port && !router->is_trusted_dir) { + /* dircacheport wasn't recognized before 0.0.8pre. (When 0.0.7 is gone, + * we can fold this back into dirport anyway.) */ + result = snprintf(s+written,maxlen-written, "opt dircacheport %d\n", + router->dir_port); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } + + if (options.ContactInfo && strlen(options.ContactInfo)) { + result = snprintf(s+written,maxlen-written, "opt contact %s\n", + options.ContactInfo); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } + + /* Write the exit policy to the end of 's'. */ + for(tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) { + in.s_addr = htonl(tmpe->addr); + /* Write: "accept 1.2.3.4" */ + result = snprintf(s+written, maxlen-written, "%s %s", + tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject", + tmpe->msk == 0 ? "*" : inet_ntoa(in)); + if(result < 0 || result+written > maxlen) { + /* apparently different glibcs do different things on snprintf error.. so check both */ + return -1; + } + written += result; + if (tmpe->msk != 0xFFFFFFFFu && tmpe->msk != 0) { + /* Write "/255.255.0.0" */ + in.s_addr = htonl(tmpe->msk); + result = snprintf(s+written, maxlen-written, "/%s", inet_ntoa(in)); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } + if (tmpe->prt_min == 0 && tmpe->prt_max == 65535) { + /* There is no port set; write ":*" */ + if (written > maxlen-4) + return -1; + strcat(s+written, ":*\n"); + written += 3; + } else if (tmpe->prt_min == tmpe->prt_max) { + /* There is only one port; write ":80". */ + result = snprintf(s+written, maxlen-written, ":%d\n", tmpe->prt_min); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } else { + /* There is a range of ports; write ":79-80". */ + result = snprintf(s+written, maxlen-written, ":%d-%d\n", tmpe->prt_min, + tmpe->prt_max); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } + } /* end for */ + if (written > maxlen-256) /* Not enough room for signature. */ + return -1; + + /* Sign the directory */ + strcat(s+written, "router-signature\n"); + written += strlen(s+written); + s[written] = '\0'; + if (router_get_router_hash(s, digest) < 0) + return -1; + + if (crypto_pk_private_sign(ident_key, digest, 20, signature) < 0) { + log_fn(LOG_WARN, "Error signing digest"); + return -1; + } + strcat(s+written, "-----BEGIN SIGNATURE-----\n"); + written += strlen(s+written); + if (base64_encode(s+written, maxlen-written, signature, 128) < 0) { + log_fn(LOG_WARN, "Couldn't base64-encode signature"); + return -1; + } + written += strlen(s+written); + strcat(s+written, "-----END SIGNATURE-----\n"); + written += strlen(s+written); + + if (written > maxlen-2) + return -1; + /* include a last '\n' */ + s[written] = '\n'; + s[written+1] = 0; + +#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING + cp = s_tmp = s_dup = tor_strdup(s); + ri_tmp = router_parse_entry_from_string(cp, NULL); + if (!ri_tmp) { + log_fn(LOG_ERR, "We just generated a router descriptor we can't parse: <<%s>>", + s); + return -1; + } + free(s_dup); + routerinfo_free(ri_tmp); +#endif + + return written+1; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/routerlist.c b/tags/tor-0_0_8pre1/src/or/routerlist.c new file mode 100644 index 0000000000..3d8751cf15 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/routerlist.c @@ -0,0 +1,892 @@ +/* Copyright 2001-2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/** + * \file routerlist.c + * + * \brief Code to + * maintain and access the global list of routerinfos for known + * servers. + **/ + +/****************************************************************************/ + +extern or_options_t options; /**< command-line and config-file options */ + +/* ********************************************************************** */ + +/* static function prototypes */ +static routerinfo_t * +router_pick_directory_server_impl(int requireauth, int preferothers); +static void mark_all_authdirservers_up(void); +static int router_resolve_routerlist(routerlist_t *dir); + +/****************************************************************************/ + +/** List of digests of keys for servers that are trusted directories. */ +static smartlist_t *trusted_dir_digests = NULL; + +/**** + * Functions to manage and access our list of known routers. (Note: + * dirservers maintain a separate, independent list of known router + * descriptors.) + *****/ + +/** Global list of all of the routers that we, as an OR or OP, know about. */ +static routerlist_t *routerlist = NULL; + +extern int has_fetched_directory; /**< from main.c */ + +/** Try to find a running dirserver. If there are no running dirservers + * in our routerlist, set all the authoritative ones as running again, + * and pick one. If there are no dirservers at all in our routerlist, + * reload the routerlist and try one last time. */ +routerinfo_t *router_pick_directory_server(int requireauth, int requireothers) { + routerinfo_t *choice; + + choice = router_pick_directory_server_impl(requireauth, requireothers); + if(choice) + return choice; + + log_fn(LOG_INFO,"No dirservers are reachable. Trying them all again."); + /* mark all authdirservers are up again */ + mark_all_authdirservers_up(); + /* try again */ + choice = router_pick_directory_server_impl(requireauth, requireothers); + if(choice) + return choice; + + log_fn(LOG_WARN,"No dirservers known. Reloading and trying again."); + has_fetched_directory=0; /* reset it */ + routerlist_clear_trusted_directories(); + if(options.RouterFile) { + if(router_load_routerlist_from_file(options.RouterFile, 1) < 0) + return NULL; + } else { + if(config_assign_default_dirservers() < 0) + return NULL; + } + /* give it one last try */ + choice = router_pick_directory_server_impl(requireauth, requireothers); + return choice; +} + +/** Pick a random running router from our routerlist. If requireauth, + * it has to be a trusted server. If requireothers, it cannot be us. + */ +static routerinfo_t * +router_pick_directory_server_impl(int requireauth, int requireothers) +{ + int i; + routerinfo_t *router; + smartlist_t *sl; + + if(!routerlist) + return NULL; + + /* Find all the running dirservers we know about. */ + sl = smartlist_create(); + for(i=0;i< smartlist_len(routerlist->routers); i++) { + router = smartlist_get(routerlist->routers, i); + if(!router->is_running || !router->dir_port) + continue; + if(requireauth && !router->is_trusted_dir) + continue; + if(requireothers && router_is_me(router)) + continue; + smartlist_add(sl, router); + } + + router = smartlist_choose(sl); + smartlist_free(sl); + return router; +} + +/** Go through and mark the auth dirservers as up */ +static void mark_all_authdirservers_up(void) { + int i; + routerinfo_t *router; + + if(!routerlist) + return; + + for(i=0; i < smartlist_len(routerlist->routers); i++) { + router = smartlist_get(routerlist->routers, i); + if(router->is_trusted_dir) { + tor_assert(router->dir_port > 0); + router->is_running = 1; + router->status_set_at = time(NULL); + } + } +} + +/** Return 0 if \exists an authoritative dirserver that's currently + * thought to be running, else return 1. + */ +int all_directory_servers_down(void) { + int i; + routerinfo_t *router; + if(!routerlist) + return 1; /* if no dirservers, I guess they're all down */ + for(i=0;i< smartlist_len(routerlist->routers); i++) { + router = smartlist_get(routerlist->routers, i); + if(router->is_running && router->is_trusted_dir) { + tor_assert(router->dir_port > 0); + return 0; + } + } + return 1; +} + +/** Given a comma-and-whitespace separated list of nicknames, see which + * nicknames in list name routers in our routerlist that are + * currently running. Add the routerinfos for those routers to sl. + */ +void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list) { + const char *start,*end; + char nick[MAX_HEX_NICKNAME_LEN+1]; + routerinfo_t *router; + + tor_assert(sl); + tor_assert(list); + + while(isspace((int)*list) || *list==',') list++; + + start = list; + while(*start) { + end=start; while(*end && !isspace((int)*end) && *end != ',') end++; + if (end-start > MAX_HEX_NICKNAME_LEN) { + log_fn(LOG_WARN,"Nickname too long; skipping"); + start = end; + continue; + } + memcpy(nick,start,end-start); + nick[end-start] = 0; /* null terminate it */ + router = router_get_by_nickname(nick); + if (router) { + if (router->is_running) + smartlist_add(sl,router); + else + log_fn(LOG_WARN,"Nickname list includes '%s' which is known but down.",nick); + } else + log_fn(has_fetched_directory ? LOG_WARN : LOG_INFO, + "Nickname list includes '%s' which isn't a known router.",nick); + while(isspace((int)*end) || *end==',') end++; + start = end; + } +} + +/** Add every router from our routerlist that is currently running to + * sl. + */ +void router_add_running_routers_to_smartlist(smartlist_t *sl) { + routerinfo_t *router; + int i; + + if(!routerlist) + return; + + for(i=0;irouters);i++) { + router = smartlist_get(routerlist->routers, i); + /* XXX008 for now, only choose verified routers */ + if(router->is_running && router->is_verified && + (!clique_mode() || + connection_get_by_identity_digest(router->identity_digest, + CONN_TYPE_OR))) + smartlist_add(sl, router); + } +} + +/** Return a random running router from the routerlist. If any node + * named in preferred is available, pick one of those. Never pick a + * node named in excluded, or whose routerinfo is in + * excludedsmartlist, even if they are the only nodes available. + */ +routerinfo_t *router_choose_random_node(char *preferred, char *excluded, + smartlist_t *excludedsmartlist) +{ + smartlist_t *sl, *excludednodes; + routerinfo_t *choice; + + excludednodes = smartlist_create(); + add_nickname_list_to_smartlist(excludednodes,excluded); + + /* try the nodes in RendNodes first */ + sl = smartlist_create(); + add_nickname_list_to_smartlist(sl,preferred); + smartlist_subtract(sl,excludednodes); + if(excludedsmartlist) + smartlist_subtract(sl,excludedsmartlist); + choice = smartlist_choose(sl); + smartlist_free(sl); + if(!choice) { + sl = smartlist_create(); + router_add_running_routers_to_smartlist(sl); + smartlist_subtract(sl,excludednodes); + if(excludedsmartlist) + smartlist_subtract(sl,excludedsmartlist); + choice = smartlist_choose(sl); + smartlist_free(sl); + } + smartlist_free(excludednodes); + if(!choice) + log_fn(LOG_WARN,"No available nodes when trying to choose node. Failing."); + return choice; +} + +/** Return the router in our routerlist whose address is addr and + * whose OR port is port. Return NULL if no such router is known. + */ +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + tor_assert(routerlist); + + for(i=0;irouters);i++) { + router = smartlist_get(routerlist->routers, i); + if ((router->addr == addr) && (router->or_port == port)) + return router; + } + return NULL; +} + +/** Return true iff the digest of router's identity key, + * encoded in hexadecimal, matches hexdigest (which is + * optionally prefixed with a single dollar sign). Return false if + * hexdigest is malformed, or it doesn't match. */ +static INLINE int router_hex_digest_matches(routerinfo_t *router, + const char *hexdigest) +{ + char digest[DIGEST_LEN]; + tor_assert(hexdigest); + if (hexdigest[0] == '$') + ++hexdigest; + + if (strlen(hexdigest) != HEX_DIGEST_LEN || + base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0) + return 0; + else + return (!memcmp(digest, router->identity_digest, DIGEST_LEN)); +} + +/* Return true if router's nickname matches nickname + * (case-insensitive), or if router's identity key digest + * matches a hexadecimal value stored in nickname. Return + * false otherwise.*/ +int router_nickname_matches(routerinfo_t *router, const char *nickname) +{ + if (nickname[0]!='$' && !strcasecmp(router->nickname, nickname)) + return 1; + else + return router_hex_digest_matches(router, nickname); +} + +/** Return the router in our routerlist whose (case-insensitive) + * nickname or (case-sensitive) hexadecimal key digest is + * nickname. Return NULL if no such router is known. + */ +routerinfo_t *router_get_by_nickname(const char *nickname) +{ + int i, maybedigest; + routerinfo_t *router; + char digest[DIGEST_LEN]; + + tor_assert(nickname); + if (!routerlist) + return NULL; + if (nickname[0] == '$') + return router_get_by_hexdigest(nickname); + + maybedigest = (strlen(nickname) == HEX_DIGEST_LEN) && + (base16_decode(digest,DIGEST_LEN,nickname,HEX_DIGEST_LEN) == 0); + + for(i=0;irouters);i++) { + router = smartlist_get(routerlist->routers, i); + if (0 == strcasecmp(router->nickname, nickname) || + (maybedigest && 0 == memcmp(digest, router->identity_digest, + DIGEST_LEN))) + return router; + } + + return NULL; +} + +/* XXX008 currently this trusted_dir_digests stuff is not used. */ +/** Return true iff digest is the digest of the identity key of + * a trusted directory. */ +int router_digest_is_trusted_dir(const char *digest) { + if (!trusted_dir_digests) + return 0; + SMARTLIST_FOREACH(trusted_dir_digests, char *, cp, + if (!memcmp(digest, cp, DIGEST_LEN)) return 1); + return 0; +} + +/** Return the router in our routerlist whose hexadecimal key digest + * is hexdigest. Return NULL if no such router is known. */ +routerinfo_t *router_get_by_hexdigest(const char *hexdigest) { + char digest[DIGEST_LEN]; + + tor_assert(hexdigest); + if (!routerlist) + return NULL; + if (hexdigest[0]=='$') + ++hexdigest; + if (strlen(hexdigest) != HEX_DIGEST_LEN || + base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN) < 0) + return NULL; + + return router_get_by_digest(digest); +} + +/** Return the router in our routerlist whose 20-byte key digest + * is hexdigest. Return NULL if no such router is known. */ +routerinfo_t *router_get_by_digest(const char *digest) { + int i; + routerinfo_t *router; + + tor_assert(digest); + + for(i=0;irouters);i++) { + router = smartlist_get(routerlist->routers, i); + if (0 == memcmp(router->identity_digest, digest, DIGEST_LEN)) + return router; + } + + return NULL; +} + +/** Set *prouterlist to the current list of all known routers. */ +void router_get_routerlist(routerlist_t **prouterlist) { + *prouterlist = routerlist; +} + +/** Free all storage held by router. */ +void routerinfo_free(routerinfo_t *router) +{ + if (!router) + return; + + tor_free(router->address); + tor_free(router->nickname); + tor_free(router->platform); + if (router->onion_pkey) + crypto_free_pk_env(router->onion_pkey); + if (router->identity_pkey) + crypto_free_pk_env(router->identity_pkey); + exit_policy_free(router->exit_policy); + free(router); +} + +/** Allocate a fresh copy of router */ +routerinfo_t *routerinfo_copy(const routerinfo_t *router) +{ + routerinfo_t *r; + struct exit_policy_t **e, *tmp; + + r = tor_malloc(sizeof(routerinfo_t)); + memcpy(r, router, sizeof(routerinfo_t)); + + r->address = tor_strdup(r->address); + r->nickname = tor_strdup(r->nickname); + r->platform = tor_strdup(r->platform); + if (r->onion_pkey) + r->onion_pkey = crypto_pk_dup_key(r->onion_pkey); + if (r->identity_pkey) + r->identity_pkey = crypto_pk_dup_key(r->identity_pkey); + e = &r->exit_policy; + while (*e) { + tmp = tor_malloc(sizeof(struct exit_policy_t)); + memcpy(tmp,*e,sizeof(struct exit_policy_t)); + *e = tmp; + (*e)->string = tor_strdup((*e)->string); + e = & ((*e)->next); + } + return r; +} + +/** Free all storage held by a routerlist rl */ +void routerlist_free(routerlist_t *rl) +{ + SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, + routerinfo_free(r)); + smartlist_free(rl->routers); + tor_free(rl->software_versions); + tor_free(rl); +} + +/** Mark the router with ID digest as non-running in our routerlist. */ +void router_mark_as_down(const char *digest) { + routerinfo_t *router; + tor_assert(digest); + router = router_get_by_digest(digest); + if(!router) /* we don't seem to know about him in the first place */ + return; + log_fn(LOG_DEBUG,"Marking %s as down.",router->nickname); + router->is_running = 0; + router->status_set_at = time(NULL); +} + +/** Add router to the routerlist, if we don't already have it. Replace + * older entries (if any) with the same name. Note: Callers should not hold + * their pointers to router after invoking this function; router + * will either be inserted into the routerlist or freed. Returns 0 if the + * router was added; -1 if it was not. + */ +int router_add_to_routerlist(routerinfo_t *router) { + int i; + routerinfo_t *r; + /* If we have a router with this name, and the identity key is the same, + * choose the newer one. If the identity key has changed, drop the router. + */ + for (i = 0; i < smartlist_len(routerlist->routers); ++i) { + r = smartlist_get(routerlist->routers, i); + /* XXXX008 should just compare digests instead. */ + if (!strcasecmp(router->nickname, r->nickname)) { + if (!crypto_pk_cmp_keys(router->identity_pkey, r->identity_pkey)) { + if (router->published_on > r->published_on) { + log_fn(LOG_DEBUG, "Replacing entry for router '%s'", + router->nickname); + /* Remember whether we trust this router as a dirserver. */ + if (r->is_trusted_dir) + router->is_trusted_dir = 1; + /* If the address hasn't changed; no need to re-resolve. */ + if (!strcasecmp(r->address, router->address)) + router->addr = r->addr; + routerinfo_free(r); + smartlist_set(routerlist->routers, i, router); + return 0; + } else { + log_fn(LOG_DEBUG, "Skipping old entry for router '%s'", + router->nickname); + /* If we now trust 'router', then we trust the one in the routerlist + * too. */ + if (router->is_trusted_dir) + r->is_trusted_dir = 1; + /* Update the is_running status to whatever we were told. */ + r->is_running = router->is_running; + routerinfo_free(router); + return -1; + } + } else { + /* XXXX008 It's okay to have two keys for a nickname as soon as + * all the 007 clients are dead. */ + log_fn(LOG_WARN, "Identity key mismatch for router '%s'", + router->nickname); + routerinfo_free(router); + return -1; + } + } + } + /* We haven't seen a router with this name before. Add it to the end of + * the list. */ + smartlist_add(routerlist->routers, router); + return 0; +} + +/** Remove any routers from the routerlist that are more than ROUTER_MAX_AGE + * seconds old. + * + * (This function is just like dirserv_remove_old_servers. One day we should + * merge them.) + */ +void +routerlist_remove_old_routers(void) +{ + int i; + time_t cutoff; + routerinfo_t *router; + if (!routerlist) + return; + + cutoff = time(NULL) - ROUTER_MAX_AGE; + for (i = 0; i < smartlist_len(routerlist->routers); ++i) { + router = smartlist_get(routerlist->routers, i); + if (router->published_on < cutoff && + !router->dir_port) { + /* Too old. Remove it. But never remove dirservers! */ + log_fn(LOG_INFO,"Forgetting obsolete routerinfo for node %s.", router->nickname); + routerinfo_free(router); + smartlist_del(routerlist->routers, i--); + } + } +} + +/* + * Code to parse router descriptors and directories. + */ + +/** Update the current router list with the one stored in + * routerfile. If trusted is true, then we'll use + * directory servers from the file. */ +int router_load_routerlist_from_file(char *routerfile, int trusted) +{ + char *string; + + string = read_file_to_str(routerfile); + if(!string) { + log_fn(LOG_WARN,"Failed to load routerfile %s.",routerfile); + return -1; + } + + if(router_load_routerlist_from_string(string, trusted) < 0) { + log_fn(LOG_WARN,"The routerfile itself was corrupt."); + free(string); + return -1; + } + /* dump_onion_keys(LOG_NOTICE); */ + + free(string); + return 0; +} + +/** Mark all directories in the routerlist as nontrusted. */ +void routerlist_clear_trusted_directories(void) +{ + if (routerlist) { + SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r, + r->is_trusted_dir = 0); + } + if (trusted_dir_digests) { + SMARTLIST_FOREACH(trusted_dir_digests, char *, cp, tor_free(cp)); + smartlist_clear(trusted_dir_digests); + } +} + +/** Helper function: read routerinfo elements from s, and throw out the + * ones that don't parse and resolve. Add all remaining elements to the + * routerlist. If trusted is true, then we'll use + * directory servers from the string + */ +int router_load_routerlist_from_string(const char *s, int trusted) +{ + routerlist_t *new_list=NULL; + + if (router_parse_list_from_string(&s, &new_list, NULL, 0)) { + log(LOG_WARN, "Error parsing router file"); + return -1; + } + if (trusted) { + int i; + if (!trusted_dir_digests) + trusted_dir_digests = smartlist_create(); + for (i=0;irouters);++i) { + routerinfo_t *r = smartlist_get(new_list->routers, i); + if (r->dir_port) { + char *b; + log_fn(LOG_DEBUG,"Trusting router %s.", r->nickname); + r->is_trusted_dir = 1; + b = tor_malloc(DIGEST_LEN); + memcpy(b, r->identity_digest, DIGEST_LEN); + smartlist_add(trusted_dir_digests, b); + } + } + } + if (routerlist) { + SMARTLIST_FOREACH(new_list->routers, routerinfo_t *, r, + router_add_to_routerlist(r)); + smartlist_clear(new_list->routers); + routerlist_free(new_list); + } else { + routerlist = new_list; + } + if (router_resolve_routerlist(routerlist)) { + log(LOG_WARN, "Error resolving routerlist"); + return -1; + } + /* dump_onion_keys(LOG_NOTICE); */ + + return 0; +} + +/** Add to the current routerlist each router stored in the + * signed directory s. If pkey is provided, check the signature against + * pkey; else check against the pkey of the signing directory server. */ +int router_load_routerlist_from_directory(const char *s, + crypto_pk_env_t *pkey) +{ + routerlist_t *new_list = NULL; + check_software_version_against_directory(s, options.IgnoreVersion); + if (router_parse_routerlist_from_directory(s, &new_list, pkey)) { + log_fn(LOG_WARN, "Couldn't parse directory."); + return -1; + } + if (routerlist) { + SMARTLIST_FOREACH(new_list->routers, routerinfo_t *, r, + router_add_to_routerlist(r)); + smartlist_clear(new_list->routers); + routerlist->published_on = new_list->published_on; + tor_free(routerlist->software_versions); + routerlist->software_versions = new_list->software_versions; + new_list->software_versions = NULL; + routerlist_free(new_list); + } else { + routerlist = new_list; + } + if (router_resolve_routerlist(routerlist)) { + log_fn(LOG_WARN, "Error resolving routerlist"); + return -1; + } + if (options.AuthoritativeDir) { + /* Learn about the descriptors in the directory. */ + dirserv_load_from_directory_string(s); + } else { + /* Remember the directory. */ + dirserv_set_cached_directory(s, routerlist->published_on); + } + return 0; +} + +/** Helper function: resolve the hostname for router. */ +static int +router_resolve(routerinfo_t *router) +{ + if (tor_lookup_hostname(router->address, &router->addr) != 0 + || !router->addr) { + log_fn(LOG_WARN,"Could not get address for router %s (%s).", + router->address, router->nickname); + return -1; + } + router->addr = ntohl(router->addr); /* get it back into host order */ + + return 0; +} + +/** Helper function: resolve every router in rl, and ensure that our own + * routerinfo is at the front. + */ +static int +router_resolve_routerlist(routerlist_t *rl) +{ + int i, remove; + routerinfo_t *r; + if (!rl) + rl = routerlist; + + i = 0; + if ((r = router_get_my_routerinfo())) { + smartlist_insert(rl->routers, 0, routerinfo_copy(r)); + ++i; + } + + for ( ; i < smartlist_len(rl->routers); ++i) { + remove = 0; + r = smartlist_get(rl->routers,i); + if (router_is_me(r)) { + remove = 1; + } else if (r->addr) { + /* already resolved. */ + } else if (router_resolve(r)) { + log_fn(LOG_WARN, "Couldn't resolve router %s; not using", r->address); + remove = 1; + } + if (remove) { + routerinfo_free(r); + smartlist_del_keeporder(rl->routers, i--); + } + } + + return 0; +} + +/** Decide whether a given addr:port is definitely accepted, definitely + * rejected, or neither by a given exit policy. If addr is 0, we + * don't know the IP of the target address. + * + * Returns -1 for "rejected", 0 for "accepted", 1 for "maybe" (since IP is + * unknown). + */ +int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port, + struct exit_policy_t *policy) +{ + int maybe_reject = 0; + int maybe_accept = 0; + int match = 0; + int maybe = 0; + struct in_addr in; + struct exit_policy_t *tmpe; + + for(tmpe=policy; tmpe; tmpe=tmpe->next) { +// log_fn(LOG_DEBUG,"Considering exit policy %s", tmpe->string); + maybe = 0; + if (!addr) { + /* Address is unknown. */ + if (port >= tmpe->prt_min && port <= tmpe->prt_max) { + /* The port definitely matches. */ + if (tmpe->msk == 0) { + match = 1; + } else { + maybe = 1; + } + } else if (!port) { + /* The port maybe matches. */ + maybe = 1; + } + } else { + /* Address is known */ + if ((addr & tmpe->msk) == (tmpe->addr & tmpe->msk)) { + if (port >= tmpe->prt_min && port <= tmpe->prt_max) { + /* Exact match for the policy */ + match = 1; + } else if (!port) { + maybe = 1; + } + } + } + if (maybe) { + if (tmpe->policy_type == EXIT_POLICY_REJECT) + maybe_reject = 1; + else + maybe_accept = 1; + } + if (match) { + in.s_addr = htonl(addr); + log_fn(LOG_DEBUG,"Address %s:%d matches exit policy '%s'", + inet_ntoa(in), port, tmpe->string); + if(tmpe->policy_type == EXIT_POLICY_ACCEPT) { + /* If we already hit a clause that might trigger a 'reject', than we + * can't be sure of this certain 'accept'.*/ + return maybe_reject ? ADDR_POLICY_UNKNOWN : ADDR_POLICY_ACCEPTED; + } else { + return maybe_accept ? ADDR_POLICY_UNKNOWN : ADDR_POLICY_REJECTED; + } + } + } + /* accept all by default. */ + return maybe_reject ? ADDR_POLICY_UNKNOWN : ADDR_POLICY_ACCEPTED; +} + +/** Return 1 if all running routers will reject addr:port, return 0 if + * any might accept it. */ +int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + for (i=0;irouters);i++) { + router = smartlist_get(routerlist->routers, i); + if (router->is_running && router_compare_addr_to_exit_policy( + addr, port, router->exit_policy) != ADDR_POLICY_REJECTED) + return 0; /* this one could be ok. good enough. */ + } + return 1; /* all will reject. */ +} + +/** Return true iff router does not permit exit streams. + */ +int router_exit_policy_rejects_all(routerinfo_t *router) { + return router_compare_addr_to_exit_policy(0, 0, router->exit_policy) + == ADDR_POLICY_REJECTED; +} + +/* Release all space held in rr. */ +void running_routers_free(running_routers_t *rr) +{ + tor_assert(rr); + if (rr->running_routers) { + SMARTLIST_FOREACH(rr->running_routers, char *, s, tor_free(s)); + smartlist_free(rr->running_routers); + } + tor_free(rr); +} + +/* Update the running/not-running status of every router in list, based + * on the contents of rr. */ +void routerlist_update_from_runningrouters(routerlist_t *list, + running_routers_t *rr) +{ + int n_routers, i; + routerinfo_t *router, *me = router_get_my_routerinfo(); + if (!list) + return; + if (list->published_on >= rr->published_on) + return; + if (list->running_routers_updated_on >= rr->published_on) + return; + + if(me) /* learn if the dirservers think I'm verified */ + router_update_status_from_smartlist(me, + rr->published_on, + rr->running_routers); + n_routers = smartlist_len(list->routers); + for (i=0; irouters, i); + router_update_status_from_smartlist(router, + rr->published_on, + rr->running_routers); + } + list->running_routers_updated_on = rr->published_on; +} + +/** Update the is_running and is_verified fields of the router router, + * based in its status in the list of strings stored in running_list. + * All entries in running_list follow one of these formats: + *
    1. nickname -- router is running and verified. + *
    2. !nickname -- router is not-running and verified. + *
    3. $hexdigest -- router is running and unverified. + *
    4. !$hexdigest -- router is not-running and unverified. + *
    + */ +void router_update_status_from_smartlist(routerinfo_t *router, + time_t list_time, + smartlist_t *running_list) +{ + int n_names, i, running, approved; + const char *name; +#if 1 + char *cp; + int n; + n = 0; + for (i=0; inickname, cp); + tor_free(cp); +#endif + + running = approved = 0; + n_names = smartlist_len(running_list); + for (i=0; istatus_set_at < list_time) { + router->status_set_at = list_time; + router->is_running = 1; + } + router->is_verified = (name[0] != '$'); + return; + } + } else { /* *name == '!' */ + name++; + if (router_nickname_matches(router, name)) { + if (router->status_set_at < list_time) { + router->status_set_at = list_time; + router->is_running = 0; + } + router->is_verified = (name[0] != '$'); + return; + } + } + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/routerparse.c b/tags/tor-0_0_8pre1/src/or/routerparse.c new file mode 100644 index 0000000000..b0bca530af --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/routerparse.c @@ -0,0 +1,1424 @@ +/* Copyright 2001-2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file routerparse.c + * + * \brief Code to parse and validate router descriptors and directories. + **/ + +/* This is required on rh7 to make strptime not complain. + */ +#define _GNU_SOURCE + +#include "or.h" + +/****************************************************************************/ + +/** Enumeration of possible token types. The ones starting with K_ + * correspond to directory 'keywords'. _UNRECOGNIZED is for an + * unrecognized keyword; _ERR is an error in the tokenizing process, + * _EOF is an end-of-file marker, and _NIL is used to encode + * not-a-token. + */ +typedef enum { + K_ACCEPT, + K_DIRECTORY_SIGNATURE, + K_RECOMMENDED_SOFTWARE, + K_REJECT, + K_ROUTER, + K_SIGNED_DIRECTORY, + K_SIGNING_KEY, + K_ONION_KEY, + K_LINK_KEY, /* XXXX obsolete; remove in June. */ + K_ROUTER_SIGNATURE, + K_PUBLISHED, + K_RUNNING_ROUTERS, + K_PLATFORM, + K_OPT, + K_BANDWIDTH, + K_PORTS, + K_DIRCACHEPORT, + K_CONTACT, + K_NETWORK_STATUS, + _UNRECOGNIZED, + _ERR, + _EOF, + _NIL +} directory_keyword; + +/** Structure to hold a single directory token. + * + * We parse a directory by breaking it into "tokens", each consisting + * of a keyword, a line full of arguments, and a binary object. The + * arguments and object are both optional, depending on the keyword + * type. + */ +typedef struct directory_token_t { + directory_keyword tp; /**< Type of the token. */ + int n_args; /**< Number of elements in args */ + char **args; /**< Array of arguments from keyword line. */ + char *object_type; /**< -----BEGIN [object_type]-----*/ + int object_size; /**< Bytes in object_body */ + char *object_body; /**< Contents of object, base64-decoded. */ + crypto_pk_env_t *key; /**< For public keys only. */ + char *error; /**< For _ERR tokens only. */ +} directory_token_t; + +/* ********************************************************************** */ + +/** We use a table of rules to decide how to parse each token type. */ + +/** Rules for how many arguments a keyword can take. */ +typedef enum { + NO_ARGS, /**< (1) no arguments, ever */ + ARGS, /**< (2) a list of arguments separated by spaces */ + CONCAT_ARGS, /**< or (3) the rest of the line, treated as a single argument. */ +} arg_syntax; + +/** Rules for whether the keyword needs an object. */ +typedef enum { + NO_OBJ, /**< (1) no object, ever */ + NEED_OBJ, /**< (2) object is required */ + NEED_KEY, /**< (3) object is required, and must be a public key. */ + OBJ_OK, /**< or (4) object is optional. */ +} obj_syntax; + +/** Rules for where a keyword can appear. */ +typedef enum { + ANY = 0, /**< Appears in router descriptor or in directory sections. */ + DIR_ONLY, /**< Appears only in directory. */ + RTR_ONLY, /**< Appears only in router descriptor or runningrouters */ +} where_syntax; + +/** Table mapping keywords to token value and to argument rules. */ +static struct { + char *t; int v; arg_syntax s; obj_syntax os; where_syntax ws; +} token_table[] = { + { "accept", K_ACCEPT, ARGS, NO_OBJ, RTR_ONLY }, + { "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ,DIR_ONLY}, + { "reject", K_REJECT, ARGS, NO_OBJ, RTR_ONLY }, + { "router", K_ROUTER, ARGS, NO_OBJ, RTR_ONLY }, + { "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ, DIR_ONLY }, + { "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ, DIR_ONLY }, + { "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY,RTR_ONLY }, + { "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR_ONLY }, + { "link-key", K_LINK_KEY, NO_ARGS, NEED_KEY,RTR_ONLY }, + { "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR_ONLY }, + { "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR_ONLY }, + { "ports", K_PORTS, ARGS, NO_OBJ, RTR_ONLY }, + { "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR_ONLY }, + { "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR_ONLY }, + { "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANY }, + { "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY }, + { "dircacheport", K_DIRCACHEPORT, ARGS, NO_OBJ, RTR_ONLY }, + { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY }, + { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY }, + { NULL, -1 } +}; + +/* static function prototypes */ +static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok); +static struct exit_policy_t *router_parse_exit_policy(directory_token_t *tok); +static int router_get_hash_impl(const char *s, char *digest, + const char *start_str, const char *end_str); +static void token_free(directory_token_t *tok); +static smartlist_t *find_all_exitpolicy(smartlist_t *s); +static directory_token_t *find_first_by_keyword(smartlist_t *s, + directory_keyword keyword); +static int tokenize_string(const char *start, const char *end, + smartlist_t *out, int is_dir); +static directory_token_t *get_next_token(const char **s, where_syntax where); +static int check_directory_signature(const char *digest, + directory_token_t *tok, + crypto_pk_env_t *pkey); + + +/** Set digest to the SHA-1 digest of the hash of the directory in + * s. Return 0 on success, nonzero on failure. + */ +int router_get_dir_hash(const char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "signed-directory","directory-signature"); +} + +/** Set digest to the SHA-1 digest of the hash of the first router in + * s. Return 0 on success, nonzero on failure. + */ +int router_get_router_hash(const char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "router ","router-signature"); +} + +/** Set digest to the SHA-1 digest of the hash of the running-routers + * string in s. Return 0 on success, nonzero on failure. + */ +int router_get_runningrouters_hash(const char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "network-status ","directory-signature"); +} + +/** Parse a date of the format "YYYY-MM-DD hh:mm:ss" and store the result into + * *t. + */ +/* XXX this should go in util.c, yes? -RD */ +static int parse_time(const char *cp, time_t *t) +{ + struct tm st_tm; +#ifdef HAVE_STRPTIME + if (!strptime(cp, "%Y-%m-%d %H:%M:%S", &st_tm)) { + log_fn(LOG_WARN, "Published time was unparseable"); return -1; + } +#else + unsigned int year=0, month=0, day=0, hour=100, minute=100, second=100; + if (sscanf(cp, "%u-%u-%u %u:%u:%u", &year, &month, + &day, &hour, &minute, &second) < 6) { + log_fn(LOG_WARN, "Published time was unparseable"); return -1; + } + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour > 23 || minute > 59 || second > 61) { + log_fn(LOG_WARN, "Published time was nonsensical"); return -1; + } + st_tm.tm_year = year; + st_tm.tm_mon = month-1; + st_tm.tm_mday = day; + st_tm.tm_hour = hour; + st_tm.tm_min = minute; + st_tm.tm_sec = second; +#endif + *t = tor_timegm(&st_tm); + return 0; +} + +/** + * Find the first instance of "recommended-software ...\n" at the start of + * a line; return a newly allocated string containing the "..." portion. + * Return NULL if no such instance was found. + */ +static char * +get_recommended_software_from_directory(const char *str) +{ +#define REC "recommended-software " + const char *cp = str, *eol; + int len = strlen(REC); + cp = str; + if (strncmp(str, REC, len)==0) { + cp += len; + } else { + cp = strstr(str, "\n"REC); + if (!cp) + return NULL; + cp += len+1; + } + eol = strchr(cp, '\n'); + if (!eol) + return NULL; + return tor_strndup(cp, eol-cp); +#undef REC +} + +/** Return 1 if myversion is not in versionlist, and if at least + * one version of Tor on versionlist is newer than myversion. + * Otherwise return 0. + * (versionlist is a comma-separated list of version strings, + * optionally prefixed with "Tor". Versions that can't be parsed are + * ignored.) */ +/* static */ int is_obsolete_version(const char *myversion, + const char *versionlist) { + char *version, *comma, *cp; + tor_version_t mine, other; + int found_newer = 0, r; + + log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, versionlist); + + if (tor_version_parse(myversion, &mine)) { + log_fn(LOG_ERR, "I couldn't parse my own version (%s)", myversion); + tor_assert(0); + } + + for(;;) { + comma = strchr(versionlist, ','); + version = tor_strndup(versionlist, + comma?(comma-versionlist):strlen(versionlist)); + cp = version; + while (isspace(*cp)) + ++cp; + if (!strncmp(cp, "Tor ", 4)) + cp += 4; + + if (tor_version_parse(cp, &other)) { + /* Couldn't parse other; it can't be a match. */ + } else { + r = tor_version_compare(&mine, &other); + if (r==0) { + tor_free(version); + return 0; /* It's a match. */ + } else if (r<0) { + found_newer = 1; + } + } + tor_free(version); + if (comma) + versionlist = comma+1; + else + break; + } + if (!found_newer) { + log_fn(LOG_WARN, "This version of Tor (%s) is newer than any on the recommended list (%s)", + myversion, versionlist); + return 0; + } else { + return 1; + } +} + +/* Return 0 if myversion is supported; else log a message and return + * -1 (or exit if ignoreversions is false) */ +int check_software_version_against_directory(const char *directory, + int ignoreversion) +{ + char *v; + v = get_recommended_software_from_directory(directory); + if (!v) { + log_fn(LOG_WARN, "No recommended-versions string found in directory"); + return -1; + } + if (!is_obsolete_version(VERSION, v)) { + tor_free(v); + return 0; + } + log(ignoreversion ? LOG_WARN : LOG_ERR, + "You are running Tor version %s, which will not work with this network.\n" + "Please use %s%s.", + VERSION, strchr(v,',') ? "one of " : "", v); + tor_free(v); + + if(ignoreversion) { + log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so."); + return -1; + } else { + fflush(0); + tor_cleanup(); + exit(0); + return -1; /* never reached */ + } +} + +/** Parse a directory from str and, when done, store the + * resulting routerlist in *dest, freeing the old value if necessary. + * If pkey is provided, we check the directory signature with pkey. + */ +int /* Should be static; exposed for unit tests */ +router_parse_routerlist_from_directory(const char *str, + routerlist_t **dest, + crypto_pk_env_t *pkey) +{ + directory_token_t *tok; + char digest[DIGEST_LEN]; + routerlist_t *new_dir = NULL; + char *versions = NULL; + smartlist_t *good_nickname_list = NULL; + time_t published_on; + int i, r; + const char *end; + smartlist_t *tokens = NULL; + + if (router_get_dir_hash(str, digest)) { + log_fn(LOG_WARN, "Unable to compute digest of directory"); + goto err; + } + log_fn(LOG_DEBUG,"Received directory hashes to %s",hex_str(digest,4)); + + if ((end = strstr(str,"\nrouter "))) { + ++end; + } else if ((end = strstr(str, "\ndirectory-signature"))) { + ++end; + } else { + end = str + strlen(str); + } + + tokens = smartlist_create(); + if (tokenize_string(str,end,tokens,1)) { + log_fn(LOG_WARN, "Error tokenizing directory"); goto err; + } + if (smartlist_len(tokens) < 1) { + log_fn(LOG_WARN, "Impossibly short directory header"); goto err; + } + if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) { + log_fn(LOG_WARN, "Unrecognized keyword in \"%s\"; can't parse directory.", + tok->args[0]); + goto err; + } + + tok = smartlist_get(tokens,0); + if (tok->tp != K_SIGNED_DIRECTORY) { + log_fn(LOG_WARN, "Directory doesn't start with signed-directory."); + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) { + log_fn(LOG_WARN, "Missing published time on directory."); + goto err; + } + tor_assert(tok->n_args == 1); + + if (parse_time(tok->args[0], &published_on) < 0) { + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_RECOMMENDED_SOFTWARE))) { + log_fn(LOG_WARN, "Missing recommended-software line from directory."); + goto err; + } + if (tok->n_args != 1) { + log_fn(LOG_WARN, "Invalid recommended-software line"); goto err; + } + versions = tor_strdup(tok->args[0]); + + if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) { + log_fn(LOG_WARN, "Missing running-routers line from directory."); + goto err; + } + + good_nickname_list = smartlist_create(); + for (i=0; in_args; ++i) { + smartlist_add(good_nickname_list, tok->args[i]); + } + tok->n_args = 0; /* Don't free the strings in good_nickname_lst yet. */ + + /* Determine if my routerinfo is considered verified. */ + { + routerinfo_t *me = router_get_my_routerinfo(); + if(me) + router_update_status_from_smartlist(me, published_on, + good_nickname_list); + } + + /* Read the router list from s, advancing s up past the end of the last + * router. */ + str = end; + if (router_parse_list_from_string(&str, &new_dir, + good_nickname_list, published_on)) { + log_fn(LOG_WARN, "Error reading routers from directory"); + goto err; + } + + new_dir->software_versions = versions; versions = NULL; + new_dir->published_on = published_on; + + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + + tokens = smartlist_create(); + if (tokenize_string(str,str+strlen(str),tokens,1)<0) { + log_fn(LOG_WARN, "Error tokenizing signature"); goto err; + } + + if (smartlist_len(tokens) != 1 || + (!(tok=smartlist_get(tokens,0))) || /* always succeeds */ + (tok->tp != K_DIRECTORY_SIGNATURE)) { + log_fn(LOG_WARN,"Expected a single directory signature"); goto err; + } + if (check_directory_signature(digest, smartlist_get(tokens,0), pkey)<0) { + goto err; + } + + if (*dest) + routerlist_free(*dest); + *dest = new_dir; + + r = 0; + goto done; + err: + r = -1; + if (new_dir) + routerlist_free(new_dir); + tor_free(versions); + done: + if (tokens) { + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + } + if (good_nickname_list) { + SMARTLIST_FOREACH(good_nickname_list, char *, n, tor_free(n)); + smartlist_free(good_nickname_list); + } + return r; +} + +running_routers_t * +router_parse_runningrouters(const char *str) +{ + char digest[DIGEST_LEN]; + running_routers_t *new_list = NULL; + directory_token_t *tok; + time_t published_on; + int i; + + smartlist_t *tokens = NULL; + + if (router_get_runningrouters_hash(str, digest)) { + log_fn(LOG_WARN, "Unable to compute digest of directory"); + goto err; + } + tokens = smartlist_create(); + if (tokenize_string(str,str+strlen(str),tokens,1)) { + log_fn(LOG_WARN, "Error tokenizing directory"); goto err; + } + if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) { + log_fn(LOG_WARN, "Unrecognized keyword in \"%s\"; can't parse directory.", + tok->args[0]); + goto err; + } + tok = smartlist_get(tokens,0); + if (tok->tp != K_NETWORK_STATUS) { + log_fn(LOG_WARN, "Network-status starts with wrong token"); + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) { + log_fn(LOG_WARN, "Missing published time on directory."); + goto err; + } + tor_assert(tok->n_args == 1); + if (parse_time(tok->args[0], &published_on) < 0) { + goto err; + } + + if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) { + log_fn(LOG_WARN, "Missing running-routers line from directory."); + goto err; + } + + new_list = tor_malloc_zero(sizeof(running_routers_t)); + new_list->published_on = published_on; + new_list->running_routers = smartlist_create(); + for (i=0;in_args;++i) { + smartlist_add(new_list->running_routers, tok->args[i]); + } + + if (!(tok = find_first_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) { + log_fn(LOG_WARN, "Missing signature on directory"); + goto err; + } + if (check_directory_signature(digest, tok, NULL)<0) { + goto err; + } + + goto done; + err: + running_routers_free(new_list); + new_list = NULL; + done: + if (tokens) { + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + } + return new_list; +} + +static int check_directory_signature(const char *digest, + directory_token_t *tok, + crypto_pk_env_t *pkey) +{ + char signed_digest[PK_BYTES]; + if (tok->n_args == 1) { + routerinfo_t *r = router_get_by_nickname(tok->args[0]); + log_fn(LOG_DEBUG, "Got directory signed by %s", tok->args[0]); + if (r && r->is_trusted_dir) { + pkey = r->identity_pkey; + } else if (!r && pkey) { + /* pkey provided for debugging purposes. */ + } else if (!r) { + log_fn(LOG_WARN, "Directory was signed by unrecognized server %s", + tok->args[0]); + return -1; + } else if (r && !r->is_trusted_dir) { + log_fn(LOG_WARN, "Directory was signed by non-trusted server %s", + tok->args[0]); + return -1; + } + } else if (tok->n_args > 1) { + log_fn(LOG_WARN, "Too many arguments to directory-signature"); + return -1; + } + if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) { + log_fn(LOG_WARN, "Bad object type or length on directory signature"); + return -1; + } + if (pkey) { + if (crypto_pk_public_checksig(pkey, tok->object_body, 128, signed_digest) + != 20) { + log_fn(LOG_WARN, "Error reading directory: invalid signature."); + return -1; + } + log(LOG_DEBUG,"Signed directory hash starts %s", hex_str(signed_digest,4)); + if (memcmp(digest, signed_digest, 20)) { + log_fn(LOG_WARN, "Error reading directory: signature does not match."); + return -1; + } + } else { + /* XXXX008 freak out, unless testing. */ + } + return 0; +} + + +/** Given a string *s containing a concatenated sequence of router + * descriptors, parses them and stores the result in *dest. If + * good_nickname_list is provided, then routers are marked as + * running/nonrunning and verified/unverified based on their status in the + * list. Otherwise, all routers are marked running and verified. Advances + * *s to a point immediately following the last router entry. Returns 0 on + * success and -1 on failure. + */ +int +router_parse_list_from_string(const char **s, routerlist_t **dest, + smartlist_t *good_nickname_list, + time_t published_on) +{ + routerinfo_t *router; + smartlist_t *routers; + const char *end; + + tor_assert(s && *s); + + routers = smartlist_create(); + + while (1) { + *s = eat_whitespace(*s); + /* Don't start parsing the rest of *s unless it contains a router. */ + if (strncmp(*s, "router ", 7)!=0) + break; + if ((end = strstr(*s+1, "\nrouter "))) { + end++; + } else if ((end = strstr(*s+1, "\ndirectory-signature"))) { + end++; + } else { + end = *s+strlen(*s); + } + + router = router_parse_entry_from_string(*s, end); + *s = end; + if (!router) { + log_fn(LOG_WARN, "Error reading router; skipping"); + continue; + } + + if (good_nickname_list) { + router_update_status_from_smartlist(router, published_on, + good_nickname_list); + } else { + router->is_running = 1; /* start out assuming all dirservers are up */ + router->is_verified = 1; + router->status_set_at = time(NULL); + } + smartlist_add(routers, router); + log_fn(LOG_DEBUG,"just added router #%d.",smartlist_len(routers)); + } + + if (*dest) + routerlist_free(*dest); + *dest = tor_malloc(sizeof(routerlist_t)); + (*dest)->routers = routers; + (*dest)->software_versions = NULL; + + return 0; +} + + +/** Helper function: reads a single router entry from *s ... + * *end. Mallocs a new router and returns it if all goes well, else + * returns NULL. + */ +routerinfo_t *router_parse_entry_from_string(const char *s, + const char *end) { + routerinfo_t *router = NULL; + char signed_digest[128]; + char digest[128]; + smartlist_t *tokens = NULL, *exit_policy_tokens = NULL; + directory_token_t *tok; + int t; + int ports_set, bw_set; + + if (!end) { + end = s + strlen(s); + } + + if (router_get_router_hash(s, digest) < 0) { + log_fn(LOG_WARN, "Couldn't compute router hash."); + return NULL; + } + tokens = smartlist_create(); + if (tokenize_string(s,end,tokens,0)) { + log_fn(LOG_WARN, "Error tokeninzing router descriptor."); goto err; + } + + if (smartlist_len(tokens) < 2) { + log_fn(LOG_WARN, "Impossibly short router descriptor."); + goto err; + } + if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) { + log_fn(LOG_WARN, "Unrecognized keyword in \"%s\"; skipping descriptor.", + tok->args[0]); + goto err; + } + + tok = smartlist_get(tokens,0); + if (tok->tp != K_ROUTER) { + log_fn(LOG_WARN,"Entry does not start with \"router\""); + goto err; + } + + router = tor_malloc_zero(sizeof(routerinfo_t)); + router->onion_pkey = router->identity_pkey = NULL; + ports_set = bw_set = 0; + + if (tok->n_args == 2 || tok->n_args == 5 || tok->n_args == 6) { + router->nickname = tor_strdup(tok->args[0]); + if (strlen(router->nickname) > MAX_NICKNAME_LEN) { + log_fn(LOG_WARN,"Router nickname too long."); + goto err; + } + if (strspn(router->nickname, LEGAL_NICKNAME_CHARACTERS) != + strlen(router->nickname)) { + log_fn(LOG_WARN, "Router nickname contains illegal characters."); + goto err; + } + router->address = tor_strdup(tok->args[1]); + router->addr = 0; + + if (tok->n_args >= 5) { + router->or_port = atoi(tok->args[2]); + router->socks_port = atoi(tok->args[3]); + router->dir_port = atoi(tok->args[4]); + ports_set = 1; + } + } else { + log_fn(LOG_WARN,"Wrong # of arguments to \"router\" (%d)",tok->n_args); + goto err; + } + + tok = find_first_by_keyword(tokens, K_PORTS); + if (tok && ports_set) { + log_fn(LOG_WARN,"Redundant ports line"); + goto err; + } else if (tok) { + if (tok->n_args != 3) { + log_fn(LOG_WARN,"Wrong # of arguments to \"ports\""); + goto err; + } + router->or_port = atoi(tok->args[0]); + router->socks_port = atoi(tok->args[1]); + router->dir_port = atoi(tok->args[2]); + ports_set = 1; + } + + tok = find_first_by_keyword(tokens, K_DIRCACHEPORT); + if (tok) { + if (router->dir_port) + log_fn(LOG_WARN,"Redundant dircacheport line"); + if (tok->n_args != 1) { + log_fn(LOG_WARN,"Wrong # of arguments to \"dircacheport\""); + goto err; + } + router->dir_port = atoi(tok->args[0]); + } + + tok = find_first_by_keyword(tokens, K_BANDWIDTH); + if (tok && bw_set) { + log_fn(LOG_WARN,"Redundant bandwidth line"); + goto err; + } else if (tok) { + /* XXX set this to "< 3" once 0.0.7 is obsolete */ + if (tok->n_args < 2) { + log_fn(LOG_WARN,"Not enough arguments to \"bandwidth\""); + goto err; + } + router->bandwidthrate = atoi(tok->args[0]); + router->bandwidthburst = atoi(tok->args[1]); + if(tok->n_args > 2) + router->advertisedbandwidth = atoi(tok->args[2]); + bw_set = 1; + } + + if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) { + log_fn(LOG_WARN, "Missing published time"); goto err; + } + tor_assert(tok->n_args == 1); + if (parse_time(tok->args[0], &router->published_on) < 0) + goto err; + + if (!(tok = find_first_by_keyword(tokens, K_ONION_KEY))) { + log_fn(LOG_WARN, "Missing onion key"); goto err; + } + /* XXX Check key length */ + router->onion_pkey = tok->key; + tok->key = NULL; /* Prevent free */ + + if ((tok = find_first_by_keyword(tokens, K_LINK_KEY))) { + log_fn(LOG_INFO, "Skipping obsolete link-key"); + } + + if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) { + log_fn(LOG_WARN, "Missing identity key"); goto err; + } + /* XXX Check key length */ + router->identity_pkey = tok->key; + tok->key = NULL; /* Prevent free */ + if (crypto_pk_get_digest(router->identity_pkey,router->identity_digest)){ + log_fn(LOG_WARN, "Couldn't calculate key digest"); goto err; + } + + if ((tok = find_first_by_keyword(tokens, K_PLATFORM))) { + router->platform = tor_strdup(tok->args[0]); + } + + exit_policy_tokens = find_all_exitpolicy(tokens); + SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t, + if (router_add_exit_policy(router,t)<0) { + log_fn(LOG_WARN,"Error in exit policy"); goto err;} + ); + + if (!(tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE))) { + log_fn(LOG_WARN, "Missing router signature"); goto err; + } + if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) { + log_fn(LOG_WARN, "Bad object type or length on router signature"); + goto err; + } + if ((t=crypto_pk_public_checksig(router->identity_pkey, tok->object_body, + 128, signed_digest)) != 20) { + log_fn(LOG_WARN, "Invalid signature %d",t); goto err; + } + if (memcmp(digest, signed_digest, 20)) { + log_fn(LOG_WARN, "Mismatched signature"); goto err; + } + + if (!ports_set) { + log_fn(LOG_WARN,"No ports declared; failing."); goto err; + } + if (!bw_set) { + log_fn(LOG_WARN,"No bandwidth declared; failing."); goto err; + } + if(!router->or_port) { + log_fn(LOG_WARN,"or_port unreadable or 0. Failing."); + goto err; + } + if (!router->bandwidthrate) { + log_fn(LOG_WARN,"bandwidthrate unreadable or 0. Failing."); + goto err; + } + if (!router->platform) { + router->platform = tor_strdup(""); + } + + log_fn(LOG_DEBUG,"or_port %d, socks_port %d, dir_port %d, bandwidthrate %u, bandwidthburst %u.", + router->or_port, router->socks_port, router->dir_port, + (unsigned) router->bandwidthrate, (unsigned) router->bandwidthburst); + + + goto done; + return router; + + err: + routerinfo_free(router); + router = NULL; + done: + if (tokens) { + SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok)); + smartlist_free(tokens); + } + if (exit_policy_tokens) { + smartlist_free(exit_policy_tokens); + } + return router; +} + +/** Parse the exit policy in the string s and return it. + */ +struct exit_policy_t * +router_parse_exit_policy_from_string(const char *s) +{ + directory_token_t *tok = NULL; + const char *cp; + char *tmp; + struct exit_policy_t *r; + int len, idx; + + /* *s might not end with \n, so we need to extend it with one. */ + len = strlen(s); + cp = tmp = tor_malloc(len+2); + for (idx = 0; idx < len; ++idx) { + tmp[idx] = tolower(s[idx]); + } + tmp[len]='\n'; + tmp[len+1]='\0'; + tok = get_next_token(&cp, RTR_ONLY); + if (tok->tp == _ERR) { + log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error); + goto err; + } + if (tok->tp != K_ACCEPT && tok->tp != K_REJECT) { + log_fn(LOG_WARN, "Expected 'accept' or 'reject'."); + goto err; + } + + /* Now that we've gotten an exit policy, add it to the router. */ + r = router_parse_exit_policy(tok); + goto done; + err: + r = NULL; + done: + free(tmp); + token_free(tok); + return r; +} + +int router_add_exit_policy_from_string(routerinfo_t *router, const char *s) +{ + struct exit_policy_t *newe, *tmpe; + newe = router_parse_exit_policy_from_string(s); + if (!newe) + return -1; + for (tmpe = router->exit_policy; tmpe; tmpe=tmpe->next) + ; + tmpe->next = newe; + + return 0; +} + + +static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok) +{ + struct exit_policy_t *newe, **tmpe; + newe = router_parse_exit_policy(tok); + if (!newe) + return -1; + for (tmpe = &router->exit_policy; *tmpe; tmpe=&((*tmpe)->next)) + ; + *tmpe = newe; + + return 0; +} + +/** Given a K_ACCEPT or K_REJECT token and a router, create a new exit_policy_t + * corresponding to the token, and add it to router */ +static struct exit_policy_t * +router_parse_exit_policy(directory_token_t *tok) { + + struct exit_policy_t*newe; + struct in_addr in; + char *arg, *address, *mask, *port, *endptr; + int bits; + + tor_assert(tok->tp == K_REJECT || tok->tp == K_ACCEPT); + + if (tok->n_args != 1) + return NULL; + arg = tok->args[0]; + + newe = tor_malloc_zero(sizeof(struct exit_policy_t)); + + newe->string = tor_malloc(8+strlen(arg)); + if (tok->tp == K_REJECT) { + strcpy(newe->string, "reject "); + newe->policy_type = EXIT_POLICY_REJECT; + } else { + strcpy(newe->string, "accept "); + newe->policy_type = EXIT_POLICY_ACCEPT; + } + strcat(newe->string, arg); /* can't overflow */ + + address = arg; + mask = strchr(arg,'/'); + port = strchr(mask?mask:arg,':'); + /* Break 'arg' into separate strings. 'arg' was already strdup'd by + * _router_get_next_token, so it's safe to modify. + */ + if (mask) + *mask++ = 0; + if (port) + *port++ = 0; + + if (strcmp(address, "*") == 0) { + newe->addr = 0; + } else if (tor_inet_aton(address, &in) != 0) { + newe->addr = ntohl(in.s_addr); + } else { + log_fn(LOG_WARN, "Malformed IP %s in exit policy; rejecting.", + address); + goto policy_read_failed; + } + if (!mask) { + if (strcmp(address, "*") == 0) + newe->msk = 0; + else + newe->msk = 0xFFFFFFFFu; + } else { + endptr = NULL; + bits = (int) strtol(mask, &endptr, 10); + if (!*endptr) { + /* strtol handled the whole mask. */ + newe->msk = ~((1<<(32-bits))-1); + } else if (tor_inet_aton(mask, &in) != 0) { + newe->msk = ntohl(in.s_addr); + } else { + log_fn(LOG_WARN, "Malformed mask %s on exit policy; rejecting.", + mask); + goto policy_read_failed; + } + } + if (!port || strcmp(port, "*") == 0) { + newe->prt_min = 0; + newe->prt_max = 65535; + } else { + endptr = NULL; + newe->prt_min = (uint16_t) strtol(port, &endptr, 10); + if (*endptr == '-') { + port = endptr+1; + endptr = NULL; + newe->prt_max = (uint16_t) strtol(port, &endptr, 10); + if (*endptr) { + log_fn(LOG_WARN, "Malformed port %s on exit policy; rejecting.", + port); + } + } else if (*endptr) { + log_fn(LOG_WARN, "Malformed port %s on exit policy; rejecting.", + port); + goto policy_read_failed; + } else { + newe->prt_max = newe->prt_min; + } + } + + in.s_addr = htonl(newe->addr); + address = tor_strdup(inet_ntoa(in)); + in.s_addr = htonl(newe->msk); + log_fn(LOG_DEBUG,"%s %s/%s:%d-%d", + newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept", + address, inet_ntoa(in), newe->prt_min, newe->prt_max); + tor_free(address); + + newe->next = NULL; + return newe; + +policy_read_failed: + tor_assert(newe->string); + log_fn(LOG_WARN,"Couldn't parse line '%s'. Dropping", newe->string); + tor_free(newe->string); + free(newe); + return NULL; +} + +/* + * Low-level tokenizer for router descriptors and directories. + */ + + +/** Free all resources allocated for tok */ +static void +token_free(directory_token_t *tok) +{ + int i; + tor_assert(tok); + if (tok->args) { + for (i = 0; i < tok->n_args; ++i) { + tor_free(tok->args[i]); + } + tor_free(tok->args); + } + tor_free(tok->object_type); + tor_free(tok->object_body); + if (tok->key) + crypto_free_pk_env(tok->key); + tor_free(tok); +} + +/** Helper function: read the next token from *s, advance *s to the end + * of the token, and return the parsed token. If 'where' is DIR_ONLY + * or RTR_ONLY, reject all tokens of the wrong type. + */ +static directory_token_t * +get_next_token(const char **s, where_syntax where) { + const char *next, *obstart; + int i, done, allocated, is_opt; + directory_token_t *tok; + arg_syntax a_syn; + obj_syntax o_syn = NO_OBJ; + +#define RET_ERR(msg) \ + do { if (tok) token_free(tok); \ + tok = tor_malloc_zero(sizeof(directory_token_t));\ + tok->tp = _ERR; \ + tok->error = msg; \ + goto done_tokenizing; } while (0) + + tok = tor_malloc_zero(sizeof(directory_token_t)); + tok->tp = _ERR; + + *s = eat_whitespace(*s); + if (!**s) { + tok->tp = _EOF; + return tok; + } + next = find_whitespace(*s); + if (!next) { + tok->error = "Unexpected EOF"; return tok; + } + /* It's a keyword... but which one? */ + is_opt = !strncmp("opt", *s, next-*s); + if (is_opt) { + *s = eat_whitespace(next); + next = NULL; + if (**s) + next = find_whitespace(*s); + if (!**s || !next) { + RET_ERR("opt without keyword"); + } + } + for (i = 0; token_table[i].t ; ++i) { + if (!strncmp(token_table[i].t, *s, next-*s)) { + /* We've found the keyword. */ + tok->tp = token_table[i].v; + a_syn = token_table[i].s; + o_syn = token_table[i].os; + if (token_table[i].ws != ANY && token_table[i].ws != where) { + if (where == DIR_ONLY) { + RET_ERR("Found a router-only token in a directory section"); + } else { + RET_ERR("Found a directory-only token in a router descriptor"); + } + } + if (a_syn == ARGS) { + /* This keyword takes multiple arguments. */ + i = 0; + done = (*next == '\n'); + allocated = 32; + tok->args = tor_malloc(sizeof(char*)*32); + *s = eat_whitespace_no_nl(next); + while (**s != '\n' && !done) { + next = find_whitespace(*s); + if (*next == '\n') + done = 1; + if (i == allocated) { + allocated *= 2; + tok->args = tor_realloc(tok->args,sizeof(char*)*allocated); + } + tok->args[i++] = tor_strndup(*s,next-*s); + *s = eat_whitespace_no_nl(next+1); + } + tok->n_args = i; + } else if (a_syn == CONCAT_ARGS) { + /* The keyword takes the line as a single argument */ + *s = eat_whitespace_no_nl(next); + next = strchr(*s, '\n'); + if (!next) + RET_ERR("Unexpected EOF"); + tok->args = tor_malloc(sizeof(char*)); + tok->args[0] = tor_strndup(*s,next-*s); + tok->n_args = 1; + *s = eat_whitespace_no_nl(next+1); + } else { + /* The keyword takes no arguments. */ + tor_assert(a_syn == NO_ARGS); + *s = eat_whitespace_no_nl(next); + if (**s != '\n') { + RET_ERR("Unexpected arguments"); + } + tok->n_args = 0; + *s = eat_whitespace_no_nl(*s+1); + } + break; + } + } + if (tok->tp == _ERR) { + if (is_opt) { + tok->tp = K_OPT; + *s = eat_whitespace_no_nl(next); + next = strchr(*s,'\n'); + if (!next) + RET_ERR("Unexpected EOF"); + tok->args = tor_malloc(sizeof(char*)); + tok->args[0] = tor_strndup(*s,next-*s); + tok->n_args = 1; + *s = eat_whitespace_no_nl(next+1); + a_syn = OBJ_OK; + } else { + tok->tp = _UNRECOGNIZED; + next = strchr(*s, '\n'); + if (!next) { + RET_ERR("Unexpected EOF"); + } + tok->args = tor_malloc(sizeof(char*)); + tok->args[0] = tor_strndup(*s,next-*s); + tok->n_args = 1; + *s = next+1; + o_syn = OBJ_OK; + } + } + *s = eat_whitespace(*s); + if (strncmp(*s, "-----BEGIN ", 11)) { + goto done_tokenizing; + } + obstart = *s; + *s += 11; /* length of "-----BEGIN ". */ + next = strchr(*s, '\n'); + if (next-*s < 6 || strncmp(next-5, "-----\n", 6)) { + RET_ERR("Malformed object: bad begin line"); + } + tok->object_type = tor_strndup(*s, next-*s-5); + *s = next+1; + next = strstr(*s, "-----END "); + if (!next) { + RET_ERR("Malformed object: missing end line"); + } + if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { + if (strncmp(next, "-----END RSA PUBLIC KEY-----\n", 29)) + RET_ERR("Malformed object: mismatched end line"); + next = strchr(next,'\n')+1; + tok->key = crypto_new_pk_env(); + if (crypto_pk_read_public_key_from_string(tok->key, obstart, next-obstart)) + RET_ERR("Couldn't parse public key."); + *s = next; + } else { + tok->object_body = tor_malloc(next-*s); /* really, this is too much RAM. */ + i = base64_decode(tok->object_body, 256, *s, next-*s); + if (i<0) { + RET_ERR("Malformed object: bad base64-encoded data"); + } + tok->object_size = i; + *s = next + 9; /* length of "-----END ". */ + i = strlen(tok->object_type); + if (strncmp(*s, tok->object_type, i) || strncmp(*s+i, "-----\n", 6)) { + RET_ERR("Malformed object: mismatched end tag"); + } + *s += i+6; + } + switch(o_syn) + { + case NO_OBJ: + if (tok->object_body) + RET_ERR("Unexpected object for keyword"); + if (tok->key) + RET_ERR("Unexpected public key for keyword"); + break; + case NEED_OBJ: + if (!tok->object_body) + RET_ERR("Missing object for keyword"); + break; + case NEED_KEY: + if (!tok->key) + RET_ERR("Missing publid key for keyword"); + break; + case OBJ_OK: + break; + } + + done_tokenizing: + +#if 0 + for (i = 0; token_table[i].t ; ++i) { + if (token_table[i].v == tok->tp) { + fputs(token_table[i].t, stdout); + break; + i = -1; + } + } + if (i) { + if (tok->tp == _UNRECOGNIZED) fputs("UNRECOGNIZED", stdout); + if (tok->tp == _ERR) fputs("ERR",stdout); + if (tok->tp == _EOF) fputs("EOF",stdout); + if (tok->tp == _NIL) fputs("_NIL",stdout); + } + for(i = 0; i < tok->n_args; ++i) { + fprintf(stdout," \"%s\"", tok->args[i]); + } + if (tok->error) { fprintf(stdout," *%s*", tok->error); } + fputs("\n",stdout); +#endif + + + return tok; +#undef RET_ERR +} + +/** Read all tokens from a string between start and end, and add + * them to out. If is_dir is true, reject all non-directory + * tokens; else reject all non-routerdescriptor tokens. + */ +static int +tokenize_string(const char *start, const char *end, smartlist_t *out, + int is_dir) +{ + const char **s; + directory_token_t *tok = NULL; + where_syntax where = is_dir ? DIR_ONLY : RTR_ONLY; + s = &start; + while (*s < end && (!tok || tok->tp != _EOF)) { + tok = get_next_token(s, where); + if (tok->tp == _ERR) { + log_fn(LOG_WARN, "parse error: %s", tok->error); + return -1; + } + smartlist_add(out, tok); + *s = eat_whitespace(*s); + } + + return 0; +} + +/** Find the first token in s whose keyword is keyword; return + * NULL if no such keyword is found. + */ +static directory_token_t * +find_first_by_keyword(smartlist_t *s, directory_keyword keyword) +{ + SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t); + return NULL; +} + +/** Return a newly allocated smartlist of all accept or reject tokens in + * s. + */ +static smartlist_t * +find_all_exitpolicy(smartlist_t *s) +{ + smartlist_t *out = smartlist_create(); + SMARTLIST_FOREACH(s, directory_token_t *, t, + if (t->tp == K_ACCEPT || t->tp == K_REJECT) + smartlist_add(out,t)); + return out; +} + +/** Compute the SHA digest of the substring of s taken from the first + * occurrence of start_str through the first newline after the first + * subsequent occurrence of end_str; store the 20-byte result in + * digest; return 0 on success. + * + * If no such substring exists, return -1. + */ +static int router_get_hash_impl(const char *s, char *digest, + const char *start_str, + const char *end_str) +{ + char *start, *end; + start = strstr(s, start_str); + if (!start) { + log_fn(LOG_WARN,"couldn't find \"%s\"",start_str); + return -1; + } + end = strstr(start+strlen(start_str), end_str); + if (!end) { + log_fn(LOG_WARN,"couldn't find \"%s\"",end_str); + return -1; + } + end = strchr(end, '\n'); + if (!end) { + log_fn(LOG_WARN,"couldn't find EOL"); + return -1; + } + ++end; + + if (crypto_digest(start, end-start, digest)) { + log_fn(LOG_WARN,"couldn't compute digest"); + return -1; + } + + return 0; +} + +int tor_version_parse(const char *s, tor_version_t *out) +{ + char *eos=NULL, *cp=NULL; + /* Format is: + * NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ -cvs ] ] + */ + tor_assert(s && out); + memset(out, 0, sizeof(tor_version_t)); + + /* Get major. */ + out->major = strtol(s,&eos,10); + if (!eos || eos==s || *eos != '.') return -1; + cp = eos+1; + + /* Get minor */ + out->minor = strtol(cp,&eos,10); + if (!eos || eos==cp || *eos != '.') return -1; + cp = eos+1; + + /* Get micro */ + out->micro = strtol(cp,&eos,10); + if (!eos || eos==cp) return -1; + if (!*eos) { + out->status = VER_RELEASE; + out->patchlevel = 0; + out->cvs = IS_NOT_CVS; + return 0; + } + cp = eos; + + /* Get status */ + if (*cp == '.') { + out->status = VER_RELEASE; + ++cp; + } else if (0==strncmp(cp, "pre", 3)) { + out->status = VER_PRE; + cp += 3; + } else if (0==strncmp(cp, "rc", 2)) { + out->status = VER_RC; + cp += 2; + } else { + return -1; + } + + /* Get patchlevel */ + out->patchlevel = strtol(cp,&eos,10); + if (!eos || eos==cp) return -1; + cp = eos; + + /* Get cvs status. */ + if (!*eos) { + out->cvs = IS_NOT_CVS; + } else if (0==strcmp(cp, "-cvs")) { + out->cvs = IS_CVS; + } else { + return -1; + } + + return 0; +} + +/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a > + * b. */ +int tor_version_compare(tor_version_t *a, tor_version_t *b) +{ + int i; + tor_assert(a && b); + if ((i = a->major - b->major)) + return i; + else if ((i = a->minor - b->minor)) + return i; + else if ((i = a->micro - b->micro)) + return i; + else if ((i = a->status - b->status)) + return i; + else if ((i = a->patchlevel - b->patchlevel)) + return i; + else if ((i = a->cvs - b->cvs)) + return i; + else + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/test.c b/tags/tor-0_0_8pre1/src/or/test.c new file mode 100644 index 0000000000..f76e66d2d8 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/test.c @@ -0,0 +1,946 @@ +/* Copyright 2001,2002,2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef MS_WINDOWS +/* For mkdir() */ +#include +#endif + +#include "or.h" +#include "../common/test.h" + +extern or_options_t options; + +int have_failed = 0; + +/* These functions are file-local, but are exposed so we can test. */ +void add_fingerprint_to_dir(const char *nickname, const char *fp); +void get_platform_str(char *platform, int len); + +void +dump_hex(char *s, int len) +{ + static const char TABLE[] = "0123456789ABCDEF"; + unsigned char *d = s; + int i, j, nyb; + for(i=0;i=0;--j) { + nyb = (((int) d[i]) >> (j*4)) & 0x0f; + tor_assert(0<=nyb && nyb <=15); + putchar(TABLE[nyb]); + } + } +} + +void +setup_directory() { + char buf[256]; + int r; + sprintf(buf, "/tmp/tor_test"); +#ifdef _MSC_VER + r = mkdir(buf); +#else + r = mkdir(buf, 0700); +#endif + if (r && errno != EEXIST) + fprintf(stderr, "Can't create directory %s", buf); +} + +void +test_buffers() { +#define MAX_BUF_SIZE 1024*1024 + char str[256]; + char str2[256]; + + buf_t *buf; + buf_t *buf2; + + int s, i, j, eof; + + /**** + * buf_new + ****/ + if (!(buf = buf_new())) + test_fail(); + + test_eq(buf_capacity(buf), 512*1024); + test_eq(buf_datalen(buf), 0); + + /**** + * read_to_buf + ****/ + s = open("/tmp/tor_test/data", O_WRONLY|O_CREAT|O_TRUNC, 0600); + for (j=0;j<256;++j) { + str[j] = (char)j; + } + write(s, str, 256); + close(s); + + s = open("/tmp/tor_test/data", O_RDONLY, 0); + eof = 0; + i = read_to_buf(s, 10, buf, &eof); + test_eq(buf_capacity(buf), 512*1024); + test_eq(buf_datalen(buf), 10); + test_eq(eof, 0); + test_eq(i, 10); + test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10); + + /* Test reading 0 bytes. */ + i = read_to_buf(s, 0, buf, &eof); + test_eq(buf_capacity(buf), 512*1024); + test_eq(buf_datalen(buf), 10); + test_eq(eof, 0); + test_eq(i, 0); + + /* Now test when buffer is filled exactly. */ + buf2 = buf_new_with_capacity(6); + i = read_to_buf(s, 6, buf2, &eof); + test_eq(buf_capacity(buf2), 6); + test_eq(buf_datalen(buf2), 6); + test_eq(eof, 0); + test_eq(i, 6); + test_memeq(str+10, (char*)_buf_peek_raw_buffer(buf2), 6); + buf_free(buf2); + + /* Now test when buffer is filled with more data to read. */ + buf2 = buf_new_with_capacity(32); + i = read_to_buf(s, 128, buf2, &eof); + test_eq(buf_capacity(buf2), 128); + test_eq(buf_datalen(buf2), 32); + test_eq(eof, 0); + test_eq(i, 32); + buf_free(buf2); + + /* Now read to eof. */ + test_assert(buf_capacity(buf) > 256); + i = read_to_buf(s, 1024, buf, &eof); + test_eq(i, (256-32-10-6)); + test_eq(buf_capacity(buf), MAX_BUF_SIZE); + test_eq(buf_datalen(buf), 256-6-32); + test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10); /* XXX Check rest. */ + test_eq(eof, 0); + + i = read_to_buf(s, 1024, buf, &eof); + test_eq(i, 0); + test_eq(buf_capacity(buf), MAX_BUF_SIZE); + test_eq(buf_datalen(buf), 256-6-32); + test_eq(eof, 1); + + close(s); + + /**** + * fetch_from_buf + ****/ + memset(str2, 255, 256); + test_eq(246, fetch_from_buf(str2, 10, buf)); + test_memeq(str2, str, 10); + test_memeq(str+10,(char*)_buf_peek_raw_buffer(buf),246); + test_eq(buf_datalen(buf),246); + + test_eq(0, fetch_from_buf(str2, 246, buf)); + test_memeq(str2, str+10, 246); + test_eq(buf_capacity(buf),MAX_BUF_SIZE); + test_eq(buf_datalen(buf),0); + + /**** + * write_to_buf + ****/ + memset((char *)_buf_peek_raw_buffer(buf), (int)'-', 256); + i = write_to_buf("Hello world", 11, buf); + test_eq(i, 11); + test_eq(buf_datalen(buf), 11); + test_memeq((char*)_buf_peek_raw_buffer(buf), "Hello world", 11); + i = write_to_buf("XYZZY", 5, buf); + test_eq(i, 16); + test_eq(buf_datalen(buf), 16); + test_memeq((char*)_buf_peek_raw_buffer(buf), "Hello worldXYZZY", 16); + /* Test when buffer is overfull. */ +#if 0 + buflen = 18; + test_eq(-1, write_to_buf("This string will not fit.", 25, + &buf, &buflen, &buf_datalen)); + test_eq(buf_datalen, 16); + test_memeq(buf, "Hello worldXYZZY--", 18); + buflen = MAX_BUF_SIZE; +#endif + + /**** + * flush_buf + ****/ + /* XXXX Needs tests. */ + + buf_free(buf); +} + +void +test_crypto_dh() +{ + crypto_dh_env_t *dh1, *dh2; + char p1[DH_BYTES]; + char p2[DH_BYTES]; + char s1[DH_BYTES]; + char s2[DH_BYTES]; + int s1len, s2len; + + dh1 = crypto_dh_new(); + dh2 = crypto_dh_new(); + test_eq(crypto_dh_get_bytes(dh1), DH_BYTES); + test_eq(crypto_dh_get_bytes(dh2), DH_BYTES); + + memset(p1, 0, DH_BYTES); + memset(p2, 0, DH_BYTES); + test_memeq(p1, p2, DH_BYTES); + test_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES)); + test_memneq(p1, p2, DH_BYTES); + test_assert(! crypto_dh_get_public(dh2, p2, DH_BYTES)); + test_memneq(p1, p2, DH_BYTES); + + memset(s1, 0, DH_BYTES); + memset(s2, 0xFF, DH_BYTES); + s1len = crypto_dh_compute_secret(dh1, p2, DH_BYTES, s1, 50); + s2len = crypto_dh_compute_secret(dh2, p1, DH_BYTES, s2, 50); + test_assert(s1len > 0); + test_eq(s1len, s2len); + test_memeq(s1, s2, s1len); + + crypto_dh_free(dh1); + crypto_dh_free(dh2); +} + +void +test_crypto() +{ + crypto_cipher_env_t *env1, *env2; + crypto_pk_env_t *pk1, *pk2; + char *data1, *data2, *data3, *cp; + int i, j, p, len; + + data1 = tor_malloc(1024); + data2 = tor_malloc(1024); + data3 = tor_malloc(1024); + test_assert(data1 && data2 && data3); + + /* Try out RNG. */ + test_assert(! crypto_seed_rng()); + crypto_rand(100, data1); + crypto_rand(100, data2); + test_memneq(data1,data2,100); + +#if 0 + /* Try out identity ciphers. */ + env1 = crypto_new_cipher_env(CRYPTO_CIPHER_IDENTITY); + test_neq(env1, 0); + test_eq(crypto_cipher_generate_key(env1), 0); + test_eq(crypto_cipher_encrypt_init_cipher(env1), 0); + for(i = 0; i < 1024; ++i) { + data1[i] = (char) i*73; + } + crypto_cipher_encrypt(env1, data1, 1024, data2); + test_memeq(data1, data2, 1024); + crypto_free_cipher_env(env1); +#endif + + /* Now, test encryption and decryption with stream cipher. */ + data1[0]='\0'; + for(i = 1023; i>0; i -= 35) + strncat(data1, "Now is the time for all good onions", i); + + memset(data2, 0, 1024); + memset(data3, 0, 1024); + env1 = crypto_new_cipher_env(); + test_neq(env1, 0); + env2 = crypto_new_cipher_env(); + test_neq(env2, 0); + j = crypto_cipher_generate_key(env1); + crypto_cipher_set_key(env2, crypto_cipher_get_key(env1)); + crypto_cipher_encrypt_init_cipher(env1); + crypto_cipher_decrypt_init_cipher(env2); + + /* Try encrypting 512 chars. */ + crypto_cipher_encrypt(env1, data1, 512, data2); + crypto_cipher_decrypt(env2, data2, 512, data3); + test_memeq(data1, data3, 512); + test_memneq(data1, data2, 512); + + /* Now encrypt 1 at a time, and get 1 at a time. */ + for (j = 512; j < 560; ++j) { + crypto_cipher_encrypt(env1, data1+j, 1, data2+j); + } + for (j = 512; j < 560; ++j) { + crypto_cipher_decrypt(env2, data2+j, 1, data3+j); + } + test_memeq(data1, data3, 560); + /* Now encrypt 3 at a time, and get 5 at a time. */ + for (j = 560; j < 1024-5; j += 3) { + crypto_cipher_encrypt(env1, data1+j, 3, data2+j); + } + for (j = 560; j < 1024-5; j += 5) { + crypto_cipher_decrypt(env2, data2+j, 5, data3+j); + } + test_memeq(data1, data3, 1024-5); + /* Now make sure that when we encrypt with different chunk sizes, we get + the same results. */ + crypto_free_cipher_env(env2); + + memset(data3, 0, 1024); + env2 = crypto_new_cipher_env(); + test_neq(env2, 0); + crypto_cipher_set_key(env2, crypto_cipher_get_key(env1)); + crypto_cipher_encrypt_init_cipher(env2); + for (j = 0; j < 1024-16; j += 17) { + crypto_cipher_encrypt(env2, data1+j, 17, data3+j); + } + for (j= 0; j < 1024-16; ++j) { + if (data2[j] != data3[j]) { + printf("%d: %d\t%d\n", j, (int) data2[j], (int) data3[j]); + } + } + test_memeq(data2, data3, 1024-16); + crypto_free_cipher_env(env1); + crypto_free_cipher_env(env2); + + /* Test vectors for stream ciphers. */ + /* XXXX Look up some test vectors for the ciphers and make sure we match. */ + + /* Test SHA-1 with a test vector from the specification. */ + i = crypto_digest("abc", 3, data1); + test_memeq(data1, + "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78" + "\x50\xC2\x6C\x9C\xD0\xD8\x9D", 20); + + /* Public-key ciphers */ + pk1 = crypto_new_pk_env(); + pk2 = crypto_new_pk_env(); + test_assert(pk1 && pk2); + test_assert(! crypto_pk_generate_key(pk1)); + test_assert(! crypto_pk_write_public_key_to_string(pk1, &cp, &i)); + test_assert(! crypto_pk_read_public_key_from_string(pk2, cp, i)); + test_eq(0, crypto_pk_cmp_keys(pk1, pk2)); + + test_eq(128, crypto_pk_keysize(pk1)); + test_eq(128, crypto_pk_keysize(pk2)); + + test_eq(128, crypto_pk_public_encrypt(pk2, "Hello whirled.", 15, data1, + PK_PKCS1_OAEP_PADDING)); + test_eq(128, crypto_pk_public_encrypt(pk1, "Hello whirled.", 15, data2, + PK_PKCS1_OAEP_PADDING)); + /* oaep padding should make encryption not match */ + test_memneq(data1, data2, 128); + test_eq(15, crypto_pk_private_decrypt(pk1, data1, 128, data3, + PK_PKCS1_OAEP_PADDING,1)); + test_streq(data3, "Hello whirled."); + memset(data3, 0, 1024); + test_eq(15, crypto_pk_private_decrypt(pk1, data2, 128, data3, + PK_PKCS1_OAEP_PADDING,1)); + test_streq(data3, "Hello whirled."); + /* Can't decrypt with public key. */ + test_eq(-1, crypto_pk_private_decrypt(pk2, data2, 128, data3, + PK_PKCS1_OAEP_PADDING,1)); + /* Try again with bad padding */ + memcpy(data2+1, "XYZZY", 5); /* This has fails ~ once-in-2^40 */ + test_eq(-1, crypto_pk_private_decrypt(pk1, data2, 128, data3, + PK_PKCS1_OAEP_PADDING,1)); + + /* File operations: save and load private key */ + test_assert(! crypto_pk_write_private_key_to_filename(pk1, + "/tmp/tor_test/pke1y")); + + test_assert(! crypto_pk_read_private_key_from_filename(pk2, + "/tmp/tor_test/pke1y")); + test_eq(15, crypto_pk_private_decrypt(pk2, data1, 128, data3, + PK_PKCS1_OAEP_PADDING,1)); + + /* Now try signing. */ + strcpy(data1, "Ossifrage"); + test_eq(128, crypto_pk_private_sign(pk1, data1, 10, data2)); + test_eq(10, crypto_pk_public_checksig(pk1, data2, 128, data3)); + test_streq(data3, "Ossifrage"); + /* Try signing digests. */ + test_eq(128, crypto_pk_private_sign_digest(pk1, data1, 10, data2)); + test_eq(20, crypto_pk_public_checksig(pk1, data2, 128, data3)); + test_eq(0, crypto_pk_public_checksig_digest(pk1, data1, 10, data2, 128)); + test_eq(-1, crypto_pk_public_checksig_digest(pk1, data1, 11, data2, 128)); + /*XXXX test failed signing*/ + + /* Try encoding */ + crypto_free_pk_env(pk2); + pk2 = NULL; + i = crypto_pk_asn1_encode(pk1, data1, 1024); + test_assert(i>0); + pk2 = crypto_pk_asn1_decode(data1, i); + test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0); + + /* Try with hybrid encryption wrappers. */ + crypto_rand(1024, data1); + for (i = 0; i < 3; ++i) { + for (j = 85; j < 140; ++j) { + memset(data2,0,1024); + memset(data3,0,1024); + if (i == 0 && j < 129) + continue; + p = (i==0)?PK_NO_PADDING: + (i==1)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING; + len = crypto_pk_public_hybrid_encrypt(pk1,data1,j,data2,p,0); + test_assert(len>=0); + len = crypto_pk_private_hybrid_decrypt(pk1,data2,len,data3,p,1); + test_eq(len,j); + test_memeq(data1,data3,j); + } + } + crypto_free_pk_env(pk1); + crypto_free_pk_env(pk2); + + /* Base64 tests */ + strcpy(data1, "Test string that contains 35 chars."); + strcat(data1, " 2nd string that contains 35 chars."); + + i = base64_encode(data2, 1024, data1, 71); + j = base64_decode(data3, 1024, data2, i); + test_streq(data3, data1); + test_eq(j, 71); + test_assert(data2[i] == '\0'); + + /* Base32 tests */ + strcpy(data1, "5chrs"); + /* bit pattern is: [35 63 68 72 73] -> + * [00110101 01100011 01101000 01110010 01110011] + * By 5s: [00110 10101 10001 10110 10000 11100 10011 10011] + */ + base32_encode(data2, 9, data1, 5); + test_streq(data2, "gvrwq4tt"); + + strcpy(data1, "\xFF\xF5\x6D\x44\xAE\x0D\x5C\xC9\x62\xC4"); + base32_encode(data2, 30, data1, 10); + test_streq(data2, "772w2rfobvomsywe"); + + /* Base16 tests */ + strcpy(data1, "6chrs\xff"); + base16_encode(data2, 13, data1, 6); + test_streq(data2, "3663687273FF"); + + strcpy(data1, "f0d678affc000100"); + i = base16_decode(data2, 8, data1, 16); + test_eq(i,0); + test_memeq(data2, "\xf0\xd6\x78\xaf\xfc\x00\x01\x00",8); + + free(data1); + free(data2); + free(data3); +} + +void +test_util() { + struct timeval start, end; + struct tm a_time; + smartlist_t *sl; + + start.tv_sec = 5; + start.tv_usec = 5000; + + end.tv_sec = 5; + end.tv_usec = 5000; + + test_eq(0L, tv_udiff(&start, &end)); + + end.tv_usec = 7000; + + test_eq(2000L, tv_udiff(&start, &end)); + + end.tv_sec = 6; + + test_eq(1002000L, tv_udiff(&start, &end)); + + end.tv_usec = 0; + + test_eq(995000L, tv_udiff(&start, &end)); + + end.tv_sec = 4; + + test_eq(0L, tv_udiff(&start, &end)); + + /* The test values here are confirmed to be correct on a platform + * with a working timegm. */ + a_time.tm_year = 2003-1900; + a_time.tm_mon = 7; + a_time.tm_mday = 30; + a_time.tm_hour = 6; + a_time.tm_min = 14; + a_time.tm_sec = 55; + test_eq((time_t) 1062224095UL, tor_timegm(&a_time)); + a_time.tm_year = 2004-1900; /* Try a leap year, after feb. */ + test_eq((time_t) 1093846495UL, tor_timegm(&a_time)); + a_time.tm_mon = 1; /* Try a leap year, in feb. */ + a_time.tm_mday = 10; + test_eq((time_t) 1076393695UL, tor_timegm(&a_time)); + + + /* Test smartlist */ + sl = smartlist_create(); + smartlist_add(sl, (void*)1); + smartlist_add(sl, (void*)2); + smartlist_add(sl, (void*)3); + smartlist_add(sl, (void*)4); + test_eq((void*)2, smartlist_del_keeporder(sl, 1)); + smartlist_insert(sl, 1, (void*)22); + smartlist_insert(sl, 0, (void*)0); + smartlist_insert(sl, 5, (void*)555); + test_eq((void*)0, smartlist_get(sl,0)); + test_eq((void*)1, smartlist_get(sl,1)); + test_eq((void*)22, smartlist_get(sl,2)); + test_eq((void*)3, smartlist_get(sl,3)); + test_eq((void*)4, smartlist_get(sl,4)); + test_eq((void*)555, smartlist_get(sl,5)); + /* XXXX test older functions. */ + smartlist_free(sl); +} + +static void* _squareAndRemoveK4(const char *key, void*val, void *data) +{ + int *ip = (int*)data; + intptr_t v; + if (strcmp(key,"K4") == 0) { + ++(*ip); + return NULL; + } + v = (intptr_t)val; + return (void*)(v*v); +} + +void test_strmap() { + strmap_t *map; + strmap_iter_t *iter; + const char *k; + void *v; + int count; + + map = strmap_new(); + v = strmap_set(map, "K1", (void*)99); + test_eq(v, NULL); + v = strmap_set(map, "K2", (void*)101); + test_eq(v, NULL); + v = strmap_set(map, "K1", (void*)100); + test_eq(v, (void*)99); + test_eq(strmap_get(map,"K1"), (void*)100); + test_eq(strmap_get(map,"K2"), (void*)101); + test_eq(strmap_get(map,"K-not-there"), NULL); + + v = strmap_remove(map,"K2"); + test_eq(v, (void*)101); + test_eq(strmap_get(map,"K2"), NULL); + test_eq(strmap_remove(map,"K2"), NULL); + + strmap_set(map, "K2", (void*)101); + strmap_set(map, "K3", (void*)102); + strmap_set(map, "K4", (void*)103); + strmap_set(map, "K5", (void*)104); + strmap_set(map, "K6", (void*)105); + + count = 0; + strmap_foreach(map, _squareAndRemoveK4, &count); + test_eq(count, 1); + test_eq(strmap_get(map, "K4"), NULL); + test_eq(strmap_get(map, "K1"), (void*)10000); + test_eq(strmap_get(map, "K6"), (void*)11025); + + iter = strmap_iter_init(map); + strmap_iter_get(iter,&k,&v); + test_streq(k, "K1"); + test_eq(v, (void*)10000); + iter = strmap_iter_next(map,iter); + strmap_iter_get(iter,&k,&v); + test_streq(k, "K2"); + test_eq(v, (void*)10201); + iter = strmap_iter_next_rmv(map,iter); + strmap_iter_get(iter,&k,&v); + test_streq(k, "K3"); + test_eq(v, (void*)10404); + iter = strmap_iter_next(map,iter); /* K5 */ + test_assert(!strmap_iter_done(iter)); + iter = strmap_iter_next(map,iter); /* K6 */ + test_assert(!strmap_iter_done(iter)); + iter = strmap_iter_next(map,iter); /* done */ + test_assert(strmap_iter_done(iter)); + + /* Make sure we removed K2, but not the others. */ + test_eq(strmap_get(map, "K2"), NULL); + test_eq(strmap_get(map, "K5"), (void*)10816); + + /* Clean up after ourselves. */ + strmap_free(map, NULL); + + /* Now try some lc functions. */ + map = strmap_new(); + strmap_set_lc(map,"Ab.C", (void*)1); + test_eq(strmap_get(map,"ab.c"), (void*)1); + test_eq(strmap_get_lc(map,"AB.C"), (void*)1); + test_eq(strmap_get(map,"AB.C"), NULL); + test_eq(strmap_remove_lc(map,"aB.C"), (void*)1); + test_eq(strmap_get_lc(map,"AB.C"), NULL); + strmap_free(map,NULL); +} + +void test_onion() { +#if 0 + char **names; + int i,num; + + names = parse_nickname_list(" foo bar\t baz quux ", &num); + test_eq(num,4); + test_streq(names[0],"foo"); + test_streq(names[1],"bar"); + test_streq(names[2],"baz"); + test_streq(names[3],"quux"); + for(i=0;i0); + + strcpy(buf2, "router Magri testaddr1.foo.bar 9000 9002 9003\n" + "platform Tor "VERSION" on "); + strcat(buf2, get_uname()); + strcat(buf2, "\n" + "published 1970-01-01 00:00:00\n" + "opt fingerprint "); + crypto_pk_get_fingerprint(pk2, fingerprint); + strcat(buf2, fingerprint); + strcat(buf2, "\nopt uptime 0\n" + /* XXX the "0" above is hardcoded, but even if we made it reflect + * uptime, that still wouldn't make it right, because the two + * descriptors might be made on different seconds... hm. */ + "bandwidth 1000 5000 10000\n" + "onion-key\n"); + strcat(buf2, pk1_str); + strcat(buf2, "signing-key\n"); + strcat(buf2, pk2_str); + strcat(buf2, "router-signature\n"); + buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same twice*/ + + test_streq(buf, buf2); + + test_assert(router_dump_router_to_string(buf, 2048, &r1, pk2)>0); + cp = buf; + rp1 = router_parse_entry_from_string((const char*)cp,NULL); + test_assert(rp1); + test_streq(rp1->address, r1.address); + test_eq(rp1->or_port, r1.or_port); + test_eq(rp1->socks_port, r1.socks_port); + test_eq(rp1->dir_port, r1.dir_port); + test_eq(rp1->bandwidthrate, r1.bandwidthrate); + test_eq(rp1->bandwidthburst, r1.bandwidthburst); + test_eq(rp1->advertisedbandwidth, r1.advertisedbandwidth); + test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0); + test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0); + test_assert(rp1->exit_policy == NULL); + +#if 0 + /* XXX Once we have exit policies, test this again. XXX */ + strcpy(buf2, "router tor.tor.tor 9005 0 0 3000\n"); + strcat(buf2, pk2_str); + strcat(buf2, "signing-key\n"); + strcat(buf2, pk1_str); + strcat(buf2, "accept *:80\nreject 18.*:24\n\n"); + test_assert(router_dump_router_to_string(buf, 2048, &r2, pk2)>0); + test_streq(buf, buf2); + + cp = buf; + rp2 = router_parse_entry_from_string(&cp); + test_assert(rp2); + test_streq(rp2->address, r2.address); + test_eq(rp2->or_port, r2.or_port); + test_eq(rp2->socks_port, r2.socks_port); + test_eq(rp2->dir_port, r2.dir_port); + test_eq(rp2->bandwidth, r2.bandwidth); + test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0); + test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0); + test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT); + test_streq(rp2->exit_policy->string, "accept *:80"); + test_streq(rp2->exit_policy->address, "*"); + test_streq(rp2->exit_policy->port, "80"); + test_eq(rp2->exit_policy->next->policy_type, EXIT_POLICY_REJECT); + test_streq(rp2->exit_policy->next->string, "reject 18.*:24"); + test_streq(rp2->exit_policy->next->address, "18.*"); + test_streq(rp2->exit_policy->next->port, "24"); + test_assert(rp2->exit_policy->next->next == NULL); +#endif + + /* Okay, now for the directories. */ + crypto_pk_get_fingerprint(pk2, buf); + add_fingerprint_to_dir("Magri", buf); + crypto_pk_get_fingerprint(pk1, buf); + add_fingerprint_to_dir("Fred", buf); + /* Make sure routers aren't too far in the past any more. */ + r1.published_on = time(NULL); + r2.published_on = time(NULL)-3*60*60; + test_assert(router_dump_router_to_string(buf, 2048, &r1, pk2)>0); + cp = buf; + test_eq(dirserv_add_descriptor((const char**)&cp), 1); + test_assert(router_dump_router_to_string(buf, 2048, &r2, pk1)>0); + cp = buf; + test_eq(dirserv_add_descriptor((const char**)&cp), 1); + options.Nickname = "DirServer"; + test_assert(!dirserv_dump_directory_to_string(buf,8192,pk3)); + cp = buf; + test_assert(!router_parse_routerlist_from_directory(buf, &dir1, pk3)); + test_eq(2, smartlist_len(dir1->routers)); + dirserv_free_fingerprint_list(); + + tor_free(pk1_str); + tor_free(pk2_str); + if (pk1) crypto_free_pk_env(pk1); + if (pk2) crypto_free_pk_env(pk2); + if (rp1) routerinfo_free(rp1); + if (rp2) routerinfo_free(rp2); + tor_free(dir1); /* XXXX And more !*/ + tor_free(dir2); /* And more !*/ + + /* Try out version parsing functionality */ + test_eq(0, tor_version_parse("0.3.4pre2-cvs", &ver1)); + test_eq(0, ver1.major); + test_eq(3, ver1.minor); + test_eq(4, ver1.micro); + test_eq(VER_PRE, ver1.status); + test_eq(2, ver1.patchlevel); + test_eq(IS_CVS, ver1.cvs); + test_eq(0, tor_version_parse("0.3.4rc1", &ver1)); + test_eq(0, ver1.major); + test_eq(3, ver1.minor); + test_eq(4, ver1.micro); + test_eq(VER_RC, ver1.status); + test_eq(1, ver1.patchlevel); + test_eq(IS_NOT_CVS, ver1.cvs); + test_eq(0, tor_version_parse("1.3.4", &ver1)); + test_eq(1, ver1.major); + test_eq(3, ver1.minor); + test_eq(4, ver1.micro); + test_eq(VER_RELEASE, ver1.status); + test_eq(0, ver1.patchlevel); + test_eq(IS_NOT_CVS, ver1.cvs); + test_eq(0, tor_version_parse("1.3.4.999", &ver1)); + test_eq(1, ver1.major); + test_eq(3, ver1.minor); + test_eq(4, ver1.micro); + test_eq(VER_RELEASE, ver1.status); + test_eq(999, ver1.patchlevel); + test_eq(IS_NOT_CVS, ver1.cvs); + + /* make sure is_obsolete_version() works */ + test_eq(1, is_obsolete_version("0.0.1", "Tor 0.0.2")); + test_eq(1, is_obsolete_version("0.0.1", "0.0.2, Tor 0.0.3")); + test_eq(1, is_obsolete_version("0.0.1", "0.0.2,Tor 0.0.3")); + test_eq(1, is_obsolete_version("0.0.1", "0.0.3,BetterTor 0.0.1")); + test_eq(0, is_obsolete_version("0.0.2", "Tor 0.0.2,Tor 0.0.3")); + test_eq(1, is_obsolete_version("0.0.2", "Tor 0.0.2pre1,Tor 0.0.3")); + test_eq(0, is_obsolete_version("0.1.0", "Tor 0.0.2,Tor 0.0.3")); + test_eq(0, is_obsolete_version("0.0.7rc2", "0.0.7,Tor 0.0.7rc2,Tor 0.0.8")); + test_eq(0, is_obsolete_version("0.0.5", "0.0.5-cvs")); + test_eq(0, is_obsolete_version("0.0.5.1-cvs", "0.0.5")); +} + +void test_rend_fns() +{ + char address1[] = "fooaddress.onion"; + char address2[] = "aaaaaaaaaaaaaaaa.onion"; + rend_service_descriptor_t *d1, *d2; + char *encoded; + int len; + crypto_pk_env_t *pk1; + time_t now; + pk1 = crypto_new_pk_env(); + + test_assert(!crypto_pk_generate_key(pk1)); + d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t)); + d1->pk = pk1; + now = time(NULL); + d1->timestamp = now; + d1->n_intro_points = 3; + d1->intro_points = tor_malloc(sizeof(char*)*3); + d1->intro_points[0] = tor_strdup("tom"); + d1->intro_points[1] = tor_strdup("crow"); + d1->intro_points[2] = tor_strdup("joel"); + test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len)); + d2 = rend_parse_service_descriptor(encoded, len); + test_assert(d2); + + test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk)); + test_eq(d2->timestamp, now); + test_eq(d2->n_intro_points, 3); + test_streq(d2->intro_points[0], "tom"); + test_streq(d2->intro_points[1], "crow"); + test_streq(d2->intro_points[2], "joel"); + + test_eq(-1, rend_parse_rendezvous_address(address1)); + test_eq( 0, rend_parse_rendezvous_address(address2)); + + rend_service_descriptor_free(d1); + rend_service_descriptor_free(d2); +} + +int +main(int c, char**v){ +#if 0 + or_options_t options; /* command-line and config-file options */ + + if(getconfig(c,v,&options)) + exit(1); +#endif + + crypto_seed_rng(); + + setup_directory(); +// puts("========================== Buffers ========================="); +// test_buffers(); + puts("\n========================== Crypto =========================="); + test_crypto(); + test_crypto_dh(); + puts("\n========================= Util ============================"); + test_util(); + test_strmap(); + puts("\n========================= Onion Skins ====================="); + test_onion(); + test_onion_handshake(); + puts("\n========================= Directory Formats ==============="); + /* add_stream_log(LOG_DEBUG, NULL, stdout); */ + test_dir_format(); + puts("\n========================= Rendezvous functionality ========"); + test_rend_fns(); + puts(""); + + if (have_failed) + return 1; + else + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/tor_main.c b/tags/tor-0_0_8pre1/src/or/tor_main.c new file mode 100644 index 0000000000..59af33a58a --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/tor_main.c @@ -0,0 +1,26 @@ +/* Copyright 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file tor_main.c + * \brief Entry point for tor binary. + **/ + +int tor_main(int argc, char *argv[]); + +/** We keep main() in a separate file so that our unit tests can use + * functions from main.c) + */ +int main(int argc, char *argv[]) +{ + return tor_main(argc, argv); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_8pre1/src/or/tree.h b/tags/tor-0_0_8pre1/src/or/tree.h new file mode 100644 index 0000000000..7b4f6a0eea --- /dev/null +++ b/tags/tor-0_0_8pre1/src/or/tree.h @@ -0,0 +1,677 @@ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-back tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ +void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +struct type *name##_RB_REMOVE(struct name *, struct type *); \ +struct type *name##_RB_INSERT(struct name *, struct type *); \ +struct type *name##_RB_FIND(struct name *, struct type *); \ +struct type *name##_RB_NEXT(struct name *, struct type *); \ +struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ +void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +struct type * \ +name##_RB_NEXT(struct name *head, struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(x, y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(head, x)) + +#endif /* _SYS_TREE_H_ */ diff --git a/tags/tor-0_0_8pre1/src/win32/orconfig.h b/tags/tor-0_0_8pre1/src/win32/orconfig.h new file mode 100644 index 0000000000..0594cfab33 --- /dev/null +++ b/tags/tor-0_0_8pre1/src/win32/orconfig.h @@ -0,0 +1,196 @@ +/* $Id */ +/* orconfig.h for Windows -- This file is *not* generated by autoconf. + * Instead, it has to be hand-edited to keep Win32 happy. + */ + +/* Windows-only defines. */ +#define MS_WINDOWS +#define MS_WIN32 +#define CONFDIR "" + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the header file. */ +#undef HAVE_GRP_H + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MACHINE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SIGNAL_H + +/* Define to 1 if you have the `socketpair' function. */ +#undef HAVE_SOCKETPAIR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H + +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strptime' function. */ +#undef HAVE_STRPTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#define PACKAGE "tor" + +/* Def +ine to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of a `char', as computed by sizeof. */ +#define SIZEOF_CHAR 1 + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `int16_t', as computed by sizeof. */ +#undef SIZEOF_INT16_T + +/* The size of a `int32_t', as computed by sizeof. */ +#undef SIZEOF_INT32_T + +/* The size of a `int64_t', as computed by sizeof. */ +#undef SIZEOF_INT64_T + +/* The size of a `int8_t', as computed by sizeof. */ +#undef SIZEOF_INT8_T + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `long long', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG + +/* The size of a `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of a `uint16_t', as computed by sizeof. */ +#undef SIZEOF_UINT16_T + +/* The size of a `uint32_t', as computed by sizeof. */ +#undef SIZEOF_UINT32_T + +/* The size of a `uint64_t', as computed by sizeof. */ +#undef SIZEOF_UINT64_T + +/* The size of a `uint8_t', as computed by sizeof. */ +#undef SIZEOF_UINT8_T + +/* The size of a `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 4 + +/* The size of a `__int64', as computed by sizeof. */ +#define SIZEOF___INT64 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS + +/* Define to 1 iff unaligned int access is allowed */ +#define UNALIGNED_INT_ACCESS_OK + +/* Version number of package */ +#define VERSION "0.0.7"