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 0000000000..1a95b8777a Binary files /dev/null and b/tags/tor-0_0_8pre1/doc/cell-struct.pdf differ diff --git a/tags/tor-0_0_8pre1/doc/cell-struct.png b/tags/tor-0_0_8pre1/doc/cell-struct.png new file mode 100644 index 0000000000..c0afa1c4a1 Binary files /dev/null and b/tags/tor-0_0_8pre1/doc/cell-struct.png differ diff --git a/tags/tor-0_0_8pre1/doc/codecon04.mgp b/tags/tor-0_0_8pre1/doc/codecon04.mgp new file mode 100644 index 0000000000..e9815fcb37 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/codecon04.mgp @@ -0,0 +1,357 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%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: 20 nodes, hundreds (?) of users + +Many improvements on earlier design + +Free software -- modified BSD license + +Design is not covered by earlier onion routing +patent + +Uses SOCKS to interface with client apps + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +We have working code + +(14 kloc of C) + +and a design document, +and a byte-level specification, +and a Debian package (in Unstable) + +Works on Linux, BSD, OSX, Cygwin, ... +User-space, doesn't need kernel mods or root + +%size 9 +http://freehaven.net/tor/ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%page +%% +%%Talk Overview +%% +%%A bit about Onion Routing +%% +%%Improvements we've made +%% +%%Some related work +%% +%%Ask me questions +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Anonymity: Who needs it? + +Private citizens + advocacy, counseling, whistleblowing, reporting, ... +%size 6 +Higher-level protocols + voting, e-cash, auctions +%size 6 +Government applications + research, law enforcement +%size 6 +Business applications +%size 5 +(hide relationships and volumes of communication) + Who is visiting job sites? + Which groups are talking to patent lawyers? + Who are your suppliers and customers? + Is the CEO talking to a buyout partner? + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Anonymity is a network effect + + Systems need traffic (many low-sensitivity users) to attract the high-sensitivity users + Most users do not value anonymity much + Weak security (fast system) can mean more users + which can mean +%cont, font "italic" +stronger +%cont, font "standard" +anonymity + High-sensitivity agents have incentive to run nodes + so they can be certain first node in their path is good + to attract traffic for their messages + There can be an optimal level of free-riding + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Onion Routing is... + +An overlay network + +Users build virtual circuits through the network + +One layer of encryption at each hop + +Fixed-size cells + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Tor's goals + +Conservative design + minimize new design work needed + +%size 6 +Support testing of future research + +Design for deployment; deploy for use + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Threat model -- what we aim for + +Protect against somebody watching Alice + +Protect against curious Bob + +Protect against `some' curious nodes in the middle + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Differences / limitations + + +We're TCP-only, not all IP (but we're user-space and very portable) + +Not as strong as high-latency systems (Mixmaster, Mixminion) + +Not peer-to-peer + +No protocol normalization + +Not unobservable (no steg, etc) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Perfect forward secrecy + + +Telescoping circuit + + negotiates keys at each hop + no more need for replay detection + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%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 + +Many TCP streams share a circuit + +Previous designs built a new circuit for each stream + + lots of public key ops per request + plus anonymity dangers from making so many circuits + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Leaky-pipe circuit topology + +Alice can direct cells to any node in her circuit + + So we can support long-range padding, + have multiple streams exiting at different places in the circuit + etc + +%size 6 +Unclear whether this is dangerous or useful + +More research needed + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Congestion control + + +Simple rate limiting + +Plus have to keep internal nodes from overflowing + +(Can't use global state or inter-node control) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Directory servers + +To solve the `introduction' problem + +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 + +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 0000000000..8def0add59 Binary files /dev/null and b/tags/tor-0_0_8pre1/doc/interaction.pdf differ diff --git a/tags/tor-0_0_8pre1/doc/interaction.png b/tags/tor-0_0_8pre1/doc/interaction.png new file mode 100644 index 0000000000..44db52a126 Binary files /dev/null and b/tags/tor-0_0_8pre1/doc/interaction.png differ diff --git a/tags/tor-0_0_8pre1/doc/latex8.bst b/tags/tor-0_0_8pre1/doc/latex8.bst new file mode 100644 index 0000000000..2dd3249633 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/latex8.bst @@ -0,0 +1,1124 @@ + +% --------------------------------------------------------------- +% +% $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. +% +% --------------------------------------------------------------- +% +% `latex8' from BibTeX standard bibliography style `abbrv' +% version 0.99a for BibTeX versions 0.99a or later, LaTeX version 2.09. +% Copyright (C) 1985, all rights reserved. +% Copying of this file is authorized only if either +% (1) you make absolutely no changes to your copy, including name, or +% (2) if you do make changes, you name it something other than +% btxbst.doc, plain.bst, unsrt.bst, alpha.bst, and abbrv.bst. +% This restriction helps ensure that all standard styles are identical. +% The file btxbst.doc has the documentation for this style. + +ENTRY + { address + author + booktitle + chapter + edition + editor + howpublished + institution + journal + key + month + note + number + organization + pages + publisher + school + series + title + type + volume + year + } + {} + { label } + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +FUNCTION {init.state.consts} +{ #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +STRINGS { s t } + +FUNCTION {output.nonnull} +{ 's := + output.state mid.sentence = + { ", " * write$ } + { output.state after.block = + { add.period$ write$ + newline$ + "\newblock " write$ + } + { output.state before.all = + 'write$ + { add.period$ " " * write$ } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +FUNCTION {output} +{ duplicate$ empty$ + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION {output.check} +{ 't := + duplicate$ empty$ + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION {output.bibitem} +{ newline$ + "\bibitem{" write$ + cite$ write$ + "}" write$ + newline$ + "" + before.all 'output.state := +} + +FUNCTION {fin.entry} +{ add.period$ + write$ + newline$ +} + +FUNCTION {new.block} +{ output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION {new.sentence} +{ output.state after.block = + 'skip$ + { output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION {not} +{ { #0 } + { #1 } + if$ +} + +FUNCTION {and} +{ 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION {or} +{ { pop$ #1 } + 'skip$ + if$ +} + +FUNCTION {new.block.checka} +{ empty$ + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.block.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.sentence.checka} +{ empty$ + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {new.sentence.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {field.or.null} +{ duplicate$ empty$ + { pop$ "" } + 'skip$ + if$ +} + +FUNCTION {emphasize} +{ duplicate$ empty$ + { pop$ "" } + { "{\em " swap$ * "}" * } + if$ +} + +INTEGERS { nameptr namesleft numnames } + +FUNCTION {format.names} +{ 's := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { 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 0000000000..76a2265153 Binary files /dev/null and b/tags/tor-0_0_8pre1/doc/tor-design.pdf differ diff --git a/tags/tor-0_0_8pre1/doc/tor-design.tex b/tags/tor-0_0_8pre1/doc/tor-design.tex new file mode 100644 index 0000000000..0a27a92525 --- /dev/null +++ b/tags/tor-0_0_8pre1/doc/tor-design.tex @@ -0,0 +1,1988 @@ +\documentclass[twocolumn]{article} +\usepackage{usenix} + +%\documentclass[times,10pt,twocolumn]{article} +%\usepackage{latex8} +%\usepackage{times} +\usepackage{url} +\usepackage{graphics} +\usepackage{amsmath} +\usepackage{epsfig} + +\pagestyle{empty} + +\renewcommand\url{\begingroup \def\UrlLeft{<}\def\UrlRight{>}\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"