From 8a08e7d281c7f0fd91091b40fbbc286a7915fbc0 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@55e972cd-5a19-0410-ae62-a4d7a52db4cd> Date: Thu, 8 Jan 2004 03:13:37 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'tor-0_0_2pre19'. svn:r980 --- tags/tor-0_0_2pre19/.cvsignore | 13 + tags/tor-0_0_2pre19/AUTHORS | 29 + tags/tor-0_0_2pre19/ChangeLog | 4 + tags/tor-0_0_2pre19/INSTALL | 51 + tags/tor-0_0_2pre19/LICENSE | 29 + tags/tor-0_0_2pre19/Makefile.am | 11 + tags/tor-0_0_2pre19/README | 46 + tags/tor-0_0_2pre19/Win32Build/or/or.dsp | 218 +++ tags/tor-0_0_2pre19/Win32Build/or/or.dsw | 29 + tags/tor-0_0_2pre19/Win32Build/tor/tor.dsw | 29 + tags/tor-0_0_2pre19/Win32Build/tor/tor.ncb | Bin 0 -> 107520 bytes tags/tor-0_0_2pre19/Win32Build/tor/tor.opt | Bin 0 -> 55808 bytes tags/tor-0_0_2pre19/autogen.sh | 5 + tags/tor-0_0_2pre19/configure.in | 188 ++ tags/tor-0_0_2pre19/depcomp | 436 +++++ tags/tor-0_0_2pre19/doc/.cvsignore | 2 + tags/tor-0_0_2pre19/doc/CLIENTS | 63 + tags/tor-0_0_2pre19/doc/FAQ | 121 ++ tags/tor-0_0_2pre19/doc/HACKING | 533 +++++ tags/tor-0_0_2pre19/doc/Makefile.am | 4 + tags/tor-0_0_2pre19/doc/TODO | 307 +++ tags/tor-0_0_2pre19/doc/latex8.bst | 1124 +++++++++++ tags/tor-0_0_2pre19/doc/latex8.sty | 170 ++ tags/tor-0_0_2pre19/doc/rendezvous.txt | 200 ++ tags/tor-0_0_2pre19/doc/tor-design.bib | 1082 ++++++++++ tags/tor-0_0_2pre19/doc/tor-design.tex | 1847 ++++++++++++++++++ tags/tor-0_0_2pre19/doc/tor-spec.txt | 522 +++++ tags/tor-0_0_2pre19/doc/tor.1.in | 139 ++ tags/tor-0_0_2pre19/install-sh | 251 +++ tags/tor-0_0_2pre19/missing | 336 ++++ tags/tor-0_0_2pre19/mkinstalldirs | 101 + tags/tor-0_0_2pre19/src/.cvsignore | 2 + tags/tor-0_0_2pre19/src/Makefile.am | 6 + tags/tor-0_0_2pre19/src/common/.cvsignore | 3 + tags/tor-0_0_2pre19/src/common/Makefile.am | 9 + tags/tor-0_0_2pre19/src/common/aes.c | 1355 +++++++++++++ tags/tor-0_0_2pre19/src/common/aes.h | 23 + tags/tor-0_0_2pre19/src/common/crypto.c | 1133 +++++++++++ tags/tor-0_0_2pre19/src/common/crypto.h | 121 ++ tags/tor-0_0_2pre19/src/common/fakepoll.c | 93 + tags/tor-0_0_2pre19/src/common/fakepoll.h | 31 + tags/tor-0_0_2pre19/src/common/log.c | 157 ++ tags/tor-0_0_2pre19/src/common/log.h | 44 + tags/tor-0_0_2pre19/src/common/test.h | 127 ++ tags/tor-0_0_2pre19/src/common/torint.h | 156 ++ tags/tor-0_0_2pre19/src/common/tortls.c | 573 ++++++ tags/tor-0_0_2pre19/src/common/tortls.h | 32 + tags/tor-0_0_2pre19/src/common/util.c | 854 ++++++++ tags/tor-0_0_2pre19/src/common/util.h | 121 ++ tags/tor-0_0_2pre19/src/config/.cvsignore | 4 + tags/tor-0_0_2pre19/src/config/Makefile.am | 5 + tags/tor-0_0_2pre19/src/config/dirservers | 87 + tags/tor-0_0_2pre19/src/config/moria1-orrc | 12 + tags/tor-0_0_2pre19/src/config/moria2-orrc | 12 + tags/tor-0_0_2pre19/src/config/moria3-orrc | 12 + tags/tor-0_0_2pre19/src/config/torrc.in | 25 + tags/tor-0_0_2pre19/src/or/.cvsignore | 5 + tags/tor-0_0_2pre19/src/or/Makefile.am | 23 + tags/tor-0_0_2pre19/src/or/buffers.c | 550 ++++++ tags/tor-0_0_2pre19/src/or/circuit.c | 1219 ++++++++++++ tags/tor-0_0_2pre19/src/or/command.c | 218 +++ tags/tor-0_0_2pre19/src/or/config.c | 439 +++++ tags/tor-0_0_2pre19/src/or/connection.c | 838 ++++++++ tags/tor-0_0_2pre19/src/or/connection_edge.c | 1023 ++++++++++ tags/tor-0_0_2pre19/src/or/connection_or.c | 284 +++ tags/tor-0_0_2pre19/src/or/cpuworker.c | 288 +++ tags/tor-0_0_2pre19/src/or/directory.c | 276 +++ tags/tor-0_0_2pre19/src/or/dirserv.c | 466 +++++ tags/tor-0_0_2pre19/src/or/dns.c | 527 +++++ tags/tor-0_0_2pre19/src/or/main.c | 671 +++++++ tags/tor-0_0_2pre19/src/or/onion.c | 802 ++++++++ tags/tor-0_0_2pre19/src/or/or.h | 832 ++++++++ tags/tor-0_0_2pre19/src/or/router.c | 524 +++++ tags/tor-0_0_2pre19/src/or/routerlist.c | 1280 ++++++++++++ tags/tor-0_0_2pre19/src/or/test.c | 726 +++++++ tags/tor-0_0_2pre19/src/or/tor_main.c | 18 + tags/tor-0_0_2pre19/src/or/tree.h | 677 +++++++ tags/tor-0_0_2pre19/src/win32/orconfig.h | 147 ++ tags/tor-0_0_2pre19/tor.sh.in | 76 + 79 files changed, 24796 insertions(+) create mode 100644 tags/tor-0_0_2pre19/.cvsignore create mode 100644 tags/tor-0_0_2pre19/AUTHORS create mode 100644 tags/tor-0_0_2pre19/ChangeLog create mode 100644 tags/tor-0_0_2pre19/INSTALL create mode 100644 tags/tor-0_0_2pre19/LICENSE create mode 100644 tags/tor-0_0_2pre19/Makefile.am create mode 100644 tags/tor-0_0_2pre19/README create mode 100644 tags/tor-0_0_2pre19/Win32Build/or/or.dsp create mode 100644 tags/tor-0_0_2pre19/Win32Build/or/or.dsw create mode 100644 tags/tor-0_0_2pre19/Win32Build/tor/tor.dsw create mode 100644 tags/tor-0_0_2pre19/Win32Build/tor/tor.ncb create mode 100644 tags/tor-0_0_2pre19/Win32Build/tor/tor.opt create mode 100755 tags/tor-0_0_2pre19/autogen.sh create mode 100644 tags/tor-0_0_2pre19/configure.in create mode 100755 tags/tor-0_0_2pre19/depcomp create mode 100644 tags/tor-0_0_2pre19/doc/.cvsignore create mode 100644 tags/tor-0_0_2pre19/doc/CLIENTS create mode 100644 tags/tor-0_0_2pre19/doc/FAQ create mode 100644 tags/tor-0_0_2pre19/doc/HACKING create mode 100644 tags/tor-0_0_2pre19/doc/Makefile.am create mode 100644 tags/tor-0_0_2pre19/doc/TODO create mode 100644 tags/tor-0_0_2pre19/doc/latex8.bst create mode 100644 tags/tor-0_0_2pre19/doc/latex8.sty create mode 100644 tags/tor-0_0_2pre19/doc/rendezvous.txt create mode 100644 tags/tor-0_0_2pre19/doc/tor-design.bib create mode 100644 tags/tor-0_0_2pre19/doc/tor-design.tex create mode 100644 tags/tor-0_0_2pre19/doc/tor-spec.txt create mode 100644 tags/tor-0_0_2pre19/doc/tor.1.in create mode 100755 tags/tor-0_0_2pre19/install-sh create mode 100755 tags/tor-0_0_2pre19/missing create mode 100755 tags/tor-0_0_2pre19/mkinstalldirs create mode 100644 tags/tor-0_0_2pre19/src/.cvsignore create mode 100644 tags/tor-0_0_2pre19/src/Makefile.am create mode 100644 tags/tor-0_0_2pre19/src/common/.cvsignore create mode 100644 tags/tor-0_0_2pre19/src/common/Makefile.am create mode 100644 tags/tor-0_0_2pre19/src/common/aes.c create mode 100644 tags/tor-0_0_2pre19/src/common/aes.h create mode 100644 tags/tor-0_0_2pre19/src/common/crypto.c create mode 100644 tags/tor-0_0_2pre19/src/common/crypto.h create mode 100644 tags/tor-0_0_2pre19/src/common/fakepoll.c create mode 100644 tags/tor-0_0_2pre19/src/common/fakepoll.h create mode 100644 tags/tor-0_0_2pre19/src/common/log.c create mode 100644 tags/tor-0_0_2pre19/src/common/log.h create mode 100644 tags/tor-0_0_2pre19/src/common/test.h create mode 100644 tags/tor-0_0_2pre19/src/common/torint.h create mode 100644 tags/tor-0_0_2pre19/src/common/tortls.c create mode 100644 tags/tor-0_0_2pre19/src/common/tortls.h create mode 100644 tags/tor-0_0_2pre19/src/common/util.c create mode 100644 tags/tor-0_0_2pre19/src/common/util.h create mode 100644 tags/tor-0_0_2pre19/src/config/.cvsignore create mode 100644 tags/tor-0_0_2pre19/src/config/Makefile.am create mode 100644 tags/tor-0_0_2pre19/src/config/dirservers create mode 100644 tags/tor-0_0_2pre19/src/config/moria1-orrc create mode 100644 tags/tor-0_0_2pre19/src/config/moria2-orrc create mode 100644 tags/tor-0_0_2pre19/src/config/moria3-orrc create mode 100644 tags/tor-0_0_2pre19/src/config/torrc.in create mode 100644 tags/tor-0_0_2pre19/src/or/.cvsignore create mode 100644 tags/tor-0_0_2pre19/src/or/Makefile.am create mode 100644 tags/tor-0_0_2pre19/src/or/buffers.c create mode 100644 tags/tor-0_0_2pre19/src/or/circuit.c create mode 100644 tags/tor-0_0_2pre19/src/or/command.c create mode 100644 tags/tor-0_0_2pre19/src/or/config.c create mode 100644 tags/tor-0_0_2pre19/src/or/connection.c create mode 100644 tags/tor-0_0_2pre19/src/or/connection_edge.c create mode 100644 tags/tor-0_0_2pre19/src/or/connection_or.c create mode 100644 tags/tor-0_0_2pre19/src/or/cpuworker.c create mode 100644 tags/tor-0_0_2pre19/src/or/directory.c create mode 100644 tags/tor-0_0_2pre19/src/or/dirserv.c create mode 100644 tags/tor-0_0_2pre19/src/or/dns.c create mode 100644 tags/tor-0_0_2pre19/src/or/main.c create mode 100644 tags/tor-0_0_2pre19/src/or/onion.c create mode 100644 tags/tor-0_0_2pre19/src/or/or.h create mode 100644 tags/tor-0_0_2pre19/src/or/router.c create mode 100644 tags/tor-0_0_2pre19/src/or/routerlist.c create mode 100644 tags/tor-0_0_2pre19/src/or/test.c create mode 100644 tags/tor-0_0_2pre19/src/or/tor_main.c create mode 100644 tags/tor-0_0_2pre19/src/or/tree.h create mode 100644 tags/tor-0_0_2pre19/src/win32/orconfig.h create mode 100644 tags/tor-0_0_2pre19/tor.sh.in diff --git a/tags/tor-0_0_2pre19/.cvsignore b/tags/tor-0_0_2pre19/.cvsignore new file mode 100644 index 0000000000..18e961dfbf --- /dev/null +++ b/tags/tor-0_0_2pre19/.cvsignore @@ -0,0 +1,13 @@ +Makefile +Makefile.in +aclocal.m4 +configure +orconfig.h +orconfig.h.in +config.cache +config.log +config.status +conftest* +stamp-h +stamp-h1 +tor.sh diff --git a/tags/tor-0_0_2pre19/AUTHORS b/tags/tor-0_0_2pre19/AUTHORS new file mode 100644 index 0000000000..db366a9ed8 --- /dev/null +++ b/tags/tor-0_0_2pre19/AUTHORS @@ -0,0 +1,29 @@ + +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: +------------- + +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. + +Aaron Turner contributed the first version of +the tor.sh initscripts shell script. + diff --git a/tags/tor-0_0_2pre19/ChangeLog b/tags/tor-0_0_2pre19/ChangeLog new file mode 100644 index 0000000000..f6464ecb20 --- /dev/null +++ b/tags/tor-0_0_2pre19/ChangeLog @@ -0,0 +1,4 @@ +hey, emacs, got something for you: -*- fundamental -*- + +ChangeLog +--------- \ No newline at end of file diff --git a/tags/tor-0_0_2pre19/INSTALL b/tags/tor-0_0_2pre19/INSTALL new file mode 100644 index 0000000000..ed83a512ac --- /dev/null +++ b/tags/tor-0_0_2pre19/INSTALL @@ -0,0 +1,51 @@ +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 (or do the two-line version below, if you're on bsd) +3) make +4) make install +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 connections to + localhost port 9050.) + (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). This step 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_2pre19/LICENSE b/tags/tor-0_0_2pre19/LICENSE new file mode 100644 index 0000000000..fbdfc69450 --- /dev/null +++ b/tags/tor-0_0_2pre19/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2001-2003, 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_2pre19/Makefile.am b/tags/tor-0_0_2pre19/Makefile.am new file mode 100644 index 0000000000..0e8fa94252 --- /dev/null +++ b/tags/tor-0_0_2pre19/Makefile.am @@ -0,0 +1,11 @@ +AUTOMAKE_OPTIONS = foreign + # else it keeps trying to put COPYING back in + +SUBDIRS = src doc + +DIST_SUBDIRS = src doc + +EXTRA_DIST = INSTALL README AUTHORS LICENSE + +#install-data-local: +# $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor diff --git a/tags/tor-0_0_2pre19/README b/tags/tor-0_0_2pre19/README new file mode 100644 index 0000000000..99fa81aa16 --- /dev/null +++ b/tags/tor-0_0_2pre19/README @@ -0,0 +1,46 @@ + +'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? + + First, edit the bottom part of your torrc. 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. Make + sure other people can reliably resolve the Address you chose. + + Then run tor to generate keys. One of the files generated + in your DataDirectory is your 'fingerprint' file. Mail it to + arma@mit.edu. + + 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. + +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_2pre19/Win32Build/or/or.dsp b/tags/tor-0_0_2pre19/Win32Build/or/or.dsp new file mode 100644 index 0000000000..77bbf8916b --- /dev/null +++ b/tags/tor-0_0_2pre19/Win32Build/or/or.dsp @@ -0,0 +1,218 @@ +# 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" /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 /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\circuit.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_ap.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection_edge.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection_exit.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\connection_or.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\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\routers.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\or\tor_main.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\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_2pre19/Win32Build/or/or.dsw b/tags/tor-0_0_2pre19/Win32Build/or/or.dsw new file mode 100644 index 0000000000..3ff4c8e840 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/Win32Build/tor/tor.dsw b/tags/tor-0_0_2pre19/Win32Build/tor/tor.dsw new file mode 100644 index 0000000000..ae4da0804f --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/Win32Build/tor/tor.ncb b/tags/tor-0_0_2pre19/Win32Build/tor/tor.ncb new file mode 100644 index 0000000000000000000000000000000000000000..535305ac99a72d848c7998344902ca674d46b893 GIT binary patch literal 107520 zcmeI534mQimG|r3?xfRMyR+|k*~m(EAcO#&4G39?Bm@G1N2lNGq)B(brk5-rl4yj0 zqJW^Hh-?Dm!XU#YItc2ps>6s2E~7+n8y9c}6-Sis_piFQ-hHn-$Ru$-8D4cN_ncZ! zo!U>;tFx-1t~J+|t8Y&&nYm=zv{XxLZgXpGQz}#2Uc0HbEt{G%W7e!bJ%^usqA{l2 zn7}j`6B_ee-+jRroJyv|ff5Ja%p7<}p7_7KRx%|HlsHi0K#2n-4wN`h;y{T5B@UE0 zP~t#|10@cWI8fq1i324LlsHi0K#2n-4wN`h;y{T5B@UE0P~t#|10@cWIPfOqfHAw? zgjAPEE^(m5ff5Hw94K+1#DNkAN*pM0pu~X^2TB|$aiGM35(i2gC~=^~ff5Hw94K+1 z#DNkAN*pM0pu~X^2TB|$aiGM3BnR?8{x3l#4wN|XR^!0*2WJJ@(esbzNguNwJF~gA zDci=UZqv;6+Sbk4_J-#AoWq-PnU2P69&c;UwII1J+C|YZaICQ zI;q*w&wcmu9R~j42)~1`dg%iZ{y5*E;Lk?xKeu>)GuTv{fo2dsB~#)+i35M{9I&1K z@14jJeI*W*IB-Zgp!vUKN*pM0;H}Alzr6UD7d7|W`M&6eGljngJ>K6;=I?fo4?yBM37X^h1I;x4c6pCNoC^4X2%Hjj@s=bP%}TNHkr*=cr}+u*&#H{ zbNruVR+ODmcDBNsu&O(CKa#%)C;n{nT-gW8U$XI4Uq_g8%l@$}!U=!08NuHg;v=cW ze*yBIf#y2lZNiT+1NiGhe$Ia((x5>mEFX#;%oFm!gbxW5BIBJ&xHn?&hMxPe@pl>d z8F(N%WXrM2^BQa%=2y7Af+ZvGCOVbQbmGS0zKvT9KaJn>*iliOCj8!xeHZqsC|t3d z^6r7H#vX<(#@FHYF>DoS{=Jypl)rz2Nr|5B{JjbeiQpkEaHRRF$Gg+phnnHi0g#b@ zZ}TBq*IxP4-_@$`FkdiJy#Ek$hPl{u1250eS%}+%zcal57&D$0y~5)Y&2B~?@&9hp z6z{&t_ea=YM)(K$-h$nqyW)Kz3Oko{<8VgWP&}xJ3aktO=Ykt(0KbYdFuNTbrF9xP6|(x zev|U(0)GK_h52n1CQj=G+~ls~+kPq!apk9P94Ah1eNo=`l9s}bgifV1pZXDfg5$6J zD=3$~KjkaGV+p?;E4O4his`=~qDNsKBJN`BN!az+3|48y^F&;>0nqRqX~#6b5BNja zo3Qs{{}n6$H>0urzq?6^cJ==~zCVeH}GdHjvxX-6Ih}nOHYoCGE1A}e*RDG#U#BK0E;it*xyV&Qks=tyc zaiGM3zs`Z~z3doGe^iegZ0vYYss8K6gQM{e?h#%_Z-30(CfxS#ZuW%#HuZPA{?}gr zBW9l;FM696bD_y8yd5w4k@{@Y-=|m2j3=gRy#kPh}+p% zjlZ*_@4e*n73|ls-@$4e`7gfX@xK&%C04M)e-ZmAR`Fv$rCBm14!qSl@TtC5{%8CD zUgTT0|J(WA_5V})#Qn3(d_R0X%=8AIuJNF|`APWe@Cxv9J3dsfVn5b=4`CwHFaAAP zzYaB16yA;xy-Zv9q40k4qZ{Jim)!1SvIe*DtJ!ZmduTmNBZaj-Oa zO#aBjv>&)G{7m>u??1|{4%P%ulD|uT46Bt(%=sQ4&wBos<_RBuqWMzrSnwO~Kgm21 zJRST==_7wL$nv%a7X_d8{?p7-v&D3WQ!f3(&B?*a;7Wx@-i7qa92TA*?)BxHZT>O* zZkY48>f$L{(n zpCo=2Ww-$wyT@Opt8kykFU~U=FZSQxH}UA1{lAY%3Dx6JC2f-j~_`T(`e(*Lc#;?(gG%oczWzAdLe_`OHnErSaea zzNJ0<-=@Axsg^j9Te=ErS-r+g!hMi|($-Cy)YelX8Gt??$M{Hl;IRy(|1=Ue^F zP2ugKy-#5GD+BEMVx5gI|3PL8`+1A^R{SB{HtJ;#Q+`JHFys-vW1R3K%-Enl_?hzi znfym_qETrq{t2Zw#taU|1iw)F-xogC{D>X9#clfI?S6TwPk$m3jlL$r2|vmFYfvBF zuK0bG{uEY3{fxzJdeh97@MGcK#_cWRulBOziKDSKxetoR+V~rXkH7IgG~QeNExwRi zGXKXoP&U%a|57wJ^UvWI!zKJ=um8!$;=0de?*m4-&Ijfv{B^%;O#;{buFa$q$FHIf zS0((_|HdS6-7owk{YUo&mA~#EeNT9lpZepU61e*FlL=h+iT)#j>-_sc;VGA11t$tK z68UL7X-&|l{%G$f4shWm?`RvL@j`ev^WL)S%T5^{<2}s%!qa{20>|UKqC9{6g{{ zXV#cA%}(z>!7L1yhi6&(qdp&8a^8G_xW+GknqGGt904pG zj70JueR`{q#=g^ZQ+&(M)uy9tN7>`Pd~3{CgKr1>Jpb34XM^7ahkN`q^M{};9OKhJ zox6L(f}i^IH<*`#-v!rr`qHL492H*W<7dqD@aV9ckKbU%gj2%@JpDP-E$kOQ;?rw2 zBZBF{+kE^EGb1=7_>ylAJItBE*5DjZ|J%)#!R^6rpWemhAA-*XLp}aZ^I-5~@Cl#) zC8jBOU+|Lmzsxk3wU+(Br}tj7rL3{6o2Tar?#tAd{eTzWlE={}R3!h7XUmbCYDkXOq?*{2TacUh2y2LCPz+?^ApgrkV0C;~T@5f`1fy z7dCc_zg_uVL0*#e-X7(t^lpa+>lS_E_?xM_7=D_5|2ud!k1#qnT*`M9R{p2pUWaYL zUW~mGJB5CrxmA8~nD`rq?GCRLMl|UA)2Ivzr@Tbd@swNN0TC^6n3r*{AbF30W z>oN)dfVi^aQ_1}OaKNtrQ%?RrA(#-@_5TQy5`~%%zcz|*O6%XiuK(`@4^r|kGZTVI z!H?)C!C>Lt%%9A-;B4<-VWu$NeN6r~e5H90wLe7Z4O4nO?Rt2R{Eg!GHusosnWa8{ zUvAs|(Om2CDpns2rd8psJfhmX-CU++y9tIU{s6PpY%{xk_-CNOVpwU$ z>-WdWuVhLb_}}8dh%v1HGbW|$i(~l>$c7a7dV4LF+y7(yeE_c2+5Pdf(X6lOufnz7 zeh8fV{on!X(HUkc=~ZQFswx>D#__ieygF5bf4P~!-yI(BW`8g9-yZL7e-HCTk5|~= z*PP(-9%ej$S9rXV-z!Zt^T=Pn*QsRun8l6TD}-Bm`m%_h5VQ#Aesd*V{4nkhe2(%3 z7b<*za~;0`v3Pw5y#pCv2AiSsU#;*%%s-n~P32f{oBnX~7c(-5aKeuw#1PX@;khqR zX-1octiVsS;f1G6nqNpv_3_7Yo9Tl=gcE-p5{Y5v2}dvZ1a3%MO#>;1+29YgxMZ&3+1C7?mspJo>E*9^U`KBiNG zQRXt?%WCjHoDz&SKT&yXd1oT;T^r-jH=EtvAakqyS1Y|a=EK1kg9yieF7lhw~nvuA1T-K;A!kFDg$mSaXUxl z1x}8o!oLn3@mJ&MT=2!%7#=>hGaQH4H@444>FcYwq9gVbEP0IRlHaRQ`tet8Z+4R! zXZt_%pQh4L<}UcA{kZUr{O#r2Ka-%I7|&Tknrkf7T3K zZcds=f3?3a3(eQU--Pd*06tIs7r)@lpllL&T6lN!J2N@3_kr#Gp9;I*w)cUTX@9Nz zz`f1N!wAp%QoDz#!3n{IO8;2p*PGGhG_zuSJbv_HoiM;$F8`GLtN1<99p;BNf8qVj zCy^M;wCM>S#BJh-&8rG;$ETs@OXh3lD$;Y~*Kl(d@`3Ao_>tyxv%x&+@zI>`y~xE({L{^I!D-<=-hY~ihqXrop5_!`&dp_k23vrw=%2rj)S9BW)uG9w!FvN{neww zZT;5R{f#|uou}}Nth{C~^h#dLYTK^&e?oa3zJwFi5$5gC=f?k~tlvhOj1Rxewy$ZH zKE*%LYz)2-v?zX~@RP_SWh&)we=l+}JDw?Kw)nYD`LASlnPZ;v^sQpQI@bJJ{ImD9 z*O>KXZDpDxz7GwWCvGHc{EhD`$GF02PqC4(TI0vR0gUew$KQ#hpM>LhN=Nbk+BcbY z9CiX_60EdjABTpgvGKWYjQ{wP|TErK=u^dUTY)<^=yAXecRlK1Q zU-XUR>vu7~Cv3@-I8friTZaSHQ|$i_Hz*pZ5aH+2%5jkK%m! zo#uXpx8)gUCh-2y=S8o@4>RxJJoQN*e!6)&7!f{Tag}$5(S3vnhyGdS-R2+N`GLKU zG@BJrcRSYBlZVFK7QUBs0k0v?40Z?h3he2GQJgsZwNac|_{p9L4GmbO8UJ1)o&#e1 z8REQ*?Me8dScUx>-``*dMsa`2SM$do_$I@}Y3N(JcWvHM=AWk1|6kMk+^zpD&iy}z z0p2kh8GJ|n%{8eW-~)mY!OOx;O^PFJZo>@;?D-+@`v}+jR!uYQc#`7hVWuo7bLWZl zPvPo6Bd4jqh2Ujobg(^G2(I(B-i$T6Ki4tC_2=I7rCZIX%zE;xu<^y%~Q71%rZ3!e0|!$^E^-!8P8$rx^mhKlFGnGmQHlA^ExZz0L4ocyOPO z-^UCMh6Z0Izv>-IuP?ub>mN+>;rnr)V??l(@vH)QNpD!F^MhL`zxFr1wLb|S2qL_y zH|GnIkJs4ph2Vqj_!r@XAIg1>hk{$kFKCdzUSAq%EZ(H_Mw&;0$Ae#b{)~p(i{1L2 z-|um=Du15Id2VmRfBaP{J?>NXW_3Rf8{wqK{h_kWWuJ!rV6y0+Y#PfhEqhY9t&geZ zg5V>;6RNM2{0}#u;l9vmzI-!H|FThK5l;HEt^D{J#kch}kMTBVp7!(}WnPuw$I_$p zjxjxX&t3YTI`4mq`A+b|;51(!YvI^f6XB$Hx>+A~gwOc$o@uTR zZVtAHzEz^X@HSG5=-x zEj(+kFgKfjz~A+UdTvYg4K5-**FHDf{&c0sw^%&9vA+p>@LL@=58-uXoU`=BR%82_ zu(rzR2fAU;J|-+%V8ZYiJ1@*7j~|fl34Aq=?14u0tJSo9%^T|Dy049_jyeAW^aPe1Z#ePad^wdCK40lkjBz}EV z&dUj-xGI1ARbEM0X}p4eoL2l*nt~Nqa7=$!c`IStG z1OJ;GICd5*b1NUxyxq^O|EHOhXwZB-C*iOAx08i4zRO?hU3Q-{UpOjK_O&TK}xM#^b%r&w@?i z#UAf%J{Rl@9`Se|^U>gb`}=i`5Ai*=)9^nFtG>5CtFo0$i34v14%q%_`>%fQ$1R<} zy^&Lr1-=z(zf{JzItK>MVcxgz^Sb*#_C7$y&F8O}SKRM=qWNC9y&sUk&z#MAAQ8S+ z_=tS`M&Z$Y0>vLme(roN}=9v`S*$Cz4=4>9X_5%eyf|8RDgE6on2_ZiVY ziuFj19S`+h@ZB_&j}unkuMkFGjg_we>+ab1`0m5Xt%h*A^S^@c8CZoqkMEuY+yShx zvHNb~UV^ve;tm-!>@vVXLmm%Og;Tql2{R~Y;IJ@Aq#aiGM3 z{~H{*nf}~wj!7}aG5?M?_V>Uin=0Wa^EY)K{`VLs|7U)+zprIJ*Li?`e{1*utOtba z_qIx}g88=xNQN5CHJ zPGWjo`g(70xVg}$-@~3C)_Ytx_=lJ^9`9-8^9wxQ%ic!`pXF})9H^VaJ$3R3uS0G+ zM>14ori^$h&o2^IbN1!n;!ErKFQ!Oo;;}4*dObVEGaBcVkkb zOuy$_2VQRX>j9^s?zjGbe^i$!DsiC1fkVN8!;jLrpGk3*+5C(f?_WR8;XLPnp0hiH z?!6q&a~tM|=Evr|DEuPuZoGfJ=NRX|0=y%*EXJ#Z&kg1V+ZQ_j)!@0HE%^A64qpqt zmfsIV_YYI2f!`JUQ=mHnqGv1ky}?(5&(=7+1N`iu(cL%V#{j}tg{#7MMd|MZf0Y{% zR-TE!@Cm_$AWMFdX9z!wzYBzuzHt4%(aJkA4%hwl_saiPho8+I54#>pIb8dj>52Hq z^LyXTq|^U3=f8|QDfcjbN*=28b)Ry9!n4&8uHWn0^HcUm!Vl-q%1nm`xA^yUrKfv4) z+!DOQ;{!QA|7h^4Pj8U9Cb%ZJ(fbcZzt;^x%Hu=K2l;*b$sQld@54VF9O3gDW^U&_ zqY94?H@6122J@(o>Y3vI2!4O_BXfpuTOT9MKK_<@e3W^Pzk834{YRU_gTsTdj6W_s z`Y(gU!42L&#jlYj2S<8*EbjrNg2NZY;m6tWDdX|+=7`{k;I|&1fc}Bwf=ZwMM00#_ zd~m(@Kg=8z92M;L_#|^=aAa_)&u_As#QP}W{5ZcUrYfik4)^#}d!KN*$ER_>azHTH zAws?FN`Uy@BF8Arpw)Y{w%Jd2P1dsdj&*y!s8Ns)F z{G-h=!7;&V@4vu|4aNo!`uN9if4q0l==~Sk@%wW={;_63upqeHhv)sVpl9%c4}ZLw z9n1~}d%T9%8b$^UzC4S#uR1XJPmlBaCGLA}_vtM`A6jK_gU6SeUO}(m-+g(PnTyZ| zafioGFgt^t!I{iIuKk^8_M(sBD(`=i*%9mrzUcAg=$pMT7~;d9Z1$j!?O2bmFqa0G z1~2*WE4h#Uj$o?yUuE7MygMlK_-eB&*k$KeH{P6L+L&K2^!;m%IX^f*Snu(-nHKKP zU+wWzd5@ws_#xw|qkpYgAFK~5efig!4Z()sGv5C+-f37D{LbU+&FR7E!Q&o3-JBMj z7F^=T!!yihgU<#Z@%RRFH~IiIzdL%*(Gv*^}+G&t4c_2x0&8-1V0H={q~iC~V8zr}ok-?#1cc!PNu zeIC#G{LbUP(&vI(ef+K5Pkl1D#rrp!`?1*7yOyWcbeLuHmLF8FEI7Izq;1r7n;q%=HOkF z*U|fSd%yKz?|%{Rul&Wl;_-LrNGkY-$1gUo@pq%gcbPx)7lyO2vrYKg9Ar}SOgMS2 z3DeTmYVRih%xS=@uq&`xY$Nt8>?znyChWF`GOXjfmhX9d>-oNo@2PxO@~z{$k?+}jJFsog zw9SMgTKVpTwhKvTJN5$X4rpsPVfj0-7lB_)dEUu)59Qm%_g&m2-c1_s#$Li*;w!P& z;rR+~wac5n? zIXwMAzn>`w*1IwaBX|8SQ*ZPsu3(+t|Aw`T{9`|T6;F0I{<@NiO7-R;4QH;U7h?W!DcxY#dFH+I)od%T#RqEE2C zqD5a>@k3VQgX*`E`uz!C!6kFx9Jq+6+Ak>u?Qct_#DNkA-Z~uEOHq^k|Lse_+ue8| zd6AXh@E)ExB6&>7lsHi0!2cZ%3|WeN-Rk4ki!{@nygSnW@iu{u9zW!7<+;)LA>7JmYx3crA4~WpdHnwlCrc$Map29zfl>5dUa)Vg;1l*#!C~|0Pgk;;{16C z?^F!q-t7o(3y+ z(c!F>9{^2a=ii+>rAf1k*T_teO4WErjTkcJ*dWD!GGUF;pL0dJ7qE#i)iQ(&ao=UV zaI69eo%|1|OEbFs=;KMj91l%rkWHlxB`OchR1>X+f90tv!@mPI?|JMPOV@5_NhX1Q}nI1K}q_n0yfT*e(SXM)tRUt(O(#cG)x5@-HmepYUGh-~j zGTFt@bTaP_*sR#Ay&+RT*Bgpa7^td;INAg9I~hk;DxHOA$TwtMA>L5X zzZwV%xq6wR5!Ngvu&c*R&ljMnJAUIVU90w!41}ci|=umKQGx*t_e%Q7!Ms^Nl<2N(9|qvn!QAW6;$j@5<7V zeva*KJ-EQqr8z);b(vOyV|h?h$UDccr%W#vFilv73rx3yNO|N?HmH269@^ufsC^l; z7n-CGe5kF*Y54#iO2wVkyS#BgqiScfw5n>+Nmb)|sO1zJ$FH^Pvd|1P~Z z`8BMF2GROPP3nyXEDTq3S&!2yCsJ$9IDV~J{%dHG9<_-kWG&j@QkSW?0Cw}kCUKDdVU;l29WC^ zqpNTL_^ajFvq^0x9>2ozj1iOcyBoQ#NyZLql2{RJsaF(}D)RXspd|9}nqa~cw+{T) z)dO4Cok>(zkKC((CcTS3$kN4yK5xR52mhu(`M{c>=ef0mxL}8g>6!vfdjEDIBYto0 zw0gsm@O6yk)a&@~->W$0+dez+iOIu1RDRWjYu7z--hh~{UqF-InRGoOe!O(4n67U^lYLLm=H_OjP8t8r9d`K~T)S`24xvfuovZ$~eKDB*4||ak+}|q1 zXZA*b9UmO1Ro14jX`EQM=zeI@`$BqKfE8MlLi~Lr2_C{Qlk?-m5zwS}MXI2z2j?LF zL(`*pPB@?vrB-}D&Nm*vu7oDNx6lnC^AV9XW42 zokZq`27K5h6VtT`n)LgVI~gfP?9NcpPmkjbFt(}s4a^+e z^){g9=N|*#hrJCeIpL#%A^dABB_yZ#EcU(FHhyPv%5Z*%PnfS_r8`ji!KLT<6qafF zjZTJzgmef>uh4MD#Np(11NMCELhvWBP5cbu%jLY6$wqNB_8jnI`CdhsoAECPm;CKH z+@8d~4E_!LX7l|#_$7q7f$t`)e*g1n{PqyO8adSA*z15LC%ccY^oJkK6n~p6Zu_t~ z;Qm;>eIU8j2iU@B-yq$F(q||ghSFR3Bg))>mCnK`_}$Nf^iFv4BlfXp^ZgiK=_8j8 z@=eH1p2SLivIncH)Y5Gzxy5PNZQys2#$$Yc$@fX}k*-7OH<(-eni-R#H}0nz-(#a@2RX@S~->}j_ zGlcq*?wLK%xD@zWtn^;Af^*Tu?8fT%Qqqqjoi9g$e=bEH*vhfoq2+rbU+H(3PIu{f z)E-6p9dCoj(&M-YD}6B1`J#6)hSSFPmf=R4Q-GzrQTiGWqrF~FyOS)WqC0JoI*=at zKTrnge~}Je>D_${EB(6KbLbsi>2j35E$N7t9!Kehmp*vyEv08`_Y^0`kWMY>(UR^g z>C2LiEWN`a-B{9xB^_AOb0yu6>u6Jl(YAg@yZQyN3ft+LV6yh+wmvjQRw9OlPJ%tl6?*G8R?Oc?wAyQ z(h(!QFwzAheJ|4SBE2rs!9GY^ zk^UCxK$2ccy-lq5i1h|>EmnIly)V3ma$kq-Ls~1!jX8qvH)sbsi}~Zw?vMTHUqAj@ zpMUw{$p1{QeC^M0JMT)|dBW2<1@jZMlL>b}wM;05m`jK(*?v!OxbzTm#lEL~NCbstZ2tYAq+ zHOC4rCsmCL4(@B`G{L&rr+z8;FzC^nGJ=Pbm&Q}Uv&c*RUGO-<=y%?NZy}7{v=KZA zSnpd1J`Gr7x8RAy)p$TNHqSOaW5*1+O9z+cPr*H)LF1y}Vfg5G`-0yKrZG%#cS@=E zW(1GJ-M;k=+zVc5tdzTC1)56)r^vTvk%Mn1yxx|P`y^n!10r}8u+|5H2SA~I-!J$o z+@;IQxmVk9Oz?QhuJKdw2w&=H&>P5qu-@EyRKqXqXT zw#I6~-6&(EgP5H9+4qB}N;7}`Gm&mEoflNwcTN<31?gzM5j+rgz0V_fEbe++K=22F zwRRIM{ZFlYmF61KwC}A0SBQs-|-PVp12x=1Z%&bwT|F2>P~Zq;8E~F?|KN% z68<8--LPB!@{LGemfC8y(OUrub2DKgy~dQKpIytyy$`W8w+S8rr8h_PUr%_A$#SoN zex3O`IB@T|+qeVmiD-A=$FF$Ct{oI+0(GI^q=dk;ZhFS9HRZ0>r+G^75XutiC8MoY z+clirrQcrf7rAtXF&4OVJ`CR?z0mZvenxMMC`==CUb4u+w*r3*H%K)fzU>>a-a2xs zw(B2-9|3JzOADSsc)i`^+=m&xLm~M6xZlQCFwM@c?*vyto7K$*oFdH|7r8K>A$+7$ zniJlBcI~I|(%qrAsRR!ojMk&hy_;Q^3$BKq6QVlp34b&v$-N(KL+d2L3khR&Dg#f) zNAGFK{R-05x>E1}Xw&b4#Pf6Ed8Ffv@wwWryA);t>FBK*!DkWI>Xic?OBnq|NOA8t z^%=YFR~X$AczBTu|4G7Yz3trl*>#8D{*+PgKM9^j82h$Aa9`-xnpW<^p-pdu2!0P? zBK>dhzuLYfCHJYgFB8&xreTl0*f#6Egw>sW{=Rekvw?ne<<@1Y6qM_j85 z0l13t>YWX_e+qZ)p9BvlO|1o8_)7a$n&4`}M0yEn^VN1uE%!y_tM^j`UriY8Ed^H) zMtdp2x^sRBxLVr_ zt|5%ptbzwo8+z|c@GQb;&nS2x<@#boLnSm=o$iK{oy1kVH3IkMofvF;KNPleB^Iv*w- zy>lhF9(ZWV!PUgoK40!V;Gy1D61Lex1;l!03OyNU#=yaEAo@G5B(Xg2CdIL{kmIF7Z96Sa( zB~Oxjcj%WqOR!FY{uJe_-|PPvN4XEfO12}I)@OTt@CQfF*|$+@JI%3sM_Mv5vYd<=j)y?{ukZysEZ) zWf#7e(HnMxHBbbSm+*S3{5F=Yo+GSoxOVX`~~$w_yFoPcmG=<@5`^H7fXM(y@A%fv1p?+lVXqBf-oLcqqB3-1|b0x^9IhJpiH zx78&I+!KoQuA|&r2``->g8RW+$zKJJ2GiTBf)^4-a$UiLDU06d6ubd<$z}zQA-v?c zf`>z?)nf`=NnSb&kb5_1mi%9E8D*4=T<}OJ(z}m>cao-bmI&@kZaSwB+>ID|OIGla zgpr(E@O@7QK8hUlHmTs#Nnd9u zf`_QpEOhXNxJ$+@_d&$g+p&UcaDOnO0kVvAu*kg+^nW?Zmlo`Fx8mN5d?inJVS3rS zb%IBerp^`w52RdrD^u`x(wEFta1Y|@%~Zi>;4ZnY;Hi{RXG4Oey+!vo1WV7lbXNW;*HoD#~{NxZWg{do$ssYg@3ir*d4*tJIkjg>E=(_{Ul;do zq^~nHxrelWo!tl?06jWG6Wkpio&N|PObp5V1@|Jn&TIq^C4HTH36}oe2{jI$4-J(} zE^;4&)!BpK#e~TEXWKUS|)2dqRWGrvwjz zwj*jBe1h^_>fmx(#OT8v+#CMr{6^uAC$`St1ot2>okCex#r?7Qy}C$_X_NKAtc-o0EG_d~{wVcpR9{fT&8d<~`3?T_|!dgF|{J zNptZXAGGQn_D=yUrg4SHMrbyCQm?YkbC@=gEB_6zz`qUkU$pmq_mA zI!Q;vI`6%vPTE9i%`x5?^D6i|tOYaCPd>Z(|8V6qlyeX=$KGc`aV-;o)rO>&c z;NhgNb0Wc&)RE3u1&74d*|FgMgweT_;6CK5vq|UP&rBQb;4+O~QQQhjpm$Og<|Cw| zGfu(7;KS854!#&nXT5Tl9y`4QEI5a|&JqRBA>Z9FP4jjYbCcd?7yn<`b5p1@E`^zZ zug=B<4}dnEV+kHhUOLMYT+aVG3l%(!G;5=D4D+MTXXW0L((7DXa4&MXCgK~X7xw&2 z?&^7;t#LG*d(%_qxu}dEB30?tRG9A6s?M(kOXrTx?gUpuo6d~|_km`esSB2tE4`5{ zxQTQgs&Vj*z&hh|aVwo3Ou|%?zI24hU89BW3J69TZqI-P527@Bw^#6Gq@(ja!DGnl zzNlQ)q@;6jx%Y+t->h-)#|eLAM9*qqodYY(B+}Fwu;6LHI_nl(4&Qzj#l4O2I=hp5 zPw3RSz2NSI()|O$A^+O5X>A27WwEckNLIfrj0-%GLOz};+$YC&obJsF9R ze*#wlj{(*!P);gp9|85$-@1(imh__!`*CELz>Wswgf?7XzJ0I*8IQHwAI;dLJQQva zX%E9!VF!Z`Lms1ih9SGrS0ngfc-NDB`(x1sfO`Ngjs~Pz4jl6B39ox$%Lq4wFx~m8 zwNmrA4+clJhCK`$GHP}s*WSSYfSrQv4nEy}N7G*VFb@vnJDSn559zoPX>sH{^K#E7zKiz;wH*&$icX!9UlOCZMTr>}cC!p@!!6RGZQN zQ*8?JP;%Q_8``t!_M8pWnyt;mu!7dMr<-zZ?JgRgbz8ETbT(ISmM+-1G*{Qrlx=Qr zOVu`KQfssA?G4SF+cq{g)NO6rxN2w0`)_Q|wQg){t=q__uA#NAqoI99or$1ywwY8~ zYoTG&&UABaCexa3ZppRsro9WAhTtSDXVU?3bLZe?BUp6ykwOb%W_PqSv}V&C%^hu! z9i`Xi6RU4%Zh$#kAir%(?bfWR+fv(_n%bIeY24`pwY7^h*wfbDO8KHN6VE!^hsY9S zQ=?Lfa_~NF+2%~TIlH4hoolA1(rsHCn!RUhwkfxbJfl)>&bAYDJBg)hTMArZOI?n) z==EQ=PE{_JSf|a6xlOf=alX)y=UVNiTt~a;u4~M-Wo-&BvQL{7+8Q$1R!Wv@NoO;g zvuRbo3aP5M4(&TzAhC64OMAMdwtb5-Zmiu&oGk?LDYa(XsKde};&Q2_rt1{BD4obH zDt;#0*szUsTeGB}uFEzy8vSQWDnt}nhYU4pnc#EJ=a6pBWs1q&)4+$5jvDlFGaXGWYNVF+5usvD)F!A~QQ6R}US*q7F~J3VEMR2R@al4# zn;S03l4U_%yUPB{i+_1hE4tsX=cdo64o{ul&^&ieYH4;;$L6HNsoBPCZCm!h`en0@W1+j?1+|YJCF;DuWeC-;VZ%MgidI zHrn;p>`t>S*O0Lebcz|BfT)rD9lqQTDcRG&Hw#xN+Dwuc`EKId;sqS8`a+3xlBZC0_yE)U8pT_LMB&O6(J%0FXZ-wsKc8zWY1EHNz ze5htSyt1ItAX+dAvdoc#iALTn*}APv7x^L6b`v1|wF9+m2Vn}Ehg z77SXzI4IrHnybsU(Od1LRpgjqLT}B*Gg{=ygp@6eW-FP6kgIL^+W|8*Ii0S{Wa}DJ zQ|n>gl*#d&IK7ZHQ#um^8(6n=NmHHmV?opv!HEg@fY*3#Mi7TIHNg3%mfBX@h(jA2 znzIc2QG1Vvdb`rlB$1k0+sZP_g|(xlBG9mtv02}0{gHQyVO4k+;mrxlS1yBzaWLjJ z=9_|*b82e5o=zmoa>p*JTt;pzxApJxuwA5eb6gftzD>Q(r4VNpbpw}%ZP<<)`D@ZD zocrn<==HTUfP-Lvv`(~*)Gr#9TbEro-^=WRHEObM{ngRjQd_sxuf^mSksS?oEXGNg zwe9}a$!E4LQ7;D>f~+WX{#yFn;V zeC{W!yDdL?F;6(I*jY?FMLVa}wsh&tej@3fPcdDv zF;2K^zu1`dl-o^{9k;dSu)~;!1tFG+$wJ#{KFVQWFKX|YWEgU~!V3$RY-0c=Rt!iw8C3Xv~zNEcjBTHcyISQnG@uuwN z!WIEH2qk_bnIb1UUwMWXrWH3koAIG2w)VY+1-Cn4CoI@c|Fj`o19ek;vILY%-N6=2 zEhCy?I|sC>#O3MKzY2U0%`6=c$=MxFB9m`sa*2CavMko!)|{)TL(t4vNEeK(bYJZm z?YIN%r8BsseIQJ1kigf)?_+AhWv02F*lq zh}sUxFY^3RK1HokzG>WT549`YoYoh4`q@cK!VxFgoNKPLYoY}0zI=WXC`bVg*hbbx zsk`i%1(Bl?*#vBH3Ve%*vjGmMawGYHV*oX~P!-wM);KfU%8m&+LX5Yz)fV6_*`@+K zyRD@FZ`G`&?i$#%LG~@)0i|CS+V#bV_xmNyJkws(9iZGmaET4#$ z7H+?SB+W@Jvd!DnqWBbxE!55PZtClD(I&2Fjn|%w(`?ZmhRMC90}*1K+Ow10Y899W zlgQJp9qn>N+o)W+1N7$819LLjYOB6LqCv}L*7|RZk6*YITT5^d+@zetwI+YKX z!eE=V^w2&-tN6%^61B88Y?CWH7b`$XazbzmbGu#P6yzC)j?%G9e-aa)3v-P{N{I{| z_mECA`!uk7h&Zzq@k?h4Ge~OLk}y2bo#@7_XkBWjzwzJcw*eM#F-C*q6y|^23$%(aX1=FVG5$__Jyabt!wV%FOP;-fn z7$q*AQHh(GF_y1hxqS7q6d8EB+!#`u*;Z?Ze_tlsc9WH(@h;CT(I{CE(=x4;?X-j( zE^=FzRY|LkCLE5xTS+?9_M^_Nh)_;ALTfdf*aRW5gb6CX@=|+LOB75I6OA%XY8wl) zXh5-BiKwj0)~`t~S-$3^Wv8;ZRcsQCDN}nc?9raZUA4D38iCXR5RAtz5O0^#zQCQE zaaUc=wp2HMx*eX6FIKqj8D|j{vte6Yml2QXV1-R2$|z9)ZSNApcQnwd?&8ELQ8XCD zQ%qc`=@zqi`H8DfTLt&Lo3GlIwrocx$JSD(5}tS!Ufc2_hXo55#g?9Q5aLdE){<-8 z$vp01=BrH|4UL(EOSJfi1Y*f`l6F_|g2%0B+qW<;*fSi`b%X50vyd34mo7gwy>|JT z%cjjP5Em$g&dVa7f?E*9Ip>{$+IO;3wC7prEqsyN=1Eq2&IgX@k3tvI-{sOOA6c`J z%PsGwx^B=6XvGA@2^Oy$Yn$7)LtWy0QF42lWaM-}>oBC%nPw}gMk1Qk8b@;;Vz_jj zJH=&VmTTN5!CC>%CajYSqiTNXOR7axTIAWew%jPJY|;Ua-JbTqoyf+t2jU$;+KccXDQlY_~IcPPUoOZuaP+ zmMZf^(F_cR=FhFZ^^f|gukBctay57QABw(V@2sU--f zrEG{ep<#bw!R-yrl4HzqNZXwDx|RmJ3sN#h|J5TO@2Jlp?uwFI(V zm^dV&UOS>X&Pd&DfVM1q*9L}EYDRKHyLPJ2wKmnZn;A1^D4gB4`*1k0Voe6-0K={z zx7AuvPKs*|1ZpJVMw3?jdMo(Jr`~Kxtxyc+JrAsC42l|NG@WsluYOt}J*aJMt+geL zx8$@&J2@>mW%cT1OV%wvW%b&#O^ZEU*S3un&=zLG*21Hz=wgSxE#OYI^AldaH0g$> z7KGcDkFM^TYIhV)OXSX!63^>y;iNGjUSYE+flgL%fb#Rkjd^!{03 zn$>LOFHuf1y*yEFjq1dzf?q%M_F7$jqD3KPHwgJ#S$;-H*KzGiCSN_tk;vYP(c(_r z=$bgTvB?E)7ttIF zxV5{EWnMRm^>o5+!`f2)gWi~Hv13Tub%|)?*>7LyvNzeL$&h7b!fdi_hj48w-wl$a zxds@Qp6ebAqFVHGeax*6TyN1K8|x$6+!XkLhXQ*ki?Kl45=&R%pvnR~hg>0>s@q=F z{-?G&wx}mL&`Jet$Fd2jAG<4G@hoh&cafW}fpIO%3K+Gxj4q!QEZ^y(-YY&wuwzBu zH6di5sy$~chlpTHQm)aCwNb`Q{#*qhX~QmtqdkOf8?5EMKj#qK+a<0&zg=7pBRmwlq`5 zn>9M^)+}dJhHXhl9U_84kBC*BhqtIAC!z#3rt19A$zk0V%uUvb0;i@< zwGxCx9DEV>x5a|PNRAcpOg(^c%<|9n24*OGJ1a_n$yxb2tZPb~NWr#o7tA_D@oCOo zaaC!GE_)oCEJ~+!LW}xYzJOpXmoqUXoC9+qA<8RClvRUv@cB55D^puM>aIp@-@@f$ zmfxIoc%QDTdBKAAmf|oA7Nohqq(x&UJ^OV%4=^NKxYo@q0WDXZ7-G?>C$5zUqKMpr z42qCuW=T?c{TOvkNr{PV3Ps6iX~PvRF3P-qN>1GW`eE{-=FW-fQU#ZT()qgK-fX&= zb9L7hyX*iEoJh%bzyt+$p&B)aD3xeil~`N7PV&jDNjBMD4Xf=|)|Kw=8uzSB!A*g} zW=XH&WD`owgORI8I#5?L?(| zL^42P)H=}dn_j0&_6zH|Zp&R+gKz*AMcJfye>#I0z* zYcDOsF?Ue7u(6?uYr~dv(NrzCeOtOC(@A36x6gzdSyoV#XUpJ8=PL77vU+Tv6t7PR-J3-REw)yJw!fRvxcV3NHsARPJuV zgn&XRyI%|yI_IVF@o9<9jN`aTHwF*?6N|zV<^>BhC3`WC>y}+7(dk^)E|ZzuI(Ebn zVu?U8UYLIziu>Hz`c5Zqj-sNagUjV$I$mBh7KyDEEU3?TFy6mwGw&T+BiDn;7Kszp zDOsF_jx*_WZ96jfO*$7&A6R2y;jH@vq^h~xjl0d+_Kf@4M4hVIjwliUYESKv?X}#% z(I_pS{Fp7QAy*fT;KE!V_CeLZJR*6PMaYsXv+XmHbScf+)|Oh}tn;+vfpzzhBeA1( z1kU8P+kr1JPsGjNU7Ls|56ia#m-vStiP@88Ux4X~z83yYq*LS-9e5PEYLlsnzsT8c zGmG4;;JLt!g}uFzNbk$v7LHnS(t%Y`hZU6Q#|}@Nq&auurQ#Lt63qQzfK$r?uggvwpi?@oo``EHmLvcL05jz ziMYc3&acp4EruepRg;{li}J97)bAGxV>!pplsI>H;dQ^h@vkHblPn}87I^J1j5}~E zkk!GTO^9B$H)IMgcHv%hS-7Y$_WqQzFS?sd#QJEpR1{-V*UoCyMIpV2MHg0!{4Beh z()@_Z&IA+BTD$JlWs6p&moK%arUhrF&|;U6E>2NGR?I>D@hiR}pZMmo8v=?v;~(gC z?TKu|p2TWhQ&4J;x{_K~njdbq+Czh)M71>Sf`L|W6p5d_ zTpMo0BS>K?cF$Yr6kW>G5_sZS^E)w>v&LvKSoA9vH?2m$kxaG(3H7{lz9kem`z3TN z=qvDb#Vl}gB`k1?2A%>pI~dxL+f%?{Ky;5N?-bovk1MtyiK1Hf>vx)`tvmh74e6}^ z`SF4UD|4GyGCLy~D{wy5?k!KywTMEGwNCuM#+_lT$nNYS%&Z&Wb0?e zc?aEJ*FDXo8&r03EBrk)46yra_mgoq9xXj7eadQ`L9ST7I_)kb}oh<>)qd(v-p zbengGhKsy|9V-atgg9xkccK!0dTV=be2s;}0%B?LXH%og#6y~EjXBUNPXerts^7C8fEc=TkIH-0Q!Er7z9-R)v zXj}GtAGD=*XJd|2wc^~DEL*uUy=Kvdm8UFoo9GA+pZ31o5p-J zcMjVrCTRQn@&ntEMZz5?=Q_9~B5?zIG;Whi@NnFLrN<`GUCBDI=uwH>R?BS|)%Wcd z(*jC%O>1s5S42~qOxrfDYN%_?sZXZXH?(!sHm26LBgM*XT;lGjtXjJ?yY1-t8y7b; zZ?s&W(OkFbpi-&h&47bVqp6-B3pd(ij@6@8k&*V8*nEQ*-I7Kxnak+qKWp?P7)Gz1 zC(W-2r`J;Tilbhw-OWpEvC#|D($*{uD|)R~T7&dbp^h%~pCo6Qft6 z?{=X7{|Um%TuPX$$V-}VWb~?XZ1m!%w6%PPm;IzEOs^-_%;9A-UYUG!f(xY=TDvlO zF*!Y^(C3>xTXrlDd##VkEML9S9h(8X*4Dr)ZLepfMOa2J%<5HXy*w$SSNx@oA_E`w z^7{4DoO|*AdU-o(qzOz~aisaHma=_?C#-Jn?X-sJCAnYD;I(EVNGpO~anx(t&%zUF zv?(@v0e7e#HN#v2KlPg2g*6tnuf98o7n!3n?jtX0laVHsi^*Tw-t;1>UNJuf{1(dd zGI>e6&Fy+oBVjU8KCzQtb8VoFNc*$Q1Xhw4*aAuU=u)tK-in_^H?S zSJQW5qZf|#ioRa){#gyLHdB_n;it3~=*3-W{uo33Pry$vGC#yunh~Z_chcApo5hSR z$55`*qH>{zTgkB7l#tOY|DI=|K#YV56N~?=rJ+7c{ zOKXXYUcl9>=COI6boGMvcN1+T_S@gLx{EsLDr^h<(JR@~s3`48qn36KwKzrlpxh%V zuS{JcjI=Z8)l0p;ye7)?i?s7waQg$eURKttt$Gn#MlS}(Mz5*s6?kdIlSY+dO?cAm zDYG8l$Vg+3%!a7%O9PpVUJlo*tg(57_9U%bGKWR=su$p8n!u%nP1-xYml#v@8h6r2 zo1`?i{V~cze$oK8Kl83ApQQinh^G2U)G=0?ar822Z1h6+a}iHZ$6v26%jkvZ*i5D@ zvC%8HdzgEql_oZNCAir9g1)I2s$+8oabnXk%|X&wDs88CFJjz)S9*b5+S;UnO0W37 z54z5w>?;%HS4_R~ygwst7}7E$?G7^1aAytmEUlZ<7+>|`wTv|1$mr!^y^dOJ^rCER z^m=V8@)&?BaI@|>lx7HY|nLwK5W>GJLaWB?-DUH3-s;C$1rGe#zUaT)^ zTR%i(COUTMdXhTpD?v^2zKC}oj`-PqhRJ%-#@nv-q=)GN@jd2&DbdVlI~BW+t+h@|=C zkF;%Rlzcs-SNgj$(m?SC>NutHl2){mpNzD2Nn=lJe#h7;ty?nEUM1}?v9VfxV0=`e5tk?GOqwjEDMChCPGfTg{HuxD(bLeMO|*eHO!6yj#Xq1PW3!erN_&^I z%;sp<(wMU@h<%yMp+iR6A$LQAG$HSyFD)XkJ=Faa)_k$Ksi@4cyR-&KE8goFX*rZR z0zR!GeZ5xSmC?)KFGqDEe`$~?HqwwGO&$9)()xE3>3luqw2r?_`;t~wX~cMxzPvV~ zRhq{3;r=_;3U}eRfp#Jdm9dd#OqpllO>CqIQ(B-~7)PY>;|l6@H*Q^-iW?dt?MND~WTfF$n#;Q~(pVzx-Nj~af7&W_EbX5AGb^B7ny2(i{iz zwC_jTkjB*t_$X~T50Q?v(@C4b+?3Ps{X61G)C(YeL30${?)?(opwC%;#x~u^F<|L7$>c z#YUP%lSbOC7EW+3(u^!E5yeIth+=a@4RZu_A}yxU-qit*ev+d9!e?pEmF9{pWtPTh zX`7UJJG_)x!`L8AGsWfv`h|?NJXI66i7`i-$7E^~{r)y+8;`AlS1)vD-@e3!(f`jx zZB|-3q_wXF`hGi)@rtyhRc9u=Jf1cst+F>_FN&i6nEa%%R+?_4ZDkZ~VjyLb#)8k* zFs{%ZKb`3FQ8fQ)f{vsqd!4>Bd~80Xw5dyrMQo%2lq`mxE_6X8AbSY()rloRd zJrCN>p*}uHTxqHJ1obHmPST7ijiE9}m%Hy^`eJOPq3bHzhBT*3yXo`LBJD4?MSPal z4Vfz@yRS4_NOO}ksL4pHOAF}^SmZR+$n7G=7HMvhk=CQ#@G3ShCCVzzN;1+AD-H2I z$)k??k-3AmB+WTVBMlu%Bh9JO&>{`s%c;-UyaXUkL8Fd}UEKFKq%RGn(hNH{YFmXN z*z?iHr43SA9gkXqJOdt1rM*gPsWd}wVw{jR4r!Y?pRqz(5@kZtlqNYDX+V>aR=|6s zG74YEI3ta=|CMl)d(ucVuS^yBN`qn2NQ=+@ObR~BoK2gMl8C3H@#*J9W2!X1NyDNv z4vnB((!L|@?$W08Uh@7v<%o^6K*vTJvy095;rkcLoa>LGZyZLNprj2=8eXN%Nm?vp zBkgt4I4G^o(s0+6*^J}-h_|u-{qRmk8dYO+qRJbYD``6vqrgbhS!{NXplwd2?@$lY zM05K%)uoI>pQ?Sc@Sw{d^_1vt z^`WVCe+}ml6`JQW4n;~)QT`xfUW_?JWVuU5holyu z%wB{!H2g~Z?&_d<7FA~S8>~Z>ocqbpp-L_4r&|twQY-Eahb~F`lZ=BWP)%6BV>xKn zb-KiL&?!c|i!O8>vgG2+T?d_@`j~xi>)@G}>=aR5xOEyjV9kf(K za^ZsbqPGS*wCDRa>GtQllYs1{&1AkQzP*X0GTcab_Ukr|()v&NVZTp&_E6?tHkK3l zPA477u9jhmV%`JY?Cns|EM2hhAGGMFuuA(p*`F~lmi)q`8ROw+v^BCf$#si z(Ps8s>~nqh1s{O(cl6sw5jjnZzI*xp{%rg=>4<$DJBI^3UY*caRg{!(UNa6R80#9PXD zSKYoq%r%?$1?@1kitja~m*QJPJd@oQ+{LGg?^pRgO*&N&@HpuYB;Mq&?F+7i`7hx< zka#D7t0HQmbY}6@=KR_7pb!5o_-_G!1$+YE9$WVXU!ld$#~sxCd?xA3eb&BUmTS!3R2PBm-II&&J$@^rLUZ$N|lS!Sa- z8||ZMb1u4xc@xiM%&};FKHk)rMQB@IfM)ciG_MoPiRL7;+?;Gyn3Z%J>Czld(;vz4 z@MyY9ie@~{jHeMzq@his@lK&bO*7Nc-Z=veqO;6w^xMug^Ux}Kq&W(`;YXWe&}?2Z IZ*30zKaoHL4*&oF literal 0 HcmV?d00001 diff --git a/tags/tor-0_0_2pre19/Win32Build/tor/tor.opt b/tags/tor-0_0_2pre19/Win32Build/tor/tor.opt new file mode 100644 index 0000000000000000000000000000000000000000..8ae08d42887fefb60b0496e21633cdd1c0ff4324 GIT binary patch literal 55808 zcmeHQPjK7Vd8cekmT2#;ck8BU+H_y#c9)7RQIxc{rFGmTK~Ux{MQTaPvNuc-5CBD3 zB*6hlTZ(5h>7kQTXRkT*()84HGSf~cGre|t%ORWF_S9+jmdxz*lvC4gf8WC&f)ojf zwmWWT9_)|!@ZN{_9v&VZo__fL{NmHs|Lw2-{%ijp8`uVDWw=rs_AK%q27xXpI?}2_F^mWi5fZha=+y~e%g06x72=vFGw?J=$5}+l} zH$WtN1N&vr3MdI$1*Jf1pmop&h~#c!e+zUQvEEi!oez2YWkqjGE5AeFY=NZ&NTgOgR0$ zU;In|FY5n1L)YPp?4x!YSLejKF%`XFpd}TwLj_S^oVKypxtVhiqOnWuI8C1NNuCc@ z>@uj`iakO72Ds_BD=3Yg9ePgATK{FLh8`AdGVZ#GYo%UzmhDwsKRv7T%$~LWuTVu^ z{WG|Kbl=Wdww&FnWi74WHab0rsU4kFj9$;|95}U(sU5a!JI5@@uk|duwr_UU*D`&x zrPpv|*(-Qw?WDd+X`?#_gLoH*aT`H#X(Waw;X|mNU7Ho6D=K>GbOA zhLl>9*FKKLXp*6g+Cv>m+iyU>1n~^~Ji-Y283fM9e&{Fhy_A%$Uypr)OIpAs{dDH@ znV(>Oii7_#_#c741pe3HzW{$3{Ab`l1^){8>AMUS@rjEnrwJ^)JzJOIHrD%%hGD;` zE~BwV^tb4;pQFppz2q*-pe-MI{p9bx!&qkO2Ou=c`aQF?(tPXbT3TMam0r&-r*7rm z!7$6NE~j&;3=OkYX>&EVnU!yzGR&xrM4x$mHuIAE>^j;)@5VPnd$4@VJx`xC)I+0d zwOTLe@uc@H(HCC7#a?p1{VUq?A0V2|{5@E{J^xsnZc0O!@b?r=XSDfK8claZ|K#oT_{u^?#l6Ny3sH@2HVdUO9dG@ zN}_k%HI^9tP7+odM@ri{2u@B{3rex1+^?oH1(_*|ZfGs1YiNm%^|+_jTgFmY6qT@h zWx29fsE#BniK8WUG)T-nP^7FZ6;Px`We(>Q(v^xLKiDmmt3jPdKlmFO60w)2^;s{CWcjL=rHp%KV%!9tI_Drj@#VngG zGrl}vS)<-R7<0yQ97UouDpJCmnid=d^swV5OSXZsItQ$0w7ZtA+Q+Pck;xWm!YN66 zJM#BZ-nyhv!dhx$y6aQ8`*|A#IfyI(LRh=dG z*I9DA4SK+mALUuHgtReceEMmMvm9a1pRd zR?h5gyUB`FDpoK~yrZ3rQ~{goF7n<;VyV>Cnn-zws&kf?N)mdA&!TA`v82Z0{Gz8? znvK4hw8i+h<3XR&%82^AMI(AqYpobZ277Oso*k6+O!p@>{{9>qcE@O~udTGqI_o=z z?Xd^C>2bIvPS>(pTmqM&w+_4XjQ_}bU1KUy55_{+QDRL!)tzS&zDNp*+GiBx*jPbPK@jUN7q8H(DSoM-3< zMnvw>n6`Di3Kcv}Bki`2yFF_pQ8#Tg(6Wz5(>u;cO0X;$E)_G!h+tbaM=#gvoa_P{ zcav7s!^NQ~%9sN9I+9CHd|vU?9!~MZy3ZP)XG{HFx8I|QS+|~e^DN%)CQ3Y5Sy_qm z%C$JA;X^Ma9_J^P4X5Agc~dj`o92sUw#p3Kw(NwnbOZbo)$ZUd9ARd6Um!{Eb&_=7 zF4tO?rnaP(>NsqRb^G;}sj<517;e&2FKLTq$fiJ-(KCKt0oF@txjTm0a>-E_H{7!-SB(55A(@zxGC;bpWv=$gl- z-Rr9@wpS<>w`tw6BUkU0vi>@SrFFe*G!jeiveP8X2Bxupn4dX!?#g5HXeJpW;;W4@ z#eUc~?BhIMq;^Bqj6_+^a7zlZN+WIsz-UU*OYu4Q(5Y%o-Y?o46{Ic=p=YBU_9$G*ud&RC7apwhN-6dq6L zsE>mNlk?&)=h{{7(d@vz@TdvXyuUak@3g|FpulPZ+v|k)^J@?YZ zY(XA34&0pewQ+gD)RZvkaa&>-^X`+!c*2Q$7@VNEXSh??_>+gGuk(X1I)r!y_@2Q3 z&>QZb%7Z_a=?^B_1paqs$W-8e0{;sp75>p6^p|O{(4G#g$rOTG#HzmT4x(Ek(q ze?tE+DfIt@{-4nQ3v?HuM(Jz#8AAUrgx3rGKTnBJ=>G}*KcW98^#8_c2@3r0Eco9z z`|EvJBzIt!Om@+yZHutzBtHSOAcD(Ilc)SYq#w`zxPn^xuze=WZv{rImS=@-#acMd zunNB>WI z{b{Pk>;Ln(bFlqh;2*Z`o5qvbJ94RfH~briF&9_IH5@njM8Z7%Ek8K<>-&~{=yajD z%HD>R!`oXd#k#ij2<8_s4wy%AWXa`Ru2_8=GIX+Y`JSQbhV34B)dUt2+JuClsBm*f z8YAq#&uYz2ih08R`?;*_g#GvFP9uc=Cr!Ts{}uSJu>ThJ-@^VoY@aXiUxEJ${I}&= ziVOR1VgK!$z%L2?Poe)Q^gmq-dtv|m;_ScEf9cv%Ub)k;dVDEn!y%iY-(7`Y8GQ&0 zfk%a~H^}xe3-U>^D|UC@{>2h0pV=5z4!%fq?M$E00X zdGE@o%o`y$@7$ZLW00S*U(J$tHrlR)1h_-$K{j#!83=j-2jJH2QcS9<_5r-D!j<-k z^Qllzc1V4222oacGq>Svx$8-|{``kD3nS!Sg^%NqLSlsEdEG#mo2%h)2=GtagEMHu z_NtCsa1yS-U3a9{vAStR<;R`lc2m_3ohYGs&2@Gi5uo9sp;%62gocYw5A{S<^NQ#F z9!5EhD7jhNi465{$x$1|skL3GccLnpQ=LvKN+9mF_HmTZ0{MyVIgxQA=GwXz*>f;m zeJ84tSzC(?Z7~ZE+L7l|^rDD+JL@zf`y2H~w_lIGUl&_V)oDhxVBR(n03@;((TK=S zx}ZnaFpKh|S}?0e_fed}hbWI5$5DmE>n-a^!;HSx1$cyl7IX9+F&;-g^$TdNVznYW z>|*y&b8ba7cTQ8C$fzvTRQQ+Iq6)#_jT{^z{)>qJLSN@Z{1*zqBI3VNUw_%G}8e}?!^@oPc+C%20Sc;fgl zKRa@zlHQglgvUXMON2nmR}hVBOh}w!sd{RVq*CU6gp5K&#fntU?^5ijaba>0U{Tu1 zPG|&Wo*pI0m-_xEUOdd1zdlFtFcC})p_wT5*97;XbYhi<20cxT!iN^}nP{ zvYY;JPaYpJ2$8ialWxau=?sPMmP@;G84;5zd%F}V)NSs`2u>uoi(8-1RwAzma#gy8 zxqJ~JpC}w=wY(=)$Bc(ysPOK-!MZ%C82{}M+i0l=h$(l?)1mPpZ~rt5wMuEPER7Aa zbV3LryvD@js>tPhx)6Cim1?@0mx6H}S?eU(@s;1t%litQvR6p}8SV9*-1sZrr(kvz zJuI=`(Ye8XajfSHlEYn;%O{0zu##;agiiPI|7+w00G|S8MqqWshv)Ir39eu8V@GQI z$oCoV9eJ@L`O!SV#E22e4=xWJIk^e14H76OBo}%;l*VI0o=hblP&_q!KE6`$G#>`% z2bTu19{KUCju?-eiwC~l-3;>S&bc`rijlJy-8DSt7yQ_bu22uqwO7!BdJ2GW~|A#;3ge;Q<{x9(V&~Om=zli@X;=hMuf6@5A1V5{4%)!6=@cL@N<133z>Hl?V|Em;i%J)B-MV)nc zfIYxJ1Abtq-QMv0zs7OmS^clxr%F8kr&)M@)lh#MwRd7Y^uu)9)o1X(`W>pm^ZzIZ=?^+6lf2$0k zHPX96|6AyPvt*v5nnM3u=zp)Y%sPW4DS@7c`;&>1u`w{m-bsw1xkN5$Hqsf5?=D|A!GkO!$8o0?`Hj7g)c* zgDg-<1x76FXpoqDph#pF!xTl+4XrhtLnb3IXCkW}2vEM$J^F!X?{x0F_<}kgyVLBo zTX*Ak>Xv?tUDr17+f3cf2>egre__qd)s(4d;Eh*J|u!hf$vA-S?t>lZ_ z1-S})drV7XiY*n3u%+Q+fj4Yx*5DbiJ~{0(-$1bJDpM?gG(2ITYnOYSn)GYht=u~pX4%!{bS{;lVYVu5uI4tg^379*SqArz z=ratnpJSNKyyRha9c{^i-VE)*@-0W^e$U!x17{2`hsk&1I!3s3ag1; z)8NkQZm^c6sV&YgG4A}2J-BvlkT?J;g#KRx?*^g&C-nbbwEmy)|LS^)75-m^|JTTm U4wHPkj&&zH!B_16LI1D+1Cr+-umAu6 literal 0 HcmV?d00001 diff --git a/tags/tor-0_0_2pre19/autogen.sh b/tags/tor-0_0_2pre19/autogen.sh new file mode 100755 index 0000000000..5b97585997 --- /dev/null +++ b/tags/tor-0_0_2pre19/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +aclocal && autoheader && autoconf && automake +./configure + diff --git a/tags/tor-0_0_2pre19/configure.in b/tags/tor-0_0_2pre19/configure.in new file mode 100644 index 0000000000..4acc8dd062 --- /dev/null +++ b/tags/tor-0_0_2pre19/configure.in @@ -0,0 +1,188 @@ + +AC_INIT +AM_INIT_AUTOMAKE(tor, 0.0.2pre19) +AM_CONFIG_HEADER(orconfig.h) + +CFLAGS="$CFLAGS -Wall -g -O2 -I/usr/kerberos/include" + +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 + ] +) + +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 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/wait.h netinet/in.h arpa/inet.h) + +AC_CHECK_FUNCS(gettimeofday ftime socketpair uname) + +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) + +dnl AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_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) + +# $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 tor.sh src/config/torrc doc/tor.1 src/Makefile doc/Makefile src/config/Makefile src/common/Makefile src/or/Makefile) + diff --git a/tags/tor-0_0_2pre19/depcomp b/tags/tor-0_0_2pre19/depcomp new file mode 100755 index 0000000000..3480ce4e96 --- /dev/null +++ b/tags/tor-0_0_2pre19/depcomp @@ -0,0 +1,436 @@ +#! /bin/sh + +# depcomp - compile a program generating dependencies as side-effects +# Copyright 1999, 2000 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi +# `libtool' can also be set to `yes' or `no'. + +if test -z "$depfile"; then + base=`echo "$object" | sed -e 's,^.*/,,' -e 's,\.\([^.]*\)$,.P\1,'` + dir=`echo "$object" | sed 's,/.*$,/,'` + if test "$dir" = "$object"; then + dir= + fi + # FIXME: should be _deps on DOS. + depfile="$dir.deps/$base" +fi + +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. This file always lives in the current directory. + # Also, the AIX compiler puts `$object:' at the start of each line; + # $object doesn't have directory information. + stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + outname="$stripped.o" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + + base=`echo "$object" | sed -e 's/\.o$//' -e 's/\.lo$//'` + tmpdepfile1="$base.o.d" + tmpdepfile2="$base.d" + if test "$libtool" = yes; then + "$@" -Wc,-MD + else + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + if test -f "$tmpdepfile1"; then + tmpdepfile="$tmpdepfile1" + else + tmpdepfile="$tmpdepfile2" + fi + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a space and a tab in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + test -z "$dashmflag" && dashmflag=-M + ( IFS=" " + case " $* " in + *" --mode=compile "*) # this is libtool, let us make it quiet + for arg + do # cycle over the arguments + case "$arg" in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" $dashmflag | sed 's:^[^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + # X makedepend + ( + shift + cleared=no + for arg in "$@"; do + case $cleared in no) + set ""; shift + cleared=yes + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift;; + -*) + ;; + *) + set fnord "$@" "$arg"; shift;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + "$@" -E | + sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the proprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + ( IFS=" " + case " $* " in + *" --mode=compile "*) + for arg + do # cycle over the arguments + case $arg in + "--mode=compile") + # insert --quiet before "--mode=compile" + set fnord "$@" --quiet + shift # fnord + ;; + esac + set fnord "$@" "$arg" + shift # fnord + shift # "$arg" + done + ;; + esac + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + ) & + proc=$! + "$@" + stat=$? + wait "$proc" + if test "$stat" != 0; then exit $stat; fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 diff --git a/tags/tor-0_0_2pre19/doc/.cvsignore b/tags/tor-0_0_2pre19/doc/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/tags/tor-0_0_2pre19/doc/CLIENTS b/tags/tor-0_0_2pre19/doc/CLIENTS new file mode 100644 index 0000000000..8e98853615 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/CLIENTS @@ -0,0 +1,63 @@ + + 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 + +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_2pre19/doc/FAQ b/tags/tor-0_0_2pre19/doc/FAQ new file mode 100644 index 0000000000..28e37e37a7 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/doc/HACKING b/tags/tor-0_0_2pre19/doc/HACKING new file mode 100644 index 0000000000..046521b3e6 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/HACKING @@ -0,0 +1,533 @@ + 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. + (No error or warning messages should be expected during normal OR or OP + operation. I expect most people to run on -l warn 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] + +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_2pre19/doc/Makefile.am b/tags/tor-0_0_2pre19/doc/Makefile.am new file mode 100644 index 0000000000..9a346307c0 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/Makefile.am @@ -0,0 +1,4 @@ + +EXTRA_DIST = tor-spec.txt CLIENTS FAQ HACKING rendezvous.txt tor-design.tex + +man_MANS = tor.1 diff --git a/tags/tor-0_0_2pre19/doc/TODO b/tags/tor-0_0_2pre19/doc/TODO new file mode 100644 index 0000000000..857e31b2ee --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/TODO @@ -0,0 +1,307 @@ + +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 0.0.2pre17: + o Put a H(K | handshake) into the onionskin response + o Make cells 512 bytes + o Reduce streamid footprint from 7 bytes to 2 bytes + X Check for collisions in streamid (now possible with + just 2 bytes), and back up & replace with padding if so + o Use the 4 reserved bytes in each cell header to keep 1/5 + of a sha1 of the ongoing relay payload (move into stream header) + o Move length into the stream header too + o Make length 2 bytes + D increase DH key length + D increase RSA key length + D Spec the stream_id stuff. Clarify that nobody on the backward + stream should look at stream_id. + +Cell: + ACI (anonymous circuit identifier) [2 bytes] + Command [1 byte] + Payload (padded with 0 bytes) [509 bytes] + +Relay payload: + Relay command [1 byte] + Stream ID [7 bytes] + Partial SHA-1 [4 bytes] + Length [2 bytes] + Relay payload [495 bytes] + +For 0.0.2pre15: + o don't pick exit nodes which will certainly reject all things. + o don't pick nodes that the directory says are down + o choose randomly from running dirservers, not just first one + o install the man page + o warn when client-side tries an address/port which no router in the dir accepts. + +For 0.0.2pre14: + o More flexible exit policies (18.*, 18.0.0.0/8) + o Work to succeed in the precense of exit policy violation + o Replace desired_path_len with opaque path-selection specifier + o Client-side DNS caching + o Add entries to client DNS cache based on END cells + o Remove port from END_REASON_EXITPOLICY cells + o Start building new circuits when we get an exit-policy + failure. (Defer exiting from the middle of existing + circuits or extending existing circuits for later.) + o Implement function to check whether a routerinfo_t + supports a given exit addr. + o Choose the exit node of an in-progress circuit based on + pending AP connections. + o Choose the exit node _first_, then beginning, then + middle nodes. + +Short-term: + - improve how it behaves when i remove a line from the approved-routers files + - Make tls connections tls_close intentionally + o Rename ACI to circID + . integrate rep_ok functions, see what breaks + - update tor faq + o obey SocksBindAddress, ORBindAddress + o warn if we're running as root + o make connection_flush_buf() more obviously obsolete + o let hup reread the config file, eg so we can get new exit + policies without restarting + o Put recommended_versions in a config entry + X use times(2) rather than gettimeofday to measure how long it + takes to process a cell + o Separate trying to rebuild a circuit because you have none from trying + to rebuild a circuit because the current one is stale + X Continue reading from socks port even while waiting for connect. + o Exit policies + o Spec how to write the exit policies + o Path selection algorithms + o Choose path more incrementally + o Let user request first/last node + o And disallow certain nodes + D Choose path by jurisdiction, etc? + o Make relay end cells have failure status and payload attached + X let non-approved routers handshake. + - Dirserver shouldn't put you in running-routers list if you haven't + uploaded a descriptor recently + . migrate to using nickname rather than addr:port for routers + o decide_aci_type + - generate onion skins + - circuit_send_next_onion_skin + - circuit_extend + - onion_generate_cpath + - get_unique_aci_by_addr_port + - circ->n_addr and circ->n_port + - circuit_enumerate_by_naddr_nport + - cpath layers + - connection_or_connect + - connection_exact_get_by_addr_port + - connection_twin_get_by_addr_port + - router_get_by_addr_port + - connection_or_init_conn_from_router + - tag_pack, tag_unpack, connection_cpu_process_inbuf + - directory_initiate_command + . Move from onions to ephemeral DH + o incremental path building + o transition circuit-level sendmes to hop-level sendmes + o implement truncate, truncated + o move from 192byte DH to 128byte DH, so it isn't so damn slow + - exiting from not-last hop + - OP logic to decide to extend/truncate a path + - make sure exiting from the not-last hop works + - logic to find last *open* hop, not last hop, in cpath + o Remember address and port when beginning. + - Extend by nickname/hostname/something, not by IP. + - Need a relay teardown cell, separate from one-way ends. + +On-going + . Better comments for functions! + . Go through log messages, reduce confusing error messages. + . make the logs include more info (fd, etc) + . Unit tests + . Update the spec so it matches the code + +Mid-term: + - Rotate tls-level connections -- make new ones, expire old ones. + So we get actual key rotation, not just symmetric key rotation + o Are there anonymity issues with sequential streamIDs? Sequential + circIDs? Eg an attacker can learn how many there have been. + The fix is to initialize them randomly rather than at 1. + - Look at having smallcells and largecells + . Redo scheduler + o fix SSL_read bug for buffered records + - make round-robining more fair + - What happens when a circuit's length is 1? What breaks? + . streams / circuits + o Implement streams + o Rotate circuits after N minutes? + X Circuits should expire when circuit->expire triggers +NICK . Handle half-open connections + o openssh is an application that uses half-open connections + o Figure out what causes connections to close, standardize + when we mark a connection vs when we tear it down + o Look at what ssl does to keep from mutating data streams + o Put CPU workers in separate processes + o Handle multiple cpu workers (one for each cpu, plus one) + o Queue for pending tasks if all workers full + o Support the 'process this onion' task + D Merge dnsworkers and cpuworkers to some extent + o Handle cpuworkers dying + . Scrubbing proxies + - Find an smtp proxy? + - Check the old smtp proxy code + o Find an ftp proxy? wget --passive + D Wait until there are packet redirectors for Linux + . Get socks4a support into Mozilla + . Develop rendezvous points + X Handle socks commands other than connect, eg, bind? + o Design + - Spec + - Implement + . Tests + o Testing harness/infrastructure + D System tests (how?) + - Performance tests, so we know when we've improved + . webload infrastructure (Bruce) + . httperf infrastructure (easy to set up) + . oprofile (installed in RH >8.0) +NICK . Daemonize and package + o Teach it to fork and background + - Red Hat spec file + - Debian spec file equivalent + . Portability + . Which .h files are we actually using? + . Port to: + o Linux + o BSD + . Solaris + o Cygwin + . Win32 + o OS X + - deal with pollhup / reached_eof on all platforms + o openssl randomness + o inet_ntoa + o stdint.h + - Make a script to set up a local network on your machine + o More flexibility in node addressing + D Support IPv6 rather than just 4 + o Handle multihomed servers (config variable to set IP) + +In the distant future: + D Load balancing between router twins + D Keep track of load over links/nodes, to + know who's hosed +SPEC!! D Non-clique topologies + D Implement our own memory management, at least for common structs + (Not ever necessary?) + D Advanced directory servers + D Automated reputation management +SPEC!! D Figure out how to do threshold directory servers + D jurisdiction info in dirserver entries? other info? + +Older (done) todo stuff: + + o Get tor to act like a socks server + o socks4, socks4a + o socks5 + o routers have identity key, link key, onion key. + o link key certs are + D signed by identity key + D not in descriptor + o not in config + D not on disk + o identity and onion keys are in descriptor (and disk) + o upon boot, if it doesn't find identity key, generate it and write it. + o also write a file with the identity key fingerprint in it + o router generates descriptor: flesh out router_get_my_descriptor() + o Routers sign descriptors with identity key + o routers put version number in descriptor + o routers should maybe have `uname -a` in descriptor? + o Give nicknames to routers + o in config + o in descriptors + o router posts descriptor + o when it boots + o every DirFetchPostPeriod seconds + D when it changes + o change tls stuff so certs don't get written to disk, or read from disk + o make directory.c 'thread'safe + o dirserver parses descriptor + o dirserver checks signature + D client checks signature? + o dirserver writes directory to file + o reads that file upon boot + o directory includes all routers, up and down + o add "up" line to directory, listing nicknames + o instruments ORs to report stats + o average cell fullness + o average bandwidth used + o configure log files. separate log file, separate severities. + o what assumptions break if we fclose(0) when we daemonize? + o make buffer struct elements opaque outside buffers.c + o add log convention to the HACKING file + o make 'make install' do the right thing + o change binary name to tor + o change config files so you look at commandline, else look in + /etc/torrc. no cascading. + o have an absolute datadir with fixed names for files, and fixed-name + keydir under that with fixed names + o Move (most of) the router/directory code out of main.c + o Simple directory servers + o Include key in source; sign directories + o Signed directory backend + o Document + o Integrate + o Add versions to code + o Have directories list recommended-versions + o Include line in directories + o Check for presence of line. + o Quit if running the wrong version + o Command-line option to override quit + o Add more information to directory server entries + o Exit policies + o Clearer bandwidth management + o Do we want to remove bandwidth from OR handshakes? + o What about OP handshakes? + X Move away from openssl + o Abstract out crypto calls + X Look at nss, others? Just include code? + o Use a stronger cipher + o aes now, by including the code ourselves + X On the fly compression of each stream + o Clean up the event loop (optimize and sanitize) + o Remove that awful concept of 'roles' + o Terminology + o Circuits, topics, cells stay named that + o 'Connection' gets divided, or renamed, or something? + o DNS farm + o Distribute queries onto the farm, get answers + o Preemptively grow a new worker before he's needed + o Prune workers when too many are idle + o DNS cache + o Clear DNS cache over time + D Honor DNS TTL info (how??) + o Have strategy when all workers are busy + o Keep track of which connections are in dns_wait + o Need to cache positives/negatives on the tor side + o Keep track of which queries have been asked + o Better error handling when + o An address doesn't resolve + o We have max workers running + o Consider taking the master out of the loop? + X Implement reply onions + o Total rate limiting + o Look at OR handshake in more detail + o Spec it + o Merge OR and OP handshakes + o rearrange connection_or so it doesn't suck so much to read + D Periodic link key rotation. Spec? + o wrap malloc with something that explodes when it fails + o Clean up the number of places that get to look at prkey + diff --git a/tags/tor-0_0_2pre19/doc/latex8.bst b/tags/tor-0_0_2pre19/doc/latex8.bst new file mode 100644 index 0000000000..2dd3249633 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/doc/latex8.sty b/tags/tor-0_0_2pre19/doc/latex8.sty new file mode 100644 index 0000000000..2028cee9c7 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/doc/rendezvous.txt b/tags/tor-0_0_2pre19/doc/rendezvous.txt new file mode 100644 index 0000000000..f006da7257 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/rendezvous.txt @@ -0,0 +1,200 @@ + How to make rendezvous points work + +0. Overview + + Rendezvous points are an implementation of location-hidden services + (server anonymity) in the onion routing network. Location-hidden + services means Bob can offer a tcp service (say, a webserver) via the + onion routing network, without revealing the IP of that service. + + The basic idea is to provide censorship resistance for Bob by allowing + him to advertise a variety of onion routers as his public location + (nodes known as his Introduction Points, see Section 1). Alice, + the client, chooses a node known as a Meeting Point (see Section + 2). This extra level of indirection is needed so Bob doesn't serve + files directly from his public locations (so these nodes don't open + themselves up to abuse, eg from serving Nazi propaganda in France). The + extra level of indirection also allows Bob to choose which requests + to respond to, and which to ignore. + + We provide the necessary glue code so that Alice can view webpages + on a location-hidden webserver, and Bob can run a location-hidden + server, with minimal invasive changes (see Section 3). Both Alice + and Bob must run local onion proxies (OPs) -- software that knows + how to talk to the onion routing network. + + The big picture follows. We direct the reader to the rest of the + document for more details and explanation. + + 1) Bob chooses some Introduction Points, and advertises them on a + Distributed Hash Table (DHT). + 2) Bob establishes onion routing connections to each of his + Introduction Points, and waits. + 3) Alice learns about Bob's service out of band (perhaps Bob gave her + a pointer, or she found it on a website). She looks up the details + of Bob's service from the DHT. + 4) Alice chooses and establishes a Meeting Point for this transaction. + 5) Alice goes to one of Bob's Introduction Points, and gives it a blob + (encrypted for Bob) which tells him about herself and the Meeting + Point she chose. The Introduction Point sends the blob to Bob. + 6) Bob chooses whether to ignore the blob, or to onion route to MP. + Let's assume the latter. + 7) MP plugs together Alice and Bob. Note that MP doesn't know (or care) + who Alice is, or who Bob is; and it can't read anything they + transmit either, because they share a session key. + 8) Alice sends a 'begin' cell along the circuit. It makes its way + to Bob's onion proxy. Bob's onion proxy connects to Bob's webserver. + 9) Data goes back and forth as usual. + +1. Introduction service + + Bob wants to learn about client requests for communication, but + wants to avoid responding unnecessarily to unauthorized clients. + Bob's proxy opens a circuit, and tells some onion router on that + circuit to expect incoming connections, and notify Bob of them. + + When establishing such an introduction point, Bob provides the onion + router with a public "introduction" key. The hash of this public + key identifies a unique Bob, and (since Bob is required to sign his + messages) prevents anybody else from usurping Bob's introduction + point in the future. Additionally, Bob can use the same public key + to establish an introduction point on another onion router (OR), + and Alice can still be confident that Bob is the same server. + + (The set-up-an-introduction-point command should come via a + RELAY_BIND_INTRODUCTION cell. This cell creates a new stream on the + circuit from Bob to the introduction point.) + + ORs that support introduction run an introduction service on a + separate port. When Alice wants to notify Bob of a meeting point, + she connects (directly or via Tor) to the introduction port, and + sends the following: + + MEETING REQUEST + RSA-OAEP encrypted with server's public key: +[20 bytes] Hash of Bob's public key (identifies which Bob to notify) +[ 0 bytes] Initial authentication [optional] + RSA encrypted with Bob's public key: +[16 bytes] Symmetric key for encrypting blob past RSA +[ 6 bytes] Meeting point (IP/port) +[ 8 bytes] Meeting cookie +[ 0 bytes] End-to-end authentication [optional] +[98 bytes] g^x part 1 (inside the RSA) +[30 bytes] g^x part 2 (symmetrically encrypted) + + The meeting point and meeting cookie allow Bob to contact Alice and + prove his identity; the end-to-end authentication enables Bob to + decide whether to talk to Alice; the initial authentication enables + the introduction point to pre-screen introduction requests before + sending them to Bob. (See Section 2 for a discussion of meeting + points; see Section 1.1 for an example authentication mechanism.) + + The authentication steps are the appropriate places for the + introduction server or Bob to do replay prevention, if desired. + + When the introduction point receives a valid meeting request, it + sends the portion intended for Bob along the stream + created by Bob's RELAY_BIND_INTRODUCTION. Bob then, at his + discretion, connects to Alice's meeting point. + +1.1. An example authentication scheme for introduction services + + Bob makes two short-term secrets SB and SN, and tells the + introduction point about SN. Bob gives Alice a cookie consisting + of A,B,C such that H(A|SB)=B and H(A|SN)=C. Alice's initial + authentication is ; Alice's end-to-end authentication is . + + [Maybe] Bob keeps a replay cache of A values, and doesn't allow any + value to be used twice. Over time, Bob rotates SB and SN. + + [Maybe] Each 'A' has an expiration time built in to it. + + In reality, we'll want to pick a scheme that (a) wasn't invented from + scratch in an evening, and (b) doesn't require Alice to remember this + many bits (see section 3.2). + +2. Meeting points + + For Bob to actually reply to Alice, Alice first establishes a + circuit to an onion router R, and sends a RELAY_BIND_MEETING cell + to that onion router. The RELAY_BIND_MEETING cell contains a + 'Meeting cookie' (MC) that Bob can use to authenticate to R. R + remembers the cookie and associates it with Alice. + + Later, Bob also routes to R and sends R a RELAY_JOIN_MEETING cell with + the meeting cookie MC. After this point, R routes all traffic from + Bob's circuit or Alice's circuit as if the two circuits were joined: + any RELAY cells that are not for a recognized topic are passed down + Alice or Bob's circuit. Bob's first cell to Alice contains g^y. + + To prevent R from reading their traffic, Alice and Bob derive two + end-to-end keys from g^{xy}, and they each treat R as just another + hop on the circuit. (These keys are used in addition to the series + of encryption keys already in use on Alice and Bob's circuits.) + + Bob's OP accepts RELAY_BEGIN, RELAY_DATA, RELAY_END, and + RELAY_SENDME cells from Alice. Alice's OP accepts RELAY_DATA, + RELAY_END, and RELAY_SENDME cells from Bob. All RELAY_BEGIN cells + to Bob must have target IP and port of zero; Bob's OP will redirect + them to the actual target IP and port of Bob's server. + + Alice and Bob's OPs disallow CREATE or RELAY_EXTEND cells as usual. + +3. Application interface + +3.1. Application interface: server side + + Bob has a service that he wants to offer to the world but keep its + location hidden. He configures his local OP to know about this + service, including the following data: + + Local IP and port of the service + Strategy for choosing introduction points + (for now, just randomly pick among the ORs offering it) + Strategy for user authentication + (for now, just accept all users) + Public (RSA) key (one for each service Bob offers) + + Bob chooses a set of N Introduction servers on which to advertise + his service. + + We assume the existence of a robust decentralized efficient lookup + system (call it "DHT" for distributed hash table -- note that the + onion routers can run nodes). Bob publishes + * Bob's Public Key for that service + * Expiration date ("don't use after") + * Introduction server 0 ... Introduction server N + (All signed by Bob's Public Key) + into DHT, indexed by the hash of Bob's Public Key. Bob should + periodically republish his introduction information with a new + expiration date (and possibly with new/different introduction servers + if he wants), so Alice can trust that DHT is giving her an up-to-date + version. The Chord CFS paper describes a sample DHT that allows + authenticated updating. + +3.2. Application interface: client side + + We require that the client interface remain a SOCKS proxy, and we + require that Alice shouldn't have to modify her applications. Thus + we encode all of the necessary information into the hostname (more + correctly, fqdn) that Alice uses, eg when clicking on a url in her + browser. + + To establish a connection to Bob, Alice needs to know an Introduction + point, Bob's PK, and some authentication cookie. Because encoding this + information into the hostname will be too long for a typical hostname, + we instead use a layer of indirection. We encode a hash of Bob's PK + (10 bytes is sufficient since we're not worrying about collisions), + and also the authentication token (empty for now). Location-hidden + services use the special top level domain called '.onion': thus + hostnames take the form x.y.onion where x is the hash of PK, and y + is the authentication cookie. If no cookie is required, the hostname + can simply be of the form x.onion. Assuming only case insensitive + alphanumeric and hyphen, we get a bit more than 6 bits encoded + per character, meaning the x part of the hostname will be about + 13 characters. + + Alice's onion proxy examines hostnames and recognizes when they're + destined for a hidden server. If so, it decodes the PK and performs + the steps in Section 0 above. + diff --git a/tags/tor-0_0_2pre19/doc/tor-design.bib b/tags/tor-0_0_2pre19/doc/tor-design.bib new file mode 100644 index 0000000000..337712b51a --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/tor-design.bib @@ -0,0 +1,1082 @@ + +% 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}, +} + +@misc{eax, + author = "M. Bellare and P. Rogaway and D. Wagner", + title = "A conventional authenticated-encryption mode", + howpublished = {Manuscript}, + month = {April}, + year = {2003}, +} + +@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 = {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 = {Stopping Timing Attacks in Low-Latency Mix-Based Systems}, + author = {Matthew Wright and Brian N. Levine and Michael K. Reiter and Chenxi Wang}, + 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}}, +} + +% author = {Roger Dingledine and Nick Mathewson}, +@Misc{tor-spec, + author = {Anonymized}, + 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 2002)", + 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}, + pages = {115--129}, + year = {2000}, +} + +@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 = {November}, +} + %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 (forthcoming)}, + 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 = {2002 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 = {December}, +} + +@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}} +} + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "tor-design" +%%% End: diff --git a/tags/tor-0_0_2pre19/doc/tor-design.tex b/tags/tor-0_0_2pre19/doc/tor-design.tex new file mode 100644 index 0000000000..ecd1999291 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/tor-design.tex @@ -0,0 +1,1847 @@ +\documentclass[times,10pt,twocolumn]{article} +\usepackage{latex8} +\usepackage{times} +\usepackage{url} +\usepackage{graphics} +\usepackage{amsmath} + +%\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}} + +\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. Tor adds perfect forward secrecy, congestion +control, directory servers, integrity checking, variable exit policies, +and a practical design for 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 +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 +low-latency TCP-based applications such as 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 flowing down the circuit is sent 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 and +publicly accessible 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 several 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:} Onion Routing +was originally vulnerable to a single hostile node recording traffic and +later compromising successive nodes in the circuit and forcing 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 link-state information through the network---an approach +that can be unreliable and complex. % open to partitioning attacks. +Tor takes a simplified view toward distributing such +information. Certain more trusted nodes act as \emph{directory +servers}: they provide signed directories that describe known +routers and their availability. Users periodically download the +directories 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 the Tor +network 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 checking 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. + +\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 only tries to anonymize +TCP streams. 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 most of the above features. Our source code is +available under a free license, and, as far as we know, is unencumbered by +patents. We have recently begun deploying a wide-area alpha network +to test the design in practice, to get more experience with usability +and users, and to provide a research platform for experimentation. + +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}. We summarize +in Section~\ref{sec:attacks} how our design stands up to +known attacks, and 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 toward +their destinations. + +Subsequent relay-based anonymity designs have diverged in two +main directions. Some have tried to maximize anonymity at +the cost of introducing comparatively large and variable latencies, +including {\bf Babel} \cite{babel}, {\bf Mixmaster} +\cite{mixmaster-spec}, and +{\bf Mixminion} \cite{minion-design}. 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. These +protocols are also vulnerable against active attacks in which an +adversary 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, %\footnote{ +% The most common approach is to pad and limit communication to a constant +% rate, or to limit +% the variation in traffic shape. Doing so can have prohibitive bandwidth +% costs and/or performance limitations. +%} +% Point in the footnote is covered above, yes? -PS + most designs protect primarily against traffic analysis rather than traffic +confirmation (cf.\ Section~\ref{subsec:threat-model}). + +The simplest low-latency designs are single-hop proxies such as the +{\bf Anonymizer} \cite{anonymizer}, wherein 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 a 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 going into and out of 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 at +about the same time as Onion Routing, provided +stronger anonymity at the cost of allowing a single user to shut +down the network simply by not sending. Systems like {\bf ISDN mixes} +\cite{isdn-mixes} were designed for other environments with +different assumptions. + +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 that circuit's 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 between 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 the circuit +all at once, using a layered ``onion'' of public-key encrypted messages, +each layer of which provides a set of 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 the circuit +in stages, extending it one hop at a time. +Section~\ref{subsubsec:constructing-a-circuit} describes how this +approach makes perfect forward secrecy feasible. + +Circuit-based anonymity designs must choose which protocol layer +to anonymize. They may choose to intercept IP packets directly, and +relay them whole (stripping the source address) along the circuit +\cite{freedom2-arch,tarzan:ccs02}. Alternatively, like +Tor, they may accept TCP streams and relay the data in those streams +along the circuit, ignoring the breakdown of that data into TCP segments +\cite{morphmix:fc04,anonnet}. Finally, they may accept application-level +protocols (such as HTTP) and relay the application requests themselves +along the circuit. +Making this protocol-layer decision requires a compromise between flexibility +and anonymity. For example, a system that understands HTTP, such as Crowds, +can strip +identifying information from those requests, can take advantage of caching +to limit the number of requests that leave the network, and can batch +or encode those requests in order 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 fairly 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 well-known inefficiencies of tunneling TCP over TCP +\cite{tcp-over-tcp-is-bad}. + +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 applications; should not introduce prohibitive delays; +and should require users to make as few configuration decisions +as possible. Finally, Tor should be easily implemented on all common +platforms; we cannot require users to change their operating system in order +to be anonymous. (The current Tor implementation runs on Windows and +assorted Unix clones including Linux, FreeBSD, and MacOS X.) + +\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 provide a definitive solution to end-to-end timing or intersection +attacks. Some approaches, such as running an onion router, 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 anonymization from +the responder is desired for 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 currently integrate +tunneling for non-stream-based protocols like UDP; this too must be +provided by an external service. + +\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 its 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 servers 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 long-term 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 destinations of +the TCP streams and relays data. + +Each onion router uses three public keys: a long-term identity key, a +short-term onion key, and a short-term link 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. Finally, +link keys are used by the TLS protocol when communicating between +onion routers. Each short-term key is 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 single 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) after the +cell header, containing a 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 describe each of these cell types and commands in more detail below. + +\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 one has been used, +and expire old used circuits that no longer have any open streams. +OPs consider making 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 delaying streams and thereby harming user experience.\\ + +\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 the second half of the +DH handshake, 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 is trying to remain 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 downstream (away from Alice) the OR then checks +whether the decrypted streamID 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 the OR recognizes the +streamID, 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 cell is discarded.) + +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. % (Because we use a +%stream cipher, encryption operations may be inverted in any order.) +If at any stage the OP recognizes the streamID, 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 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 streamID 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 streamID 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 whole circuit, Alice sends a \emph{destroy} control +cell. Each OR in the circuit receives the \emph{destroy} cell, closes +all open 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 the 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, all 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 +none is available), 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 streamID of zero (so the OR will recognize it), containing as +its relay payload a new randomly generated streamID, the destination +address, and the destination port. Once the +exit node completes the connection 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 proxy, while others resolve it into an IP +address first and then pass the IP address to the proxy. If the +application does DNS resolution first, Alice will thereby +reveal her destination to the DNS server. Common applications +like Mozilla and SSH have this flaw. + +In the case of Mozilla, the flaw is easy to address: the filtering HTTP +proxy called Privoxy does the SOCKS call safely, and Mozilla talks to +Privoxy safely. 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 not portable. Forcing the resolver +library to do resolution via TCP rather than UDP is +hard, and also has portability problems. We could also provide a +tool similar to \emph{dig} to perform a private lookup through the +Tor network. Our current answer is to 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. When the other side has sent +back its own \emph{relay end} cell, the stream can be torn down. 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, 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. +(Even an external adversary could do this, despite link encryption, by +inverting bits on the wire.) + +This weakness allowed an adversary 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~*}. Any OR or external adversary +along the circuit could introduce such corruption in a stream, if it +knew or could guess the encrypted content. + +Tor prevents external adversaries from mounting this attack by +using TLS on its links, which provides integrity checking. +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 include 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; tagging attacks performed +within the circuit provide no additional information to the attacker. + +Thus, we check integrity only at the edges of each stream. 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. From +then on 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 Alice or Bob tear down the circuit if they +receive a bad hash. + +\SubSection{Rate limiting and fairness} +\label{subsec:rate-limit} + +Volunteers are generally more willing to run services that can limit +their own 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 generates roughly the same number of outgoing +bytes as incoming bytes, 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 256-byte cell. +(We can't just wait for more bytes, because the local application may +be waiting for a reply.) Therefore, we treat this case as if the entire +cell size had been read, regardless of the fullness of the cell. + +Further, inspired by Rennhard et al's design in \cite{anonnet}, a +circuit's edges 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). + +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 +%are probably not optimal; more +%research remains to find which parameters +seem to give tolerable throughput and delay; more research remains. + +\Section{Other design decisions} + +\SubSection{Resource management and 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 who sends a +\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. + +Several approaches exist to address these attacks. First, ORs may +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 may 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 (comparatively +inexpensive) work of symmetric cryptography needed to 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. Indeed, this same loss of service +occurs 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 that no streams are lost unless the entry or exit point itself 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 antisocial or illegal attacks. +%Indeed, because of its limited +%anonymity, Tor is probably not a good way to commit crimes. +But because the +onion routers can easily be mistaken for the originators of the abuse, +and the volunteers who run them may not want to deal with the hassle of +repeatedly explaining anonymity networks, we must block or limit +the abuse that travels through the Tor network. + +To mitigate abuse issues, in Tor, 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. Using a private +exit (if one exists) is a more secure way for a client to connect to a +given host or network---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 will function as +\emph{restricted exits} that permit connections to the world at large, +but prevent access to certain abuse-prone addresses and services. +Additionally, in some cases the OR can 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 will 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. + +A further solution may be to use 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 participants 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 state 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. + +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 automatically 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. + +Tor directory servers build a consensus directory through a simple +four-round broadcast protocol. In round one, each server dates and +signs its current opinion, and broadcasts it to the other directory +servers; then in round two, each server rebroadcasts all the signed +opinions it has received. At this point all directory servers check +to see whether any server has signed multiple opinions in the same +period. Such a server is either broken or cheating, so the protocol +stops and notifies the administrators, who either remove the cheater +or wait for the broken server to be fixed. If there are no +discrepancies, each directory server then locally computes an algorithm +(described below) +on the set of opinions, resulting in a uniform shared directory. In +round three servers sign this directory and broadcast it; and finally +in round four the servers rebroadcast the directory and all the +signatures. If any directory server drops out of the network, its +signature is not included on the final directory. + +The rebroadcast steps ensure that a directory server is heard by +either all of the other servers or none of them, even when some links +are down (assuming that any two directory servers can talk directly or +via a third). Broadcasts are feasible because there are relatively few +directory servers (currently 3, but we expect as many as 9 as the network +scales). Computing the shared directory locally is a straightforward +threshold voting process: we include an OR if a majority of directory +servers believe it to be good. + +To avoid attacks where a router connects to all the directory servers +but refuses to relay traffic from other routers, the directory servers +must build circuits and use them to anonymously test router reliability +\cite{mix-acc}. + +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. +Thus directory servers are not a performance +bottleneck when we have many users, and do not aid traffic analysis by +forcing clients to periodically announce their existence to any +central point. + +\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 its IP address. +This type of anonymity protects against distributed DoS attacks: +attackers are forced to attack the onion routing network as a whole +rather than just Bob's IP address. + +Our design for location-hidden servers has the following goals. +\textbf{Access-controlled:} Bob needs a way to filter incoming requests, +so an attacker cannot flood Bob simply by making many connections to him. +\textbf{Robust:} 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 tie his service +to new ORs. \textbf{Smear-resistant:} +A social attacker who offers an illegal or disreputable location-hidden +service should not be able to ``frame'' a rendezvous router by +making observers believe the router created that service. +%slander-resistant? defamation-resistant? +\textbf{Application-transparent:} 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 DHT itself. At first, we can run 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. + +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 chooses some introduction points, and advertises them on + the DHT. He can add more later. +\item Bob builds a circuit to each of his introduction points, + and waits 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 DHT. +\item Alice chooses an OR to be the rendezvous point (RP) for this + transaction. She builds a circuit to RP, and gives it a + rendezvous cookie that it will use to recognize Bob. +\item Alice opens an anonymous stream to one of Bob's introduction + points, and gives it a message (encrypted to Bob's public key) + which tells him + about herself, her chosen RP and the rendezvous cookie, and the + first half 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 now sends a \emph{relay begin} cell along the circuit. It + arrives at Bob's onion proxy. Bob's onion proxy 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 a public ``introduction'' key. The hash of this public key +identifies a unique service, and (since Bob is required to sign his +messages) prevents anybody else from usurping Bob's introduction point +in the future. Bob uses the same public key when establishing the other +introduction points for that service. Bob periodically refreshes his +entry in the DHT. + +The message that Alice gives +the introduction point includes a hash of Bob's public key to identify +the service, along with 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 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. + +Since Bob's introduction points might themselves be subject to DoS he +could have to choose between keeping many +introduction connections open or risking such an attack. In this case, +he can provide selected users +with a current list and/or future schedule of introduction points that +are not advertised in the DHT\@. This is most likely to be practical +if there is a relatively stable and large group of introduction points +available. Alternatively, Bob could give secret public keys +to selected users for consulting the DHT\@. All of these approaches +have the advantage of limiting exposure even when +some of the selected high-priority 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 a public key. Bob +publishes the public key, an expiration time (``not valid after''), and +the current introduction points for his service into the DHT, indexed +by the hash of the 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 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} + +Rendezvous points in low-latency anonymity systems were first +described for use in ISDN telephony \cite{isdn-mixes,jerichow-jsac98}. +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 low-latency +Internet connections was suggested in early Onion Routing work +\cite{or-ih96}; however, the first published design of rendezvous +points for low-latency Internet connections 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 +via Diffie-Hellman, so plaintext is not exposed even at the rendezvous point. Third, +our design tries to minimize the exposure associated with running the +service, to encourage volunteers to offer introduction and rendezvous +point services. Tor's introduction points do not output any bytes to the +clients, and 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{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 local +configuration options. For example, clients concerned about request +linkability should rotate circuits more often than those concerned +about traceability. There is economic incentive to attract users by +allowing this choice; but at the same time, a set of 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 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 256 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 +all of 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), she 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$ out of $N$ nodes, he should be able to correlate at most +$\left(\frac{m}{N}\right)^2$ of the traffic in this way---although an +adversary +could possibly attract a disproportionately large amount of traffic +by running an OR with an unusually 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 will require 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, not its introduction point, 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, forcing 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{Open Questions in Low-latency Anonymity} +\label{sec:maintaining-anonymity} + +In addition to the non-goals in +Section~\ref{subsec:non-goals}, many other 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 only ever 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 nondeterministic path length (say, +increasing it 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 on 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 at once, it may be necessary +to support far more servers than Tor currently anticipates. +This introduces several issues. First, if approval by a centralized 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 at all +times, 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, what kind of 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, since no centralized authority is tracking +server reliability, how do we prevent unreliable servers from rendering +the network unusable? Fifth, do clients receive so much anonymity benefit +from running their own servers that we should expect them all to do so +\cite{econymics}, or do we need to find 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. + +\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, 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{Implement location-hidden services:} The design in +Section~\ref{sec:rendezvous} has not yet been implemented. While doing +so we are likely to encounter additional issues that must be resolved, +both in terms of usability and anonymity. + +\emph{Further specification review:} Although have a public +byte-level specification for the Tor protocols, it needs +extensive external review. We hope that as Tor +is more widely 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} + Peter Palfrader, Geoff Goodell, Adam Shostack, Joseph Sokol-Margolis, + John Bashinski, 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. + Cathy Meadows for formal analysis of the \emph{extend} protocol. + This work 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_2pre19/doc/tor-spec.txt b/tags/tor-0_0_2pre19/doc/tor-spec.txt new file mode 100644 index 0000000000..6bb9028c3e --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/tor-spec.txt @@ -0,0 +1,522 @@ +$Id$ + +Tor Spec + +Note: This is an attempt to specify Tor as it exists as implemented in +early June, 2003. It is not recommended that others implement this +design as it stands; future versions of Tor will implement improved +protocols. + +TODO: (very soon) + - Specify truncate/truncated payloads? + - Specify RELAY_END payloads. [It's 1 byte of reason, then X bytes of + data, right?] + - Sendme w/stream0 is circuit sendme + - Integrate -NM and -RD comments + - EXTEND cells should have hostnames or nicknames, so that OPs never + resolve OR hostnames. Else DNS servers can give different answers to + different OPs, and compromise their anonymity. + +EVEN LATER: + - Do TCP-style sequencing and ACKing of DATA cells so that we can afford + to lose some data cells. + +0. Notation: + + PK -- a public key. + SK -- a private key + K -- a key for a symmetric cypher + + a|b -- concatenation of 'a' with 'b'. + + 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, using the cipher suite + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA". An OR always sends a + self-signed X.509 certificate whose commonName is the server's + nickname, and whose public key is in the server directory. + + All parties receiving certificates must confirm that the public + key is as it appears in the server directory, and close the + connection if it is not. + + 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. 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 '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: Unused. + CREATE: Payload contains the handshake challenge. + CREATED: Payload contains the handshake response. + RELAY: Payload contains the relay header and relay body. + DESTROY: Unused. + + The payload is padded with 0 bytes. + + PADDING cells are currently used to implement connection + keepalive. 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, users 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 user 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', consisting of: + RSA-encrypted data [128 bytes] + Symmetrically-encrypted data [16 bytes] + + The RSA-encrypted portion contains: + Symmetric key [16 bytes] + First part of DH data (g^x) [112 bytes] + The symmetrically encrypted portion contains: + Second part of DH data (g^x) [16 bytes] + + The two parts of DH data, once decrypted and concatenated, form + g^x as calculated by the client. + + The relay payload for an EXTEND relay cell consists of: + Address [4 bytes] + Port [2 bytes] + Onion skin [144 bytes] + + The port and address field denote the IPV4 address and port of the + next onion router in the circuit. + +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 60 bytes of key data as K = SHA1(g^xy | + [00]) | SHA1(g^xy | [01]) | SHA1(g^xy | [02]) 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, the next 16 bytes of K + form Kf, and the next 16 bytes of K form Kb. + + KH is used in the handshake response to demonstrate knowledge of the + computed shared key. 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 + performs the following steps: + + 1. Choose a chain of N onion routers (R_1...R_N) to constitute + the path, such that no router appears in the path twice. + [this is wrong, see October 2003 discussion on or-dev] + + 2. If not already connected to the first router in the chain, + open a new connection to that router. + + 3. Choose a circID not already in use on the connection with the + first router in the chain. If we are an onion router and our + nickname is lexicographically greater than the nickname of the + other side, then let the high bit of the circID be 1, else 0. + + 4. 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 circuit + creator performs these steps: + + 1. Create an onion skin, encrypting the RSA-encrypted part with + R's public key. + + 2. Encrypt and send the onion skin in a relay EXTEND cell along + the circuit (see section 5). + + 3. When a relay EXTENDED cell is received, 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.3. above, concerning choosing circIDs.) + + 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 key 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, sometime after receiving the CREATE cell, it completes + the DH handshake, and replies with a CREATED cell, containing g^y + as its [128 byte] payload. 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. + + To tear down part of a circuit, the OP sends 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. + + [We'll have to reevaluate this section once we figure out cleaner + circuit/connection killing conventions. -RD] + +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 length + field and the payload with AES/CTR, as follows: + 'Forward' relay cell (same direction as CREATE): + Use Kf as key; encrypt. + 'Back' relay cell (opposite direction from CREATE): + Use Kb as key; decrypt. + If the OR recognizes the stream ID on the cell (it is either the ID + of an open stream or the signaling (zero) ID), the OR processes the + contents of the relay cell. Otherwise, it passes the decrypted + relay cell along the circuit if the circuit continues, or drops the + cell if it's the end of the circuit. [Getting an unrecognized + relay cell at the end of the circuit must be allowed for now; + we can reexamine this once we've designed full tcp-style close + handshakes. -RD] + + Otherwise, if the data cell is coming from the OP edge of the + circuit, the OP decrypts the length and payload fields with AES/CTR as + follows: + OP sends data cell to node R_M: + For I=1...M, decrypt with Kf_I. + + Otherwise, if the data cell is arriving at the OP edge if the + circuit, the OP encrypts the length and payload fields with AES/CTR as + follows: + OP receives data cell: + For I=N...1, + Encrypt with Kb_I. If the stream ID is a recognized + stream for R_I, or if the stream ID is the signaling + ID (zero), then stop and process the payload. + + For more information, see section 5 below. + +5. Application connections and stream management + +5.1. Streams + + 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 first 8 bytes of each relay cell are reserved as follows: + Relay command [1 byte] + Stream ID [7 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 + + All RELAY cells pertaining to the same tunneled stream have the + same stream ID. Stream ID's are chosen randomly by the OP. A + stream ID is considered "recognized" on a circuit C by an OP or an + OR if it already has an existing stream established on that + circuit, or if the stream ID is equal to the signaling stream ID, + which is all zero: [00 00 00 00 00 00 00] + + To create a new anonymized TCP connection, the OP sends a + RELAY_BEGIN data cell with a payload encoding the address and port + of the destination host. The stream ID is zero. The payload format is: + NEWSTREAMID | ADDRESS | ':' | PORT | '\000' + where NEWSTREAMID is the newly generated Stream ID to use for + this stream, ADDRESS may be a DNS hostname, or an IPv4 address in + dotted-quad format; and where PORT is encoded in decimal. + + Upon receiving this packet, 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. + Otherwise, the exit node replies with a RELAY_CONNECTED cell. + + 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 RELAY_DROP cells are long-range dummies; upon receiving such + a cell, the OR or OP must drop it. + +5.2. Closing streams + + [Note -- TCP streams can only be half-closed for reading. Our + Bickford's conversation was incorrect. -NM] + + 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_END' + cell along the circuit and changes its state to 'DONE_PACKAGING'. + Upon receiving a 'RELAY_END' 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_END' along the circuit, and changes its state + to 'CLOSED'. When a stream already in 'DONE_PACKAGING' receives a + 'RELAY_END' cell, it sends a 'FIN' and changes its state to + 'CLOSED'. + + [Note: Please rename 'RELAY_END2'. :) -NM ] + + If an edge node encounters an error on any stram, it sends a + 'RELAY_END2' cell along the circuit (if possible) and closes the + TCP connection immediately. If an edge node receives a + 'RELAY_END2' cell for any stream, it closes the TCP connection + completely, and sends nothing along the circuit. + +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. Router descriptor format. + +(Unless otherwise noted, tokens on the same line are space-separated.) + +Router ::= Router-Line Date-Line Onion-Key Link-Key Signing-Key Exit-Policy Router-Signature NL +Router-Line ::= "router" nickname address ORPort SocksPort DirPort bandwidth NL +Date-Line ::= "published" YYYY-MM-DD HH:MM:SS NL +Onion-key ::= "onion-key" NL a public key in PEM format NL +Link-key ::= "link-key" NL a public key in PEM format NL +Signing-Key ::= "signing-key" NL a public key in PEM format NL +Exit-Policy ::= Exit-Line* +Exit-Line ::= ("accept"|"reject") string NL +Router-Signature ::= "router-signature" NL Signature +Signature ::= "-----BEGIN SIGNATURE-----" NL + Base-64-encoded-signature NL "-----END SIGNATURE-----" NL + +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 ::= maximum bandwidth, in bytes/s + +nickname ::= between 1 and 32 alphanumeric characters. case-insensitive. + +Example: +router moria1 moria.mit.edu 9001 9021 9031 100000 +published 2003-09-24 19:36:05 +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMBBuk1sYxEg5jLAJy86U3GGJ7EGMSV7yoA6mmcsEVU3pwTUrpbpCmwS +7BvovoY3z4zk63NZVBErgKQUDkn3pp8n83xZgEf4GI27gdWIIwaBjEimuJlEY+7K +nZ7kVMRoiXCbjL6VAtNa4Zy1Af/GOm0iCIDpholeujQ95xew7rQnAgMA//8= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +7BvovoY3z4zk63NZVBErgKQUDkn3pp8n83xZgEf4GI27gdWIIwaBjEimuJlEY+7K +MIGJAoGBAMBBuk1sYxEg5jLAJy86U3GGJ7EGMSV7yoA6mmcsEVU3pwTUrpbpCmwS +f/GOm0iCIDpholeujQ95xew7rnZ7kVMRoiXCbjL6VAtNa4Zy1AQnAgMA//8= +-----END RSA PUBLIC KEY----- +reject 18.0.0.0/24 + +Note: The extra newline at the end of the router block is intentional. + +7.2. Directory format + +Directory ::= Directory-Header Directory-Router Router* Signature +Directory-Header ::= "signed-directory" NL Software-Line NL +Software-Line: "recommended-software" comma-separated-version-list +Directory-Router ::= Router +Directory-Signature ::= "directory-signature" NL Signature +Signature ::= "-----BEGIN SIGNATURE-----" NL + Base-64-encoded-signature NL "-----END SIGNATURE-----" NL + +Note: The router block 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. + +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_2pre19/doc/tor.1.in b/tags/tor-0_0_2pre19/doc/tor.1.in new file mode 100644 index 0000000000..9bba81a516 --- /dev/null +++ b/tags/tor-0_0_2pre19/doc/tor.1.in @@ -0,0 +1,139 @@ +.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 +\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 +\fBtotalbandwidth \fR\fINUM\fP +A token bucket limits the average incoming bandwidth on this node to NUM bytes per second. (Default: 800000) +.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 +\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) + +.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 "reject ADDR/MASK:PORT". 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. +.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 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_2pre19/install-sh b/tags/tor-0_0_2pre19/install-sh new file mode 100755 index 0000000000..398a88e142 --- /dev/null +++ b/tags/tor-0_0_2pre19/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + : + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + : + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + : + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/tags/tor-0_0_2pre19/missing b/tags/tor-0_0_2pre19/missing new file mode 100755 index 0000000000..dd583709f5 --- /dev/null +++ b/tags/tor-0_0_2pre19/missing @@ -0,0 +1,336 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.4 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. + You can get \`$1Help2man' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar ${1+"$@"} && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar ${1+"$@"} && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" ${1+"$@"} && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/tags/tor-0_0_2pre19/mkinstalldirs b/tags/tor-0_0_2pre19/mkinstalldirs new file mode 100755 index 0000000000..994d71ce7a --- /dev/null +++ b/tags/tor-0_0_2pre19/mkinstalldirs @@ -0,0 +1,101 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id$ + +errstatus=0 +dirmode="" + +usage="\ +Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." + +# process command line arguments +while test $# -gt 0 ; do + case "${1}" in + -h | --help | --h* ) # -h for help + echo "${usage}" 1>&2; exit 0 ;; + -m ) # -m PERM arg + shift + test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; } + dirmode="${1}" + shift ;; + -- ) shift; break ;; # stop option processing + -* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option + * ) break ;; # first non-opt arg + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in +0) exit 0 ;; +esac + +case $dirmode in +'') + if mkdir -p -- . 2>/dev/null; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + fi ;; +*) + if mkdir -m "$dirmode" -p -- . 2>/dev/null; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + fi ;; +esac + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + + lasterr="" + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 3 +# End: +# mkinstalldirs ends here diff --git a/tags/tor-0_0_2pre19/src/.cvsignore b/tags/tor-0_0_2pre19/src/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/tags/tor-0_0_2pre19/src/Makefile.am b/tags/tor-0_0_2pre19/src/Makefile.am new file mode 100644 index 0000000000..ba296a6b0f --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/src/common/.cvsignore b/tags/tor-0_0_2pre19/src/common/.cvsignore new file mode 100644 index 0000000000..051d1bd50b --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/tags/tor-0_0_2pre19/src/common/Makefile.am b/tags/tor-0_0_2pre19/src/common/Makefile.am new file mode 100644 index 0000000000..7bd90b0bf4 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/Makefile.am @@ -0,0 +1,9 @@ + +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 + diff --git a/tags/tor-0_0_2pre19/src/common/aes.c b/tags/tor-0_0_2pre19/src/common/aes.c new file mode 100644 index 0000000000..1d9f916165 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/aes.c @@ -0,0 +1,1355 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* Implementation of a simple AES counter mode. We include AES because + * 1) it didn't come with any versions of OpenSSL before 0.9.7. + * We include counter mode because OpenSSL doesn't do it right. + */ + +#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]); +#if 0 +static int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits); +static void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]); +#endif + +/*======================================================================*/ +/* 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; +}; + +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); + cipher->pos = 0; +} + +aes_cnt_cipher_t* +aes_new_cipher() +{ + aes_cnt_cipher_t* result = (aes_cnt_cipher_t*) tor_malloc(sizeof(aes_cnt_cipher_t)); + memset(result->rk, 0, 4*(MAXNR+1)); + memset(result->buf, 0, 16); + + return result; +} + +void +aes_set_key(aes_cnt_cipher_t *cipher, 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); +} + +void +aes_free_cipher(aes_cnt_cipher_t *cipher) +{ + assert(cipher); + memset(cipher, 0, sizeof(cipher)); + free(cipher); +} + +void +aes_crypt(aes_cnt_cipher_t *cipher, 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); + } +} + +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; +} + +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); +} + +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 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +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; +} + +#if 0 +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) { + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + return Nr; +} +#endif + +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); +} + +#if 0 +void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[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(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (Nr > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (Nr > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += Nr << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(pt , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(pt + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(pt + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(pt + 12, s3); +} +#endif diff --git a/tags/tor-0_0_2pre19/src/common/aes.h b/tags/tor-0_0_2pre19/src/common/aes.h new file mode 100644 index 0000000000..9187708897 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/aes.h @@ -0,0 +1,23 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* Implements a minimal interface to counter-mode AES. */ + +#ifndef __AES_H +#define __AES_H + +#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, unsigned char *key, int key_bits); +void aes_crypt(aes_cnt_cipher_t *cipher, 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 diff --git a/tags/tor-0_0_2pre19/src/common/crypto.c b/tags/tor-0_0_2pre19/src/common/crypto.c new file mode 100644 index 0000000000..9ffc9934df --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/crypto.c @@ -0,0 +1,1133 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "crypto.h" +#include "../or/or.h" +#include "log.h" +#include "aes.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 + +struct crypto_pk_env_t +{ + int type; + int refs; /* reference counting; so we don't have to copy keys */ + unsigned char *key; + /* auxiliary data structure(s) used by the underlying crypto library */ + unsigned char *aux; +}; + +struct crypto_cipher_env_t +{ + int type; + unsigned char *key; + unsigned char *iv; + /* auxiliary data structure(s) used by the underlying crypto library */ + unsigned char *aux; +}; + +/* static INLINE const EVP_CIPHER * + crypto_cipher_evp_cipher(int type, int enc); +*/ + +static INLINE int +crypto_cipher_iv_length(int type) { + /* + printf("%d -> %d IV\n",type, + EVP_CIPHER_iv_length(crypto_cipher_evp_cipher(type,0))); + */ + switch(type) + { + case CRYPTO_CIPHER_IDENTITY: return 0; + case CRYPTO_CIPHER_DES: return 8; + case CRYPTO_CIPHER_RC4: return 16; + case CRYPTO_CIPHER_3DES: return 8; + case CRYPTO_CIPHER_AES_CTR: return 0; + default: assert(0); return -1; + } +} + +static INLINE int +crypto_cipher_key_length(int type) { + /* + printf("%d -> %d\n",type, + EVP_CIPHER_key_length(crypto_cipher_evp_cipher(type,0))); + */ + switch(type) + { + case CRYPTO_CIPHER_IDENTITY: return 0; + case CRYPTO_CIPHER_DES: return 8; + case CRYPTO_CIPHER_RC4: return 16; + case CRYPTO_CIPHER_3DES: return 16; + case CRYPTO_CIPHER_AES_CTR: return 16; + default: assert(0); return -1; + } +} + +static INLINE const EVP_CIPHER * +crypto_cipher_evp_cipher(int type, int enc) { + switch(type) + { + case CRYPTO_CIPHER_IDENTITY: return EVP_enc_null(); + case CRYPTO_CIPHER_DES: return EVP_des_ofb(); + case CRYPTO_CIPHER_RC4: return EVP_rc4(); + case CRYPTO_CIPHER_3DES: return EVP_des_ede_ofb(); + default: return NULL; + } +} + +static int _crypto_global_initialized = 0; + +int crypto_global_init() +{ + if (!_crypto_global_initialized) { + ERR_load_crypto_strings(); + _crypto_global_initialized = 1; + } + return 0; +} + +int crypto_global_cleanup() +{ + ERR_free_strings(); + return 0; +} + +crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa) +{ + crypto_pk_env_t *env; + assert(rsa); + env = (crypto_pk_env_t *)tor_malloc(sizeof(crypto_pk_env_t)); + env->type = CRYPTO_PK_RSA; + env->refs = 1; + env->key = (unsigned char*)rsa; + env->aux = NULL; + return env; +} + +RSA *_crypto_pk_env_get_rsa(crypto_pk_env_t *env) +{ + if (env->type != CRYPTO_PK_RSA) + return NULL; + return (RSA*)env->key; +} + +EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env) +{ + RSA *key = NULL; + EVP_PKEY *pkey = NULL; + if (env->type != CRYPTO_PK_RSA) + return NULL; + assert(env->key); + if (!(key = RSAPrivateKey_dup((RSA*)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; +} + +crypto_pk_env_t *crypto_new_pk_env(int type) +{ + RSA *rsa; + + switch(type) { + case CRYPTO_PK_RSA: + rsa = RSA_new(); + if (!rsa) return NULL; + return _crypto_new_pk_env_rsa(rsa); + default: + return NULL; + } +} + +void crypto_free_pk_env(crypto_pk_env_t *env) +{ + assert(env); + + if(--env->refs > 0) + return; + + switch(env->type) { + case CRYPTO_PK_RSA: + if (env->key) + RSA_free((RSA *)env->key); + break; + default: + break; + } + + free(env); +} + + +/* Create a new crypto_cipher_env_t for a given onion cipher type, key, + * iv, and encryption flag (1=encrypt, 0=decrypt). Return the crypto object + * on success; NULL on failure. + */ +crypto_cipher_env_t * +crypto_create_init_cipher(int cipher_type, char *key, char *iv, int encrypt_mode) +{ + int r; + crypto_cipher_env_t *crypto = NULL; + + if (! (crypto = crypto_new_cipher_env(cipher_type))) { + log_fn(LOG_WARN, "Unable to allocate crypto object"); + return NULL; + } + + if (crypto_cipher_set_key(crypto, key)) { + log_fn(LOG_WARN, "Unable to set key: %s", crypto_perror()); + goto error; + } + + if (crypto_cipher_set_iv(crypto, iv)) { + log_fn(LOG_WARN, "Unable to set iv: %s", crypto_perror()); + goto error; + } + + if (encrypt_mode) + r = crypto_cipher_encrypt_init_cipher(crypto); + else + r = crypto_cipher_decrypt_init_cipher(crypto); + + if (r) { + log_fn(LOG_WARN, "Unable to initialize cipher: %s", crypto_perror()); + goto error; + } + return crypto; + + error: + if (crypto) + crypto_free_cipher_env(crypto); + return NULL; +} + +crypto_cipher_env_t *crypto_new_cipher_env(int type) +{ + crypto_cipher_env_t *env; + int iv_len, key_len; + + env = (crypto_cipher_env_t *)tor_malloc(sizeof(crypto_cipher_env_t)); + + env->type = type; + env->key = NULL; + env->iv = NULL; + env->aux = NULL; + + iv_len = crypto_cipher_iv_length(type); + key_len = crypto_cipher_key_length(type); + + if (type == CRYPTO_CIPHER_AES_CTR) { + env->aux = (unsigned char *)aes_new_cipher(); + } else if (! crypto_cipher_evp_cipher(type,0)) + /* This is not an openssl cipher */ + goto err; + else { + env->aux = (unsigned char *)tor_malloc(sizeof(EVP_CIPHER_CTX)); + EVP_CIPHER_CTX_init((EVP_CIPHER_CTX *)env->aux); + } + + if(iv_len) + env->iv = (unsigned char *)tor_malloc(iv_len); + + if(key_len) + env->key = (unsigned char *)tor_malloc(key_len); + + return env; +err: + if (env->key) + free(env->key); + if (env->iv) + free(env->iv); + if (env->aux) + free(env->aux); + if (env) + free(env); + return NULL; +} + +void crypto_free_cipher_env(crypto_cipher_env_t *env) +{ + assert(env); + + if (env->type == CRYPTO_CIPHER_AES_CTR) { + assert(env->aux); + aes_free_cipher((aes_cnt_cipher_t*)env->aux); + env->aux = NULL; + } else if (crypto_cipher_evp_cipher(env->type,0)) { + /* This is an openssl cipher */ + assert(env->aux); + EVP_CIPHER_CTX_cleanup((EVP_CIPHER_CTX *)env->aux); + } + + if (env->aux) + free((void *)env->aux); + if (env->iv) + free((void *)env->iv); + if (env->key) + free((void *)env->key); + + free((void *)env); +} + +/* public key crypto */ +int crypto_pk_generate_key(crypto_pk_env_t *env) +{ + assert(env); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (env->key) + RSA_free((RSA *)env->key); + env->key = (unsigned char *)RSA_generate_key(1024,65537, NULL, NULL); + if (!env->key) + return -1; + break; + default: + return -1; + } + + return 0; +} + +int crypto_pk_read_private_key_from_file(crypto_pk_env_t *env, FILE *src) +{ + assert(env && src); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (env->key) + RSA_free((RSA *)env->key); + env->key = (unsigned char *)PEM_read_RSAPrivateKey(src, NULL, NULL, NULL); + if (!env->key) + return -1; + break; + default : + return -1; + } + + return 0; +} + +int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, const char *keyfile) +{ + FILE *f_pr; + + 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) { + log_fn(LOG_WARN,"Error reading private key : %s",crypto_perror()); + fclose(f_pr); + return -1; + } + fclose(f_pr); + + /* check the private key */ + switch(crypto_pk_check_key(env)) { + case 0: + log_fn(LOG_WARN,"Private key read but is invalid : %s.", crypto_perror()); + return -1; + case -1: + log_fn(LOG_WARN,"Private key read but validity checking failed : %s",crypto_perror()); + return -1; + /* case 1: fall through */ + } + return 0; +} + +int crypto_pk_read_public_key_from_file(crypto_pk_env_t *env, FILE *src) +{ + assert(env && src); + + switch(env->type) { + case CRYPTO_PK_RSA: + if(env->key) + RSA_free((RSA *)env->key); + env->key = (unsigned char *)PEM_read_RSAPublicKey(src, NULL, NULL, NULL); + if (!env->key) + return -1; + break; + default : + return -1; + } + + return 0; +} + +int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int *len) { + BUF_MEM *buf; + BIO *b; + + assert(env && env->key && dest); + + switch(env->type) { + case CRYPTO_PK_RSA: + + 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, (RSA *)env->key)) + 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); + + break; + default: + return -1; + } + + return 0; +} + +int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, int len) { + BIO *b; + + assert(env && src); + + switch(env->type) { + case CRYPTO_PK_RSA: + b = BIO_new(BIO_s_mem()); /* Create a memory BIO */ + + BIO_write(b, src, len); + + RSA_free((RSA *)env->key); + env->key = (unsigned char *)PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL); + if(!env->key) + return -1; + + BIO_free(b); + break; + default: + return -1; + } + + return 0; +} + +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; + assert(env->type == CRYPTO_PK_RSA); + if (!(bio = BIO_new(BIO_s_mem()))) + return -1; + if (PEM_write_bio_RSAPrivateKey(bio, (RSA*)env->key, NULL,NULL,0,NULL,NULL) + == 0) { + 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; +} + +int crypto_pk_write_private_key_to_file(crypto_pk_env_t *env, FILE *dest) +{ + assert(env && dest); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (!env->key) + return -1; + if (PEM_write_RSAPrivateKey(dest, (RSA *)env->key, NULL, NULL, 0,0, NULL) == 0) + return -1; + break; + default : + return -1; + } + + return 0; +} +int crypto_pk_write_public_key_to_file(crypto_pk_env_t *env, FILE *dest) +{ + assert(env && dest); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (!env->key) + return -1; + if (PEM_write_RSAPublicKey(dest, (RSA *)env->key) == 0) + return -1; + break; + default : + return -1; + } + + return 0; +} + +int crypto_pk_check_key(crypto_pk_env_t *env) +{ + assert(env); + + switch(env->type) { + case CRYPTO_PK_RSA: + return RSA_check_key((RSA *)env->key); + default: + return -1; + } +} + +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; + + if (a->type != b->type) + return -1; + + switch(a->type) { + case CRYPTO_PK_RSA: + assert(((RSA *)a->key)->n && ((RSA *)a->key)->e && ((RSA *)b->key)->n && ((RSA *)b->key)->e); + result = BN_cmp(((RSA *)a->key)->n, ((RSA *)b->key)->n); + if (result) + return result; + return BN_cmp(((RSA *)a->key)->e, ((RSA *)b->key)->e); + default: + return -1; + } +} + +int crypto_pk_keysize(crypto_pk_env_t *env) +{ + assert(env && env->key); + + return RSA_size((RSA *)env->key); +} + +crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *env) { + assert(env && env->key); + + switch(env->type) { + case CRYPTO_PK_RSA: + env->refs++; + break; + default: + return NULL; + } + + return env; +} + +int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding) +{ + assert(env && from && to); + + switch(env->type) { + case CRYPTO_PK_RSA: + return RSA_public_encrypt(fromlen, from, to, (RSA *)env->key, padding); + default: + return -1; + } +} + +int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding) +{ + assert(env && from && to); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (!(((RSA*)env->key)->p)) + return -1; + return RSA_private_decrypt(fromlen, from, to, (RSA *)env->key, padding); + default: + return -1; + } +} + +int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to) +{ + assert(env && from && to); + + switch(env->type) { + case CRYPTO_PK_RSA: + return RSA_public_decrypt(fromlen, from, to, (RSA *)env->key, + RSA_PKCS1_PADDING); + default: + return -1; + } +} + +int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to) +{ + assert(env && from && to); + + switch(env->type) { + case CRYPTO_PK_RSA: + if (!(((RSA*)env->key)->p)) + return -1; + return RSA_private_encrypt(fromlen, from, to, (RSA *)env->key, + RSA_PKCS1_PADDING); + default: + return -1; + } +} + +int +crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out) +{ + unsigned char *buf, *bufp; + unsigned char digest[20]; + int len; + int i; + assert(pk->type == CRYPTO_PK_RSA); + len = i2d_RSAPublicKey((RSA*)pk->key, NULL); + if (len < 0) + return -1; + if (lenkey, &bufp); + if (len < 0) { + free(buf); + return -1; + } + if (crypto_SHA_digest(buf, len, digest) < 0) { + free(buf); + return -1; + } + bufp = buf; + for (i = 0; i < 20; ++i) { + sprintf(bufp,"%02X",digest[i]); + bufp += 2; + if (i%2 && i != 19) { + *bufp++ = ' '; + } + } + *bufp = '\0'; + assert(strlen(buf) == FINGERPRINT_LEN); + assert(crypto_pk_check_fingerprint_syntax(buf)); + strcpy(fp_out, buf); + free(buf); + return 0; +} + +int +crypto_pk_check_fingerprint_syntax(const char *s) +{ + int i; + for (i = 0; i < FINGERPRINT_LEN; ++i) { + if ((i%5) == 4) { + if (!isspace(s[i])) return 0; + } else { + if (!isxdigit(s[i])) return 0; + } + } + if (s[FINGERPRINT_LEN]) return 0; + return 1; +} + +/* symmetric crypto */ +int crypto_cipher_generate_key(crypto_cipher_env_t *env) +{ + int key_len; + assert(env); + + key_len = crypto_cipher_key_length(env->type); + + if (key_len > 0) + return crypto_rand(key_len, env->key); + else if (key_len == 0) + return 0; + else + return -1; +} + +int crypto_cipher_set_iv(crypto_cipher_env_t *env, unsigned char *iv) +{ + int iv_len; + assert(env && iv); + + iv_len = crypto_cipher_iv_length(env->type); + if (!iv_len) + return 0; + + if (!env->iv) + return -1; + + memcpy((void*)env->iv, (void*)iv, iv_len); + + return 0; +} + +int crypto_cipher_set_key(crypto_cipher_env_t *env, unsigned char *key) +{ + int key_len; + assert(env && key); + + key_len = crypto_cipher_key_length(env->type); + if (!key_len) + return 0; + + if (!env->key) + return -1; + + memcpy((void*)env->key, (void*)key, key_len); + + return 0; +} + +unsigned char *crypto_cipher_get_key(crypto_cipher_env_t *env) +{ + return env->key; +} + +int crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env) +{ + assert(env); + + if (crypto_cipher_evp_cipher(env->type, 1)) { + RETURN_SSL_OUTCOME(EVP_EncryptInit((EVP_CIPHER_CTX *)env->aux, + crypto_cipher_evp_cipher(env->type, 1), + env->key, env->iv)); + } else if (env->type == CRYPTO_CIPHER_AES_CTR) { + aes_set_key((aes_cnt_cipher_t*)env->aux, env->key, 128); + return 0; + } else { + return -1; + } +} + +int crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env) +{ + assert(env); + + if (crypto_cipher_evp_cipher(env->type, 0)) { + RETURN_SSL_OUTCOME(EVP_EncryptInit((EVP_CIPHER_CTX *)env->aux, + crypto_cipher_evp_cipher(env->type, 0), + env->key, env->iv)); + } else if (env->type == CRYPTO_CIPHER_AES_CTR) { + aes_set_key((aes_cnt_cipher_t*)env->aux, env->key, 128); + return 0; + } else { + return -1; + } +} + +int crypto_cipher_encrypt(crypto_cipher_env_t *env, unsigned char *from, unsigned int fromlen, unsigned char *to) +{ + int tolen; + + assert(env && from && to); + + if (env->type == CRYPTO_CIPHER_AES_CTR) { + aes_crypt((aes_cnt_cipher_t*)env->aux, from, fromlen, to); + return 0; + } else { + RETURN_SSL_OUTCOME(EVP_EncryptUpdate((EVP_CIPHER_CTX *)env->aux, to, &tolen, from, fromlen)); + } +} + +int crypto_cipher_decrypt(crypto_cipher_env_t *env, unsigned char *from, unsigned int fromlen, unsigned char *to) +{ + int tolen; + + assert(env && from && to); + + if (env->type == CRYPTO_CIPHER_AES_CTR) { + aes_crypt((aes_cnt_cipher_t*)env->aux, from, fromlen, to); + return 0; + } else { + RETURN_SSL_OUTCOME(EVP_DecryptUpdate((EVP_CIPHER_CTX *)env->aux, to, &tolen, from, fromlen)); + } +} + +int +crypto_cipher_rewind(crypto_cipher_env_t *env, long delta) +{ + return crypto_cipher_advance(env, -delta); +} + +int +crypto_cipher_advance(crypto_cipher_env_t *env, long delta) +{ + if (env->type == CRYPTO_CIPHER_AES_CTR) { + aes_adjust_counter((aes_cnt_cipher_t*)env->aux, delta); + return 0; + } else { + return -1; + } +} + +/* SHA-1 */ +int crypto_SHA_digest(const unsigned char *m, int len, unsigned char *digest) +{ + assert(m && digest); + return (SHA1(m,len,digest) == NULL); +} + +struct crypto_digest_env_t { + SHA_CTX d; +}; + +crypto_digest_env_t * +crypto_new_digest_env(int type) +{ + crypto_digest_env_t *r; + assert(type == CRYPTO_SHA1_DIGEST); + r = tor_malloc(sizeof(crypto_digest_env_t)); + SHA1_Init(&r->d); + return r; +} + +void +crypto_free_digest_env(crypto_digest_env_t *digest) { + if(digest) + free(digest); +} + +void +crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data, + size_t len) +{ + assert(digest); + assert(data); + SHA1_Update(&digest->d, (void*)data, len); +} + +void crypto_digest_get_digest(crypto_digest_env_t *digest, + char *out, size_t out_len) +{ + static char r[SHA_DIGEST_LENGTH]; + assert(digest && out); + assert(out_len <= SHA_DIGEST_LENGTH); + SHA1_Final(r, &digest->d); + memcpy(out, r, out_len); +} + +crypto_digest_env_t * +crypto_digest_dup(const crypto_digest_env_t *digest) +{ + crypto_digest_env_t *r; + assert(digest); + r = tor_malloc(sizeof(crypto_digest_env_t)); + memcpy(r,digest,sizeof(crypto_digest_env_t)); + return r; +} + +void +crypto_digest_assign(crypto_digest_env_t *into, + const crypto_digest_env_t *from) +{ + assert(into && from); + memcpy(into,from,sizeof(crypto_digest_env_t)); +} + +/* DH */ +static BIGNUM *dh_param_p = NULL; +static BIGNUM *dh_param_g = NULL; + +static void init_dh_param() { + BIGNUM *p, *g; + int r; + if (dh_param_p && dh_param_g) + return; + + p = BN_new(); + g = BN_new(); + 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"); + assert(r); + + r = BN_set_word(g, 2); + assert(r); + dh_param_p = p; + dh_param_g = g; +} + +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: + if (res && res->dh) DH_free(res->dh); /* frees p and g too */ + if (res) free(res); + return NULL; +} +int crypto_dh_get_bytes(crypto_dh_env_t *dh) +{ + assert(dh); + return DH_size(dh->dh); +} +int crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey, int pubkey_len) +{ + int bytes; + assert(dh); + if (!DH_generate_key(dh->dh)) + return -1; + + 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)) +int crypto_dh_compute_secret(crypto_dh_env_t *dh, + char *pubkey, int pubkey_len, + char *secret_out, int secret_bytes_out) +{ + unsigned char hash[20]; + unsigned char *secret_tmp = NULL; + BIGNUM *pubkey_bn = NULL; + int secret_len; + int i; + assert(dh); + assert(secret_bytes_out/20 <= 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 += 20) { + secret_tmp[secret_len] = (unsigned char) i/20; + if (crypto_SHA_digest(secret_tmp, secret_len+1, hash)) + goto error; + memcpy(secret_out+i, hash, MIN(20, secret_bytes_out-i)); + } + secret_len = secret_bytes_out; + + goto done; + error: + secret_len = -1; + done: + if (pubkey_bn) + BN_free(pubkey_bn); + tor_free(secret_tmp); + return secret_len; +} +void crypto_dh_free(crypto_dh_env_t *dh) +{ + assert(dh && dh->dh); + DH_free(dh->dh); + free(dh); +} + +/* random numbers */ +int crypto_seed_rng() +{ + static char *filenames[] = { + "/dev/srandom", "/dev/urandom", "/dev/random", NULL + }; + int i, n; + char buf[21]; + FILE *f; + + for (i = 0; filenames[i]; ++i) { + f = fopen(filenames[i], "rb"); + if (!f) continue; + log_fn(LOG_INFO, "Seeding RNG from %s", filenames[i]); + n = fread(buf, 1, 20, f); + fclose(f); + if (n != 20) { + log_fn(LOG_WARN, "Error reading from entropy source"); + return -1; + } + RAND_seed(buf, 20); + return 0; + } + + log_fn(LOG_WARN, "Cannot seed RNG -- no entropy source found."); + return -1; +} + +int crypto_rand(unsigned int n, unsigned char *to) +{ + assert(to); + return (RAND_bytes(to, n) != 1); +} + +void crypto_pseudo_rand(unsigned int n, unsigned char *to) +{ + assert(to); + if (RAND_pseudo_bytes(to, n) == -1) { + log_fn(LOG_ERR, "RAND_pseudo_bytes failed unexpectedly."); + exit(1); + } +} + +/* return a pseudo random number between 0 and max-1 */ +int crypto_pseudo_rand_int(unsigned int max) { + unsigned int val; + unsigned int cutoff; + assert(max < UINT_MAX); + 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; + } +} + +/* errors */ +char *crypto_perror() +{ + return (char *)ERR_reason_error_string(ERR_get_error()); +} + +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; +} +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; +} diff --git a/tags/tor-0_0_2pre19/src/common/crypto.h b/tags/tor-0_0_2pre19/src/common/crypto.h new file mode 100644 index 0000000000..4ada97e519 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/crypto.h @@ -0,0 +1,121 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __CRYPTO_H +#define __CRYPTO_H + +#include +#include +#include + +/* available encryption primitives */ +#define CRYPTO_CIPHER_IDENTITY 0 +#define CRYPTO_CIPHER_DES 1 +#define CRYPTO_CIPHER_RC4 2 +#define CRYPTO_CIPHER_3DES 3 +#define CRYPTO_CIPHER_AES_CTR 4 + +#define CRYPTO_PK_RSA 0 + +#define CRYPTO_SHA1_DIGEST 0 + +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; + +/* global state */ +int crypto_global_init(); +int crypto_global_cleanup(); + +/* environment setup */ +crypto_pk_env_t *crypto_new_pk_env(int type); +void crypto_free_pk_env(crypto_pk_env_t *env); + +crypto_cipher_env_t *crypto_new_cipher_env(int type); +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_read_private_key_from_file(crypto_pk_env_t *env, FILE *src); +int crypto_pk_read_public_key_from_file(crypto_pk_env_t *env, FILE *src); +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_file(crypto_pk_env_t *env, FILE *dest); +int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname); +int crypto_pk_write_public_key_to_file(crypto_pk_env_t *env, FILE *dest); +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_set_key(crypto_pk_env_t *env, unsigned char *key); +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, unsigned char *from, int fromlen, unsigned char *to, int padding); +int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding); +int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to); +int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to); +#define FINGERPRINT_LEN 49 +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); + +/* Key negotiation */ +typedef struct crypto_dh_env_st { + DH *dh; +} crypto_dh_env_t; + +/* #define CRYPTO_DH_SIZE (1536 / 8) */ +#define CRYPTO_DH_SIZE (1024 / 8) +crypto_dh_env_t *crypto_dh_new(); +int crypto_dh_get_bytes(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, + 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_iv(crypto_cipher_env_t *env, unsigned char *iv); +int crypto_cipher_set_key(crypto_cipher_env_t *env, 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); +unsigned char *crypto_cipher_get_key(crypto_cipher_env_t *env); + +int crypto_cipher_encrypt(crypto_cipher_env_t *env, unsigned char *from, unsigned int fromlen, unsigned char *to); +int crypto_cipher_decrypt(crypto_cipher_env_t *env, 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, set_iv, and init. */ +crypto_cipher_env_t *crypto_create_init_cipher(int cipher_type, char *key, char *iv, int encrypt_mode); + +/* SHA-1 */ +int crypto_SHA_digest(const unsigned char *m, int len, unsigned char *digest); +crypto_digest_env_t *crypto_new_digest_env(int type); +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); + +/* errors */ +char *crypto_perror(); +#endif diff --git a/tags/tor-0_0_2pre19/src/common/fakepoll.c b/tags/tor-0_0_2pre19/src/common/fakepoll.c new file mode 100644 index 0000000000..f9e044eaf1 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/fakepoll.c @@ -0,0 +1,93 @@ +/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* On systems where 'poll' doesn't exist, fake it with 'select'. */ + +#include "orconfig.h" +#include "fakepoll.h" + +#ifdef USE_FAKE_POLL +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif + +/* by default, windows handles only 64 fd's */ +#if defined(MS_WINDOWS) && !defined(FD_SETSIZE) +#define FD_SETSIZE MAXCONNECTIONS +#endif + +#include "util.h" + +int +poll(struct pollfd *ufds, unsigned int nfds, int timeout) +{ + int idx, 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; + 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 diff --git a/tags/tor-0_0_2pre19/src/common/fakepoll.h b/tags/tor-0_0_2pre19/src/common/fakepoll.h new file mode 100644 index 0000000000..61959d6c92 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/fakepoll.h @@ -0,0 +1,31 @@ +/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __FAKEPOLL_H +#define __FAKEPOLL_H + +#include "orconfig.h" + +#ifndef HAVE_POLL_H +#ifndef HAVE_SYS_POLL_H +#define USE_FAKE_POLL + +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 + +int poll(struct pollfd *ufds, unsigned int nfds, int timeout); + +#endif +#endif +#endif diff --git a/tags/tor-0_0_2pre19/src/common/log.c b/tags/tor-0_0_2pre19/src/common/log.c new file mode 100644 index 0000000000..947029079a --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/log.c @@ -0,0 +1,157 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "../or/or.h" + +struct logfile_t; +typedef struct logfile_t { + struct logfile_t *next; + const char *filename; + FILE *file; + int needs_close; + int loglevel; + int max_loglevel; +} logfile_t; + +static INLINE const char *sev_to_string(int severity) { + switch(severity) { + case LOG_DEBUG: return "debug"; + case LOG_INFO: return "info"; + case LOG_WARN: return "warn"; + case LOG_ERR: return "err"; + default: assert(0); return "UNKNOWN"; + } +} + +static logfile_t *logfiles = NULL; + +/* 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) +{ + time_t t; + struct timeval now; + int n; + + buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ + + 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 */ + + 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'; +} + +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; + } + fputs(buf, lf->file); + fflush(lf->file); + /* XXX check for EOF */ + } +} + +/* Outputs a message to stdout */ +void _log(int severity, const char *format, ...) +{ + va_list ap; + va_start(ap,format); + logv(severity, NULL, format, ap); + va_end(ap); +} + +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); +} + +void close_logs() +{ + logfile_t *victim; + while(logfiles) { + victim = logfiles; + logfiles = logfiles->next; + if (victim->needs_close) + fclose(victim->file); + free(victim); + } +} + +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"); + } + } +} + +void add_stream_log(int loglevel, const char *name, FILE *stream) +{ + logfile_t *lf; + lf = tor_malloc(sizeof(logfile_t)); + lf->filename = name; + lf->needs_close = 0; + lf->loglevel = loglevel; + lf->max_loglevel = LOG_ERR; + lf->file = stream; + lf->next = logfiles; + logfiles = lf; +} + +/* + * If opening the logfile fails, -1 is returned and + * errno is set appropriately (by fopen) + */ +int add_file_log(int loglevel, const char *filename) +{ + FILE *f; + f = fopen(filename, "a"); + if (!f) return -1; + add_stream_log(loglevel, filename, f); + logfiles->needs_close = 1; + return 0; +} + diff --git a/tags/tor-0_0_2pre19/src/common/log.h b/tags/tor-0_0_2pre19/src/common/log.h new file mode 100644 index 0000000000..87a74bafe3 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/log.h @@ -0,0 +1,44 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __LOG_H + +#ifdef HAVE_SYSLOG_H +#include +#define LOG_WARN LOG_WARNING +#else +#define LOG_DEBUG 0 +#define LOG_INFO 1 +#define LOG_WARN 3 +#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 + +void add_stream_log(int loglevel, const char *name, FILE *stream); +int add_file_log(int severity, const char *filename); +void close_logs(); +void reset_logs(); + +/* 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); +#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 diff --git a/tags/tor-0_0_2pre19/src/common/test.h b/tags/tor-0_0_2pre19/src/common/test.h new file mode 100644 index 0000000000..017ecf22df --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/test.h @@ -0,0 +1,127 @@ +/* Copyright 2001,2002,2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __TEST_H +#define __TEST_H + +#include + +#define STMT_BEGIN do { +#define STMT_END } while (0) + +#ifdef __GNUC__ +#define PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define PRETTY_FUNCTION "" +#endif + +#define test_fail() \ + STMT_BEGIN \ + printf("\nFile %s: line %d (%s): assertion failed.", \ + __FILE__, \ + __LINE__, \ + PRETTY_FUNCTION); \ + return; \ + STMT_END + +#define test_assert(expr) \ + STMT_BEGIN \ + if(expr) { printf("."); } else { \ + 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("."); } else { \ + 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("."); } else { \ + 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 \ + char *v1=(expr1), *v2=(expr2); \ + if(!strcmp(v1,v2)) { printf("."); } else { \ + 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 \ + char *v1=(expr1), *v2=(expr2); \ + if(strcmp(v1,v2)) { printf("."); } else { \ + 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("."); } else { \ + 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("."); } else { \ + 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_2pre19/src/common/torint.h b/tags/tor-0_0_2pre19/src/common/torint.h new file mode 100644 index 0000000000..d3efe62122 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/torint.h @@ -0,0 +1,156 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#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_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 + + +#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 */ diff --git a/tags/tor-0_0_2pre19/src/common/tortls.c b/tags/tor-0_0_2pre19/src/common/tortls.c new file mode 100644 index 0000000000..89009ea13e --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/tortls.c @@ -0,0 +1,573 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* TLS wrappers for The Onion Router. (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" + +/* Copied from or.h */ +#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +#include +#include +#include +#include +#include +#include + +/* How long do certificates live? (sec) */ +#define CERT_LIFETIME (365*24*60*60) +/* How much clock skew do we tolerate when checking certificates? (sec) */ +#define CERT_ALLOW_SKEW (30*60) + +struct tor_tls_context_st { + SSL_CTX *ctx; +}; + +struct tor_tls_st { + SSL *ssl; + int socket; + enum { + TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE, + TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED + } state; + 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, + const char *nickname); + +/* global tls context, keep it here because nobody else needs to touch it */ +static tor_tls_context *global_tls_context = NULL; +static int tls_library_is_initialized = 0; + +#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); +crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa); + +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 + +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; + log(severity, "TLS error: (errno=%d)",errno); + 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; + } +} + +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; + } +} + +static int always_accept_verify_cb(int preverify_ok, + X509_STORE_CTX *x509_ctx) +{ + /* We always accept peer certs and complete the handshake. We don't validate + * them until later. */ + return 1; +} + +/* Generate a self-signed certificate with the private key 'rsa' and + * commonName 'nickname', and write it, PEM-encoded, to the file named + * by 'certfile'. Return 0 on success, -1 for failure. + */ +X509 * +tor_tls_create_certificate(crypto_pk_env_t *rsa, + const char *nickname) +{ + time_t start_time, end_time; + EVP_PKEY *pkey = NULL; + X509 *x509 = NULL; + X509_NAME *name = NULL; + int nid; + + tor_tls_init(); + + start_time = time(NULL); + + assert(rsa && nickname); + if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa))) + return NULL; + 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*)nickname, -1, -1, 0))) goto error; + + if (!(X509_set_issuer_name(x509, name))) + goto error; + if (!(X509_set_subject_name(x509, name))) + 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, pkey, EVP_sha1())) + goto error; + + goto done; + error: + if (x509) { + X509_free(x509); + x509 = NULL; + } + done: + if (pkey) + EVP_PKEY_free(pkey); + if (name) + X509_NAME_free(name); + return x509; +} + + +#ifdef EVERYONE_HAS_AES +/* Everybody is running OpenSSL 0.9.7 or later, so no backward compatibiliy + * 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, certfile set to a + * filename for a certificate file, and RSA set to the private key + * used for that certificate. Return -1 if failure, else 0. + */ +int +tor_tls_context_new(crypto_pk_env_t *rsa, + int isServer, const char *nickname) +{ + crypto_dh_env_t *dh = NULL; + EVP_PKEY *pkey = NULL; + tor_tls_context *result; + X509 *cert = NULL; + + tor_tls_init(); + + if (rsa) { + cert = tor_tls_create_certificate(rsa, nickname); + if (!cert) { + log(LOG_WARN, "Error creating certificate"); + return -1; + } + } + + result = tor_malloc(sizeof(tor_tls_context)); + result->ctx = NULL; +#ifdef EVERYONE_HAS_AES + /* Tell OpenSSL to only use TLS1 */ + if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) + goto error; +#else + /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */ + if (!(result->ctx = SSL_CTX_new(SSLv23_method()))) + goto error; + SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); +#endif + if (!SSL_CTX_set_cipher_list(result->ctx, CIPHER_LIST)) + goto error; + if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) + goto error; + SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); + if (rsa) { + if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa))) + goto error; + if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) + goto error; + EVP_PKEY_free(pkey); + pkey = NULL; + if (cert) { + if (!SSL_CTX_check_private_key(result->ctx)) + goto error; + } + } + dh = crypto_dh_new(); + SSL_CTX_set_tmp_dh(result->ctx, dh->dh); + crypto_dh_free(dh); + SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, + always_accept_verify_cb); + /* let us realloc bufs that we're writing from */ + SSL_CTX_set_mode(result->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); + free(global_tls_context); + } + global_tls_context = result; + return 0; + + error: + if (pkey) + EVP_PKEY_free(pkey); + if (dh) + crypto_dh_free(dh); + if (result && result->ctx) + SSL_CTX_free(result->ctx); + if (result) + free(result); + + return -1; +} + +/* Create a new TLS object from a TLS context, a filedescriptor, and + * a flag to determine whether it is functioning as a server. + */ +tor_tls * +tor_tls_new(int sock, int isServer) +{ + tor_tls *result = tor_malloc(sizeof(tor_tls)); + assert(global_tls_context); /* make sure somebody made it first */ + if (!(result->ssl = SSL_new(global_tls_context->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; + assert(tls && tls->ssl); + 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); + if (err == _TOR_TLS_ZERORETURN) { + tls->state = TOR_TLS_ST_CLOSED; + return TOR_TLS_CLOSE; + } else { + 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; + assert(tls && tls->ssl); + 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 */ + assert(n >= tls->wantwrite_n); + log_fn(LOG_INFO,"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; + assert(tls && tls->ssl); + 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]; + 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; +} + +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 (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; +} + +/* If the provided tls connection is authenticated and has a + * certificate that is currently valid and is correctly self-signed, + * return its public key. Otherwise return NULL. + */ +crypto_pk_env_t * +tor_tls_verify(tor_tls *tls) +{ + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + time_t now, t; + crypto_pk_env_t *r = NULL; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + return NULL; + + now = time(NULL); + t = now + CERT_ALLOW_SKEW; + if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { + log_fn(LOG_WARN,"Certificate becomes valid in the future: possible clock skew."); + goto done; + } + t = now - CERT_ALLOW_SKEW; + if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { + log_fn(LOG_WARN,"Certificate already expired; possible clock skew."); + goto done; + } + + /* Get the public key. */ + if (!(pkey = X509_get_pubkey(cert))) { + log_fn(LOG_WARN,"X509_get_pubkey returned null"); + goto done; + } + if (X509_verify(cert, pkey) <= 0) { + log_fn(LOG_WARN,"X509_verify on cert and pkey returned <= 0"); + goto done; + } + + rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + pkey = NULL; + if (!rsa) { + log_fn(LOG_WARN,"EVP_PKEY_get1_RSA(pkey) returned null"); + goto done; + } + + r = _crypto_new_pk_env_rsa(rsa); + rsa = NULL; + + done: + if (cert) + X509_free(cert); + if (pkey) + EVP_PKEY_free(pkey); + if (rsa) + RSA_free(rsa); + return r; +} + +int +tor_tls_get_pending_bytes(tor_tls *tls) +{ + assert(tls); + return SSL_pending(tls->ssl); +} diff --git a/tags/tor-0_0_2pre19/src/common/tortls.h b/tags/tor-0_0_2pre19/src/common/tortls.h new file mode 100644 index 0000000000..fdfa161920 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/tortls.h @@ -0,0 +1,32 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef _TORTLS_H +#define _TORTLS_H + +#include "../common/crypto.h" + +typedef struct tor_tls_context_st tor_tls_context; +typedef struct tor_tls_st tor_tls; + +#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 + +/* X509* tor_tls_write_certificate(char *certfile, crypto_pk_env_t *rsa, char *nickname); */ +int tor_tls_context_new(crypto_pk_env_t *rsa, int isServer, const char *nickname); +tor_tls *tor_tls_new(int sock, int isServer); +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); +crypto_pk_env_t *tor_tls_verify(tor_tls *tls); +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); + +#endif diff --git a/tags/tor-0_0_2pre19/src/common/util.c b/tags/tor-0_0_2pre19/src/common/util.c new file mode 100644 index 0000000000..98b16a59ba --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/util.c @@ -0,0 +1,854 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "../or/or.h" + +#ifdef HAVE_UNAME +#include +#endif + +/* + * Memory wrappers + */ + +void *tor_malloc(size_t size) { + void *result; + + 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; +} + +void *tor_malloc_zero(size_t size) { + void *result = tor_malloc(size); + memset(result, 0, size); + return result; +} + +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; +} + +char *tor_strdup(const char *s) { + char *dup; + assert(s); + + dup = strdup(s); + if(!dup) { + log_fn(LOG_ERR,"Out of memory. Dying."); + exit(1); + } + return dup; +} + +char *tor_strndup(const char *s, size_t n) { + char *dup; + assert(s); + dup = tor_malloc(n+1); + strncpy(dup, s, n); + dup[n] = 0; + return dup; +} + +/* + * A simple smartlist interface to make an unordered list of acceptable + * nodes and then choose a random one. + * smartlist_create() mallocs the list, _free() frees the list, + * _add() adds an element, _remove() removes an element if it's there, + * _choose() returns a random element. + */ + +smartlist_t *smartlist_create(int max_elements) { + smartlist_t *sl = tor_malloc(sizeof(smartlist_t)); + sl->list = tor_malloc(sizeof(void *) * max_elements); + sl->num_used = 0; + sl->max = max_elements; + return sl; +} + +void smartlist_free(smartlist_t *sl) { + free(sl->list); + free(sl); +} + +/* add element to the list, but only if there's room */ +void smartlist_add(smartlist_t *sl, void *element) { + if(sl->num_used < sl->max) + sl->list[sl->num_used++] = element; + else + log_fn(LOG_WARN,"We've already got %d elements, discarding.",sl->max); +} + +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 */ + } +} + +int smartlist_isin(smartlist_t *sl, void *element) { + int i; + for(i=0; i < sl->num_used; i++) + if(sl->list[i] == element) + return 1; + return 0; +} + +int smartlist_overlap(smartlist_t *sl1, 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 elements of sl1 that aren't in sl2 */ +void smartlist_intersect(smartlist_t *sl1, 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 all elements of sl2 from sl1 */ +void smartlist_subtract(smartlist_t *sl1, smartlist_t *sl2) { + int i; + for(i=0; i < sl2->num_used; i++) + smartlist_remove(sl1, sl2->list[i]); +} + +void *smartlist_choose(smartlist_t *sl) { + if(sl->num_used) + return sl->list[crypto_pseudo_rand_int(sl->num_used)]; + return NULL; /* no elements to choose from */ +} + +/* + * String manipulation + */ + +/* return the first char of s that is not whitespace and not a comment */ +const char *eat_whitespace(const char *s) { + assert(s); + + while(isspace(*s) || *s == '#') { + while(isspace(*s)) + s++; + if(*s == '#') { /* read to a \n or \0 */ + while(*s && *s != '\n') + s++; + if(!*s) + return s; + } + } + return s; +} + +const char *eat_whitespace_no_nl(const char *s) { + while(*s == ' ' || *s == '\t') + ++s; + return s; +} + +/* return the first char of s that is whitespace or '#' or '\0 */ +const char *find_whitespace(const char *s) { + assert(s); + + while(*s && !isspace(*s) && *s != '#') + s++; + + return s; +} + +/* + * Time + */ + +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) + ftime(timeval); +#else +#error "No way to get time." +#endif + return; +} + +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; +} + +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; +} + +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; +} + +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); +} +static const int days_per_month[] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +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; + assert(year >= 1970); + 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. + */ + +/* a wrapper for write(2) that makes sure to write all count bytes. + * Only use if fd is a blocking fd. */ +int write_all(int fd, const char *buf, size_t count) { + int written = 0; + int result; + + while(written != count) { + result = write(fd, buf+written, count-written); + if(result<0) + return -1; + written += result; + } + return count; +} + +/* a wrapper for read(2) that makes sure to read all count bytes. + * Only use if fd is a blocking fd. */ +int read_all(int fd, char *buf, size_t count) { + int numread = 0; + int result; + + while(numread != count) { + result = read(fd, buf+numread, count-numread); + if(result<=0) + return -1; + numread += result; + } + return count; +} + +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); + assert(0); /* Should never reach here. */ + return 0; /* suppress "control-reaches-end-of-non-void" warning. */ + } else { + /* Parent */ + return 0; + } +#endif +} + +void spawn_exit() +{ +#ifdef MS_WINDOWS + _endthread(); +#else + exit(0); +#endif +} + + +/* + * Windows compatibility. + */ +int +tor_socketpair(int family, int type, int protocol, int fd[2]) +{ +#ifdef HAVE_SOCKETPAIR_XXXX + /* For testing purposes, we never fall back to real socketpairs. */ + return socketpair(family, type, protocol, fd); +#else + 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; + close(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) + close(listener); + if (connector != -1) + close(connector); + if (acceptor != -1) + close(acceptor); + errno = save_errno; + return -1; + } +#endif +} + +#ifdef MS_WINDOWS +int correct_socket_errno(int s) +{ + int optval, optvallen=sizeof(optval); + assert(errno == WSAEWOULDBLOCK); + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&optval, &optvallen)) + return errno; + if (optval) + return optval; + return WSAEWOULDBLOCK; +} +#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 returns + 0. Else returns -1. */ +int check_private_dir(const char *dirname, int create) +{ + struct stat st; + 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); + if (mkdir(dirname, 0700)) { + 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; + } + if (st.st_uid != getuid()) { + log(LOG_WARN, "%s is not owned by this UID (%d)", 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; + } + } + return 0; +} + +int +write_str_to_file(const char *fname, const char *str) +{ + char tempname[1024]; + int fd; + FILE *file; + if (strlen(fname) > 1000) { + log(LOG_WARN, "Filename %s is too long.", fname); + return -1; + } + strcpy(tempname,fname); + strcat(tempname,".tmp"); + 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); + if (rename(tempname, fname)) { + log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno)); + return -1; + } + return 0; +} + +char *read_file_to_str(const char *filename) { + int fd; /* router file */ + struct stat statbuf; + char *string; + + 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) != 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 one with a well-formed "key value". + * 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(*s)); + + key = line; + while(isspace(*key)) + key++; + if(*key == 0) + goto try_next_line; /* this line has nothing on it */ + end = key; + while(*end && !isspace(*end)) + end++; + value = end; + while(*value && isspace(*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. Skipping.",key); + goto try_next_line; + } + *end = 0; /* null it out */ + + log_fn(LOG_DEBUG,"got keyword '%s', value '%s'", key, value); + *key_out = key, *value_out = value; + return 1; +} + +static char uname_result[256]; +static int uname_result_is_set = 0; + +const char * +get_uname(void) +{ +#ifdef HAVE_UNAME + struct utsname u; +#endif + if (!uname_result_is_set) { +#ifdef HAVE_UNAME + if (!uname((&u))) { + snprintf(uname_result, 255, "%s %s %s %s %s", + u.sysname, u.nodename, u.release, u.version, 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]; +void start_daemon(void) +{ + pid_t pid; + + if (start_daemon_called) + return; + start_daemon_called = 1; + + /* Don't hold the wrong FS mounted */ + if (chdir("/") < 0) { + perror("chdir"); + exit(1); + } + + pipe(daemon_filedes); + pid = fork(); + if (pid < 0) { + perror("fork"); + 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; + } +} + +void finish_daemon(void) +{ + int nullfd; + char c = '.'; + if (finish_daemon_called) + return; + if (!start_daemon_called) + start_daemon(); + finish_daemon_called = 1; + + nullfd = open("/dev/null", + O_CREAT | O_RDWR | O_APPEND); + if (nullfd < 0) { + perror("/dev/null"); + 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) { + perror("dup2"); /* Should never happen... */ + exit(1); + } + write(daemon_filedes[1], &c, sizeof(char)); /* signal success */ + close(daemon_filedes[1]); +} +#else +/* defined(MS_WINDOWS) */ +void start_daemon(void) {} +void finish_daemon(void) {} +#endif + +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", getpid()); + fclose(pidfile); + } +#endif +} + +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; +} + diff --git a/tags/tor-0_0_2pre19/src/common/util.h b/tags/tor-0_0_2pre19/src/common/util.h new file mode 100644 index 0000000000..fd300fa805 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/common/util.h @@ -0,0 +1,121 @@ +/* Copyright 2003 Roger Dingledine */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __UTIL_H +#define __UTIL_H + +#include "../or/or.h" + +#if _MSC_VER > 1300 +#include +#include +#elif defined(_MSC_VER) +#include +#endif +#ifndef HAVE_GETTIMEOFDAY +#ifdef HAVE_FTIME +#define USING_FAKE_TIMEVAL +#include +#define timeval timeb +#define tv_sec time +#define tv_usec millitm +#endif +#endif + +#ifdef MS_WINDOWS +/* Windows names string functions funnily. */ +#define strncasecmp strnicmp +#define strcasecmp stricmp +#define INLINE __inline +#else +#define INLINE inline +#endif + +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) + +typedef struct { + void **list; + int num_used; + int max; +} smartlist_t; + +smartlist_t *smartlist_create(int max_elements); +void smartlist_free(smartlist_t *sl); +void smartlist_add(smartlist_t *sl, void *element); +void smartlist_remove(smartlist_t *sl, void *element); +int smartlist_isin(smartlist_t *sl, void *element); +int smartlist_overlap(smartlist_t *sl1, smartlist_t *sl2); +void smartlist_intersect(smartlist_t *sl1, smartlist_t *sl2); +void smartlist_subtract(smartlist_t *sl1, smartlist_t *sl2); +void *smartlist_choose(smartlist_t *sl); + +const char *eat_whitespace(const char *s); +const char *eat_whitespace_no_nl(const char *s); +const char *find_whitespace(const char *s); + +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 read_all(int fd, char *buf, size_t count); + +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); + +int spawn_func(int (*func)(void *), void *data); +void spawn_exit(); + +int tor_socketpair(int family, int type, int protocol, int fd[2]); + +const char *get_uname(void); + +/* 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(void); +/* 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); + +void write_pidfile(char *filename); +int switch_id(char *user, char *group); + +/* For stupid historical reasons, windows sockets have an independent set of + * errnos which they use as the fancy strikes them. + */ +#ifdef MS_WINDOWS +#define ERRNO_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK) +#define ERRNO_EINPROGRESS(e) ((e) == WSAEINPROGRESS) +#define ERRNO_CONN_EINPROGRESS(e) ((e) == WSAEINPROGRESS || (e) == WSAEINVAL) +int correct_socket_errno(int s); +#else +#define ERRNO_EAGAIN(e) ((e) == EAGAIN) +#define ERRNO_EINPROGRESS(e) ((e) == EINPROGRESS) +#define ERRNO_CONN_EINPROGRESS(e) ((e) == EINPROGRESS) +#define correct_socket_errno(s) (errno) +#endif + + +#endif diff --git a/tags/tor-0_0_2pre19/src/config/.cvsignore b/tags/tor-0_0_2pre19/src/config/.cvsignore new file mode 100644 index 0000000000..6d4eb70ddf --- /dev/null +++ b/tags/tor-0_0_2pre19/src/config/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +sample-server-torrc +torrc diff --git a/tags/tor-0_0_2pre19/src/config/Makefile.am b/tags/tor-0_0_2pre19/src/config/Makefile.am new file mode 100644 index 0000000000..86a192b9b5 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/config/Makefile.am @@ -0,0 +1,5 @@ +confdir = $(sysconfdir)/tor + +EXTRA_DIST = dirservers + +conf_DATA = dirservers torrc diff --git a/tags/tor-0_0_2pre19/src/config/dirservers b/tags/tor-0_0_2pre19/src/config/dirservers new file mode 100644 index 0000000000..19a82692ef --- /dev/null +++ b/tags/tor-0_0_2pre19/src/config/dirservers @@ -0,0 +1,87 @@ +# This is no longer a manually-editable file. It's just a +# concatenation of a bunch of signed router descriptors. + +router moria1 moria.mit.edu 9001 9021 9031 800000 +platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686 +published 2003-09-30 23:14:08 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANoIvHieyHUTzIacbnWOnyTyzGrLOdXqbcjz2GGMxyHEd5K1bO1ZBNHP +9i5qLQpN5viFk2K2rEGuG8tFgDEzSWZEtBqv3NVfUdiumdERWMBwlaQ0MVK4C+jf +y5gZ8KI3o9ZictgPS1AQF+Kk932/vIHTuRIUKb4ILTnQilNvID0NAgMBAAE= +-----END RSA PUBLIC KEY----- +link-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAPt97bGDd9siVjPd7Xuq2s+amMEOLIj9961aSdP6/OT+BS1Q4TX2dNOX +ZNAl63Z2fQISsR81+nfoqRLYCKxhajsD7LRvRTaRwUrWemVqFevmZ4nJrHw6FoU3 +xNUIHRMA8X2DZ+l5qgnWZb7JU50ohhX5OpMSyysXnik51J8hD5mBAgMBAAE= +-----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----- +router-signature +-----BEGIN SIGNATURE----- +Td3zb5d6uxO8oYGlmEHGzIdLuVm9s1Afqtm29JvRnnviQ36j6FZPlzPUaMVOUayn +Wtz/CbaMj7mHSufpQ68wCLb1lQrtQkn7MkAWcQPIvZjpYh3UrcWrpfm7f/D+nKeN +Z7UovF36xhCacjATNHhQNHHZHH6yONwN+Rf/N4kyPHw= +-----END SIGNATURE----- + +router moria2 moria.mit.edu 9002 9022 9032 800000 +platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686 +published 2003-09-30 23:14:05 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAM4Cc/npgYC54XrYLC+grVxJp7PDmNO2DRRJOxKttBBtvLpnR1UaueTi +kyknT5kmlx+ihgZF/jmye//2dDUp2+kK/kSkpRV4xnDLXZmed+sNSQxqmm9TtZQ9 +/hjpxhp5J9HmUTYhntBs+4E4CUKokmrI6oRLoln4SA39AX9QLPcnAgMBAAE= +-----END RSA PUBLIC KEY----- +link-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAN7JVeCIJ7+0ZJew5ScOU58rTUqjGt1Z1Rkursc7WabEb8jno45VZwIs +dkjnl31i36KHyyS7kQdHgkvG5EiyZiRipFAcoTaYv3Gvf1No9cXL6IhT3y/37dJ/ +kFPEMb/G2wdkJCC+D8fMwHBwMuqAg0JGuhoBOz0ArCgK3fq0BLilAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48 +rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0 +nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE= +-----END RSA PUBLIC KEY----- +router-signature +-----BEGIN SIGNATURE----- +X10a9Oc0LKNYKLDVzjRTIVT3NnE0y+xncllDDHSJSXR97fz3MBHGDqhy0Vgha/fe +H/Y2E59oG01lYQ73j3JN+ibsCMtkzJDx2agCpV0LmakAD9ekHrYDWm/S41Ru6kf+ +PsyHpXlh7cZuGEX4U1pblSDFrQZ9L1vTkpfW+COzEvI= +-----END SIGNATURE----- + +router moria3 moria.mit.edu 9003 9023 9033 800000 +platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686 +published 2003-09-30 23:14:07 +onion-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBANS6J/Er9fYo03fjUUVesc7We9Z6xIevyDJH39pYS4NUlcr5ExYgSVFJ +95aLCNx1x8Rf5YtiBKYuT3plBO/+rfuX+0iAGNkz/y3SlJVGz6aeptU3wN8CkvCL +zATEcnl4QSPhHX0wFB9A3t7wZ+Bat1PTI029lax/BkoS9JG5onHPAgMBAAE= +-----END RSA PUBLIC KEY----- +link-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAKUMY8p+7LBu7dEJnOR9HqbfcD6c4/f9GqJt3o29uu4XJPD8z2XGVBik +pZBLijhYS6U7GFg0NLR4zBlsLyB8TxHeaz5KJidJjy+BfC01jz1xwVTYDlmGVpc1 +0mw0Ag0ND6aOQKKhelxhTI3Bf0R9olEXuSUKEWx3EMIz2qhLd9oDAgMBAAE= +-----END RSA PUBLIC KEY----- +signing-key +-----BEGIN RSA PUBLIC KEY----- +MIGJAoGBAMqgq83cwzSid2LSvzsn2rvkD8U0tWvqF6PuQAsKP3QHFqtBO+66pnIm +CbiY2e6o01tmR47t557LuUCodEc8Blggxjg3ZEzvP42hsGB9LwQbcrU7grPRk0G0 +IltsOF9TZ+66gCeU7LxExLdAMqT2Tx6VT4IREPJMeNxSiceEjbABAgMBAAE= +-----END RSA PUBLIC KEY----- +router-signature +-----BEGIN SIGNATURE----- +GWpK2Ux/UwDaNUHwq+Xn7denyYFGS8SIWwqiMgHyUzc5wj1t2gWubJ/rMyGL59U3 +o6L/9qV34aa5UyNNBHXwYkxy7ixgPURaRYpAbkQKPU3ew8BgNXG/MNLYllIUkrbb +h6G5u8RGbto+Nby/OjIh9TqdgK/B1sOdwAHI/IXiDoY= +-----END SIGNATURE----- + diff --git a/tags/tor-0_0_2pre19/src/config/moria1-orrc b/tags/tor-0_0_2pre19/src/config/moria1-orrc new file mode 100644 index 0000000000..a2d4c43d15 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/src/config/moria2-orrc b/tags/tor-0_0_2pre19/src/config/moria2-orrc new file mode 100644 index 0000000000..b29db3e05c --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/src/config/moria3-orrc b/tags/tor-0_0_2pre19/src/config/moria3-orrc new file mode 100644 index 0000000000..4c86445eff --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/src/config/torrc.in b/tags/tor-0_0_2pre19/src/config/torrc.in new file mode 100644 index 0000000000..35077af7a2 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/config/torrc.in @@ -0,0 +1,25 @@ +# 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 + +##################### Below is just for servers ##################### + +## 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 fqdn for this server + +#ORPort 9001 # where to listen for cell-speaking connections +#ORBindAddress 0.0.0.0 # accept connections from anywhere + +## A comma-separated list of exit policies. They're considered in +## order, first match wins. +#ExitPolicy 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-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:* + diff --git a/tags/tor-0_0_2pre19/src/or/.cvsignore b/tags/tor-0_0_2pre19/src/or/.cvsignore new file mode 100644 index 0000000000..14f9ecd6db --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +tor +test +.deps diff --git a/tags/tor-0_0_2pre19/src/or/Makefile.am b/tags/tor-0_0_2pre19/src/or/Makefile.am new file mode 100644 index 0000000000..f79381a828 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/Makefile.am @@ -0,0 +1,23 @@ +TESTS = test + +noinst_PROGRAMS = test + +bin_PROGRAMS = tor + +tor_SOURCES = buffers.c circuit.c command.c connection.c \ + connection_or.c config.c dirserv.c \ + onion.c router.c routerlist.c directory.c dns.c connection_edge.c \ + cpuworker.c main.c tor_main.c + +tor_LDADD = ../common/libor.a + +test_SOURCES = buffers.c circuit.c command.c connection.c \ + connection_or.c config.c dirserv.c \ + onion.c router.c routerlist.c directory.c dns.c connection_edge.c \ + cpuworker.c main.c test.c + +test_LDADD = ../common/libor.a + +noinst_HEADERS = or.h tree.h + + diff --git a/tags/tor-0_0_2pre19/src/or/buffers.c b/tags/tor-0_0_2pre19/src/or/buffers.c new file mode 100644 index 0000000000..9a759a7c28 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/buffers.c @@ -0,0 +1,550 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* buffers.c */ + +#include "or.h" + +struct buf_t { + char *mem; + size_t len; + size_t datalen; +}; + +/* 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) +/* 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) +#define BUF_OK(b) ((b) && (b)->mem && (b)->datalen <= (b)->len) + +/* Change a buffer's capacity. Must only be called when */ +static INLINE void buf_resize(buf_t *buf, size_t new_capacity) +{ + assert(buf->datalen <= new_capacity); + 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) { + assert(buf->datalen >= n); + buf->datalen -= n; + memmove(buf->mem, buf->mem+n, buf->datalen); + buf_shrink_if_underfull(buf); +} + +/* Find the first instance of str on buf. If none exists, return -1. + * Otherwise, return index of the first character in buf _after_ the + * first instance of str. + */ +static int find_str_in_str(const char *str, int str_len, + const char *buf, int buf_len) +{ + const char *location; + const char *last_possible = buf + buf_len - str_len; + + assert(str && str_len > 0 && buf); + + if(buf_len < str_len) + return -1; + + for(location = buf; location <= last_possible; location++) + if((*location == *str) && !memcmp(location+1, str+1, str_len-1)) + return location-buf+str_len; + + return -1; +} + +int find_on_inbuf(char *string, int string_len, buf_t *buf) { + return find_str_in_str(string, string_len, buf->mem, buf->datalen); +} + +/* Create and return a new buf of size 'size' + */ +buf_t *buf_new_with_capacity(size_t size) { + buf_t *buf; + buf = (buf_t*)tor_malloc(sizeof(buf_t)); + buf->mem = (char *)tor_malloc(size); + buf->len = size; + buf->datalen = 0; +// memset(buf->mem,0,size); + + assert(BUF_OK(buf)); + return buf; +} + +buf_t *buf_new() +{ + return buf_new_with_capacity(INITIAL_BUF_SIZE); +} + + +size_t buf_datalen(const buf_t *buf) +{ + return buf->datalen; +} + +size_t buf_capacity(const buf_t *buf) +{ + return buf->len; +} + +const char *_buf_peek_raw_buffer(const buf_t *buf) +{ + return buf->mem; +} + +void buf_free(buf_t *buf) { + assert(buf && buf->mem); + free(buf->mem); + free(buf); +} + +/* read from socket s, writing onto end of buf. + * read at most 'at_most' bytes, and in any case don't read more than will fit based on buflen. + * If read() returns 0, set *reached_eof to 1 and return 0. If you want to tear + * down the connection return -1, else return the number of bytes read. + */ +int read_to_buf(int s, int at_most, buf_t *buf, int *reached_eof) { + + int read_result; +#ifdef MS_WINDOWS + int e; +#endif + + assert(BUF_OK(buf) && reached_eof && (s>=0)); + + if (buf_ensure_capacity(buf,buf->datalen+at_most)) + return -1; + + if(at_most > buf->len - buf->datalen) + 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 = read(s, buf->mem+buf->datalen, at_most); + if (read_result < 0) { + if(!ERRNO_EAGAIN(errno)) { /* it's a real error */ + return -1; + } +#ifdef MS_WINDOWS + e = correct_socket_errno(s); + if(!ERRNO_EAGAIN(e)) { /* no, it *is* a real error! */ + return -1; + } +#endif + return 0; + } 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; + } +} + +int read_to_buf_tls(tor_tls *tls, int at_most, buf_t *buf) { + int r; + assert(tls && BUF_OK(buf)); + + if (buf_ensure_capacity(buf, at_most+buf->datalen)) + return -1; + + if (at_most > buf->len - buf->datalen) + at_most = buf->len - buf->datalen; + + if (at_most == 0) + return 0; + + 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.",r, (int)buf->datalen); + return r; +} + +int flush_buf(int s, buf_t *buf, int *buf_flushlen) +{ + + /* push from buf onto s + * then memmove to front of buf + * return -1 or how many bytes remain to be flushed */ + + int write_result; +#ifdef MS_WINDOWS + int e; +#endif + + assert(BUF_OK(buf) && buf_flushlen && (s>=0) && (*buf_flushlen <= buf->datalen)); + + if(*buf_flushlen == 0) /* nothing to flush */ + return 0; + + write_result = write(s, buf->mem, *buf_flushlen); + if (write_result < 0) { + if(!ERRNO_EAGAIN(errno)) { /* it's a real error */ + return -1; + } +#ifdef MS_WINDOWS + e = correct_socket_errno(s); + if(!ERRNO_EAGAIN(e)) { /* no, it *is* a real error! */ + return -1; + } +#endif + 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 *buf_flushlen; + /* XXX USE_TLS should change to return write_result like any sane function would */ + } +} + +int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen) +{ + int r; + assert(tls && BUF_OK(buf) && 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; +} + +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 + */ + + assert(string && 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; +} + +int fetch_from_buf(char *string, int 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. */ + + assert(string && BUF_OK(buf)); + assert(string_len <= buf->datalen); /* make sure we don't ask for too much */ + + 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. + * 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 and body into the supplied args (and null terminate + * them), 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 max_bodylen) { + char *headers, *body; + int i; + int headerlen, bodylen, contentlen; + + assert(BUF_OK(buf)); + + headers = buf->mem; + i = find_on_inbuf("\r\n\r\n", 4, buf); + if(i < 0) { + log_fn(LOG_DEBUG,"headers not all here yet."); + return 0; + } + body = buf->mem+i; + 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: " + i = find_str_in_str(CONTENT_LENGTH, strlen(CONTENT_LENGTH), + headers, headerlen); + if(i > 0) { + contentlen = atoi(headers+i); + /* XXX What if content-length is malformed? */ + 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 */ + } + 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) { + *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 *reply + * and set *replylen, else leave *replylen alone. + * If returning 0 or -1, *addr_out and *port_out 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); + assert(!req->socks_version); + log_fn(LOG_DEBUG,"socks5: learning offered methods"); + if(buf->datalen < 2+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] = 0xFF; /* 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 */ + if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */ + log_fn(LOG_WARN,"socks5: command %d not '1'.",*(buf->mem+1)); + 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", + (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 < 7+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", + len+1,MAX_SOCKS_ADDR_LEN); + return -1; + } + memcpy(req->address,buf->mem+5,len); + req->address[len] = 0; + req->port = ntohs(*(uint16_t*)(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",*(buf->mem+3)); + return -1; + } + assert(0); + case 4: /* socks4 */ + + req->socks_version = 4; + if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */ + return 0; /* not yet */ + + if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */ + log_fn(LOG_WARN,"socks4: command %d not '1'.",*(buf->mem+1)); + return -1; + } + + req->port = ntohs(*(uint16_t*)(buf->mem+2)); + destip = ntohl(*(uint32_t*)(buf->mem+4)); + if(!req->port || !destip) { + log_fn(LOG_WARN,"socks4: Port or DestIP is zero."); + 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.", + (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); + if(!next) { + log_fn(LOG_DEBUG,"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,"Destaddr not here yet."); + return 0; + } + if(MAX_SOCKS_ADDR_LEN <= next-startaddr) { + log_fn(LOG_WARN,"Destaddr too long."); + return -1; + } + } + log_fn(LOG_DEBUG,"Everything is here. Success."); + strcpy(req->address, socks4_prot == socks4 ? tmpbuf : startaddr); + buf_remove_from_front(buf, next-buf->mem+1); /* next points to the final \0 on inbuf */ + return 1; + + default: /* version is not socks4 or socks5 */ + log_fn(LOG_WARN,"Socks version %d not recognized. (Tor is not an httpd proxy.)",*(buf->mem)); + return -1; + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/circuit.c b/tags/tor-0_0_2pre19/src/or/circuit.c new file mode 100644 index 0000000000..071c947269 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/circuit.c @@ -0,0 +1,1219 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +static int relay_crypt(circuit_t *circ, cell_t *cell, char 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 void circuit_free_cpath_node(crypt_path_t *victim); +static uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type); + +unsigned long stats_n_relay_cells_relayed = 0; +unsigned long stats_n_relay_cells_delivered = 0; + +/********* START VARIABLES **********/ + +static circuit_t *global_circuitlist=NULL; + +char *circuit_state_to_string[] = { + "doing handshakes", /* 0 */ + "processing the onion", /* 1 */ + "connecting to firsthop", /* 2 */ + "open" /* 3 */ +}; + +/********* END VARIABLES ************/ + +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; + } +} + +void circuit_remove(circuit_t *circ) { + circuit_t *tmpcirc; + + assert(circ && global_circuitlist); + + if(global_circuitlist == circ) { + global_circuitlist = global_circuitlist->next; + return; + } + + for(tmpcirc = global_circuitlist;tmpcirc->next;tmpcirc = tmpcirc->next) { + if(tmpcirc->next == circ) { + tmpcirc->next = circ->next; + return; + } + } +} + +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) { + circuit_t *circ; + + circ = tor_malloc_zero(sizeof(circuit_t)); + + 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; +} + +void circuit_free(circuit_t *circ) { + assert(circ); + 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); + tor_free(circ->build_state); + circuit_free_cpath(circ->cpath); + free(circ); +} + +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); +} + +static 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 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; + + 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; +} + +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->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; + } + } + } + return NULL; +} + +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->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; + } + return NULL; +} + +/* Find the newest circ that conn can use, preferably one which is + * dirty. Circ must not be too old. + * If !conn, return newest. + * + * If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN. + */ +circuit_t *circuit_get_newest(connection_t *conn, int must_be_open) { + circuit_t *circ, *newest=NULL, *leastdirty=NULL; + routerinfo_t *exitrouter; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(!circ->cpath) + continue; /* this circ doesn't start at us */ + if(must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn)) + continue; /* ignore non-open circs */ + if(conn) { + if(circ->state == CIRCUIT_STATE_OPEN && circ->n_conn) /* open */ + exitrouter = router_get_by_addr_port(circ->cpath->prev->addr, circ->cpath->prev->port); + else /* not open */ + exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); + if(!exitrouter || connection_ap_can_use_exit(conn, exitrouter) < 0) { + /* can't exit from this router */ + continue; + } + } + if(!newest || newest->timestamp_created < circ->timestamp_created) { + newest = circ; + } + if(conn && circ->timestamp_dirty && + (!leastdirty || leastdirty->timestamp_dirty < circ->timestamp_dirty)) { + leastdirty = circ; + } + } + + if(leastdirty && + leastdirty->timestamp_dirty+options.NewCircuitPeriod > time(NULL)) { +/* log_fn(LOG_DEBUG,"Choosing in-use circuit %s:%d:%d.", + leastdirty->n_conn->address, leastdirty->n_port, leastdirty->n_circ_id); */ + return leastdirty; + } + if(newest) { +/* log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.", + newest->n_conn->address, newest->n_port, newest->n_circ_id); */ + return newest; + } + return NULL; +} + +#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 3 +/* circuits that were born at the end of their second might be expired + * after 3.1 seconds; circuits born at the beginning might be expired + * after closer to 4 seconds. + */ + +/* 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(void) { + int now = time(NULL); + circuit_t *victim, *circ = global_circuitlist; + + while(circ) { + victim = circ; + circ = circ->next; + if(victim->cpath && + victim->state != CIRCUIT_STATE_OPEN && + victim->timestamp_created + MIN_SECONDS_BEFORE_EXPIRING_CIRC+1 < now) { + if(victim->n_conn) + log_fn(LOG_INFO,"Abandoning circ %s:%d:%d (state %d:%s)", + victim->n_conn->address, victim->n_port, victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state]); + else + log_fn(LOG_INFO,"Abandoning circ %d (state %d:%s)", victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state]); + circuit_close(victim); + } + } +} + +/* count the number of circs starting at us that aren't open */ +int circuit_count_building(void) { + circuit_t *circ; + int num=0; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->cpath && circ->state != CIRCUIT_STATE_OPEN) + num++; + } + return num; +} + +#define MIN_CIRCUITS_HANDLING_STREAM 2 +/* return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open 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; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->cpath && circ->state != CIRCUIT_STATE_OPEN) { + exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); + if(exitrouter && connection_ap_can_use_exit(conn, exitrouter) >= 0) + if(++num >= MIN_CIRCUITS_HANDLING_STREAM) + return 1; + } + } + return 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); +} + +/* 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; +} + +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 crypt: %s", crypto_perror()); + 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, check digest, find right conn, deliver to edge. + - else connection_or_write_cell_to_buf to the right conn +*/ +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; + + assert(cell && circ); + assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN); + + 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 to exit."); + if (connection_edge_process_relay_cell(cell, circ, conn, EDGE_EXIT, NULL) < 0) { + log_fn(LOG_WARN,"connection_edge_process_relay_cell (at exit) failed."); + return -1; + } + } + if(cell_direction == CELL_DIRECTION_IN) { + ++stats_n_relay_cells_delivered; + log_fn(LOG_DEBUG,"Sending to AP."); + if (connection_edge_process_relay_cell(cell, circ, conn, EDGE_AP, layer_hint) < 0) { + log_fn(LOG_WARN,"connection_edge_process_relay_cell (at AP) 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) { + log_fn(LOG_WARN,"Didn't recognize cell, but circ stops here! Dropping."); + return 0; /* XXX if this warning never triggers, then 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; +} + +/* wrap this into receive_relay_cell one day */ +static int relay_crypt(circuit_t *circ, cell_t *cell, char cell_direction, + crypt_path_t **layer_hint, char *recognized) { + crypt_path_t *thishop; + relay_header_t rh; + + assert(circ && cell && recognized); + assert(cell_direction == CELL_DIRECTION_IN || cell_direction == CELL_DIRECTION_OUT); + + if(cell_direction == CELL_DIRECTION_IN) { + if(circ->cpath) { /* we're at the beginning of the circuit. + We'll want to do layered crypts. */ + thishop = circ->cpath; + if(thishop->state != CPATH_STATE_OPEN) { + log_fn(LOG_WARN,"Relay cell before first created cell?"); + return -1; + } + do { /* Remember: cpath is in forward order, that is, first hop first. */ + 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. Dropping."); + return 0; + } 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: + - if from AP: encrypt it to the right conn if 'recognized' doesn't + conflict, else insert dummies first as appropriate + - if from exit: encrypt it + - connection_or_write_cell_to_buf to the right conn +*/ +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_INFO,"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 { + 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_INFO,"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; +} + +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; + + if(cell_direction == CELL_DIRECTION_OUT) + tmpconn = circ->n_streams; + else + tmpconn = circ->p_streams; + + for( ; 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; + } + log_fn(LOG_DEBUG,"considered stream %d, not it.",tmpconn->stream_id); + } + return NULL; /* probably a begin relay cell */ +} + +void circuit_resume_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint) { + connection_t *conn; + + assert(edge_type == EDGE_EXIT || edge_type == EDGE_AP); + + log_fn(LOG_DEBUG,"resuming"); + + if(edge_type == EDGE_EXIT) + conn = circ->n_streams; + else + conn = circ->p_streams; + + for( ; conn; conn=conn->next_stream) { + if((edge_type == EDGE_EXIT && conn->package_window > 0) || + (edge_type == EDGE_AP && conn->package_window > 0 && conn->cpath_layer == layer_hint)) { + connection_start_reading(conn); + connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */ + + /* 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, edge_type, layer_hint)) + return; + } + } +} + +/* returns 1 if the window is empty, else 0. If it's empty, tell edge conns to stop reading. */ +int circuit_consider_stop_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint) { + connection_t *conn = NULL; + + assert(edge_type == EDGE_EXIT || edge_type == EDGE_AP); + assert(edge_type == EDGE_EXIT || layer_hint); + + log_fn(LOG_DEBUG,"considering"); + if(edge_type == EDGE_EXIT && circ->package_window <= 0) + conn = circ->n_streams; + else if(edge_type == EDGE_AP && layer_hint->package_window <= 0) + conn = circ->p_streams; + else + return 0; + + for( ; conn; conn=conn->next_stream) + if(!layer_hint || conn->cpath_layer == layer_hint) + connection_stop_reading(conn); + + log_fn(LOG_DEBUG,"yes. stopped."); + return 1; +} + +void circuit_consider_sending_sendme(circuit_t *circ, int edge_type, crypt_path_t *layer_hint) { + while((edge_type == EDGE_AP ? layer_hint->deliver_window : circ->deliver_window) < + CIRCWINDOW_START - CIRCWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"Queueing circuit sendme."); + if(edge_type == EDGE_AP) + 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. Returning."); + return; /* the circuit's closed, don't continue */ + } + } +} + +void circuit_close(circuit_t *circ) { + connection_t *conn; + + assert(circ); + circuit_remove(circ); + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + onion_pending_remove(circ); + } + + 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_send_destroy(circ->n_circ_id, conn); + } + if(circ->p_conn) + connection_send_destroy(circ->n_circ_id, circ->p_conn); + for(conn=circ->p_streams; conn; conn=conn->next_stream) { + connection_send_destroy(circ->p_circ_id, conn); + } + if (circ->state == CIRCUIT_STATE_BUILDING || + circ->state == CIRCUIT_STATE_OR_WAIT) { + /* 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(); + } + circuit_free(circ); +} + +void circuit_about_to_close_connection(connection_t *conn) { + /* send destroys for all circuits using 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; + connection_t *prevconn; + + switch(conn->type) { + case CONN_TYPE_OR: + /* We must close all the 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_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; + + if(!conn->has_sent_end) { + log_fn(LOG_INFO,"Edge connection hasn't sent end yet? Bug."); + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + } + + if(conn == circ->p_streams) { + circ->p_streams = conn->next_stream; + return; + } + if(conn == circ->n_streams) { + circ->n_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; + } + log_fn(LOG_ERR,"edge conn not in circuit's list?"); + assert(0); /* should never get here */ + } /* end switch */ +} + +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(circ->cpath) { /* 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); + for(hop=circ->cpath;hop->next != circ->cpath; hop=hop->next) + log(severity,"hop: state %d, addr %x, port %d", hop->state, + (unsigned int)hop->addr, + (int)hop->port); + } +} + +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); + } + } + } +} + +void circuit_expire_unused_circuits(void) { + circuit_t *circ, *tmpcirc; + time_t now = time(NULL); + + circ = global_circuitlist; + while(circ) { + tmpcirc = circ; + circ = circ->next; + if(tmpcirc->timestamp_dirty && + tmpcirc->timestamp_dirty + options.NewCircuitPeriod < now && + !tmpcirc->p_conn && !tmpcirc->p_streams) { + log_fn(LOG_DEBUG,"Closing n_circ_id %d",tmpcirc->n_circ_id); + circuit_close(tmpcirc); + } + } +} + +/* Number of consecutive failures so far; should only be touched by + * circuit_launch_new and circuit_*_failure_count. + */ +static int n_circuit_failures = 0; + +/* Return -1 if you aren't going to try to make a circuit, 0 if you did try. */ +int circuit_launch_new(void) { + + if(!options.SocksPort) /* we're not an application proxy. no need for circuits. */ + return -1; + + if(n_circuit_failures > 5) { /* too many failed circs in a row. don't try. */ +// log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures); + return -1; + } + + /* try a circ. if it fails, circuit_close will increment n_circuit_failures */ + circuit_establish_circuit(); + + return 0; +} + +void circuit_increment_failure_count(void) { + ++n_circuit_failures; + log_fn(LOG_DEBUG,"n_circuit_failures now %d.",n_circuit_failures); +} + +void circuit_reset_failure_count(void) { + n_circuit_failures = 0; +} + +int circuit_establish_circuit(void) { + 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(); + + if (! circ->build_state) { + log_fn(LOG_INFO,"Generating cpath length failed."); + circuit_close(circ); + return -1; + } + + onion_extend_cpath(&circ->cpath, circ->build_state, &firsthop); + if(!circ->cpath) { + log_fn(LOG_INFO,"Generating first cpath hop failed."); + circuit_close(circ); + return -1; + } + + /* 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); + n_conn = connection_twin_get_by_addr_port(firsthop->addr,firsthop->or_port); + 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(options.ORPort) { /* we would be connected if he were up. and he's not. */ + log_fn(LOG_INFO,"Route's firsthop isn't connected."); + circuit_close(circ); + return -1; + } + + if(!n_conn) { /* launch the connection */ + n_conn = connection_or_connect(firsthop); + if(!n_conn) { /* connect failed, forget the whole thing */ + log_fn(LOG_INFO,"connect to firsthop failed. Closing."); + circuit_close(circ); + return -1; + } + } + + log_fn(LOG_DEBUG,"connecting in progress (or finished). Good."); + return 0; /* return success. The onion/circuit/etc will be taken care of automatically + * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. + */ + } 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_close(circ); + return -1; + } + } + return 0; +} + +/* find circuits that are waiting on me, if any, and get them to send the onion */ +void circuit_n_conn_open(connection_t *or_conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->cpath && circ->n_addr == or_conn->addr && circ->n_port == or_conn->port) { + assert(circ->state == CIRCUIT_STATE_OR_WAIT); + log_fn(LOG_DEBUG,"Found circ %d, sending onion skin.", circ->n_circ_id); + circ->n_conn = or_conn; + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing."); + circuit_close(circ); + continue; + /* XXX could this be bad, eg if next_onion_skin failed because conn died? */ + } + } + } +} + +int circuit_send_next_onion_skin(circuit_t *circ) { + cell_t cell; + crypt_path_t *hop; + routerinfo_t *router; + int r; + int circ_id_type; + char payload[6+ONIONSKIN_CHALLENGE_LEN]; + + assert(circ && circ->cpath); + + if(circ->cpath->state == CPATH_STATE_CLOSED) { + assert(circ->n_conn && circ->n_conn->type == CONN_TYPE_OR); + + log_fn(LOG_DEBUG,"First skin; sending create cell."); + 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); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATE; + cell.circ_id = circ->n_circ_id; + + if(onion_skin_create(circ->n_conn->onion_pkey, &(circ->cpath->handshake_state), cell.payload) < 0) { + log_fn(LOG_WARN,"onion_skin_create (first hop) failed."); + return -1; + } + + connection_or_write_cell_to_buf(&cell, circ->n_conn); + + circ->cpath->state = CPATH_STATE_AWAITING_KEYS; + circ->state = CIRCUIT_STATE_BUILDING; + log_fn(LOG_DEBUG,"first skin; finished sending create cell."); + } else { + assert(circ->cpath->state == CPATH_STATE_OPEN); + 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(); + /* Tell any AP connections that have been waiting for a new + * circuit that one is ready. */ + connection_ap_attach_pending(); + 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); + if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), payload+6) < 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, sizeof(payload), 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; + int circ_id_type; + cell_t newcell; + + if(circ->n_conn) { + log_fn(LOG_WARN,"n_conn already set. Bug/attack. Closing."); + return -1; + } + + circ->n_addr = ntohl(*(uint32_t*)(cell->payload+RELAY_HEADER_SIZE)); + circ->n_port = ntohs(*(uint16_t*)(cell->payload+RELAY_HEADER_SIZE+4)); + + n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + if(!n_conn || n_conn->type != CONN_TYPE_OR) { + /* i've disabled making connections through OPs, but it's definitely + * possible here. I'm not sure if it would be a bug or a feature. -RD + */ + /* note also that this will close circuits where the onion has the same + * router twice in a row in the path. i think that's ok. -RD + */ + struct in_addr in; + in.s_addr = htonl(circ->n_addr); + log_fn(LOG_INFO,"Next router (%s:%d) not connected. Closing.", inet_ntoa(in), circ->n_port); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + 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; + log_fn(LOG_DEBUG,"n_conn is %s:%u",n_conn->address,n_conn->port); + + circ_id_type = decide_circ_id_type(options.Nickname, n_conn->nickname); + + log_fn(LOG_DEBUG,"circ_id_type = %u.",circ_id_type); + 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(&newcell, 0, sizeof(cell_t)); + newcell.command = CELL_CREATE; + newcell.circ_id = circ->n_circ_id; + + memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+6, + ONIONSKIN_CHALLENGE_LEN); + + connection_or_write_cell_to_buf(&newcell, circ->n_conn); + return 0; +} + +int circuit_finish_handshake(circuit_t *circ, char *reply) { + unsigned char iv[16]; + unsigned char keys[40+32]; + crypt_path_t *hop; + + memset(iv, 0, 16); + + assert(circ->cpath); + 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; + } + } + assert(hop->state == CPATH_STATE_AWAITING_KEYS); + + if(onion_skin_client_handshake(hop->handshake_state, reply, keys, 40+32) < 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; + + log_fn(LOG_INFO,"hop %d init digest forward %u, backward %u.", + (int)hop, (unsigned)*(uint32_t*)keys, (unsigned)*(uint32_t*)(keys+20)); + hop->f_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST); + crypto_digest_add_bytes(hop->f_digest, keys, 20); + hop->b_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST); + crypto_digest_add_bytes(hop->b_digest, keys+20, 20); + + log_fn(LOG_DEBUG,"hop %d init cipher forward %u, backward %u.", + (int)hop, (unsigned)*(uint32_t*)(keys+40), (unsigned) *(uint32_t*)(keys+40+16)); + if (!(hop->f_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40,iv,1))) { + log(LOG_WARN,"forward cipher initialization failed."); + return -1; + } + if (!(hop->b_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40+16,iv,0))) { + log(LOG_WARN,"backward cipher initialization failed."); + return -1; + } + + hop->state = CPATH_STATE_OPEN; + log_fn(LOG_INFO,"finished"); + return 0; +} + +int circuit_truncated(circuit_t *circ, crypt_path_t *layer) { + crypt_path_t *victim; + connection_t *stream; + + assert(circ); + 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_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->marked_for_close = 1; + stream->has_sent_end = 1; + } + } + + layer->next = victim->next; + circuit_free_cpath_node(victim); + } + + log_fn(LOG_INFO, "finished"); + return 0; +} + + +void assert_cpath_layer_ok(const crypt_path_t *cp) +{ + assert(cp->f_crypto); + assert(cp->b_crypto); + assert(cp->addr); + assert(cp->port); + switch(cp->state) + { + case CPATH_STATE_CLOSED: + case CPATH_STATE_OPEN: + assert(!cp->handshake_state); + break; + case CPATH_STATE_AWAITING_KEYS: + assert(cp->handshake_state); + break; + default: + assert(0); + } + assert(cp->package_window >= 0); + assert(cp->deliver_window >= 0); +} + +void assert_cpath_ok(const crypt_path_t *cp) +{ + while(cp->prev) + cp = cp->prev; + + while(cp->next) { + assert_cpath_layer_ok(cp); + /* layers must be in sequence of: "open* awaiting? closed*" */ + if (cp->prev) { + if (cp->prev->state == CPATH_STATE_OPEN) { + assert(cp->state == CPATH_STATE_CLOSED || + cp->state == CPATH_STATE_AWAITING_KEYS); + } else { + assert(cp->state == CPATH_STATE_CLOSED); + } + } + cp = cp->next; + } +} + +void assert_circuit_ok(const circuit_t *c) +{ + connection_t *conn; + + assert(c->n_addr); + assert(c->n_port); + assert(c->n_conn); + assert(c->n_conn->type == CONN_TYPE_OR); + if (c->p_conn) + assert(c->p_conn->type == CONN_TYPE_OR); + for (conn = c->p_streams; conn; conn = conn->next_stream) + assert(c->p_conn->type == CONN_TYPE_EXIT); + for (conn = c->n_streams; conn; conn = conn->next_stream) + assert(conn->type == CONN_TYPE_EXIT); + + assert(c->deliver_window >= 0); + assert(c->package_window >= 0); + if (c->state == CIRCUIT_STATE_OPEN) { + if (c->cpath) { + assert(!c->n_crypto); + assert(!c->p_crypto); + assert(!c->n_digest); + assert(!c->p_digest); + } else { + assert(c->n_crypto); + assert(c->p_crypto); + assert(c->n_digest); + assert(c->p_digest); + } + } + if (c->cpath) { + assert_cpath_ok(c->cpath); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/command.c b/tags/tor-0_0_2pre19/src/or/command.c new file mode 100644 index 0000000000..0a3b91843f --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/command.c @@ -0,0 +1,218 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +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; + +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); + +static void command_time_process_cell(cell_t *cell, connection_t *conn, + int *num, int *time, + void (*func)(cell_t *, connection_t *)) { + struct timeval start, end; + long time_passed; + + *num += 1; + + tor_gettimeofday(&start); + + (*func)(cell, conn); + + tor_gettimeofday(&end); + time_passed = tv_udiff(&start, &end) ; + + if (time_passed > 5000) { /* more than 5ms */ + log_fn(LOG_INFO,"That call just took %ld ms.",time_passed/1000); + } + *time += time_passed; +} + +void command_process_cell(cell_t *cell, connection_t *conn) { + static int num_create=0, num_created=0, num_relay=0, num_destroy=0; + 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:"); + log(LOG_INFO,"Create: %d (%d ms)", num_create, create_time/1000); + log(LOG_INFO,"Created: %d (%d ms)", num_created, created_time/1000); + log(LOG_INFO,"Relay: %d (%d ms)", num_relay, relay_time/1000); + log(LOG_INFO,"Destroy: %d (%d ms)", 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; + command_time_process_cell(cell, conn, &num_create, &create_time, + command_process_create_cell); + break; + case CELL_CREATED: + ++stats_n_created_cells_processed; + command_time_process_cell(cell, conn, &num_created, &created_time, + command_process_created_cell); + break; + case CELL_RELAY: + ++stats_n_relay_cells_processed; + command_time_process_cell(cell, conn, &num_relay, &relay_time, + command_process_relay_cell); + break; + case CELL_DESTROY: + ++stats_n_destroy_cells_processed; + command_time_process_cell(cell, conn, &num_destroy, &destroy_time, + command_process_destroy_cell); + break; + default: + log_fn(LOG_WARN,"Cell of unknown type (%d) received. Dropping.", cell->command); + break; + } +} + +static void command_process_create_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_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; + + 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_close(circ); + return; + } + log_fn(LOG_INFO,"success: handed off onionskin."); +} + +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_close(circ); + return; + } + + if(circ->cpath) { /* 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_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_close(circ); /* XXX push this circuit_close lower */ + return; + } + } else { /* pack it into an extended relay cell, and send it. */ + log_fn(LOG_INFO,"Converting created cell to extended relay cell, sending."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTENDED, + cell->payload, ONIONSKIN_REPLY_LEN, NULL); + } +} + +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. Dropping.", cell->circ_id); + return; + } + + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + log_fn(LOG_WARN,"circuit in create_wait. Closing."); + circuit_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_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_close(circ); + return; + } + } +} + +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. Dropping.", cell->circ_id); + return; + } + + log_fn(LOG_DEBUG,"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 || circ->cpath) { + /* either the destroy came from behind, or we're the AP */ + circ->p_conn = NULL; + circuit_close(circ); + } else { /* the destroy came from ahead */ + circ->n_conn = NULL; + log_fn(LOG_DEBUG, "Delivering 'truncated' back."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/config.c b/tags/tor-0_0_2pre19/src/or/config.c new file mode 100644 index 0000000000..16efde10e9 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/config.c @@ -0,0 +1,439 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/* enumeration of types which option values can take */ +#define CONFIG_TYPE_STRING 0 +#define CONFIG_TYPE_CHAR 1 +#define CONFIG_TYPE_INT 2 +#define CONFIG_TYPE_LONG 3 +#define CONFIG_TYPE_DOUBLE 4 +#define CONFIG_TYPE_BOOL 5 + +#define CONFIG_LINE_MAXLEN 4096 + +struct config_line { + char *key; + char *value; + struct config_line *next; +}; + +static FILE *config_open(const unsigned char *filename); +static int config_close(FILE *f); +static struct config_line *config_get_commandlines(int argc, char **argv); +static struct config_line *config_get_lines(FILE *f); +static void config_free_lines(struct config_line *front); +static int config_compare(struct config_line *c, char *key, int type, void *arg); +static void config_assign(or_options_t *options, struct config_line *list); + +/* open configuration file for reading */ +static FILE *config_open(const unsigned char *filename) { + assert(filename); + if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) { + /* filename has illegal letters */ + return NULL; + } + return fopen(filename, "r"); +} + +/* close configuration file */ +static int config_close(FILE *f) { + assert(f); + return fclose(f); +} + +static struct config_line *config_get_commandlines(int argc, char **argv) { + struct config_line *new; + struct config_line *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)); + 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; +} + +/* parse the config file and strdup into key/value strings. Return list, + * or NULL if parsing the file failed. + * Warn and ignore mangled lines. */ +static struct config_line *config_get_lines(FILE *f) { + struct config_line *new; + struct config_line *front = NULL; + char line[CONFIG_LINE_MAXLEN]; + int result; + char *key, *value; + + while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) { + new = tor_malloc(sizeof(struct config_line)); + new->key = tor_strdup(key); + new->value = tor_strdup(value); + + new->next = front; + front = new; + } + if(result < 0) + return NULL; + return front; +} + +static void config_free_lines(struct config_line *front) { + struct config_line *tmp; + + while(front) { + tmp = front; + front = tmp->next; + + free(tmp->key); + free(tmp->value); + free(tmp); + } +} + +static int config_compare(struct config_line *c, char *key, int type, void *arg) { + int i; + + if(strncasecmp(c->key,key,strlen(c->key))) + return 0; + + /* 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; + } + return 1; +} + +static void config_assign(or_options_t *options, struct config_line *list) { + + /* iterate through list. for each item convert as appropriate and assign to 'options'. */ + + 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, "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_STRING, &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_STRING, &options->ExitPolicy) || + config_compare(list, "ExcludedNodes", CONFIG_TYPE_STRING, &options->ExcludedNodes) || + + 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_STRING, &options->LogLevel) || + config_compare(list, "LogFile", CONFIG_TYPE_STRING, &options->LogFile) || + 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_STRING, &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, "SocksPort", CONFIG_TYPE_INT, &options->SocksPort) || + config_compare(list, "SocksBindAddress",CONFIG_TYPE_STRING,&options->SocksBindAddress) || + + config_compare(list, "TotalBandwidth", CONFIG_TYPE_INT, &options->TotalBandwidth) || + config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) || + + config_compare(list, "User", CONFIG_TYPE_STRING, &options->User) + ) { + /* then we're ok. it matched something. */ + } else { + log_fn(LOG_WARN,"Ignoring unknown keyword '%s'.",list->key); + } + + list = list->next; + } +} + +/* prints the usage of tor. */ +void print_usage(void) { + printf("tor -f [args]\n" + "-d \t\tDebug file\n" + "-m \t\tMax number of connections\n" + "-l \t\tLog level\n" + "-t \t\tTotal bandwidth\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" + ); +} + +void free_options(or_options_t *options) { + tor_free(options->LogLevel); + tor_free(options->LogFile); + 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->ExcludedNodes); + tor_free(options->ExitPolicy); + tor_free(options->SocksBindAddress); + tor_free(options->ORBindAddress); + tor_free(options->DirBindAddress); + tor_free(options->RecommendedVersions); + tor_free(options->User); + tor_free(options->Group); +} + +void init_options(or_options_t *options) { +/* give reasonable values for each option. Defaults to zero. */ + memset(options,0,sizeof(or_options_t)); + options->LogLevel = tor_strdup("warn"); + options->ExitNodes = tor_strdup(""); + options->EntryNodes = tor_strdup(""); + options->ExcludedNodes = tor_strdup(""); + options->ExitPolicy = tor_strdup("reject *:25,reject 127.0.0.0/8:*,reject 0.0.0.0/8,accept *:*"); + options->SocksBindAddress = tor_strdup("127.0.0.1"); + options->ORBindAddress = tor_strdup("0.0.0.0"); + options->DirBindAddress = tor_strdup("0.0.0.0"); + options->RecommendedVersions = tor_strdup("none"); + options->loglevel = LOG_INFO; + 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 = 60; /* once a minute */ + options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */ + options->NumCpus = 1; +} + +/* return 0 if success, <0 if failure. */ +int getconfig(int argc, char **argv, or_options_t *options) { + struct config_line *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; + + 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; + free_options(options); + } + init_options(options); + + if(argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) { + print_usage(); + 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 = argv[i+1]; + } else { /* didn't find one, try CONFDIR */ + fname = CONFDIR "/torrc"; + } + log(LOG_DEBUG,"Opening config file '%s'",fname); + + cf = config_open(fname); + if(!cf) { + log(LOG_WARN, "Unable to open configuration file '%s'.",fname); + return -1; + } + + cl = config_get_lines(cf); + if(!cl) return -1; + config_assign(options,cl); + config_free_lines(cl); + config_close(cf); + +/* go through command-line variables too */ + cl = config_get_commandlines(argc,argv); + config_assign(options,cl); + 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(options->LogLevel) { + if(!strcmp(options->LogLevel,"err")) + options->loglevel = LOG_ERR; + else if(!strcmp(options->LogLevel,"warn")) + options->loglevel = LOG_WARN; + else if(!strcmp(options->LogLevel,"info")) + options->loglevel = LOG_INFO; + else if(!strcmp(options->LogLevel,"debug")) + options->loglevel = LOG_DEBUG; + else { + log(LOG_WARN,"LogLevel must be one of err|warn|info|debug."); + result = -1; + } + } + + if(options->RouterFile == NULL) { + log(LOG_WARN,"RouterFile option required, but not found."); + result = -1; + } + + if(options->ORPort < 0) { + log(LOG_WARN,"ORPort option can't be negative."); + result = -1; + } + + if(options->ORPort && options->DataDirectory == NULL) { + log(LOG_WARN,"DataDirectory option required if ORPort is set, but not found."); + result = -1; + } + + if(options->ORPort && options->Nickname == NULL) { + log_fn(LOG_WARN,"Nickname required if ORPort is set, but not found."); + 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->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->KeepalivePeriod < 1) { + log(LOG_WARN,"KeepalivePeriod option must be positive."); + result = -1; + } + + return result; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/connection.c b/tags/tor-0_0_2pre19/src/or/connection.c new file mode 100644 index 0000000000..03134d3355 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/connection.c @@ -0,0 +1,838 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/********* START VARIABLES **********/ + +extern or_options_t options; /* command-line and config-file options */ + +extern int global_read_bucket; + +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 */ +}; + +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 */ + { "ready" }, /* app listener, 0 */ + { "", /* 0 */ + "", /* 1 */ + "", /* 2 */ + "", /* 3 */ + "awaiting dest info", /* app, 4 */ + "waiting for safe circuit", /* 5 */ + "open" }, /* 6 */ + { "ready" }, /* dir listener, 0 */ + { "", /* dir, 0 */ + "connecting (fetch)", /* 1 */ + "connecting (upload)", /* 2 */ + "client sending fetch", /* 3 */ + "client sending upload", /* 4 */ + "client reading fetch", /* 5 */ + "client reading upload", /* 6 */ + "awaiting command", /* 7 */ + "writing" }, /* 8 */ + { "", /* 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_init_accepted_conn(connection_t *conn); +static int connection_handle_listener_read(connection_t *conn, int new_type); + +/**************************************************************/ + +connection_t *connection_new(int type) { + connection_t *conn; + time_t now = time(NULL); + + conn = tor_malloc_zero(sizeof(connection_t)); + conn->s = -1; /* give it a default of '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; +} + +void connection_free(connection_t *conn) { + assert(conn); + + 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(); + if (conn->tls) + tor_tls_free(conn->tls); + } + + if (conn->onion_pkey) + crypto_free_pk_env(conn->onion_pkey); + if (conn->link_pkey) + crypto_free_pk_env(conn->link_pkey); + 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); + close(conn->s); + } + free(conn); +} + +void connection_free_all(void) { + int i, n; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;ih_length != 4) + return -1; /* XXX complain */ + memcpy(&(bindaddr.sin_addr.s_addr),rent->h_addr,rent->h_length); + + 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, &one, sizeof(one)); + + if(bind(s,(struct sockaddr *)&bindaddr,sizeof(bindaddr)) < 0) { + log_fn(LOG_WARN,"Could not bind to port %u: %s",bindport,strerror(errno)); + return -1; + } + + if(listen(s,SOMAXCONN) < 0) { + log_fn(LOG_WARN,"Could not listen on port %u: %s",bindport,strerror(errno)); + 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], bindport); + + conn->state = LISTENER_STATE_READY; + connection_start_reading(conn); + + return 0; +} + +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 */ +#ifdef MS_WINDOWS + int e; +#endif + + news = accept(conn->s,(struct sockaddr *)&remote,&remotelen); + if (news == -1) { /* accept() error */ + if(ERRNO_EAGAIN(errno)) { +#ifdef MS_WINDOWS + e = correct_socket_errno(conn->s); + if (ERRNO_EAGAIN(e)) + return 0; +#else + return 0; /* he hung up before we could accept(). that's fine. */ +#endif + } + /* else there was a real error. */ + log_fn(LOG_WARN,"accept() failed. Closing listener."); + return -1; + } + log(LOG_INFO,"Connection accepted on socket %d (child of fd %d).",news, conn->s); + + 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) { + newconn->marked_for_close = 1; + return 0; + } + return 0; +} + +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: + conn->state = AP_CONN_STATE_SOCKS_WAIT; + break; + case CONN_TYPE_DIR: + 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. + */ +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_CONN_EINPROGRESS(errno)) { + /* yuck. kill it. */ + log_fn(LOG_INFO,"Connect() to %s:%u failed: %s",address,port,strerror(errno)); + close(s); + return -1; + } else { + /* it's in progress. set state appropriately and return. */ + conn->s = s; + 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; + return 1; +} + +static void listener_close_if_present(int type) { + connection_t *conn = connection_get_by_type(type); + if (conn) { + close(conn->s); + conn->s = -1; + conn->marked_for_close = 1; + } +} + +/* start all connections that should be up but aren't */ +int retry_all_connections(void) { + + if(options.ORPort) { + router_retry_connections(); + } + + if(options.ORPort) { + listener_close_if_present(CONN_TYPE_OR_LISTENER); + if(connection_create_listener(options.ORBindAddress, options.ORPort, + CONN_TYPE_OR_LISTENER) < 0) + return -1; + } + + if(options.DirPort) { + listener_close_if_present(CONN_TYPE_DIR_LISTENER); + if(connection_create_listener(options.DirBindAddress, options.DirPort, + CONN_TYPE_DIR_LISTENER) < 0) + return -1; + } + + if(options.SocksPort) { + listener_close_if_present(CONN_TYPE_AP_LISTENER); + if(connection_create_listener(options.SocksBindAddress, options.SocksPort, + CONN_TYPE_AP_LISTENER) < 0) + return -1; + } + + 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) { + if(conn->type == CONN_TYPE_DIR && + (conn->state == DIR_CONN_STATE_CONNECTING_FETCH || + conn->state == DIR_CONN_STATE_CONNECTING_UPLOAD)) { + /* 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->nickname); + } + return -1; + } + if(connection_process_inbuf(conn) < 0) { +// log_fn(LOG_DEBUG,"connection_process_inbuf returned -1."); + return -1; + } + return 0; +} + +/* return -1 if we want to break conn, else return 0 */ +int connection_read_to_buf(connection_t *conn) { + int result; + int at_most; + + if(options.LinkPadding) { + at_most = global_read_bucket; + } else { + /* do a rudimentary round-robin so one connection can't hog a thickpipe */ + if(connection_speaks_cells(conn)) { + at_most = 30*(CELL_NETWORK_SIZE); + } else { + at_most = 30*(RELAY_PAYLOAD_SIZE); + } + + if(at_most > global_read_bucket) + at_most = global_read_bucket; + } + + if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) { + if(conn->state == OR_CONN_STATE_HANDSHAKING) + return connection_tls_continue_handshake(conn); + + /* else open, or closing */ + if(at_most > conn->receiver_bucket) + at_most = conn->receiver_bucket; + 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."); + 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 */ + return 0; + } + } 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; + } + + global_read_bucket -= result; assert(global_read_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 0; + } + if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) { + conn->receiver_bucket -= result; assert(conn->receiver_bucket >= 0); + if(conn->receiver_bucket == 0) { + log_fn(LOG_DEBUG,"receiver bucket exhausted. Pausing."); + conn->wants_to_read = 1; + connection_stop_reading(conn); + return 0; + } + } + return 0; +} + +int connection_fetch_from_buf(char *string, int len, connection_t *conn) { + return fetch_from_buf(string, len, conn->inbuf); +} + +int connection_find_on_inbuf(char *string, int len, connection_t *conn) { + return find_on_inbuf(string, len, conn->inbuf); +} + +int connection_wants_to_flush(connection_t *conn) { + return conn->outbuf_flushlen; +} + +int connection_outbuf_too_full(connection_t *conn) { + return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE); +} + +/* return -1 if you want to break the conn, else return 0 */ +int connection_handle_write(connection_t *conn) { + + if(connection_is_listener(conn)) { + log_fn(LOG_WARN,"Got a listener socket. Can't happen!"); + return -1; + } + + conn->timestamp_lastwritten = time(NULL); + + if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) { + if(conn->state == OR_CONN_STATE_HANDSHAKING) { + connection_stop_writing(conn); + return connection_tls_continue_handshake(conn); + } + + /* else open, or closing */ + switch(flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen)) { + case TOR_TLS_ERROR: + case TOR_TLS_CLOSE: + log_fn(LOG_INFO,"tls error. breaking."); + 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 { + if(flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen) < 0) + return -1; + /* conns in CONNECTING state will fall through... */ + } + + if(!connection_wants_to_flush(conn)) /* it's done flushing */ + if(connection_finished_flushing(conn) < 0) /* ...and get handled here. */ + return -1; + + return 0; +} + +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) { + log_fn(LOG_WARN,"write_to_buf failed. Closing connection (fd %d).", conn->s); + conn->marked_for_close = 1; + return; + } + + /* XXX if linkpadding, this only applies to conns that aren't open OR connections */ + connection_start_writing(conn); + conn->outbuf_flushlen += len; +} + +connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port) { + int i, n; + connection_t *conn; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;iaddr == addr && conn->port == port && !conn->marked_for_close) + return conn; + } + return NULL; +} + +connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port) { + /* Find a connection to the router described by addr and port, + * or alternately any router which knows its key. + * This connection *must* be in 'open' state. + * If not, return NULL. + */ + 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_INFO,"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;ionion_pkey, router->onion_pkey)) { + log(LOG_INFO,"connection_twin_get_by_addr_port(): Found twin (%s).",conn->address); + return conn; + } + } + return NULL; +} + +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; +} + +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; +} + +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; +} + +int connection_receiver_bucket_should_increase(connection_t *conn) { + 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 */ + + assert(conn->bandwidth > 0); + if(conn->receiver_bucket > 9*conn->bandwidth) + return 0; + + return 1; +} + +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; +} + +int connection_state_is_open(connection_t *conn) { + 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; +} + +int connection_send_destroy(uint16_t circ_id, connection_t *conn) { + cell_t cell; + + assert(conn); + + if(!connection_speaks_cells(conn)) { + log_fn(LOG_INFO,"CircID %d: At an edge. Marking connection for close.", + circ_id); + if(connection_edge_end(conn, END_STREAM_REASON_DESTROY, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + /* if they already sent a destroy, they know. XXX can just close? */ + return 0; + } + + 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; +} + +int connection_process_inbuf(connection_t *conn) { + + 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; + } +} + +int connection_finished_flushing(connection_t *conn) { + + 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; + } +} + +void assert_connection_ok(connection_t *conn, time_t now) +{ + return; + assert(conn); + assert(conn->type >= _CONN_TYPE_MIN); + assert(conn->type <= _CONN_TYPE_MAX); + + /* XXX check: wants_to_read, wants_to_write, s, poll_index, + * marked_for_close. */ + + /* buffers */ + if (!connection_is_listener(conn)) { + assert(conn->inbuf); + assert(conn->outbuf); + } + + assert(!now || conn->timestamp_lastread <= now); + assert(!now || conn->timestamp_lastwritten <= now); + assert(conn->timestamp_created <= conn->timestamp_lastread); + assert(conn->timestamp_created <= conn->timestamp_lastwritten); + + /* XXX Fix this; no longer so.*/ +#if 0 + if(conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) + 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) { + assert(!conn->tls); + } else { + if(conn->state == OR_CONN_STATE_OPEN) { + assert(conn->bandwidth > 0); + assert(conn->receiver_bucket >= 0); + assert(conn->receiver_bucket <= 10*conn->bandwidth); + } + assert(conn->addr && conn->port); + assert(conn->address); + if (conn->state != OR_CONN_STATE_CONNECTING) + assert(conn->tls); + } + + if (conn->type != CONN_TYPE_EXIT && conn->type != CONN_TYPE_AP) { + assert(!conn->stream_id); + assert(!conn->next_stream); + assert(!conn->cpath_layer); + assert(!conn->package_window); + assert(!conn->deliver_window); + assert(!conn->done_sending); + assert(!conn->done_receiving); + } else { + assert(!conn->next_stream || + conn->next_stream->type == CONN_TYPE_EXIT || + conn->next_stream->type == CONN_TYPE_AP); + if(conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) + assert(conn->cpath_layer); + if(conn->cpath_layer) + assert_cpath_layer_ok(conn->cpath_layer); + /* XXX unchecked, package window, deliver window. */ + } + if (conn->type != CONN_TYPE_AP) { + assert(!conn->socks_request); + } + + switch(conn->type) + { + case CONN_TYPE_OR_LISTENER: + case CONN_TYPE_AP_LISTENER: + case CONN_TYPE_DIR_LISTENER: + assert(conn->state == LISTENER_STATE_READY); + break; + case CONN_TYPE_OR: + assert(conn->state >= _OR_CONN_STATE_MIN && + conn->state <= _OR_CONN_STATE_MAX); + break; + case CONN_TYPE_EXIT: + assert(conn->state >= _EXIT_CONN_STATE_MIN && + conn->state <= _EXIT_CONN_STATE_MAX); + break; + case CONN_TYPE_AP: + assert(conn->state >= _AP_CONN_STATE_MIN && + conn->state <= _AP_CONN_STATE_MAX); + assert(conn->socks_request); + break; + case CONN_TYPE_DIR: + assert(conn->state >= _DIR_CONN_STATE_MIN && + conn->state <= _DIR_CONN_STATE_MAX); + break; + case CONN_TYPE_DNSWORKER: + assert(conn->state == DNSWORKER_STATE_IDLE || + conn->state == DNSWORKER_STATE_BUSY); + case CONN_TYPE_CPUWORKER: + assert(conn->state >= _CPUWORKER_STATE_MIN && + conn->state <= _CPUWORKER_STATE_MAX); + break; + default: + assert(0); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ + diff --git a/tags/tor-0_0_2pre19/src/or/connection_edge.c b/tags/tor-0_0_2pre19/src/or/connection_edge.c new file mode 100644 index 0000000000..d8c23d90c3 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/connection_edge.c @@ -0,0 +1,1023 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#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]; + +static int connection_ap_handshake_process_socks(connection_t *conn); +static int connection_ap_handshake_attach_circuit(connection_t *conn); +static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ); +static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply, + int replylen, char success); + +static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); +static void connection_edge_consider_sending_sendme(connection_t *conn); + +static uint32_t client_dns_lookup_entry(const char *address); +static void client_dns_set_entry(const char *address, uint32_t val); + +void relay_header_pack(char *dest, const relay_header_t *src) { + *(uint8_t*)(dest) = src->command; + *(uint16_t*)(dest+1) = htons(src->recognized); + *(uint16_t*)(dest+3) = htons(src->stream_id); + memcpy(dest+5, src->integrity, 4); + *(uint16_t*)(dest+9) = htons(src->length); +} + +void relay_header_unpack(relay_header_t *dest, const char *src) { + dest->command = *(uint8_t*)(src); + dest->recognized = ntohs(*(uint16_t*)(src+1)); + dest->stream_id = ntohs(*(uint16_t*)(src+3)); + memcpy(dest->integrity, src+5, 4); + dest->length = ntohs(*(uint16_t*)(src+9)); +} + +int connection_edge_process_inbuf(connection_t *conn) { + + assert(conn); + 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) { + if(connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + } 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); + if(connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"2: I called connection_edge_end redundantly."); + return -1; +#endif + } + + switch(conn->state) { + case AP_CONN_STATE_SOCKS_WAIT: + if(connection_ap_handshake_process_socks(conn) < 0) { + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"3: I called connection_edge_end redundantly."); + return -1; + } + return 0; + case AP_CONN_STATE_OPEN: + case EXIT_CONN_STATE_OPEN: + if(conn->package_window <= 0) { + log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); + return 0; + } + if(connection_edge_package_raw_inbuf(conn) < 0) { + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"4: I called connection_edge_end redundantly."); + return -1; + } + return 0; + case EXIT_CONN_STATE_CONNECTING: + log_fn(LOG_INFO,"text from server while in 'connecting' state at exit. Leaving it on buffer."); + return 0; + } + + return 0; +} + +static 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 < END_STREAM_REASON_MISC || *payload > END_STREAM_REASON_DONE) { + 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"; + } + assert(0); + return ""; +} + +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) { + *(uint32_t *)(payload+1) = htonl(conn->addr); + payload_len += 4; + } + + circ = circuit_get_by_conn(conn); + if(circ) { + 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); + } + + conn->marked_for_close = 1; + conn->has_sent_end = 1; + return 0; +} + +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, int relay_command, + void *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."); + return -1; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; +// if(fromconn && fromconn->type == CONN_TYPE_AP) { + 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_close(circ); + return -1; + } + return 0; +} + +/* an incoming relay cell has arrived. return -1 if you want to tear down the + * circuit, else 0. */ +int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_t *conn, + int edge_type, crypt_path_t *layer_hint) { + static int num_seen=0; + uint32_t addr; + relay_header_t rh; + + 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) { + if(conn->type == CONN_TYPE_EXIT && rh.command == RELAY_COMMAND_END) { + log_fn(LOG_INFO,"Exit got end (%s) before we're connected. Marking for close.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length)); + if(conn->state == EXIT_CONN_STATE_RESOLVING) { + log_fn(LOG_INFO,"...and informing resolver we don't want the answer anymore."); + dns_cancel_pending_resolve(conn->address, conn); + } + conn->marked_for_close = 1; + conn->has_sent_end = 1; + return 0; + } else { + 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]); + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + return -1; + } + } + + 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(edge_type == EDGE_AP) { + 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((edge_type == EDGE_AP && --layer_hint->deliver_window < 0) || + (edge_type == EDGE_EXIT && --circ->deliver_window < 0)) { + log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing."); + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"2: I called connection_edge_end redundantly."); + return -1; + } + log_fn(LOG_DEBUG,"circ deliver_window now %d.", edge_type == EDGE_AP ? + layer_hint->deliver_window : circ->deliver_window); + + circuit_consider_sending_sendme(circ, edge_type, 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; + } + if(rh.length >= 5 && + *(cell->payload+RELAY_HEADER_SIZE) == END_STREAM_REASON_EXITPOLICY) { + /* No need to close the connection. We'll hold it open while + * we try a new exit node. + * cell->payload+RELAY_HEADER_SIZE+1 holds the destination addr. + */ + addr = ntohl(*(uint32_t*)(cell->payload+RELAY_HEADER_SIZE+1)); + client_dns_set_entry(conn->socks_request->address, addr); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + switch(connection_ap_handshake_attach_circuit(conn)) { + case -1: /* it will never work */ + break; /* conn will get closed below */ + case 0: /* no useful circuits available */ + if(!circuit_get_newest(conn, 0)) /* is one already on the way? */ + circuit_launch_new(); + return 0; + case 1: /* it succeeded, great */ + return 0; + } + } + 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) { + conn->marked_for_close = 1; + conn->has_sent_end = 1; /* no need to send end, we just got one! */ + } +#else + conn->marked_for_close = 1; + conn->has_sent_end = 1; /* no need to send end, we just got one! */ +#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(edge_type == EDGE_EXIT) { + log_fn(LOG_WARN,"'extended' unsupported at exit. 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(edge_type == EDGE_AP) { + log_fn(LOG_WARN,"'truncate' unsupported at AP. 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(edge_type == EDGE_EXIT) { + log_fn(LOG_WARN,"'truncated' unsupported at exit. Dropping."); + return 0; + } + circuit_truncated(circ, layer_hint); + return 0; + case RELAY_COMMAND_CONNECTED: + if(edge_type == EDGE_EXIT) { + log_fn(LOG_WARN,"'connected' unsupported at exit. Dropping."); + return 0; + } + if(!conn) { + log_fn(LOG_INFO,"connected cell dropped, unknown stream."); + return 0; + } + log_fn(LOG_INFO,"Connected! Notifying application."); + if (rh.length >= 4) { + addr = ntohl(*(uint32_t*)(cell->payload + RELAY_HEADER_SIZE)); + client_dns_set_entry(conn->socks_request->address, addr); + } + if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) { + log_fn(LOG_INFO,"Writing to socks-speaking application failed. Closing."); + if(connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer) < 0) + log_fn(LOG_WARN,"3: I called connection_edge_end redundantly."); + } + return 0; + case RELAY_COMMAND_SENDME: + if(!conn) { + if(edge_type == EDGE_AP) { + assert(layer_hint); + layer_hint->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at AP, packagewindow %d.", + layer_hint->package_window); + circuit_resume_edge_reading(circ, EDGE_AP, layer_hint); + } else { + assert(!layer_hint); + circ->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at exit, packagewindow %d.", + circ->package_window); + circuit_resume_edge_reading(circ, EDGE_EXIT, 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; + } + log_fn(LOG_WARN,"unknown relay command %d.",rh.command); + return -1; +} + +int connection_edge_finished_flushing(connection_t *conn) { + unsigned char connected_payload[4]; + int e, len=sizeof(e); + + assert(conn); + assert(conn->type == CONN_TYPE_AP || conn->type == CONN_TYPE_EXIT); + + switch(conn->state) { + case EXIT_CONN_STATE_CONNECTING: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */ + if(!ERRNO_CONN_EINPROGRESS(errno)) { + /* yuck. kill it. */ + log_fn(LOG_DEBUG,"in-progress exit connect failed. Removing."); + return -1; + } else { + log_fn(LOG_DEBUG,"in-progress exit connect still waiting."); + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + 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. */ + *(uint32_t*)connected_payload = htonl(conn->addr); + if(connection_edge_send_command(conn, circuit_get_by_conn(conn), + RELAY_COMMAND_CONNECTED, NULL, 0, NULL) < 0) + return 0; /* circuit is closed, don't continue */ + assert(conn->package_window > 0); + return connection_edge_process_inbuf(conn); /* in case the server has written anything */ + 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_CIRCUIT_WAIT: + connection_stop_writing(conn); + return 0; + default: + log_fn(LOG_WARN,"BUG: called in unexpected state: %d", conn->state); + return -1; + } + return 0; +} + +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; + +int connection_edge_package_raw_inbuf(connection_t *conn) { + int amount_to_process, length; + char payload[CELL_PAYLOAD_SIZE]; + circuit_t *circ; + + assert(conn); + 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->type, 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->type == CONN_TYPE_EXIT) { + assert(circ->package_window > 0); + circ->package_window--; + } else { /* we're an AP */ + assert(conn->type == CONN_TYPE_AP); + 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->type, 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; +} + +/* Tell any APs 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->type != CONN_TYPE_AP || + conn->state != AP_CONN_STATE_CIRCUIT_WAIT) + continue; + switch(connection_ap_handshake_attach_circuit(conn)) { + case -1: /* it will never work */ + conn->marked_for_close = 1; + conn->has_sent_end = 1; + break; + case 0: /* we need to build another circuit */ + if(!circuit_get_newest(conn, 0)) { + /* if there are no acceptable clean or not-very-dirty circs on the way */ + circuit_launch_new(); + } + break; + case 1: /* it succeeded, great */ + break; + } + } +} + +static 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 */ + } + } +} + +static int connection_ap_handshake_process_socks(connection_t *conn) { + socks_request_t *socks; + int sockshere; + + assert(conn); + assert(conn->type == CONN_TYPE_AP); + assert(conn->state == AP_CONN_STATE_SOCKS_WAIT); + 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."); + } + return sockshere; + } /* else socks handshake is done, continue processing */ + + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + switch(connection_ap_handshake_attach_circuit(conn)) { + case -1: /* it will never work */ + return -1; + case 0: /* no useful circuits available */ + if(!circuit_get_newest(conn, 0)) /* is one already on the way? */ + circuit_launch_new(); + break; + case 1: /* it succeeded, great */ + break; + } + return 0; +} + +/* 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; else tell conn to stop reading and return 0. + * Otherwise, associate conn with a safe live circuit, start + * sending a BEGIN cell down the circuit, and return 1. + */ +static int connection_ap_handshake_attach_circuit(connection_t *conn) { + circuit_t *circ; + uint32_t addr; + + assert(conn); + assert(conn->type == CONN_TYPE_AP); + assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + assert(conn->socks_request); + + /* find the circuit that we should use, if there is one. */ + circ = circuit_get_newest(conn, 1); + + if(!circ) { + log_fn(LOG_INFO,"No safe circuit ready for edge connection; delaying."); + 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 node exists that will handle exit to %s:%d. Rejecting.", + conn->socks_request->address, conn->socks_request->port); + return -1; + } + connection_stop_reading(conn); /* don't read until the connected cell arrives */ + return 0; + } + + connection_start_reading(conn); + + if(!circ->timestamp_dirty) + circ->timestamp_dirty = time(NULL); + + /* 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); + conn->next_stream = circ->p_streams; + /* assert_connection_ok(conn, time(NULL)); */ + circ->p_streams = conn; + + assert(circ->cpath && circ->cpath->prev); + assert(circ->cpath->prev->state == CPATH_STATE_OPEN); + conn->cpath_layer = circ->cpath->prev; + + connection_ap_handshake_send_begin(conn, circ); + + return 1; +} + +/* Iterate over the two bytes of stream_id until we get one that is not + * already in use. 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; +} + +/* deliver the destaddr:destport in a relay cell */ +static void 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; + + assert(ap_conn->type == CONN_TYPE_AP); + assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + assert(ap_conn->socks_request); + + ap_conn->stream_id = get_unique_stream_id_by_circ(circ); + if (ap_conn->stream_id==0) { + ap_conn->marked_for_close = 1; + return; + } + + 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); + 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; /* circuit is closed, don't continue */ + + ap_conn->package_window = STREAMWINDOW_START; + ap_conn->deliver_window = STREAMWINDOW_START; + ap_conn->state = AP_CONN_STATE_OPEN; + /* XXX Right now, we rely on the socks client not to send us any data + * XXX until we've sent back a socks reply. (If it does, we could wind + * XXX up packaging that data and sending it to the exit, then later having + * XXX the exit refuse us.) + * XXX Perhaps we should grow an AP_CONN_STATE_CONNECTING state. + */ + log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id); + return; +} + +static int 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 flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */ + } + 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); + return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */ + } + 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); + return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); /* try to flush it */ + } + return 0; /* if socks_version isn't 4 or 5, don't send anything */ +} + +static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { + connection_t *n_stream; + relay_header_t rh; + char *colon; + + 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->stream_id = rh.stream_id; + n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE); + n_stream->port = atoi(colon+1); + n_stream->state = EXIT_CONN_STATE_RESOLVING; + /* 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(connection_add(n_stream) < 0) { /* no space, forget it */ + log_fn(LOG_WARN,"connection_add failed. Dropping."); + connection_free(n_stream); + return 0; + } + + /* add it into the linked list of streams on this circuit */ + n_stream->next_stream = circ->n_streams; + circ->n_streams = n_stream; + + /* send it off to the gethostbyname farm */ + switch(dns_resolve(n_stream)) { + case 1: /* resolve worked */ + connection_exit_connect(n_stream); + return 0; + case -1: /* resolve failed */ + log_fn(LOG_INFO,"Resolve failed (%s).", n_stream->address); + if(connection_edge_end(n_stream, END_STREAM_REASON_RESOLVEFAILED, NULL) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + /* case 0, resolve added to pending list */ + } + return 0; +} + +void connection_exit_connect(connection_t *conn) { + unsigned char connected_payload[4]; + + if(router_compare_to_my_exit_policy(conn) < 0) { + log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port); + if(connection_edge_end(conn, END_STREAM_REASON_EXITPOLICY, NULL) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + return; + } + + switch(connection_connect(conn, conn->address, conn->addr, conn->port)) { + case -1: + if(connection_edge_end(conn, END_STREAM_REASON_CONNECTFAILED, NULL) < 0) + log_fn(LOG_WARN,"2: I called connection_edge_end redundantly."); + return; + case 0: + connection_set_poll_socket(conn); + 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 */ + } + + connection_set_poll_socket(conn); + 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. */ + *((uint32_t*) connected_payload) = htonl(conn->addr); + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, + connected_payload, 4, NULL); +} + +int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit) +{ + uint32_t addr; + + assert(conn); + assert(conn->type == CONN_TYPE_AP); + 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); + addr = client_dns_lookup_entry(conn->socks_request->address); + return router_supports_exit_address(addr, conn->socks_request->port, exit); +} + +/* ***** 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. + */ +struct client_dns_entry { + SPLAY_ENTRY(client_dns_entry) node; + char *address; + uint32_t addr; + time_t expires; +}; +static int client_dns_size = 0; +static SPLAY_HEAD(client_dns_tree, client_dns_entry) client_dns_root; + +static int compare_client_dns_entries(struct client_dns_entry *a, + struct client_dns_entry *b) +{ + return strcasecmp(a->address, b->address); +} + +static void client_dns_entry_free(struct client_dns_entry *ent) +{ + tor_free(ent->address); + tor_free(ent); +} + +SPLAY_PROTOTYPE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries); +SPLAY_GENERATE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries); + +void client_dns_init(void) { + SPLAY_INIT(&client_dns_root); + client_dns_size = 0; +} + +static uint32_t client_dns_lookup_entry(const char *address) +{ + struct client_dns_entry *ent; + struct client_dns_entry search; + struct in_addr in; + time_t now; + + assert(address); + + if (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); + } + search.address = (char*)address; + ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search); + if (!ent) { + 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); + SPLAY_REMOVE(client_dns_tree, &client_dns_root, ent); + client_dns_entry_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; + } +} +static void client_dns_set_entry(const char *address, uint32_t val) +{ + struct client_dns_entry *ent; + struct client_dns_entry search; + struct in_addr in; + time_t now; + + assert(address); + assert(val); + + if (inet_aton(address, &in)) + return; + search.address = (char*) address; + now = time(NULL); + ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search); + if (ent) { + 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; + } else { + in.s_addr = htonl(val); + log_fn(LOG_DEBUG, "Caching result for address %s: %s", address, + inet_ntoa(in)); + ent = tor_malloc(sizeof(struct client_dns_entry)); + ent->address = tor_strdup(address); + ent->addr = val; + ent->expires = now+MAX_DNS_ENTRY_AGE; + SPLAY_INSERT(client_dns_tree, &client_dns_root, ent); + ++client_dns_size; + } +} + +void client_dns_clean(void) +{ + struct client_dns_entry **expired_entries; + int n_expired_entries = 0; + struct client_dns_entry *ent; + time_t now; + int i; + + if(!client_dns_size) + return; + expired_entries = tor_malloc(client_dns_size * + sizeof(struct client_dns_entry *)); + + now = time(NULL); + SPLAY_FOREACH(ent, client_dns_tree, &client_dns_root) { + if (ent->expires < now) { + expired_entries[n_expired_entries++] = ent; + } + } + for (i = 0; i < n_expired_entries; ++i) { + SPLAY_REMOVE(client_dns_tree, &client_dns_root, expired_entries[i]); + client_dns_entry_free(expired_entries[i]); + } + tor_free(expired_entries); +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/connection_or.c b/tags/tor-0_0_2pre19/src/or/connection_or.c new file mode 100644 index 0000000000..7124036807 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/connection_or.c @@ -0,0 +1,284 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#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_cell_from_inbuf(connection_t *conn); + +/**************************************************************/ + +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); +} + +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); +} + +/**************************************************************/ + +int connection_or_process_inbuf(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_OR); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_INFO,"conn reached eof. Closing."); + return -1; + } + + if(conn->state != OR_CONN_STATE_OPEN) + return 0; /* don't do anything */ + return connection_or_process_cell_from_inbuf(conn); +} + +int connection_or_finished_flushing(connection_t *conn) { + int e, len=sizeof(e); + + assert(conn && conn->type == CONN_TYPE_OR); + + switch(conn->state) { + case OR_CONN_STATE_CONNECTING: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */ + if(!ERRNO_CONN_EINPROGRESS(errno)){ + log_fn(LOG_DEBUG,"in-progress connect failed. Removing."); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + log_fn(LOG_INFO,"OR connect() to router %s:%u finished.", + conn->address,conn->port); + + if(connection_tls_start_handshake(conn, 0) < 0) + return -1; + return 0; + case OR_CONN_STATE_OPEN: + connection_stop_writing(conn); + return 0; + default: + log_fn(LOG_WARN,"BUG: called in unexpected state."); + return 0; + } +} + +/*********************/ + +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->bandwidth; + conn->onion_pkey = crypto_pk_dup_key(router->onion_pkey); + conn->link_pkey = crypto_pk_dup_key(router->link_pkey); + conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + conn->nickname = tor_strdup(router->nickname); + tor_free(conn->address); + conn->address = tor_strdup(router->address); +} + +connection_t *connection_or_connect(routerinfo_t *router) { + connection_t *conn; + + assert(router); + + if(options.Nickname && !strcmp(router->nickname,options.Nickname)) { + log_fn(LOG_WARN,"You asked me to connect to myself! Failing."); + return NULL; + } + + /* this function should never be called if we're already connected to router, but */ + /* check first to be sure */ + conn = connection_exact_get_by_addr_port(router->addr,router->or_port); + 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_router(conn, router); + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return NULL; + } + + switch(connection_connect(conn, router->address, router->addr, router->or_port)) { + case -1: + connection_remove(conn); + connection_free(conn); + return NULL; + case 0: + connection_set_poll_socket(conn); + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link on windows */ + conn->state = OR_CONN_STATE_CONNECTING; + return conn; + /* case 1: fall through */ + } + + connection_set_poll_socket(conn); + + if(connection_tls_start_handshake(conn, 0) >= 0) + return conn; + + /* failure */ + connection_remove(conn); + connection_free(conn); + return NULL; +} + +/* ********************************** */ + +int connection_tls_start_handshake(connection_t *conn, int receiving) { + conn->state = OR_CONN_STATE_HANDSHAKING; + conn->tls = tor_tls_new(conn->s, receiving); + 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; +} + +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 connection_tls_finish_handshake(connection_t *conn) { + crypto_pk_env_t *pk; + routerinfo_t *router; + char nickname[MAX_NICKNAME_LEN+1]; + + conn->state = OR_CONN_STATE_OPEN; + directory_set_dirty(); + 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 (options.ORPort) { /* 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 claims to be '%s'", nickname); + pk = tor_tls_verify(conn->tls); + if(!pk) { + log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.", + nickname, conn->address, conn->port); + return -1; + } + router = router_get_by_link_pk(pk); + if (!router) { + log_fn(LOG_WARN,"Unrecognized public key from peer '%s' (%s:%d). Closing.", + nickname, conn->address, conn->port); + crypto_free_pk_env(pk); + return -1; + } + if(conn->link_pkey) { /* I initiated this connection. */ + if(crypto_pk_cmp_keys(conn->link_pkey, pk)) { + log_fn(LOG_WARN,"We connected to '%s' (%s:%d) but he gave us a different key. Closing.", + nickname, conn->address, conn->port); + crypto_free_pk_env(pk); + return -1; + } + log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good."); + } else { + if(connection_exact_get_by_addr_port(router->addr,router->or_port)) { + log_fn(LOG_INFO,"Router %s is already connected. Dropping.", router->nickname); + crypto_free_pk_env(pk); + return -1; + } + connection_or_init_conn_from_router(conn, router); + } + crypto_free_pk_env(pk); + if (strcmp(conn->nickname, nickname)) { + log_fn(LOG_WARN,"Other side claims to be '%s', but we wanted '%s'", + nickname, conn->nickname); + return -1; + } + if (!options.ORPort) { /* If I'm an OP... */ + conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; + circuit_n_conn_open(conn); /* send the pending creates, if any. */ + } + return 0; +} + +/* ********************************** */ + +void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn) { + char networkcell[CELL_NETWORK_SIZE]; + char *n = networkcell; + + assert(cell && conn); + assert(connection_speaks_cells(conn)); + + cell_pack(n, cell); + + connection_write_to_buf(n, CELL_NETWORK_SIZE, conn); +} + +/* if there's a whole cell there, pull it off and process it. */ +static int connection_or_process_cell_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_2pre19/src/or/cpuworker.c b/tags/tor-0_0_2pre19/src/or/cpuworker.c new file mode 100644 index 0000000000..51c582c1c0 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/cpuworker.c @@ -0,0 +1,288 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" +extern or_options_t options; /* command-line and config-file options */ + +#define MAX_CPUWORKERS 16 +#define MIN_CPUWORKERS 1 + +#define TAG_LEN 8 +#define LEN_ONION_QUESTION (1+TAG_LEN+ONIONSKIN_CHALLENGE_LEN) +#define LEN_ONION_RESPONSE (1+TAG_LEN+ONIONSKIN_REPLY_LEN+40+32) + +int num_cpuworkers=0; +int num_cpuworkers_busy=0; + +int cpuworker_main(void *data); +static int spawn_cpuworker(void); +static void spawn_enough_cpuworkers(void); +static void process_pending_task(connection_t *cpuworker); + +void cpu_init(void) { + spawn_enough_cpuworkers(); +} + +int connection_cpu_finished_flushing(connection_t *conn) { + assert(conn && conn->type == CONN_TYPE_CPUWORKER); + connection_stop_writing(conn); + return 0; +} + +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; +} + +static void tag_unpack(char *tag, uint32_t *addr, uint16_t *port, uint16_t *circ_id) { + struct in_addr in; + + *addr = *(uint32_t *)tag; + *port = *(uint16_t *)(tag+4); + *circ_id = *(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); +} + +int connection_cpu_process_inbuf(connection_t *conn) { + unsigned char buf[LEN_ONION_RESPONSE]; + uint32_t addr; + uint16_t port; + uint16_t circ_id; + connection_t *p_conn; + circuit_t *circ; + + assert(conn && conn->type == CONN_TYPE_CPUWORKER); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_WARN,"Read eof. Worker has died."); + 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 leaving a circuit waiting. Oh well."); + num_cpuworkers_busy--; + } + num_cpuworkers--; + spawn_enough_cpuworkers(); /* try to regrow. hope we don't end up spinning. */ + return -1; + } + + if(conn->state == CPUWORKER_STATE_BUSY_ONION) { + if(buf_datalen(conn->inbuf) < LEN_ONION_RESPONSE) /* entire answer available? */ + return 0; /* not yet */ + assert(buf_datalen(conn->inbuf) == LEN_ONION_RESPONSE); + + connection_fetch_from_buf(buf,LEN_ONION_RESPONSE,conn); + + /* parse out the circ it was talking about */ + tag_unpack(buf+1, &addr, &port, &circ_id); + circ = NULL; + 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(!circ) { + log_fn(LOG_INFO,"processed onion for a circ that's gone. Dropping."); + goto done_processing; + } + assert(circ->p_conn); + if(*buf == 0) { + log_fn(LOG_WARN,"decoding onionskin failed. Closing."); + circuit_close(circ); + goto done_processing; + } + if(onionskin_answer(circ, buf+1+TAG_LEN, buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) { + log_fn(LOG_WARN,"onionskin_answer failed. Closing."); + circuit_close(circ); + goto done_processing; + } + log_fn(LOG_DEBUG,"onionskin_answer succeeded. Yay."); + } else { + assert(0); /* don't ask me to do handshakes yet */ + } + +done_processing: + conn->state = CPUWORKER_STATE_IDLE; + num_cpuworkers_busy--; + process_pending_task(conn); + return 0; +} + +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]; + + close(fdarray[0]); /* this is the side of the socketpair the parent uses */ + fd = fdarray[1]; /* this side is ours */ + connection_free_all(); /* so the child doesn't hold the parent's fd's open */ + + for(;;) { + + if(read(fd, &question_type, 1) != 1) { + log_fn(LOG_ERR,"read type failed. Exiting."); + spawn_exit(); + } + assert(question_type == CPUWORKER_TASK_ONION); + + if(read_all(fd, tag, TAG_LEN) != TAG_LEN) { + log_fn(LOG_ERR,"read tag failed. Exiting."); + spawn_exit(); + } + + if(read_all(fd, question, ONIONSKIN_CHALLENGE_LEN) != ONIONSKIN_CHALLENGE_LEN) { + log_fn(LOG_ERR,"read question failed. Exiting."); + spawn_exit(); + } + + if(question_type == CPUWORKER_TASK_ONION) { + if(onion_skin_server_handshake(question, get_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_INFO,"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) != LEN_ONION_RESPONSE) { + log_fn(LOG_ERR,"writing response buf failed. Exiting."); + spawn_exit(); + } + log_fn(LOG_DEBUG,"finished writing response."); + } + } + return 0; /* windows wants this function to return an int */ +} + +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", strerror(errno)); + exit(1); + } + + spawn_func(cpuworker_main, (void*)fd); + log_fn(LOG_DEBUG,"just spawned a worker."); + close(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 */ +} + +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++; + } +} + +static void process_pending_task(connection_t *cpuworker) { + circuit_t *circ; + + 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]; + + 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); + + 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_2pre19/src/or/directory.c b/tags/tor-0_0_2pre19/src/or/directory.c new file mode 100644 index 0000000000..6ea453a983 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/directory.c @@ -0,0 +1,276 @@ +/* Copyright 2001,2002,2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +static int directory_send_command(connection_t *conn, int command); +static int directory_handle_command(connection_t *conn); + +/********* START VARIABLES **********/ + +extern or_options_t options; /* command-line and config-file options */ + +static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n"; +static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n"; + +/********* END VARIABLES ************/ + +void directory_initiate_command(routerinfo_t *router, int command) { + connection_t *conn; + + if (command == DIR_CONN_STATE_CONNECTING_FETCH) + log_fn(LOG_DEBUG,"initiating directory fetch"); + else + log_fn(LOG_DEBUG,"initiating directory upload"); + + if (!router) { /* i guess they didn't have one in mind for me to use */ + log_fn(LOG_WARN,"No running dirservers known. Not trying."); + return; + } + + 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); + assert(router->identity_pkey); + conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return; + } + + switch(connection_connect(conn, router->address, router->addr, router->dir_port)) { + case -1: + router_mark_as_down(conn->nickname); /* don't try him again */ + connection_remove(conn); + connection_free(conn); + return; + case 0: + connection_set_poll_socket(conn); + connection_watch_events(conn, POLLIN | POLLOUT | POLLERR); + /* writable indicates finish, readable indicates broken link, + error indicates broken link in windowsland. */ + conn->state = command; + return; + /* case 1: fall through */ + } + + connection_set_poll_socket(conn); + if(directory_send_command(conn, command) < 0) { + connection_remove(conn); + connection_free(conn); + } +} + +static int directory_send_command(connection_t *conn, int command) { + const char *s; + char tmp[8192]; + + assert(conn && conn->type == CONN_TYPE_DIR); + + switch(command) { + case DIR_CONN_STATE_CONNECTING_FETCH: + connection_write_to_buf(fetchstring, strlen(fetchstring), conn); + conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH; + break; + case DIR_CONN_STATE_CONNECTING_UPLOAD: + s = router_get_my_descriptor(); + if(!s) { + log_fn(LOG_WARN,"Failed to get my descriptor."); + return -1; + } + snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s", + (int)strlen(s), s); + connection_write_to_buf(tmp, strlen(tmp), conn); + conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD; + break; + } + return 0; +} + +int connection_dir_process_inbuf(connection_t *conn) { + char *directory; + int directorylen=0; + + assert(conn && conn->type == CONN_TYPE_DIR); + + if(conn->inbuf_reached_eof) { + switch(conn->state) { + case DIR_CONN_STATE_CLIENT_READING_FETCH: + /* kill it, but first fetch/process the directory to learn about new routers. */ + switch(fetch_from_buf_http(conn->inbuf, + NULL, 0, &directory, 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 */ + } + /* XXX check headers, at least make sure returned 2xx */ + directorylen = strlen(directory); + log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, directory); + if(directorylen == 0) { + log_fn(LOG_INFO,"Empty directory. Ignoring."); + free(directory); + return -1; + } + if(router_set_routerlist_from_directory(directory, conn->identity_pkey) < 0){ + log_fn(LOG_INFO,"...but parsing failed. Ignoring."); + } else { + log_fn(LOG_INFO,"updated routers."); + } + if(options.ORPort) { /* connect to them all */ + router_retry_connections(); + } + free(directory); + return -1; + case DIR_CONN_STATE_CLIENT_READING_UPLOAD: + /* XXX make sure there's a 200 OK on the buffer */ + log_fn(LOG_INFO,"eof while reading upload response. Finished."); + return -1; + default: + log_fn(LOG_INFO,"conn reached eof, not reading. Closing."); + return -1; + } + } + + if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) + return directory_handle_command(conn); + + /* 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 int directory_handle_command_get(connection_t *conn, + char *headers, char *body) { + size_t dlen; + const char *cp; + + /* XXX should check url and http version */ + log_fn(LOG_DEBUG,"Received GET command."); + + dlen = dirserv_get_directory(&cp); + + if(dlen == 0) { + log_fn(LOG_WARN,"My directory is empty. Closing."); + return -1; /* XXX send some helpful http error code */ + } + + log_fn(LOG_DEBUG,"Dumping directory to client."); + connection_write_to_buf(answerstring, strlen(answerstring), conn); + connection_write_to_buf(cp, dlen, conn); + conn->state = DIR_CONN_STATE_SERVER_WRITING; + return 0; +} + +static int directory_handle_command_post(connection_t *conn, + char *headers, char *body) { + const char *cp; + + /* XXX should check url and http version */ + log_fn(LOG_DEBUG,"Received POST command."); + cp = body; + if(dirserv_add_descriptor(&cp) < 0) { + log_fn(LOG_WARN,"dirserv_add_descriptor() failed. Dropping."); + return -1; /* XXX should write an http failed code */ + } + dirserv_get_directory(&cp); /* rebuild and write to disk */ + connection_write_to_buf(answerstring, strlen(answerstring), conn); + conn->state = DIR_CONN_STATE_SERVER_WRITING; + return 0; +} + +static int directory_handle_command(connection_t *conn) { + char *headers=NULL, *body=NULL; + int r; + +#define MAX_HEADERS_SIZE 2048 +#define MAX_BODY_SIZE 500000 + + assert(conn && conn->type == CONN_TYPE_DIR); + + switch(fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, &body, 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); + else if (!strncasecmp(headers,"POST",4)) + r = directory_handle_command_post(conn, headers, body); + else { + log_fn(LOG_WARN,"Got headers '%s' with unknown command. Closing.", headers); + r = -1; + } + + tor_free(headers); tor_free(body); + return r; +} + +int connection_dir_finished_flushing(connection_t *conn) { + int e, len=sizeof(e); + + assert(conn && conn->type == CONN_TYPE_DIR); + + switch(conn->state) { + case DIR_CONN_STATE_CONNECTING_FETCH: + case DIR_CONN_STATE_CONNECTING_UPLOAD: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */ + if(!ERRNO_CONN_EINPROGRESS(errno)) { + log_fn(LOG_DEBUG,"in-progress connect failed. Removing."); + router_mark_as_down(conn->nickname); /* don't try him again */ + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + log_fn(LOG_INFO,"Dir connection to router %s:%u established.", + conn->address,conn->port); + + return directory_send_command(conn, conn->state); + case DIR_CONN_STATE_CLIENT_SENDING_FETCH: + log_fn(LOG_DEBUG,"client finished sending fetch command."); + conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH; + connection_watch_events(conn, POLLIN); + return 0; + case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD: + log_fn(LOG_DEBUG,"client finished sending upload command."); + conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD; + connection_watch_events(conn, POLLIN); + return 0; + case DIR_CONN_STATE_SERVER_WRITING: + log_fn(LOG_INFO,"Finished writing server response. Closing."); + return -1; /* kill it */ + default: + log_fn(LOG_WARN,"BUG: called in unexpected state."); + return -1; + } + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/dirserv.c b/tags/tor-0_0_2pre19/src/or/dirserv.c new file mode 100644 index 0000000000..a3a29d9391 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/dirserv.c @@ -0,0 +1,466 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/* 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 */ + +static int the_directory_is_dirty = 1; + +static int list_running_servers(char **nicknames_out); + +/************** Fingerprint handling code ************/ + +typedef struct fingerprint_entry_t { + char *nickname; + char *fingerprint; +} fingerprint_entry_t; + +static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR]; +static int n_fingerprints = 0; + +static void +add_fingerprint_to_dir(const char *nickname, const char *fp) +{ + int i; + for (i = 0; i < n_fingerprints; ++i) { + if (!strcasecmp(fingerprint_list[i].nickname,nickname)) { + free(fingerprint_list[i].fingerprint); + fingerprint_list[i].fingerprint = tor_strdup(fp); + return; + } + } + fingerprint_list[n_fingerprints].nickname = tor_strdup(nickname); + fingerprint_list[n_fingerprints].fingerprint = tor_strdup(fp); + ++n_fingerprints; +} + +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; +} + +/* return 0 on success, -1 on failure */ +int +dirserv_parse_fingerprint_file(const char *fname) +{ + FILE *file; + char line[FINGERPRINT_LEN+MAX_NICKNAME_LEN+20+1]; + char *nickname, *fingerprint; + fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR]; + int n_fingerprints_tmp = 0; + int i, result; + + if(!(file = fopen(fname, "r"))) { + log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname); + return -1; + } + 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 < n_fingerprints_tmp; ++i) { + if (0==strcasecmp(fingerprint_list_tmp[i].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 == n_fingerprints_tmp) { /* not a duplicate */ + fingerprint_list_tmp[n_fingerprints_tmp].nickname = tor_strdup(nickname); + fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = tor_strdup(fingerprint); + ++n_fingerprints_tmp; + } + } + fclose(file); + if(result == 0) { /* eof; replace the global fingerprints list. */ + dirserv_free_fingerprint_list(); + memcpy(fingerprint_list, fingerprint_list_tmp, + sizeof(fingerprint_entry_t)*n_fingerprints_tmp); + n_fingerprints = n_fingerprints_tmp; + return 0; + } + /* error */ + log_fn(LOG_WARN, "Error reading from fingerprint file"); + for (i = 0; i < n_fingerprints_tmp; ++i) { + free(fingerprint_list_tmp[i].nickname); + free(fingerprint_list_tmp[i].fingerprint); + } + return -1; +} + +/* return 1 if router's identity and nickname match, + * -1 if they don't match, 0 if the nickname is not known. */ +int +dirserv_router_fingerprint_is_known(const routerinfo_t *router) +{ + int i; + fingerprint_entry_t *ent =NULL; + char fp[FINGERPRINT_LEN+1]; + + log_fn(LOG_DEBUG, "%d fingerprints known.", n_fingerprints); + for (i=0;inickname, fingerprint_list[i].nickname); + if (!strcasecmp(router->nickname,fingerprint_list[i].nickname)) { + ent = &fingerprint_list[i]; + break; + } + } + + if (!ent) { /* 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. */ + } +} + +void +dirserv_free_fingerprint_list() +{ + int i; + for (i = 0; i < n_fingerprints; ++i) { + free(fingerprint_list[i].nickname); + free(fingerprint_list[i].fingerprint); + } + n_fingerprints = 0; +} + +/* + * Descriptor list + */ +typedef struct descriptor_entry_t { + char *nickname; + time_t published; + size_t desc_len; + char *descriptor; +} descriptor_entry_t; + +static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR]; +static int n_descriptors = 0; + +static void free_descriptor_entry(descriptor_entry_t *desc) +{ + tor_free(desc->descriptor); + tor_free(desc->nickname); + free(desc); +} + +void +dirserv_free_descriptors() +{ + int i; + for (i = 0; i < n_descriptors; ++i) { + free_descriptor_entry(descriptor_list[i]); + } + n_descriptors = 0; +} + +/* Return 0 if descriptor is well-formed; -1 if descriptor is not + * well-formed. Update *desc to point after the descriptor if the + * descriptor is well-formed. + */ +/* XXX down the road perhaps we should return 1 for accepted, 0 for + * well-formed but rejected, -1 for not-well-formed. So remote servers + * can know if their submission was accepted and not just whether it + * was well-formed. ...Or maybe we shouldn't give them that info? + */ +int +dirserv_add_descriptor(const char **desc) +{ + descriptor_entry_t **desc_ent_ptr; + routerinfo_t *ri = NULL; + int i, r; + char *start, *end; + char *desc_tmp = NULL; + const char *cp; + size_t desc_len; + + start = strstr(*desc, "router "); + if (!start) { + log(LOG_WARN, "no descriptor found."); + goto err; + } + 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_get_entry_from_string(&cp); + if (!ri) { + log(LOG_WARN, "Couldn't parse descriptor"); + goto err; + } + tor_free(desc_tmp); + /* Okay. Now check whether the fingerprint is recognized. */ + r = dirserv_router_fingerprint_is_known(ri); + if(r<1) { + if(r==0) { + log_fn(LOG_WARN, "Unknown nickname %s. Not adding.", ri->nickname); + } else { + log_fn(LOG_WARN, "Known nickname %s, wrong fingerprint. Not adding.", ri->nickname); + } + routerinfo_free(ri); + *desc = end; + return 0; + } + /* Is there too much clock skew? */ + if (ri->published_on > time(NULL)+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; + } + /* Do we already have an entry for this router? */ + desc_ent_ptr = NULL; + for (i = 0; i < n_descriptors; ++i) { + if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) { + desc_ent_ptr = &descriptor_list[i]; + break; + } + } + if (desc_ent_ptr) { + /* if so, decide whether to update it. */ + if ((*desc_ent_ptr)->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. */ + routerinfo_free(ri); + *desc = end; + return 0; + } + /* We don't have a newer one; we'll update this one. */ + free_descriptor_entry(*desc_ent_ptr); + } else { + /* Add this at the end. */ + desc_ent_ptr = &descriptor_list[n_descriptors++]; + /* XXX check if n_descriptors is too big */ + } + + (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t)); + (*desc_ent_ptr)->nickname = tor_strdup(ri->nickname); + (*desc_ent_ptr)->published = ri->published_on; + (*desc_ent_ptr)->desc_len = desc_len; + (*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1); + strncpy((*desc_ent_ptr)->descriptor, start, desc_len); + (*desc_ent_ptr)->descriptor[desc_len] = '\0'; + *desc = end; + directory_set_dirty(); + + routerinfo_free(ri); + return 0; + err: + tor_free(desc_tmp); + if (ri) + routerinfo_free(ri); + + return -1; +} + +void +directory_set_dirty() +{ + the_directory_is_dirty = 1; +} + +int +dirserv_init_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)) { + return -1; + } + --cp; /*Back up to newline.*/ + } + return 0; +} + +static int +list_running_servers(char **nicknames_out) +{ + char *nickname_lst[MAX_ROUTERS_IN_DIR]; + connection_t **connection_array; + int n_conns; + connection_t *conn; + char *cp; + int n = 0, i; + int length; + *nicknames_out = NULL; + nickname_lst[n++] = options.Nickname; + + get_connection_array(&connection_array, &n_conns); + for (i = 0; itype != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) + continue; /* only list successfully handshaked OR's. */ + if(!conn->nickname) /* it's an OP, don't list it */ + continue; + nickname_lst[n++] = conn->nickname; + } + length = n + 1; /* spaces + EOS + 1. */ + for (i = 0; idescriptor, descriptor_list[i]->desc_len); + /* XXX Nick: do strncat and friends null-terminate? man page is ambiguous. */ + cp += descriptor_list[i]->desc_len; + assert(!*cp); + } + /* These multiple strlen calls are inefficient, but dwarfed by the RSA + signature. + */ + i = strlen(s); + strncat(s, "directory-signature\n", maxlen-i); + i = strlen(s); + cp = s + i; + + 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 %02x:%02x:%02x:%02x", + ((int)digest[0])&0xff,((int)digest[1])&0xff, + ((int)digest[2])&0xff,((int)digest[3])&0xff); + + strncpy(cp, "-----BEGIN SIGNATURE-----\n", maxlen-i); + cp[maxlen-i-1] = 0; + + 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; + } + + i = strlen(s); + cp = s+i; + strncat(cp, "-----END SIGNATURE-----\n", maxlen-i); + i = strlen(s); + if (i == maxlen) { + log_fn(LOG_WARN,"tried to exceed string length."); + return -1; + } + + return 0; +} + +static char *the_directory = NULL; +static int the_directory_len = -1; + +size_t dirserv_get_directory(const char **directory) +{ + char *new_directory; + char filename[512]; + 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_set_routerlist_from_directory(new_directory, get_identity_key())) { + log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying."); + exit(0); + } + free(new_directory); + sprintf(filename,"%s/cached-directory", options.DataDirectory); + 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; +} + diff --git a/tags/tor-0_0_2pre19/src/or/dns.c b/tags/tor-0_0_2pre19/src/or/dns.c new file mode 100644 index 0000000000..88607172de --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/dns.c @@ -0,0 +1,527 @@ +/* Copyright 2003 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/* 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" + +#define MAX_ADDRESSLEN 256 + +#define MAX_DNSWORKERS 50 +#define MIN_DNSWORKERS 3 +#define MAX_IDLE_DNSWORKERS 10 + +int num_dnsworkers=0; +int num_dnsworkers_busy=0; + +static void purge_expired_resolves(uint32_t now); +static int assign_to_dnsworker(connection_t *exitconn); +static void dns_found_answer(char *address, uint32_t addr); +int dnsworker_main(void *data); +static int spawn_dnsworker(void); +static void spawn_enough_dnsworkers(void); + +struct pending_connection_t { + struct connection_t *conn; + struct pending_connection_t *next; +}; + +struct cached_resolve { + SPLAY_ENTRY(cached_resolve) node; + char address[MAX_ADDRESSLEN]; /* the hostname to be resolved */ + uint32_t addr; /* in host order. I know I'm horrible for assuming ipv4 */ + 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 untouched items from cache after some time? */ + struct pending_connection_t *pending_connections; + struct cached_resolve *next; +}; + +static SPLAY_HEAD(cache_tree, cached_resolve) cache_root; + +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); + +static void init_cache_tree(void) { + SPLAY_INIT(&cache_root); +} + +void dns_init(void) { + init_cache_tree(); + spawn_enough_dnsworkers(); +} + +static struct cached_resolve *oldest_cached_resolve = NULL; /* linked list, */ +static struct cached_resolve *newest_cached_resolve = NULL; /* oldest to newest */ + +static void purge_expired_resolves(uint32_t now) { + struct cached_resolve *resolve; + + /* 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); + 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); + free(resolve); + } +} + +#if 0 +uint32_t dns_lookup(const char *address) { + struct in_addr in; + uint32_t now = time(NULL); + + /* first take this opportunity to see if there are any expired + resolves in the tree.*/ + purge_expired_resolves(now); + + if (inet_aton(address, &in)) { + log_fn(LOG_DEBUG, "Using static address %s (%08X)", address, + ntohl(in.s_addr)); + return ntohl(in.s_addr); + } + + strncpy(search.address, address, MAX_ADDRESSLEN); + search.address[MAX_ADDRESSLEN-1] = 0; + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(resolve) { /* it's there */ + if(resolve->state == CACHE_STATE_VALID) { + in.s_addr = htonl(resolve->addr); + log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address, + inet_ntoa(in)); + return resolve->addr; + } + log_fn(LOG_DEBUG, "Entry found for address %s but it's not valid. Returning 0.", + address); + return 0; + } + /* it's not there */ + log_fn(LOG_DEBUG, "No entry found for address %s", address); + return 0; +} +#endif + +/* 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; + uint32_t now = time(NULL); + + /* first 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(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); + 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); + return 1; + case CACHE_STATE_FAILED: + return -1; + } + 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(sizeof(struct pending_connection_t)); + pending_connection->conn = exitconn; + pending_connection->next = resolve->pending_connections; + resolve->pending_connections = pending_connection; + + /* 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); +} + +static int assign_to_dnsworker(connection_t *exitconn) { + connection_t *dnsconn; + unsigned char len; + + 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, NULL); + return -1; + } + + log_fn(LOG_DEBUG, "Connection (fd %d) needs to resolve '%s'; assigning to DNSWorker (fd %d)", + exitconn->s, exitconn->address, dnsconn->s); + + 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; +} + +/* if onlyconn is NULL, cancel the whole thing. if onlyconn is defined, + * then remove onlyconn from the pending list, and if the pending list + * is now empty, cancel the whole thing. + */ +void dns_cancel_pending_resolve(char *address, connection_t *onlyconn) { + struct pending_connection_t *pend, *victim; + struct cached_resolve search; + struct cached_resolve *resolve, *tmp; + + 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; + } + + assert(resolve->state == CACHE_STATE_PENDING); + assert(resolve->pending_connections); + + if(onlyconn) { + pend = resolve->pending_connections; + if(pend->conn == onlyconn) { + resolve->pending_connections = pend->next; + free(pend); + if(resolve->pending_connections) {/* more pending, don't cancel it */ + log_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'", + onlyconn->s, address); + return; + } + } else { + for( ; pend->next; pend = pend->next) { + if(pend->next->conn == onlyconn) { + victim = pend->next; + pend->next = victim->next; + free(victim); + log_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'", + onlyconn->s, address); + return; /* more are pending */ + } + } + assert(0); /* not reachable unless onlyconn not in pending list */ + } + } else { + /* 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; + if(connection_edge_end(pend->conn, END_STREAM_REASON_MISC, NULL) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + resolve->pending_connections = pend->next; + free(pend); + } + } + + /* 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) ; + 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); + + free(resolve); +} + +static void dns_found_answer(char *address, uint32_t addr) { + struct pending_connection_t *pend; + struct cached_resolve search; + struct cached_resolve *resolve; + + 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) { + log_fn(LOG_WARN, "Resolved '%s' which was already resolved; ignoring", + address); + 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. */ + /* assert(resolve->state == CACHE_STATE_PENDING); */ + + resolve->addr = ntohl(addr); + if(resolve->addr) + resolve->state = CACHE_STATE_VALID; + else + resolve->state = CACHE_STATE_FAILED; + + while(resolve->pending_connections) { + pend = resolve->pending_connections; + pend->conn->addr = resolve->addr; + if(resolve->state == CACHE_STATE_FAILED) { + if(connection_edge_end(pend->conn, END_STREAM_REASON_RESOLVEFAILED, NULL) < 0) + log_fn(LOG_WARN,"1: I called connection_edge_end redundantly."); + } else + connection_exit_connect(pend->conn); + resolve->pending_connections = pend->next; + free(pend); + } +} + +/******************************************************************/ + +int connection_dns_finished_flushing(connection_t *conn) { + assert(conn && conn->type == CONN_TYPE_DNSWORKER); + connection_stop_writing(conn); + return 0; +} + +int connection_dns_process_inbuf(connection_t *conn) { + uint32_t addr; + + assert(conn && conn->type == CONN_TYPE_DNSWORKER); + + if(conn->inbuf_reached_eof) { + log_fn(LOG_WARN,"Read eof. Worker dying."); + if(conn->state == DNSWORKER_STATE_BUSY) { + dns_cancel_pending_resolve(conn->address, NULL); + num_dnsworkers_busy--; + } + num_dnsworkers--; + return -1; + } + + assert(conn->state == DNSWORKER_STATE_BUSY); + if(buf_datalen(conn->inbuf) < 4) /* entire answer available? */ + return 0; /* not yet */ + assert(buf_datalen(conn->inbuf) == 4); + + connection_fetch_from_buf((char*)&addr,sizeof(addr),conn); + + log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'", + conn->s, conn->address); + + dns_found_answer(conn->address, addr); + + free(conn->address); + conn->address = tor_strdup(""); + conn->state = DNSWORKER_STATE_IDLE; + num_dnsworkers_busy--; + + return 0; +} + +int dnsworker_main(void *data) { + char address[MAX_ADDRESSLEN]; + unsigned char address_len; + struct hostent *rent; + int *fdarray = data; + int fd; + + close(fdarray[0]); /* this is the side of the socketpair the parent uses */ + fd = fdarray[1]; /* this side is ours */ + connection_free_all(); /* so the child doesn't hold the parent's fd's open */ + + for(;;) { + + if(read(fd, &address_len, 1) != 1) { + log_fn(LOG_INFO,"read length failed. Child exiting."); + spawn_exit(); + } + assert(address_len > 0); + + if(read_all(fd, address, address_len) != address_len) { + log_fn(LOG_ERR,"read hostname failed. Child exiting."); + spawn_exit(); + } + address[address_len] = 0; /* null terminate it */ + + rent = gethostbyname(address); + if (!rent) { + log_fn(LOG_INFO,"Could not resolve dest addr %s. Returning nulls.",address); + if(write_all(fd, "\0\0\0\0", 4) != 4) { + log_fn(LOG_ERR,"writing nulls failed. Child exiting."); + spawn_exit(); + } + } else { + assert(rent->h_length == 4); /* break to remind us if we move away from ipv4 */ + if(write_all(fd, rent->h_addr, 4) != 4) { + log_fn(LOG_INFO,"writing answer failed. Child exiting."); + spawn_exit(); + } + log_fn(LOG_INFO,"Resolved address '%s'.",address); + } + } + return 0; /* windows wants this function to return an int */ +} + +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", strerror(errno)); + exit(1); + } + + spawn_func(dnsworker_main, (void*)fd); + log_fn(LOG_DEBUG,"just spawned a worker."); + close(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 */ +} + +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); + assert(dnsconn); + + log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.", + MAX_DNSWORKERS); + /* tell the exit connection that it's failed */ + dns_cancel_pending_resolve(dnsconn->address, NULL); + + dnsconn->marked_for_close = 1; + 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); + assert(dnsconn); + dnsconn->marked_for_close = 1; + num_dnsworkers--; + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/main.c b/tags/tor-0_0_2pre19/src/or/main.c new file mode 100644 index 0000000000..cf2800a853 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/main.c @@ -0,0 +1,671 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +/********* START PROTOTYPES **********/ + +static void dumpstats(int severity); /* log stats */ +static int init_from_config(int argc, char **argv); + +/********* START VARIABLES **********/ + +extern char *conn_type_to_string[]; +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 */ + +static int stats_prev_global_read_bucket; +static uint64_t stats_n_bytes_read = 0; +static long stats_n_seconds_reading = 0; + +static connection_t *connection_array[MAXCONNECTIONS] = + { NULL }; + +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 */ +#endif /* signal stuff */ + +/********* 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). +* +****************************************************************************/ + +int connection_add(connection_t *conn) { + + if(nfds >= options.MaxConn-1) { + log(LOG_WARN,"connection_add(): failing because nfds is too high."); + return -1; + } + + conn->poll_index = nfds; + connection_set_poll_socket(conn); + connection_array[nfds] = conn; + + /* 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(LOG_INFO,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds); + + return 0; +} + +void connection_set_poll_socket(connection_t *conn) { + poll_array[conn->poll_index].fd = conn->s; +} + +/* 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; + + assert(conn); + assert(nfds>0); + + log(LOG_INFO,"connection_remove(): removing socket %d, nfds now %d",conn->s, nfds-1); + /* 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); + + current_index = conn->poll_index; + if(current_index == nfds-1) { /* this is the end */ + nfds--; + return 0; + } + + /* we replace this one with the one at the end, then free it */ + 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; +} + +void get_connection_array(connection_t ***array, int *n) { + *array = connection_array; + *n = nfds; +} + +void connection_watch_events(connection_t *conn, short events) { + + assert(conn && conn->poll_index < nfds); + + poll_array[conn->poll_index].events = events; +} + +int connection_is_reading(connection_t *conn) { + return poll_array[conn->poll_index].events & POLLIN; +} + +void connection_stop_reading(connection_t *conn) { + + assert(conn && 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; +} + +void connection_start_reading(connection_t *conn) { + + assert(conn && conn->poll_index < nfds); + + poll_array[conn->poll_index].events |= POLLIN; +} + +void connection_stop_writing(connection_t *conn) { + + assert(conn && conn->poll_index < nfds); + + if(poll_array[conn->poll_index].events & POLLOUT) + poll_array[conn->poll_index].events -= POLLOUT; +} + +void connection_start_writing(connection_t *conn) { + + assert(conn && conn->poll_index < nfds); + + poll_array[conn->poll_index].events |= POLLOUT; +} + +static void conn_read(int i) { + connection_t *conn = connection_array[i]; + + /* 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)); + + if( + /* XXX does POLLHUP also mean it's definitely broken? */ +#ifdef MS_WINDOWS + (poll_array[i].revents & POLLERR) || +#endif + connection_handle_read(conn) < 0) + { + /* this connection is broken. remove it */ + log_fn(LOG_INFO,"%s connection broken, removing.", + conn_type_to_string[conn->type]); + connection_remove(conn); + connection_free(conn); + if(is); + + assert_connection_ok(conn, time(NULL)); + + if(connection_handle_write(conn) < 0) { /* this connection is broken. remove it. */ + log_fn(LOG_INFO,"%s connection broken, removing.", conn_type_to_string[conn->type]); + connection_remove(conn); + connection_free(conn); + if(imarked_for_close) { + log_fn(LOG_INFO,"Cleaning up connection (fd %d).",conn->s); + if(conn->s >= 0) { /* might be an incomplete edge connection */ + /* FIXME there's got to be a better way to check for this -- and make other checks? */ + if(connection_speaks_cells(conn)) { + if(conn->state == OR_CONN_STATE_OPEN) + flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen); + } else { + flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); + } + if(connection_wants_to_flush(conn) && buf_datalen(conn->outbuf)) { + log_fn(LOG_WARN,"Conn (socket %d) still wants to flush. Losing %d bytes!", + conn->s, (int)buf_datalen(conn->outbuf)); + } + } + connection_remove(conn); + connection_free(conn); + if(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->receiver_bucket > 0)) { + /* and either a non-cell conn or a cell conn with non-empty bucket */ + conn->wants_to_read = 0; + connection_start_reading(conn); + if(conn->wants_to_write == 1) { + conn->wants_to_write = 0; + connection_start_writing(conn); + } + } + + /* check connections to see whether we should send a keepalive, expire, or wait */ + if(!connection_speaks_cells(conn)) + return; + + if(now >= conn->timestamp_lastwritten + options.KeepalivePeriod) { + if((!options.ORPort && !circuit_get_by_conn(conn)) || + (!connection_state_is_open(conn))) { + /* we're an onion proxy, with no circuits; or our handshake has expired. kill it. */ + log_fn(LOG_INFO,"Expiring connection to %d (%s:%d).", + i,conn->address, conn->port); + conn->marked_for_close = 1; + } else { + /* either a full router, 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); + } + } +} + +/* 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 long time_to_new_circuit = 0; + circuit_t *circ; + int i; + + /* 1. Every DirFetchPostPeriod seconds, we get a new directory and upload + * our descriptor (if any). */ + if(time_to_fetch_directory < now) { + /* it's time to fetch a new directory and/or post our descriptor */ + if(options.ORPort) { + router_rebuild_descriptor(); + router_upload_desc_to_dirservers(); + } + if(!options.DirPort) { + /* NOTE directory servers do not currently fetch directories. + * Hope this doesn't bite us later. */ + directory_initiate_command(router_pick_directory_server(), + DIR_CONN_STATE_CONNECTING_FETCH); + } + time_to_fetch_directory = now + options.DirFetchPostPeriod; + } + + /* 2. Every second, we examine pending circuits and prune the + * ones which have been pending for more than 3 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(); + + /* 3. 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(options.SocksPort) { + + /* launch a new circ for any pending streams that need one */ + connection_ap_attach_pending(); + + circ = circuit_get_newest(NULL, 1); + if(time_to_new_circuit < now) { + client_dns_clean(); + circuit_expire_unused_circuits(); + circuit_reset_failure_count(); + if(circ && circ->timestamp_dirty) { + log_fn(LOG_INFO,"Youngest circuit dirty; launching replacement."); + circuit_launch_new(); /* make a new circuit */ + } + time_to_new_circuit = now + options.NewCircuitPeriod; + } +#define CIRCUIT_MIN_BUILDING 2 + if(!circ && circuit_count_building() < CIRCUIT_MIN_BUILDING) { + /* if there's no open circ, and less than 2 are on the way, + * go ahead and try another. + */ + circuit_launch_new(); + } + } + + /* 4. Every second, we check how much bandwidth we've consumed and + * increment global_read_bucket. + */ + stats_n_bytes_read += stats_prev_global_read_bucket-global_read_bucket; + if(global_read_bucket < 9*options.TotalBandwidth) { + global_read_bucket += options.TotalBandwidth; + log_fn(LOG_DEBUG,"global_read_bucket now %d.", global_read_bucket); + } + stats_prev_global_read_bucket = global_read_bucket; + + /* 5. We do housekeeping for each connection... */ + for(i=0;i current_second) { /* the second has rolled over. check more stuff. */ + + ++stats_n_seconds_reading; + run_scheduled_events(now.tv_sec); + + 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? */ +} + +static int init_from_config(int argc, char **argv) { + if(getconfig(argc,argv,&options)) { + log_fn(LOG_ERR,"Reading config failed. For usage, try -h."); + return -1; + } + close_logs(); /* we'll close, then open with correct loglevel if necessary */ + if(!options.LogFile && !options.RunAsDaemon) + add_stream_log(options.loglevel, "", stdout); + if(options.LogFile) { + if (add_file_log(options.loglevel, options.LogFile) != 0) { + /* opening the log file failed! Use stderr and log a warning */ + add_stream_log(options.loglevel, "", stderr); + log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", options.LogFile, strerror(errno)); + } + log_fn(LOG_WARN, "Successfully opened LogFile '%s', redirecting output.", + options.LogFile); + } + if(options.DebugLogFile) { + if (add_file_log(LOG_DEBUG, options.DebugLogFile) != 0) + log_fn(LOG_WARN, "Cannot write to DebugLogFile '%s': %s.", options.DebugLogFile, strerror(errno)); + log_fn(LOG_DEBUG, "Successfully opened DebugLogFile '%s'.", options.DebugLogFile); + } + + global_read_bucket = options.TotalBandwidth; /* start it at 1 second of traffic */ + stats_prev_global_read_bucket = global_read_bucket; + + if(options.User || options.Group) { + if(switch_id(options.User, options.Group) != 0) { + return -1; + } + } + + 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; +} + +static int do_main_loop(void) { + int i; + int timeout; + int poll_result; + + /* load the routers file */ + if(router_set_routerlist_from_file(options.RouterFile) < 0) { + log_fn(LOG_ERR,"Error loading router list."); + return -1; + } + + /* load the private keys, if we're supposed to have them, and set up the + * TLS context. */ + if (init_keys() < 0) { + log_fn(LOG_ERR,"Error initializing keys; exiting"); + return -1; + } + + if(options.ORPort) { + cpu_init(); /* launch cpuworkers. Need to do this *after* we've read the onion key. */ + router_upload_desc_to_dirservers(); /* upload our descriptor to all dirservers */ + } + + /* start up the necessary connections based on which ports are + * non-zero. This is where we try to connect to all the other ORs, + * and start the listeners. + */ + if(retry_all_connections() < 0) { + log_fn(LOG_ERR,"Failed to bind one of the listener ports."); + return -1; + } + + for(;;) { +#ifndef MS_WINDOWS /* do signal stuff only on unix */ + if(please_dumpstats) { + /* prefer to log it at INFO, but make sure we always see it */ + dumpstats(options.loglevel>LOG_INFO ? options.loglevel : LOG_INFO); + please_dumpstats = 0; + } + if(please_reset) { + log_fn(LOG_WARN,"Received sighup. Reloading config."); + /* first, reload config variables, in case they've changed */ + if (init_from_config(0, NULL) < 0) { + /* no need to provide argc/v, they've been cached inside init_from_config */ + exit(1); + } + if(retry_all_connections() < 0) { + log_fn(LOG_ERR,"Failed to bind one of the listener ports."); + return -1; + } + if(options.DirPort) { + /* reload the fingerprint file */ + char keydir[512]; + sprintf(keydir,"%s/approved-routers", options.DataDirectory); + 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."); + } + } else { + /* fetch a new directory */ + directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH); + } + + 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 = poll(poll_array, nfds, timeout); + + /* let catch() handle things like ^c, and otherwise don't worry about it */ + if(poll_result < 0) { + if(errno != EINTR) { /* let the program survive things like ^z */ + log_fn(LOG_ERR,"poll failed."); + 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], 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 %ld secs ago)",i, + (int)buf_datalen(conn->inbuf), + now - conn->timestamp_lastread); + log(severity,"Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)",i, + (int)buf_datalen(conn->outbuf), 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" + " %10lud 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 outgoing 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 incoming cell fullness: %2.3f%%", + 100*(((double)stats_n_data_bytes_received) / + (stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) ); + + if (stats_n_seconds_reading) + log(severity,"Average bandwidth used: %d bytes/sec", + (int) (stats_n_bytes_read/stats_n_seconds_reading)); +} + +int tor_main(int argc, char *argv[]) { + + /* give it somewhere to log to initially */ + add_stream_log(LOG_INFO, "", stdout); + log_fn(LOG_WARN,"Tor v%s. This is experimental software. Do not use it if you need anonymity.",VERSION); + +#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 (init_from_config(argc,argv) < 0) + return -1; + + if (options.RunAsDaemon) { + start_daemon(); + } + + if(options.ORPort) { /* only spawn dns handlers if we're a router */ + dns_init(); /* initialize the dns resolve tree, and spawn workers */ + } + if(options.SocksPort) { + client_dns_init(); /* init the client dns cache */ + } + +#ifndef MS_WINDOWS /* do signal stuff only on unix */ + signal (SIGINT, catch); /* catch kills so we can exit cleanly */ + signal (SIGTERM, catch); + signal (SIGUSR1, catch); /* to dump stats */ + signal (SIGHUP, catch); /* to reload directory */ + signal (SIGCHLD, catch); /* for exiting dns/cpu workers */ +#endif /* signal stuff */ + + crypto_global_init(); + crypto_seed_rng(); + do_main_loop(); + crypto_global_cleanup(); + return -1; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/onion.c b/tags/tor-0_0_2pre19/src/or/onion.c new file mode 100644 index 0000000000..e25dac8c18 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/onion.c @@ -0,0 +1,802 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len); + +int decide_circ_id_type(char *local_nick, char *remote_nick) { + int result; + + assert(remote_nick); + if(!local_nick) + return CIRC_ID_TYPE_LOWER; + result = strcmp(local_nick, remote_nick); + assert(result); + if(result < 0) + return CIRC_ID_TYPE_LOWER; + return CIRC_ID_TYPE_HIGHER; +} + +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; +static int ol_length=0; + +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) { + assert(!ol_list); + assert(!ol_length); + ol_list = tmp; + ol_tail = tmp; + ol_length++; + return 0; + } + + assert(ol_list); + 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; + +} + +circuit_t *onion_next_task(void) { + circuit_t *circ; + + if(!ol_list) + return NULL; /* no onions pending, we're done */ + + assert(ol_list->circ); + assert(ol_list->circ->p_conn); /* make sure it's still valid */ + 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 response payload and keys, initialize, then send a created cell back */ +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) { + unsigned char iv[16]; + cell_t cell; + + memset(iv, 0, 16); + + 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 %d, backward %d.", + *(uint32_t*)(keys), *(uint32_t*)(keys+20)); + circ->n_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST); + crypto_digest_add_bytes(circ->n_digest, keys, 20); + circ->p_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST); + crypto_digest_add_bytes(circ->p_digest, keys+20, 20); + + log_fn(LOG_DEBUG,"init cipher forward %d, backward %d.", + *(uint32_t*)(keys+40), *(uint32_t*)(keys+40+16)); + if (!(circ->n_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40,iv,0))) { + log_fn(LOG_WARN,"Cipher initialization failed (n)."); + return -1; + } + if (!(circ->p_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40+16,iv,1))) { + log_fn(LOG_WARN,"Cipher initialization failed (p)."); + return -1; + } + + connection_or_write_cell_to_buf(&cell, circ->p_conn); + log_fn(LOG_DEBUG,"Finished sending 'created' cell."); + + return 0; +} + +static void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) { + char *start,*end; + char nick[MAX_NICKNAME_LEN]; + routerinfo_t *router; + + while(isspace(*list) || *list==',') list++; + + start = list; + while(*start) { + end=start; while(*end && !isspace(*end) && *end != ',') end++; + memcpy(nick,start,end-start); + nick[end-start] = 0; /* null terminate it */ + router = router_get_by_nickname(nick); + if(router && router->is_running) + smartlist_add(sl,router); + else + log_fn(LOG_WARN,"Nickname list includes '%s' which isn't a known router.",nick); + while(isspace(*end) || *end==',') end++; + start = end; + } +} + +static int new_route_len(double cw, routerinfo_t **rarray, int rarray_len) { + int num_acceptable_routers; + int routelen; + + assert((cw >= 0) && (cw < 1) && rarray); /* valid parameters */ + + for(routelen=3; ; routelen++) { /* 3, increment until coinflip says we're done */ + if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */ + break; + } + log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, rarray_len); + + num_acceptable_routers = count_acceptable_routers(rarray, rarray_len); + + if(num_acceptable_routers < 2) { + log_fn(LOG_INFO,"Not enough acceptable routers. Failing."); + 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; +} + +static routerinfo_t *choose_good_exit_server(routerlist_t *dir) +{ + int *n_supported; + int *n_maybe_supported; + int i, j; + int n_pending_connections = 0; + connection_t **carray; + int n_connections; + int best_support = -1; + int best_maybe_support = -1; + int best_support_idx = -1; + int best_maybe_support_idx = -1; + int n_best_support=0, n_best_maybe_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 _definitely_ exit from that + * router (n_supported[i]) and how many could _possibly_ exit from + * that router (n_maybe_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)*dir->n_routers); + n_maybe_supported = tor_malloc(sizeof(int)*dir->n_routers); + for (i = 0; i < dir->n_routers; ++i) { /* iterate over routers */ + if(!dir->routers[i]->is_running) { + n_supported[i] = n_maybe_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.", + dir->routers[i]->nickname, i); + continue; /* skip routers that are known to be down */ + } + if(router_exit_policy_rejects_all(dir->routers[i])) { + n_supported[i] = n_maybe_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", + dir->routers[i]->nickname, i); + continue; /* skip routers that reject all */ + } + n_supported[i] = n_maybe_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 */ + switch (connection_ap_can_use_exit(carray[j], dir->routers[i])) + { + case -1: + log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.", + dir->routers[i]->nickname, i); + break; /* would be rejected; try next connection */ + case 0: + ++n_supported[i]; + log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", + dir->routers[i]->nickname, i, n_supported[i]); + ; /* Fall through: If it is supported, it is also maybe supported. */ + case 1: + ++n_maybe_supported[i]; + log_fn(LOG_DEBUG,"%s is maybe supported. n_maybe_supported[%d] now %d.", + dir->routers[i]->nickname, i, n_maybe_supported[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]; best_support_idx = i; n_best_support=1; + log_fn(LOG_DEBUG,"%s is new best supported option so far.", + dir->routers[i]->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; + } + /* As above, but for 'maybe-supported' connections */ + if (n_maybe_supported[i] > best_maybe_support) { + best_maybe_support = n_maybe_supported[i]; best_maybe_support_idx = i; + n_best_maybe_support = 1; + log_fn(LOG_DEBUG,"%s is new best maybe-supported option so far.", + dir->routers[i]->nickname); + } else if (n_maybe_supported[i] == best_maybe_support) { + ++n_best_maybe_support; + } + } + log_fn(LOG_INFO, "Found %d servers that will definitely support %d/%d " + "pending connections, and %d that might support %d/%d.", + n_best_support, best_support, n_pending_connections, + n_best_maybe_support, best_maybe_support, n_pending_connections); + + preferredexits = smartlist_create(MAX_ROUTERS_IN_DIR); + add_nickname_list_to_smartlist(preferredexits,options.ExitNodes); + + excludedexits = smartlist_create(MAX_ROUTERS_IN_DIR); + add_nickname_list_to_smartlist(excludedexits,options.ExcludedNodes); + + sl = smartlist_create(MAX_ROUTERS_IN_DIR); + + /* If any routers definitely support any pending connections, choose one + * at random. */ + if (best_support > 0) { + for (i = best_support_idx; i < dir->n_routers; i++) + if (n_supported[i] == best_support) + smartlist_add(sl, dir->routers[i]); + + smartlist_subtract(sl,excludedexits); + if (smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = smartlist_choose(sl); + } else if (best_maybe_support > 0) { + /* If any routers _maybe_ support pending connections, choose one at + * random, as above. */ + for(i = best_maybe_support_idx; i < dir->n_routers; i++) + if(n_maybe_supported[i] == best_maybe_support) + smartlist_add(sl, 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. */ + for(i = best_maybe_support_idx; i < dir->n_routers; i++) + if(n_supported[i] != -1) + smartlist_add(sl, 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); tor_free(n_maybe_supported); + if(router) { + log_fn(LOG_DEBUG, "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; +} + +cpath_build_state_t *onion_new_cpath_build_state(void) { + routerlist_t *rl; + int r; + cpath_build_state_t *info; + routerinfo_t *exit; + + router_get_routerlist(&rl); + r = new_route_len(options.PathlenCoinWeight, rl->routers, rl->n_routers); + if (r < 0) + return NULL; + exit = choose_good_exit_server(rl); + if(!exit) + return NULL; + info = tor_malloc(sizeof(cpath_build_state_t)); + info->desired_path_len = r; + info->chosen_exit = tor_strdup(exit->nickname); + return info; +} + +static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) { + int i, j; + int num=0; + connection_t *conn; + + for(i=0;iis_running == 0) { + log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i); + goto next_i_loop; + } + if(options.ORPort) { + conn = connection_exact_get_by_addr_port(rarray[i]->addr, rarray[i]->or_port); + 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, rarray[j]->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: + ; /* our compiler may need an explicit statement after the label */ + } + + return num; +} + +/* prototypes for smartlist operations from routerlist.h + * they're here to prevent precedence issues with the .h files + */ +void router_add_running_routers_to_smartlist(smartlist_t *sl); + +static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) { + int i; + routerinfo_t *r; + + if(twin == NULL) + return; + +/* XXX abstraction violation: this function reaches inside smartlist :( */ + for(i=0; i < sl->num_used; i++) { + r = sl->list[i]; + if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) { + sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */ + i--; /* so we process the new i'th element */ + } + } +} + +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; + + assert(head_ptr); + 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(MAX_ROUTERS_IN_DIR); + add_nickname_list_to_smartlist(excludednodes,options.ExcludedNodes); + + if(cur_len == state->desired_path_len - 1) { /* Picking last node */ + log_fn(LOG_DEBUG, "Contemplating last hop: choice already made."); + choice = router_get_by_nickname(state->chosen_exit); + if(!choice) { + log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Failing.", + state->chosen_exit); + return -1; + } + } else if(cur_len == 0) { /* picking first node */ + /* try the nodes in EntryNodes first */ + sl = smartlist_create(MAX_ROUTERS_IN_DIR); + 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_nickname(state->chosen_exit)); + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + if(!choice) { + sl = smartlist_create(MAX_ROUTERS_IN_DIR); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); + 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. Failing."); + return -1; + } + } else { + log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice."); + sl = smartlist_create(MAX_ROUTERS_IN_DIR); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); + for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) { + r = router_get_by_addr_port(cpath->addr, cpath->port); + 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. Failing."); + return -1; + } + } + + log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)", + choice->nickname, cur_len, state->chosen_exit); + + hop = (crypt_path_t *)tor_malloc_zero(sizeof(crypt_path_t)); + + /* link hop into the cpath, at the end. */ + if (*head_ptr) { + hop->next = (*head_ptr); + hop->prev = (*head_ptr)->prev; + (*head_ptr)->prev->next = hop; + (*head_ptr)->prev = hop; + } else { + *head_ptr = hop; + hop->prev = hop->next = hop; + } + + hop->state = CPATH_STATE_CLOSED; + + hop->port = choice->or_port; + hop->addr = choice->addr; + + 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; +} + +/*----------------------------------------------------------------------*/ + +/* Given a router's 128 byte public key, + stores the following in onion_skin_out: +[16 bytes] Symmetric key for encrypting blob past RSA +[112 bytes] g^x part 1 (inside the RSA) +[16 bytes] g^x part 2 (symmetrically encrypted) +[ 6 bytes] Meeting point (IP/port) +[ 8 bytes] Meeting cookie +[16 bytes] End-to-end authentication [optional] + + * 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 iv[16]; + char *challenge = NULL; + crypto_dh_env_t *dh = NULL; + crypto_cipher_env_t *cipher = NULL; + int dhbytes, pkbytes; + + *handshake_state_out = NULL; + memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN); + memset(iv, 0, 16); + + if (!(dh = crypto_dh_new())) + goto err; + + dhbytes = crypto_dh_get_bytes(dh); + pkbytes = crypto_pk_keysize(dest_router_key); + assert(dhbytes == 128); + assert(pkbytes == 128); + challenge = (char *)tor_malloc_zero(ONIONSKIN_CHALLENGE_LEN); + + if (crypto_rand(16, challenge)) + 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 'challenge' to 0. This means that our symmetric key is really + * only 127 bits. + */ + challenge[0] &= 0x7f; + + if (crypto_dh_get_public(dh, challenge+16, dhbytes)) + goto err; + +#ifdef DEBUG_ONION_SKINS +#define PA(a,n) \ + { int _i; for (_i = 0; _i +#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" +#ifdef HAVE_SYS_POLL_H +#include +#elif HAVE_POLL_H +#include +#else +#include "../common/fakepoll.h" +#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_PWD_H +#include +#endif +#ifdef HAVE_GRP_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 +#define WIN32_LEAN_AND_MEAN +#include +#define snprintf _snprintf +#endif + +#include "../common/crypto.h" +#include "../common/tortls.h" +#include "../common/log.h" +#include "../common/util.h" + +#define MAXCONNECTIONS 1000 /* upper bound on max connections. + can be lowered by config file */ + +#define DEFAULT_BANDWIDTH_OP (1024 * 1000) +#define MAX_NICKNAME_LEN 32 +#define MAX_DIR_SIZE 500000 + +#define MAX_DNS_ENTRY_AGE (15*60) + +#define CIRC_ID_TYPE_LOWER 0 +#define CIRC_ID_TYPE_HIGHER 1 + +#define _CONN_TYPE_MIN 3 +#define CONN_TYPE_OR_LISTENER 3 +#define CONN_TYPE_OR 4 +#define CONN_TYPE_EXIT 5 +#define CONN_TYPE_AP_LISTENER 6 +#define CONN_TYPE_AP 7 +#define CONN_TYPE_DIR_LISTENER 8 +#define CONN_TYPE_DIR 9 +#define CONN_TYPE_DNSWORKER 10 +#define CONN_TYPE_CPUWORKER 11 +#define _CONN_TYPE_MAX 11 + +#define LISTENER_STATE_READY 0 + +#define _DNSWORKER_STATE_MIN 1 +#define DNSWORKER_STATE_IDLE 1 +#define DNSWORKER_STATE_BUSY 2 +#define _DNSWORKER_STATE_MAX 2 + +#define _CPUWORKER_STATE_MIN 1 +#define CPUWORKER_STATE_IDLE 1 +#define CPUWORKER_STATE_BUSY_ONION 2 +#define CPUWORKER_STATE_BUSY_HANDSHAKE 3 +#define _CPUWORKER_STATE_MAX 3 + +#define CPUWORKER_TASK_ONION CPUWORKER_STATE_BUSY_ONION + +#define _OR_CONN_STATE_MIN 1 +#define OR_CONN_STATE_CONNECTING 1 /* waiting for connect() to finish */ +#define OR_CONN_STATE_HANDSHAKING 2 /* SSL is handshaking, not done yet */ +#define OR_CONN_STATE_OPEN 3 /* ready to send/receive cells. */ +#define _OR_CONN_STATE_MAX 3 + +#define _EXIT_CONN_STATE_MIN 1 +#define EXIT_CONN_STATE_RESOLVING 1 /* waiting for response from dns farm */ +#define EXIT_CONN_STATE_CONNECTING 2 /* waiting for connect() to finish */ +#define EXIT_CONN_STATE_OPEN 3 +#define _EXIT_CONN_STATE_MAX 3 +#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 4 +#define AP_CONN_STATE_SOCKS_WAIT 4 +#define AP_CONN_STATE_CIRCUIT_WAIT 5 +#define AP_CONN_STATE_OPEN 6 +#define _AP_CONN_STATE_MAX 6 + +#define _DIR_CONN_STATE_MIN 1 +#define DIR_CONN_STATE_CONNECTING_FETCH 1 +#define DIR_CONN_STATE_CONNECTING_UPLOAD 2 +#define DIR_CONN_STATE_CLIENT_SENDING_FETCH 3 +#define DIR_CONN_STATE_CLIENT_SENDING_UPLOAD 4 +#define DIR_CONN_STATE_CLIENT_READING_FETCH 5 +#define DIR_CONN_STATE_CLIENT_READING_UPLOAD 6 +#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 7 +#define DIR_CONN_STATE_SERVER_WRITING 8 +#define _DIR_CONN_STATE_MAX 8 + +#define CIRCUIT_STATE_BUILDING 0 /* I'm the OP, still haven't done all my handshakes */ +#define CIRCUIT_STATE_ONIONSKIN_PENDING 1 /* waiting to process the onionskin */ +#define CIRCUIT_STATE_OR_WAIT 2 /* I'm the OP, my firsthop is still connecting */ +#define CIRCUIT_STATE_OPEN 3 /* onionskin(s) processed, ready to send/receive cells */ + +#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 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 + +/* default cipher function */ +#define DEFAULT_CIPHER CRYPTO_CIPHER_AES_CTR +/* Used to en/decrypt onion skins */ +#define ONION_CIPHER DEFAULT_CIPHER +/* Used to en/decrypt RELAY cells */ +#define CIRCUIT_CIPHER DEFAULT_CIPHER + +#define CELL_DIRECTION_IN 1 +#define CELL_DIRECTION_OUT 2 +#define EDGE_EXIT CONN_TYPE_EXIT +#define EDGE_AP CONN_TYPE_AP +#define CELL_DIRECTION(x) ((x) == EDGE_EXIT ? CELL_DIRECTION_IN : CELL_DIRECTION_OUT) + +#define CIRCWINDOW_START 1000 +#define CIRCWINDOW_INCREMENT 100 + +#define STREAMWINDOW_START 500 +#define STREAMWINDOW_INCREMENT 50 + +/* 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 filename */ +#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/" +/* 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] + */ + +#if 0 +#define CELL_RELAY_COMMAND(c) (*(uint8_t*)((c).payload)) +#define SET_CELL_RELAY_COMMAND(c,cmd) (*(uint8_t*)((c).payload) = (cmd)) + +#define CELL_RELAY_RECOGNIZED(c) (ntohs(*(uint16_t*)((c).payload+1))) +#define SET_CELL_RELAY_RECOGNIZED(c,r) (*(uint16_t*)((c).payload+1) = htons(r)) + +#define STREAM_ID_SIZE 2 +//#define SET_CELL_STREAM_ID(c,id) memcpy((c).payload+1,(id),STREAM_ID_SIZE) +#define CELL_RELAY_STREAM_ID(c) (ntohs(*(uint16_t*)((c).payload+3))) +#define SET_CELL_RELAY_STREAM_ID(c,id) (*(uint16_t*)((c).payload+3) = htons(id)) +#define ZERO_STREAM 0 + +/* integrity is the first 32 bits (in network order) of a sha-1 of all + * cell payloads that are relay cells that have been sent / delivered + * to the hop on the * circuit (the integrity is zeroed while doing + * each calculation) + */ +#define CELL_RELAY_INTEGRITY(c) (ntohl(*(uint32_t*)((c).payload+5))) +#define SET_CELL_RELAY_INTEGRITY(c,i) (*(uint32_t*)((c).payload+5) = htonl(i)) + +/* relay length is how many bytes are used in the cell payload past relay_header_size */ +#define CELL_RELAY_LENGTH(c) (ntohs(*(uint16_t*)((c).payload+9))) +#define SET_CELL_RELAY_LENGTH(c,len) (*(uint16_t*)((c).payload+9) = htons(len)) +#endif + +#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) + +/* cell definition */ +typedef struct { + uint16_t circ_id; + unsigned char command; + unsigned char payload[CELL_PAYLOAD_SIZE]; +} cell_t; + +typedef struct { + uint8_t command; + uint16_t recognized; + uint16_t stream_id; + char integrity[4]; + uint16_t length; +} relay_header_t; + +typedef struct buf_t buf_t; +typedef struct socks_request_t socks_request_t; + +struct connection_t { + + uint8_t type; + uint8_t state; + uint8_t wants_to_read; /* should we start reading again once + * the bandwidth throttler allows it? + */ + uint8_t wants_to_write; /* should we start writing again once + * the bandwidth throttler allows reads? + */ + int s; /* our socket */ + int poll_index; /* index of this conn into the poll_array */ + int marked_for_close; /* should we close this conn on the next + * iteration of the main loop? + */ + + buf_t *inbuf; + int inbuf_reached_eof; /* did read() return 0 on this conn? */ + time_t timestamp_lastread; /* when was the last time poll() said we could read? */ + + buf_t *outbuf; + 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; /* these two uniquely identify a router. Both in host order. */ + uint16_t port; /* if non-zero, they identify the guy 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 *onion_pkey; /* public RSA key for the other side's onions */ + crypto_pk_env_t *link_pkey; /* public RSA key for the other side's TLS */ + crypto_pk_env_t *identity_pkey; /* public RSA key for the other side's signing */ + char *nickname; + +/* Used only by OR connections: */ + tor_tls *tls; + 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.*/ + + /* bandwidth and receiver_bucket only used by ORs in OPEN state: */ + uint32_t bandwidth; /* connection bandwidth. */ + int receiver_bucket; /* when this hits 0, stop receiving. Every second we + * add 'bandwidth' to this, capping it at 10*bandwidth. + */ + +/* Used only by edge connections: */ + uint16_t stream_id; + struct connection_t *next_stream; /* points to the next stream at this edge, if any */ + struct crypt_path_t *cpath_layer; /* a pointer to which node in the circ this conn exits at */ + int package_window; /* how many more relay cells can i send into the circuit? */ + int deliver_window; /* how many more relay cells can end at me? */ + + int done_sending; /* for half-open connections; not used currently */ + int done_receiving; + char has_sent_end; /* for debugging: set once we've set the stream end, + and check in circuit_about_to_close_connection() */ + + /* Used only by AP connections */ + socks_request_t *socks_request; +}; + +typedef struct connection_t connection_t; + +#define EXIT_POLICY_ACCEPT 1 +#define EXIT_POLICY_REJECT 2 + +struct exit_policy_t { + char policy_type; + char *string; + uint32_t addr; + uint32_t msk; + uint16_t prt_min; + uint16_t prt_max; + + struct exit_policy_t *next; +}; + +/* config stuff we know about the other ORs in the network */ +typedef struct { + char *address; + char *nickname; + + uint32_t addr; /* all host order */ + uint16_t or_port; + uint16_t socks_port; + uint16_t dir_port; + + time_t published_on; + + crypto_pk_env_t *onion_pkey; /* public RSA key for onions */ + crypto_pk_env_t *link_pkey; /* public RSA key for TLS */ + crypto_pk_env_t *identity_pkey; /* public RSA key for signing */ + + int is_running; + + /* link info */ + uint32_t bandwidth; + struct exit_policy_t *exit_policy; +} routerinfo_t; + +#define MAX_ROUTERS_IN_DIR 1024 +typedef struct { + routerinfo_t **routers; + int n_routers; + char *software_versions; + time_t published_on; +} routerlist_t; + +struct crypt_path_t { + + /* crypto environments */ + crypto_cipher_env_t *f_crypto; + crypto_cipher_env_t *b_crypto; + + crypto_digest_env_t *f_digest; /* for integrity checking */ + crypto_digest_env_t *b_digest; + + crypto_dh_env_t *handshake_state; + + uint32_t addr; + uint16_t port; + + uint8_t state; +#define CPATH_STATE_CLOSED 0 +#define CPATH_STATE_AWAITING_KEYS 1 +#define CPATH_STATE_OPEN 2 + struct crypt_path_t *next; + struct crypt_path_t *prev; /* doubly linked list */ + + int package_window; + int deliver_window; +}; + +#define DH_KEY_LEN CRYPTO_DH_SIZE +#define ONIONSKIN_CHALLENGE_LEN (16+DH_KEY_LEN) +#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+20) + +typedef struct crypt_path_t crypt_path_t; + +typedef struct { + int desired_path_len; + char *chosen_exit; /* nickname of planned exit node */ +} cpath_build_state_t; + +/* struct for a path (circuit) through the network */ +struct circuit_t { + uint32_t n_addr; + uint16_t n_port; + connection_t *p_conn; + connection_t *n_conn; /* for the OR conn, if there is one */ + connection_t *p_streams; + connection_t *n_streams; + uint16_t next_stream_id; + int package_window; + int deliver_window; + + uint16_t p_circ_id; /* circuit identifiers */ + uint16_t n_circ_id; + + crypto_cipher_env_t *p_crypto; /* used only for intermediate hops */ + crypto_cipher_env_t *n_crypto; + + crypto_digest_env_t *p_digest; /* for integrity checking, */ + crypto_digest_env_t *n_digest; /* intermediate hops only */ + + cpath_build_state_t *build_state; + crypt_path_t *cpath; + + char onionskin[ONIONSKIN_CHALLENGE_LEN]; /* for storage while onionskin pending */ + time_t timestamp_created; + time_t timestamp_dirty; /* when the circuit was first used, or 0 if clean */ + + uint8_t state; + + void *next; +}; + +typedef struct circuit_t circuit_t; + +typedef struct { + char *LogLevel; + char *LogFile; + char *DebugLogFile; + char *DataDirectory; + char *RouterFile; + char *Nickname; + char *Address; + char *PidFile; + char *ExitNodes; + char *EntryNodes; + char *ExcludedNodes; + char *ExitPolicy; + char *SocksBindAddress; + char *ORBindAddress; + char *DirBindAddress; + char *RecommendedVersions; + char *User; + char *Group; + double PathlenCoinWeight; + int ORPort; + int SocksPort; + int DirPort; + int MaxConn; + int TrafficShaping; + int LinkPadding; + int IgnoreVersion; + int RunAsDaemon; + int DirRebuildPeriod; + int DirFetchPostPeriod; + int KeepalivePeriod; + int MaxOnionsPending; + int NewCircuitPeriod; + int TotalBandwidth; + int NumCpus; + int loglevel; +} or_options_t; + +/* XXX are these good enough defaults? */ +#define MAX_SOCKS_REPLY_LEN 256 +/* Not 256; addresses must fit in a begin cell. */ +#define MAX_SOCKS_ADDR_LEN 200 +struct socks_request_t { + char socks_version; + int replylen; + char reply[MAX_SOCKS_REPLY_LEN]; + char address[MAX_SOCKS_ADDR_LEN]; + uint16_t port; +}; + +/* all the function prototypes go here */ + +/********************************* buffers.c ***************************/ + +int find_on_inbuf(char *string, int string_len, buf_t *buf); + +buf_t *buf_new(); +buf_t *buf_new_with_capacity(size_t size); +void buf_free(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, int at_most, buf_t *buf, int *reached_eof); +int read_to_buf_tls(tor_tls *tls, int 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, int string_len, buf_t *buf); +int fetch_from_buf_http(buf_t *buf, + char **headers_out, int max_headerlen, + char **body_out, int max_bodylen); +int fetch_from_buf_socks(buf_t *buf, socks_request_t *req); + +/********************************* circuit.c ***************************/ + +void circuit_add(circuit_t *circ); +void circuit_remove(circuit_t *circ); +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn); +void circuit_free(circuit_t *circ); +void circuit_free_cpath(crypt_path_t *cpath); + +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_newest(connection_t *conn, int must_be_open); + +void circuit_expire_building(void); +int circuit_count_building(void); +int circuit_stream_is_being_handled(connection_t *conn); + +int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction); +int circuit_package_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction, crypt_path_t *layer_hint); + +void circuit_resume_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint); +int circuit_consider_stop_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint); +void circuit_consider_sending_sendme(circuit_t *circ, int edge_type, crypt_path_t *layer_hint); + +void circuit_close(circuit_t *circ); +void circuit_about_to_close_connection(connection_t *conn); + +void circuit_dump_by_conn(connection_t *conn, int severity); + +void circuit_expire_unused_circuits(void); +int circuit_launch_new(void); +void circuit_increment_failure_count(void); +void circuit_reset_failure_count(void); +int circuit_establish_circuit(void); +void circuit_n_conn_open(connection_t *or_conn); +int circuit_send_next_onion_skin(circuit_t *circ); +int circuit_extend(cell_t *cell, circuit_t *circ); +int circuit_finish_handshake(circuit_t *circ, char *reply); +int circuit_truncated(circuit_t *circ, crypt_path_t *layer); + +void assert_cpath_ok(const crypt_path_t *c); +void assert_cpath_layer_ok(const crypt_path_t *c); +void assert_circuit_ok(const circuit_t *c); + +extern unsigned long stats_n_relay_cells_relayed; +extern unsigned long stats_n_relay_cells_delivered; + +/********************************* 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 ***************************/ + +int getconfig(int argc, char **argv, or_options_t *options); + +/********************************* connection.c ***************************/ + +connection_t *connection_new(int type); +void connection_free(connection_t *conn); +void connection_free_all(void); + +int connection_create_listener(char *bindaddress, uint16_t bindport, int type); + +int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port); +int retry_all_connections(void); + +int connection_handle_read(connection_t *conn); +int connection_read_to_buf(connection_t *conn); + +int connection_fetch_from_buf(char *string, int len, connection_t *conn); +int connection_find_on_inbuf(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_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); + +int connection_receiver_bucket_should_increase(connection_t *conn); + +#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_send_destroy(uint16_t circ_id, connection_t *conn); + +int connection_process_inbuf(connection_t *conn); +int connection_finished_flushing(connection_t *conn); + +void assert_connection_ok(connection_t *conn, time_t now); + +/********************************* connection_edge.c ***************************/ + +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_process_inbuf(connection_t *conn); +int connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer); + +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, int relay_command, + void *payload, int payload_len, crypt_path_t *cpath_layer); + +int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_t *conn, + int edge_type, crypt_path_t *layer_hint); +int connection_edge_finished_flushing(connection_t *conn); + +int connection_edge_package_raw_inbuf(connection_t *conn); + +void connection_exit_connect(connection_t *conn); +int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit); +void connection_ap_attach_pending(void); + +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; + +void client_dns_init(void); +void client_dns_clean(void); + +/********************************* connection_or.c ***************************/ + +int connection_or_process_inbuf(connection_t *conn); +int connection_or_finished_flushing(connection_t *conn); + +void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router); +connection_t *connection_or_connect(routerinfo_t *router); + +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); +int connection_cpu_finished_flushing(connection_t *conn); +int connection_cpu_process_inbuf(connection_t *conn); +int cpuworker_main(void *data); +int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type, + void *task); + +/********************************* directory.c ***************************/ + +void directory_initiate_command(routerinfo_t *router, int command); +int connection_dir_process_inbuf(connection_t *conn); +int connection_dir_finished_flushing(connection_t *conn); + +/********************************* dns.c ***************************/ + +void dns_init(void); +int connection_dns_finished_flushing(connection_t *conn); +int connection_dns_process_inbuf(connection_t *conn); +void dns_cancel_pending_resolve(char *question, connection_t *onlyconn); +int dns_resolve(connection_t *exitconn); + +/********************************* main.c ***************************/ + +int connection_add(connection_t *conn); +int connection_remove(connection_t *conn); +void connection_set_poll_socket(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); +void connection_stop_writing(connection_t *conn); +void connection_start_writing(connection_t *conn); + +int main(int argc, char *argv[]); + +/********************************* onion.c ***************************/ + +int decide_circ_id_type(char *local_nick, char *remote_nick); + +int onion_pending_add(circuit_t *circ); +circuit_t *onion_next_task(void); +void onion_pending_remove(circuit_t *circ); + +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys); + +int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, + routerinfo_t **router_out); + +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, + 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); + +cpath_build_state_t *onion_new_cpath_build_state(void); + +/********************************* router.c ***************************/ + +void set_onion_key(crypto_pk_env_t *k); +crypto_pk_env_t *get_onion_key(void); +void set_identity_key(crypto_pk_env_t *k); +crypto_pk_env_t *get_identity_key(void); +crypto_pk_env_t *get_link_key(void); +int init_keys(void); + +void router_retry_connections(void); +void router_upload_desc_to_dirservers(void); +int router_compare_to_my_exit_policy(connection_t *conn); +const char *router_get_my_descriptor(void); +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(void); +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); +routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk); +routerinfo_t *router_get_by_nickname(char *nickname); +void router_get_routerlist(routerlist_t **prouterlist); +void routerinfo_free(routerinfo_t *router); +void router_mark_as_down(char *nickname); +int router_set_routerlist_from_file(char *routerfile); +int router_get_dir_hash(const char *s, char *digest); +int router_get_router_hash(const char *s, char *digest); +int router_set_routerlist_from_directory(const char *s, crypto_pk_env_t *pkey); +routerinfo_t *router_get_entry_from_string(const char **s); +int router_add_exit_policy_from_string(routerinfo_t *router, const char *s); +int router_supports_exit_address(uint32_t addr, uint16_t port, + routerinfo_t *router); +int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port, + struct exit_policy_t *policy); +int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port); +int router_exit_policy_rejects_all(routerinfo_t *router); + +/********************************* 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_init_from_directory_string(const char *dir); +void dirserv_free_descriptors(); +int dirserv_dump_directory_to_string(char *s, int maxlen, + crypto_pk_env_t *private_key); +void directory_set_dirty(); +size_t dirserv_get_directory(const char **cp); + + +#endif + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/router.c b/tags/tor-0_0_2pre19/src/or/router.c new file mode 100644 index 0000000000..af1309c9d9 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/router.c @@ -0,0 +1,524 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/************************************************************/ + +/* private keys */ +static crypto_pk_env_t *onionkey=NULL; +static crypto_pk_env_t *linkkey=NULL; +static crypto_pk_env_t *identitykey=NULL; + +void set_onion_key(crypto_pk_env_t *k) { + onionkey = k; +} + +crypto_pk_env_t *get_onion_key(void) { + assert(onionkey); + return onionkey; +} + +void set_link_key(crypto_pk_env_t *k) +{ + linkkey = k; +} + +crypto_pk_env_t *get_link_key(void) +{ + assert(linkkey); + return linkkey; +} + +void set_identity_key(crypto_pk_env_t *k) { + identitykey = k; +} + +crypto_pk_env_t *get_identity_key(void) { + assert(identitykey); + return identitykey; +} + +/************************************************************/ + +static crypto_pk_env_t *init_key_from_file(const char *fname) +{ + crypto_pk_env_t *prkey = NULL; + int fd = -1; + FILE *file = NULL; + + if (!(prkey = crypto_new_pk_env(CRYPTO_PK_RSA))) { + 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 key: %s", crypto_perror()); + 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: + assert(0); + } + + error: + if (prkey) + crypto_free_pk_env(prkey); + if (fd >= 0 && !file) + close(fd); + if (file) + fclose(file); + return NULL; +} + +int init_keys(void) { + char keydir[512]; + char fingerprint[FINGERPRINT_LEN+MAX_NICKNAME_LEN+3]; + char *cp; + const char *tmp, *mydesc; + crypto_pk_env_t *prkey; + + /* OP's don't need keys. Just initialize the TLS context.*/ + if (!options.ORPort) { + assert(!options.DirPort); + if (tor_tls_context_new(NULL, 0, NULL)<0) { + log_fn(LOG_ERR, "Error creating TLS context for OP."); + return -1; + } + return 0; + } + assert(options.DataDirectory); + if (strlen(options.DataDirectory) > (512-128)) { + log_fn(LOG_ERR, "DataDirectory is too long."); + return -1; + } + if (check_private_dir(options.DataDirectory, 1)) { + return -1; + } + sprintf(keydir,"%s/keys",options.DataDirectory); + 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. */ + strcpy(cp, "/identity.key"); + log_fn(LOG_INFO,"Reading/making identity key %s...",keydir); + prkey = init_key_from_file(keydir); + if (!prkey) return -1; + set_identity_key(prkey); + /* 2. Read onion key. Make it if none is found. */ + strcpy(cp, "/onion.key"); + log_fn(LOG_INFO,"Reading/making onion key %s...",keydir); + prkey = init_key_from_file(keydir); + if (!prkey) return -1; + set_onion_key(prkey); + + /* 3. Initialize link key and TLS context. */ + strcpy(cp, "/link.key"); + log_fn(LOG_INFO,"Reading/making link key %s...",keydir); + prkey = init_key_from_file(keydir); + if (!prkey) return -1; + set_link_key(prkey); + if (tor_tls_context_new(prkey, 1, options.Nickname) < 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. */ + if (!(router_get_my_descriptor())) { + log_fn(LOG_ERR, "Error initializing descriptor."); + return -1; + } + /* 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; + } + tmp = mydesc = router_get_my_descriptor(); + if (dirserv_add_descriptor(&tmp)) { + log(LOG_ERR, "Unable to add own descriptor to directory."); + return -1; + } + sprintf(keydir,"%s/router.desc", options.DataDirectory); + 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", options.DataDirectory); + log_fn(LOG_INFO,"Dumping fingerprint to %s...",keydir); + 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(!options.DirPort) + return 0; + /* 6. [dirserver only] load approved-routers file */ + sprintf(keydir,"%s/approved-routers", options.DataDirectory); + 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. [dirserver only] load old directory, if it's there */ + sprintf(keydir,"%s/cached-directory", options.DataDirectory); + 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_init_from_directory_string(cp) < 0) { + log_fn(LOG_ERR, "Cached directory %s is corrupt", keydir); + free(cp); + return -1; + } + free(cp); + } + /* success */ + return 0; +} + +/************************************************************/ + +static routerinfo_t *desc_routerinfo = NULL; /* my descriptor */ +static char descriptor[8192]; /* string representation of my descriptor */ + +void router_retry_connections(void) { + int i; + routerinfo_t *router; + routerlist_t *rl; + + router_get_routerlist(&rl); + for (i=0;in_routers;i++) { + router = rl->routers[i]; + if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) { + /* not in the list */ + log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port); + connection_or_connect(router); + } + } +} + +void router_upload_desc_to_dirservers(void) { + int i; + routerinfo_t *router; + routerlist_t *rl; + + router_get_routerlist(&rl); + if(!rl) + return; + + if (!router_get_my_descriptor()) { + log_fn(LOG_WARN, "No descriptor; skipping upload"); + return; + } + + for(i=0;in_routers;i++) { + router = rl->routers[i]; + if(router->dir_port > 0) + directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD); + } +} + +static void router_add_exit_policy_from_config(routerinfo_t *router) { + char *s = options.ExitPolicy, *e; + int last=0; + char line[1024]; + + if(!s) { + log_fn(LOG_INFO,"No exit policy configured. Ok."); + return; /* nothing to see here */ + } + if(!*s) { + log_fn(LOG_INFO,"Exit policy is empty. Ok."); + return; /* nothing to see here */ + } + + 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); + if(router_add_exit_policy_from_string(router,line) < 0) + log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line); + if(last) + return; + s = e+1; + } +} + +/* Return 0 if my exit policy says to allow connection to conn. + * Else return -1. + */ +int router_compare_to_my_exit_policy(connection_t *conn) { + assert(desc_routerinfo); + assert(conn->addr); /* make sure it's resolved to something. this + way we can't get a 'maybe' below. */ + + if (router_compare_addr_to_exit_policy(conn->addr, conn->port, + desc_routerinfo->exit_policy) == 0) + return 0; + else + return -1; +} + +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; +} + +int router_rebuild_descriptor(void) { + routerinfo_t *ri; + char localhostname[256]; + char *address = options.Address; + + if(!address) { /* if not specified in config, we find a default */ + if(gethostname(localhostname,sizeof(localhostname)) < 0) { + log_fn(LOG_WARN,"Error obtaining local hostname"); + return -1; + } + address = localhostname; + if(!strchr(address,'.')) { + 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; + } + } + ri = tor_malloc(sizeof(routerinfo_t)); + ri->address = tor_strdup(address); + ri->nickname = tor_strdup(options.Nickname); + /* No need to set 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()); + ri->link_pkey = crypto_pk_dup_key(get_link_key()); + ri->identity_pkey = crypto_pk_dup_key(get_identity_key()); + ri->bandwidth = options.TotalBandwidth; + ri->exit_policy = NULL; /* zero it out first */ + router_add_exit_policy_from_config(ri); + 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; +} + +static 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 +int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, + crypto_pk_env_t *ident_key) { + char *onion_pkey; + char *link_pkey; + char *identity_pkey; + struct in_addr in; + char platform[256]; + char digest[20]; + char signature[128]; + char published[32]; + int onion_pkeylen, link_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 + + get_platform_str(platform, sizeof(platform)); + + 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; + } + + 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; + } + + 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!"); + return -1; + } + + if(crypto_pk_write_public_key_to_string(router->link_pkey, + &link_pkey,&link_pkeylen)<0) { + log_fn(LOG_WARN,"write link_pkey to string failed!"); + return -1; + } + strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on)); + + result = snprintf(s, maxlen, + "router %s %s %d %d %d %d\n" + "platform %s\n" + "published %s\n" + "onion-key\n%s" + "link-key\n%s" + "signing-key\n%s", + router->nickname, + router->address, + router->or_port, + router->socks_port, + router->dir_port, + (int) router->bandwidth, + platform, + published, + onion_pkey, link_pkey, identity_pkey); + + free(onion_pkey); + free(link_pkey); + free(identity_pkey); + + if(result < 0 || result >= maxlen) { + /* apparently different glibcs do different things on snprintf error.. so check both */ + return -1; + } + written = result; + + for(tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) { + in.s_addr = htonl(tmpe->addr); + 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) { + 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 == 1 && tmpe->prt_max == 65535) { + if (written > maxlen-4) + return -1; + strcat(s+written, ":*\n"); + written += 3; + } else if (tmpe->prt_min == tmpe->prt_max) { + result = snprintf(s+written, maxlen-written, ":%d\n", tmpe->prt_min); + if (result<0 || result+written > maxlen) + return -1; + written += result; + } else { + 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; + + 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_get_entry_from_string(&cp); + 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_2pre19/src/or/routerlist.c b/tags/tor-0_0_2pre19/src/or/routerlist.c new file mode 100644 index 0000000000..01e863dca2 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/routerlist.c @@ -0,0 +1,1280 @@ +/* Copyright 2001-2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n" +#define OR_PUBLICKEY_END_TAG "-----END RSA PUBLIC KEY-----\n" +#define OR_SIGNATURE_BEGIN_TAG "-----BEGIN SIGNATURE-----\n" +#define OR_SIGNATURE_END_TAG "-----END SIGNATURE-----\n" + +#define _GNU_SOURCE +/* XXX this is required on rh7 to make strptime not complain. how bad + * is this for portability? + */ + +#include "or.h" + +/****************************************************************************/ + +static routerlist_t *routerlist = NULL; /* router array */ +extern or_options_t options; /* command-line and config-file options */ + +/****************************************************************************/ + +/* Enumeration of possible token types. The ones starting with K_ correspond + * to directory 'keywords'. _SIGNATURE and _PUBLIC_KEY are self-explanitory. + * _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, + K_ROUTER_SIGNATURE, + K_PUBLISHED, + K_RUNNING_ROUTERS, + K_PLATFORM, + _SIGNATURE, + _PUBLIC_KEY, + _ERR, + _EOF, + _NIL +} directory_keyword; + +/* Struct containing a directory token. */ +#define MAX_ARGS 1024 +typedef struct directory_token_t { + directory_keyword tp; /* Type of the token. */ + union { + struct { + char *args[MAX_ARGS+1]; /* For K_xxx tokens only: an array and count */ + int n_args; /* of arguments provided on the same line */ + } cmd; + char *signature; /* For _SIGNATURE tokens only. */ + char *error; /* For _ERR tokens only. */ + crypto_pk_env_t *public_key; /* For _PUBLIC_KEY tokens only. */ + } val; +} directory_token_t; + +/****************************************************************************/ + + + +/* static function prototypes */ +static int router_set_routerlist_from_string(const char *s); +static int +router_get_list_from_string_impl(const char **s, routerlist_t **dest, + int n_good_nicknames, + const char **good_nickname_lst); +static int +router_get_routerlist_from_directory_impl(const char *s, routerlist_t **dest, + crypto_pk_env_t *pkey); +static int router_add_exit_policy(routerinfo_t *router, + directory_token_t *tok); +static int router_resolve_routerlist(routerlist_t *dir); + + + +static int _router_get_next_token(const char **s, directory_token_t *tok); +#ifdef DEBUG_ROUTER_TOKENS +static int router_get_next_token(const char **s, directory_token_t *tok); +#else +#define router_get_next_token _router_get_next_token +#endif +static int router_get_hash_impl(const char *s, char *digest, + const char *start_str, + const char *end_str); +static void router_release_token(directory_token_t *tok); + + +/****************************************************************************/ + +/* pick a random running router with a positive dir_port */ +routerinfo_t *router_pick_directory_server(void) { + int i; + routerinfo_t *router, *dirserver=NULL; + smartlist_t *sl; + + if(!routerlist) + return NULL; + + sl = smartlist_create(MAX_ROUTERS_IN_DIR); + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if(router->dir_port > 0 && router->is_running) + smartlist_add(sl, router); + } + + router = smartlist_choose(sl); + smartlist_free(sl); + + if(router) + return router; + log_fn(LOG_INFO,"No dirservers are reachable. Trying them all again."); + /* no running dir servers found? go through and mark them all as up, + * and we'll cycle through the list again. */ + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if(router->dir_port > 0) { + router->is_running = 1; + dirserver = router; + } + } + if(!dirserver) + log_fn(LOG_WARN,"No dirservers in directory! Returning NULL."); + return dirserver; +} + +void router_add_running_routers_to_smartlist(smartlist_t *sl) { + routerinfo_t *router; + int i; + + if(!routerlist) + return; + + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if(router->is_running && + (!options.ORPort || + connection_twin_get_by_addr_port(router->addr, router->or_port) )) + smartlist_add(sl, router); + } +} + +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + assert(routerlist); + + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if ((router->addr == addr) && (router->or_port == port)) + return router; + } + return NULL; +} + +routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk) +{ + int i; + routerinfo_t *router; + + assert(routerlist); + + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if (0 == crypto_pk_cmp_keys(router->link_pkey, pk)) + return router; + } + return NULL; +} + +routerinfo_t *router_get_by_nickname(char *nickname) +{ + int i; + routerinfo_t *router; + + assert(routerlist); + + for(i=0;in_routers;i++) { + router = routerlist->routers[i]; + if (0 == strcmp(router->nickname, nickname)) + return router; + } + return NULL; +} + +/* a way to access routerlist outside this file */ +void router_get_routerlist(routerlist_t **prouterlist) { + *prouterlist = routerlist; +} + +/* delete a router from memory */ +void routerinfo_free(routerinfo_t *router) +{ + struct exit_policy_t *e; + + if (!router) + return; + + tor_free(router->address); + tor_free(router->nickname); + if (router->onion_pkey) + crypto_free_pk_env(router->onion_pkey); + if (router->link_pkey) + crypto_free_pk_env(router->link_pkey); + if (router->identity_pkey) + crypto_free_pk_env(router->identity_pkey); + while (router->exit_policy) { + e = router->exit_policy; + router->exit_policy = e->next; + tor_free(e->string); + free(e); + } + free(router); +} + +static void routerlist_free(routerlist_t *rl) +{ + int i; + for (i = 0; i < rl->n_routers; ++i) + routerinfo_free(rl->routers[i]); + tor_free(rl->routers); + tor_free(rl->software_versions); + free(rl); +} + +void router_mark_as_down(char *nickname) { + routerinfo_t *router = router_get_by_nickname(nickname); + 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; +} + +/* ------------------------------------------------------------ */ + +/* Replace the current router list with the one stored in 'routerfile'. */ +int router_set_routerlist_from_file(char *routerfile) +{ + char *string; + + string = read_file_to_str(routerfile); + if(!string) { + log_fn(LOG_WARN,"Failed to load routerfile %s.",routerfile); + return -1; + } + + if(router_set_routerlist_from_string(string) < 0) { + log_fn(LOG_WARN,"The routerfile itself was corrupt."); + free(string); + return -1; + } + + free(string); + return 0; +} + + +/* Helper function: read routerinfo elements from s, and throw out the + * ones that don't parse and resolve. Replace the current + * routerlist. */ +static int router_set_routerlist_from_string(const char *s) +{ + if (router_get_list_from_string_impl(&s, &routerlist, -1, NULL)) { + log(LOG_WARN, "Error parsing router file"); + return -1; + } + if (router_resolve_routerlist(routerlist)) { + log(LOG_WARN, "Error resolving routerlist"); + return -1; + } + return 0; +} + +/* 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"); +} + +/* return 0 if myversion is in versionlist. Else return -1. (versionlist + * contains a comma-separated list of versions.) */ +int compare_recommended_versions(const char *myversion, + const char *versionlist) { + int len_myversion = strlen(myversion); + char *comma; + const char *end = versionlist + strlen(versionlist); + + log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, versionlist); + + for(;;) { + comma = strchr(versionlist, ','); + if( ((comma ? comma : end) - versionlist == len_myversion) && + !strncmp(versionlist, myversion, len_myversion)) + /* only do strncmp if the length matches */ + return 0; /* success, it's there */ + if(!comma) + return -1; /* nope */ + versionlist = comma+1; + } +} + +/* Replace the current routerlist with the routers stored in the directory + * 's'. If pkey is provided, make sure that 's' is signed with pkey. + */ +int router_set_routerlist_from_directory(const char *s, crypto_pk_env_t *pkey) +{ + if (router_get_routerlist_from_directory_impl(s, &routerlist, pkey)) { + log_fn(LOG_WARN, "Couldn't parse directory."); + return -1; + } + if (router_resolve_routerlist(routerlist)) { + log_fn(LOG_WARN, "Error resolving routerlist"); + return -1; + } + if (compare_recommended_versions(VERSION, routerlist->software_versions) < 0) { + log(options.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(routerlist->software_versions,',') ? "one of " : "", + routerlist->software_versions); + if(options.IgnoreVersion) { + log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so."); + } else { + fflush(0); + exit(0); + } + } + + return 0; +} + +/* Helper function: resolve the hostname for 'router' */ +static int +router_resolve(routerinfo_t *router) +{ + struct hostent *rent; + + rent = (struct hostent *)gethostbyname(router->address); + if (!rent) { + log_fn(LOG_WARN,"Could not get address for router %s.",router->address); + return -1; + } + assert(rent->h_length == 4); + memcpy(&router->addr, rent->h_addr,rent->h_length); + router->addr = ntohl(router->addr); /* get it back into host order */ + + return 0; +} + +/* Helper function: resolve every router in rl. */ +static int +router_resolve_routerlist(routerlist_t *rl) +{ + int i, max, remove; + if (!rl) + rl = routerlist; + + max = rl->n_routers; + for (i = 0; i < max; ++i) { + remove = 0; + if (router_resolve(rl->routers[i])) { + log_fn(LOG_WARN, "Couldn't resolve router %s; not using", + rl->routers[i]->address); + remove = 1; + } else if (options.Nickname && + !strcmp(rl->routers[i]->nickname, options.Nickname)) { + remove = 1; + } + if (remove) { + routerinfo_free(rl->routers[i]); + rl->routers[i] = rl->routers[--max]; + --rl->n_routers; + --i; + } + } + + return 0; +} + +/* Addr is 0 for "IP unknown". + * + * Returns -1 for 'rejected', 0 for accepted, 1 for 'maybe' (since IP is + * unknown. + */ +int router_supports_exit_address(uint32_t addr, uint16_t port, + routerinfo_t *router) +{ + return router_compare_addr_to_exit_policy(addr, port, router->exit_policy); +} + +/* Addr is 0 for "IP unknown". + * + * 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 match = 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); + if (!addr) { + /* Address is unknown. */ + if (tmpe->msk == 0 && (port >= tmpe->prt_min && port <= tmpe->prt_max)) { + /* The exit policy is accept/reject *:port */ + match = 1; + } else if (port >= tmpe->prt_min && port <= tmpe->prt_max && + tmpe->policy_type == EXIT_POLICY_REJECT) { + /* The exit policy is reject ???:port */ + maybe_reject = 1; + } + } else { + /* Address is known */ + if ( (addr & tmpe->msk) == (tmpe->addr & tmpe->msk) && + (port >= tmpe->prt_min && port <= tmpe->prt_max) ) { + /* Exact match for the policy */ + match = 1; + } + } + if (match) { + in.s_addr = htonl(addr); + log_fn(LOG_INFO,"Address %s:%d matches exit policy '%s'", + inet_ntoa(in), port, tmpe->string); + if(tmpe->policy_type == EXIT_POLICY_ACCEPT) + return 0; + else + return -1; + } + } + if (maybe_reject) + return 1; + else + return 0; /* accept all by default. */ +} + +/* 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;in_routers;i++) { + router = routerlist->routers[i]; + if (router->is_running && router_compare_addr_to_exit_policy(addr, + port, router->exit_policy) >= 0) + return 0; /* this one could be ok. good enough. */ + } + return 1; /* all will reject. */ +} + +int router_exit_policy_rejects_all(routerinfo_t *router) { + if (router_compare_addr_to_exit_policy(0, 0, router->exit_policy) < 0) + return 1; /* yes, rejects all */ + else + return 0; /* no, might accept some */ +} + +/* Helper function: parse a directory from 's' 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. + */ +static int +router_get_routerlist_from_directory_impl(const char *s, routerlist_t **dest, + crypto_pk_env_t *pkey) +{ + directory_token_t tok; + char digest[20]; + char signed_digest[128]; + routerlist_t *new_dir = NULL; + char *versions = NULL; + struct tm published; + time_t published_on; + char *good_nickname_lst[1024]; + int n_good_nicknames = 0; + int i; + + /* Local helper macro: get the next token from s (advancing s) and + * bail on failure. */ +#define NEXT_TOK() \ + do { \ + if (router_get_next_token(&s, &tok)) { \ + log_fn(LOG_WARN, "Error reading directory: %s", tok.val.error); \ + goto err; \ + } } while (0) + /* Local helper macro: bail if the most recently read token is not of + * the given type. */ +#define TOK_IS(type,name) \ + do { \ + if (tok.tp != type) { \ + router_release_token(&tok); \ + log_fn(LOG_WARN, "Error reading directory: expected %s", name); \ + goto err; \ + } } while(0) + /* Local helper macro: Number of args in most recent token. */ +#define N_ARGS tok.val.cmd.n_args + /* Local helper macro: Array of args to most recent token. */ +#define ARGS tok.val.cmd.args + + tok.tp = _NIL; + + if (router_get_dir_hash(s, digest)) { + log_fn(LOG_WARN, "Unable to compute digest of directory"); + goto err; + } + log(LOG_DEBUG,"Received directory hashes to %02x:%02x:%02x:%02x", + ((int)digest[0])&0xff,((int)digest[1])&0xff, + ((int)digest[2])&0xff,((int)digest[3])&0xff); + + NEXT_TOK(); + TOK_IS(K_SIGNED_DIRECTORY, "signed-directory"); + + NEXT_TOK(); + TOK_IS(K_PUBLISHED, "published"); + assert(N_ARGS == 1); + if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) { + log_fn(LOG_WARN, "Published time was unparseable"); goto err; + } + published_on = tor_timegm(&published); + + NEXT_TOK(); + TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software"); + if (N_ARGS != 1) { + log_fn(LOG_WARN, "Invalid recommended-software line"); + goto err; + } + versions = ARGS[0]; + tok.val.cmd.n_args = 0; /* Don't let the versions string get freed. */ + + NEXT_TOK(); + TOK_IS(K_RUNNING_ROUTERS, "running-routers"); + n_good_nicknames = N_ARGS; + memcpy(good_nickname_lst, ARGS, n_good_nicknames*sizeof(char *)); + N_ARGS = 0; /* Don't free the strings in good_nickname_lst. */ + + /* Read the router list from s, advancing s up past the end of the last + * router. */ + if (router_get_list_from_string_impl(&s, &new_dir, + n_good_nicknames, + (const char**)good_nickname_lst)) { + log_fn(LOG_WARN, "Error reading routers from directory"); + goto err; + } + new_dir->software_versions = versions; versions = NULL; + new_dir->published_on = published_on; + + NEXT_TOK(); + TOK_IS(K_DIRECTORY_SIGNATURE, "directory-signature"); + NEXT_TOK(); + TOK_IS(_SIGNATURE, "signature"); + if (pkey) { + if (crypto_pk_public_checksig(pkey, tok.val.signature, 128, signed_digest) + != 20) { + log_fn(LOG_WARN, "Error reading directory: invalid signature."); + goto err; + } + log(LOG_DEBUG,"Signed directory hash starts %02x:%02x:%02x:%02x", + ((int)signed_digest[0])&0xff,((int)signed_digest[1])&0xff, + ((int)signed_digest[2])&0xff,((int)signed_digest[3])&0xff); + if (memcmp(digest, signed_digest, 20)) { + log_fn(LOG_WARN, "Error reading directory: signature does not match."); + goto err; + } + } + + NEXT_TOK(); + TOK_IS(_EOF, "end of directory"); + + if (*dest) + routerlist_free(*dest); + *dest = new_dir; + + return 0; + + err: + router_release_token(&tok); + if (new_dir) + routerlist_free(new_dir); + tor_free(versions); + for (i = 0; i < n_good_nicknames; ++i) { + tor_free(good_nickname_lst[i]); + } + return -1; +#undef NEXT_TOK +#undef TOK_IS +#undef ARGS +#undef N_ARGS +} + +/* Helper function: Given a string *s containing a concatenated + * sequence of router descriptors, parses them and stores the result + * in *dest. If good_nickname_lst is provided, then routers whose + * nicknames are not listed are marked as nonrunning. Advances *s to + * a point immediately following the last router entry. Returns 0 on + * success and -1 on failure. + */ +static int +router_get_list_from_string_impl(const char **s, routerlist_t **dest, + int n_good_nicknames, + const char **good_nickname_lst) +{ + routerinfo_t *router; + routerinfo_t **rarray; + int rarray_len = 0; + int i; + + assert(s && *s); + + rarray = (routerinfo_t **) + tor_malloc((sizeof(routerinfo_t *))*MAX_ROUTERS_IN_DIR); + + 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; + router = router_get_entry_from_string(s); + if (!router) { + log_fn(LOG_WARN, "Error reading router"); + for(i=0;i= MAX_ROUTERS_IN_DIR) { + log_fn(LOG_WARN, "too many routers"); + routerinfo_free(router); + continue; + } + if (n_good_nicknames>=0) { + router->is_running = 0; + for (i = 0; i < n_good_nicknames; ++i) { + if (0==strcasecmp(good_nickname_lst[i], router->nickname)) { + router->is_running = 1; + break; + } + } + } else { + router->is_running = 1; /* start out assuming all dirservers are up */ + } + rarray[rarray_len++] = router; + log_fn(LOG_DEBUG,"just added router #%d.",rarray_len); + } + + if (*dest) + routerlist_free(*dest); + *dest = (routerlist_t *)tor_malloc(sizeof(routerlist_t)); + (*dest)->routers = rarray; + (*dest)->n_routers = rarray_len; + (*dest)->software_versions = NULL; + return 0; +} + + +/* Helper function: reads a single router entry from *s, and advances + * *s so it points to just after the router it just read. + * mallocs a new router and returns it if all goes well, else returns + * NULL. + */ +routerinfo_t *router_get_entry_from_string(const char**s) { + routerinfo_t *router = NULL; + char signed_digest[128]; + char digest[128]; + directory_token_t _tok; + directory_token_t *tok = &_tok; + struct tm published; + int t; + + /* Helper macro: read the next token from *s, advance *s, and bail + if there's an error */ +#define NEXT_TOKEN() \ + do { if (router_get_next_token(s, tok)) { \ + log_fn(LOG_WARN, "Error reading directory: %s", tok->val.error);\ + goto err; \ + } } while(0) + +#define ARGS tok->val.cmd.args +#define N_ARGS tok->val.cmd.n_args + + _tok.tp = _NIL; + + if (router_get_router_hash(*s, digest) < 0) { + log_fn(LOG_WARN, "Couldn't compute router hash."); + return NULL; + } + + NEXT_TOKEN(); + + 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 = router->link_pkey = NULL; + + if (N_ARGS != 6) { + log_fn(LOG_WARN,"Wrong # of arguments to \"router\""); + goto err; + } + router->nickname = tor_strdup(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; + } + + /* read router.address */ + router->address = tor_strdup(ARGS[1]); + router->addr = 0; + + /* Read router->or_port */ + router->or_port = atoi(ARGS[2]); + if(!router->or_port) { + log_fn(LOG_WARN,"or_port unreadable or 0. Failing."); + goto err; + } + + /* Router->socks_port */ + router->socks_port = atoi(ARGS[3]); + + /* Router->dir_port */ + router->dir_port = atoi(ARGS[4]); + + /* Router->bandwidth */ + router->bandwidth = atoi(ARGS[5]); + if (!router->bandwidth) { + log_fn(LOG_WARN,"bandwidth unreadable or 0. Failing."); + goto err; + } + + log_fn(LOG_DEBUG,"or_port %d, socks_port %d, dir_port %d, bandwidth %u.", + router->or_port, router->socks_port, router->dir_port, + (unsigned) router->bandwidth); + + /* XXX Later, require platform before published. */ + NEXT_TOKEN(); + if (tok->tp == K_PLATFORM) { + NEXT_TOKEN(); + } + + if (tok->tp != K_PUBLISHED) { + log_fn(LOG_WARN, "Missing published time"); goto err; + } + assert(N_ARGS == 1); + if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) { + log_fn(LOG_WARN, "Published time was unparseable"); goto err; + } + router->published_on = tor_timegm(&published); + + NEXT_TOKEN(); + if (tok->tp != K_ONION_KEY) { + log_fn(LOG_WARN, "Missing onion-key"); goto err; + } + NEXT_TOKEN(); + if (tok->tp != _PUBLIC_KEY) { + log_fn(LOG_WARN, "Missing onion key"); goto err; + } /* XXX Check key length */ + router->onion_pkey = tok->val.public_key; + + NEXT_TOKEN(); + if (tok->tp != K_LINK_KEY) { + log_fn(LOG_WARN, "Missing link-key"); goto err; + } + NEXT_TOKEN(); + if (tok->tp != _PUBLIC_KEY) { + log_fn(LOG_WARN, "Missing link key"); goto err; + } /* XXX Check key length */ + router->link_pkey = tok->val.public_key; + + NEXT_TOKEN(); + if (tok->tp != K_SIGNING_KEY) { + log_fn(LOG_WARN, "Missing signing-key"); goto err; + } + NEXT_TOKEN(); + if (tok->tp != _PUBLIC_KEY) { + log_fn(LOG_WARN, "Missing signing key"); goto err; + } + router->identity_pkey = tok->val.public_key; + + NEXT_TOKEN(); + while (tok->tp == K_ACCEPT || tok->tp == K_REJECT) { + router_add_exit_policy(router, tok); + NEXT_TOKEN(); + } + + if (tok->tp != K_ROUTER_SIGNATURE) { + log_fn(LOG_WARN,"Missing router signature"); + goto err; + } + NEXT_TOKEN(); + if (tok->tp != _SIGNATURE) { + log_fn(LOG_WARN,"Missing router signature"); + goto err; + } + assert (router->identity_pkey); + + if ((t=crypto_pk_public_checksig(router->identity_pkey, tok->val.signature, + 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; + } + + router_release_token(tok); /* free the signature */ + return router; + +err: + router_release_token(tok); + routerinfo_free(router); + return NULL; +#undef ARGS +#undef N_ARGS +#undef NEXT_TOKEN +} + +/* Parse the exit policy in the string 's' and add it to 'router'. + */ +int +router_add_exit_policy_from_string(routerinfo_t *router, const char *s) +{ + directory_token_t tok; + const char *cp; + char *tmp; + int r; + int len, idx; + + tok.tp = _NIL; + + /* *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'; + if (router_get_next_token(&cp, &tok)) { + log_fn(LOG_WARN, "Error reading exit policy: %s", tok.val.error); + free(tmp); + return -1; + } + if (tok.tp != K_ACCEPT && tok.tp != K_REJECT) { + log_fn(LOG_WARN, "Expected 'accept' or 'reject'."); + free(tmp); + return -1; + } + + /* Now that we've gotten an exit policy, add it to the router. */ + r = router_add_exit_policy(router, &tok); + free(tmp); + return r; +} + +/* 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 int router_add_exit_policy(routerinfo_t *router, + directory_token_t *tok) { + + struct exit_policy_t *tmpe, *newe; + struct in_addr in; + char *arg, *address, *mask, *port, *endptr; + int bits; + + assert(tok->tp == K_REJECT || tok->tp == K_ACCEPT); + + if (tok->val.cmd.n_args != 1) + return -1; + arg = tok->val.cmd.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); + + 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 (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 (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 = 1; + newe->prt_max = 65535; + } else { + endptr = NULL; + newe->prt_min = strtol(port, &endptr, 10); + if (*endptr == '-') { + port = endptr+1; + endptr = NULL; + newe->prt_max = 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); + + /* now link newe onto the end of exit_policy */ + + if(!router->exit_policy) { + router->exit_policy = newe; + return 0; + } + + for(tmpe=router->exit_policy; tmpe->next; tmpe=tmpe->next) ; + tmpe->next = newe; + + return 0; + +policy_read_failed: + assert(newe->string); + log_fn(LOG_WARN,"Couldn't parse line '%s'. Dropping", newe->string); + tor_free(newe->string); + free(newe); + return -1; +} + +/* ------------------------------------------------------------ */ +/* Tokenizer for router descriptors and directories. */ + +/* Every keyword takes either... */ +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; + +/* Table mapping keywods to token value and to argument rules. */ +static struct { char *t; int v; arg_syntax s; } token_table[] = { + { "accept", K_ACCEPT, ARGS }, + { "directory-signature", K_DIRECTORY_SIGNATURE, NO_ARGS }, + { "reject", K_REJECT, ARGS }, + { "router", K_ROUTER, ARGS }, + { "recommended-software", K_RECOMMENDED_SOFTWARE, ARGS }, + { "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS }, + { "signing-key", K_SIGNING_KEY, NO_ARGS }, + { "onion-key", K_ONION_KEY, NO_ARGS }, + { "link-key", K_LINK_KEY, NO_ARGS }, + { "router-signature", K_ROUTER_SIGNATURE, NO_ARGS }, + { "published", K_PUBLISHED, CONCAT_ARGS }, + { "running-routers", K_RUNNING_ROUTERS, ARGS }, + { "platform", K_PLATFORM, ARGS }, + { NULL, -1 } +}; + +/* Free any malloced resources allocated for a token. Does not free + * the token itself. + */ +static void +router_release_token(directory_token_t *tok) +{ + int i; + switch (tok->tp) + { + case _SIGNATURE: + free(tok->val.signature); + break; + case _PUBLIC_KEY: + crypto_free_pk_env(tok->val.public_key); + break; + case _ERR: + case _EOF: + case _NIL: + break; + default: + for (i = 0; i < tok->val.cmd.n_args; ++i) { + tor_free(tok->val.cmd.args[i]); + } + } + tok->tp = _NIL; +} + +/* Helper function: read the next token from *s, and stores it into *tok. + * If *tok already contains a token (tok->tp != _NIL), free the resources + * held by *tok. Advance *s to a point immediately after the token. + * + * + */ +static int +_router_get_next_token(const char **s, directory_token_t *tok) { + const char *next; + crypto_pk_env_t *pkey = NULL; + char *signature = NULL; + int i, done; + + tok->tp = _ERR; + tok->val.error = ""; + + router_release_token(tok); + + *s = eat_whitespace(*s); + if (!**s) { + tok->tp = _EOF; + return 0; + } else if (**s == '-') { + next = strchr(*s, '\n'); + if (! next) { tok->val.error = "No newline at EOF"; return -1; } + ++next; + if (! strncmp(*s, OR_PUBLICKEY_BEGIN_TAG, next-*s)) { + /* We have a ----BEGIN PUBLIC KEY----- */ + next = strstr(*s, OR_PUBLICKEY_END_TAG); + if (!next) { tok->val.error = "No public key end tag found"; return -1; } + next = strchr(next, '\n'); /* Part of OR_PUBLICKEY_END_TAG; can't fail.*/ + ++next; + if (!(pkey = crypto_new_pk_env(CRYPTO_PK_RSA))) + return -1; + if (crypto_pk_read_public_key_from_string(pkey, *s, next-*s)) { + crypto_free_pk_env(pkey); + tok->val.error = "Couldn't parse public key."; + return -1; + } + tok->tp = _PUBLIC_KEY; + tok->val.public_key = pkey; + *s = next; + return 0; + } else if (! strncmp(*s, OR_SIGNATURE_BEGIN_TAG, next-*s)) { + /* We have a -----BEGIN SIGNATURE----- */ + /* Advance past newline; can't fail. */ + *s = strchr(*s, '\n'); + ++*s; + /* Find end of base64'd data */ + next = strstr(*s, OR_SIGNATURE_END_TAG); + if (!next) { tok->val.error = "No signature end tag found"; return -1; } + + signature = tor_malloc(256); + i = base64_decode(signature, 256, *s, next-*s); + if (i<0) { + free(signature); + tok->val.error = "Error decoding signature."; return -1; + } else if (i != 128) { + free(signature); + tok->val.error = "Bad length on decoded signature."; return -1; + } + tok->tp = _SIGNATURE; + tok->val.signature = signature; + + next = strchr(next, '\n'); /* Part of OR_SIGNATURE_END_TAG; can't fail.*/ + *s = next+1; + return 0; + } else { + tok->val.error = "Unrecognized begin line"; return -1; + } + } else { + next = find_whitespace(*s); + if (!next) { + tok->val.error = "Unexpected EOF"; return -1; + } + /* It's a keyword... but which one? */ + 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; + + if (token_table[i].s == ARGS) { + /* This keyword takes multiple arguments. */ + i = 0; + done = (*next == '\n'); + *s = eat_whitespace_no_nl(next); + while (**s != '\n' && i < MAX_ARGS && !done) { + next = find_whitespace(*s); + if (*next == '\n') + done = 1; + tok->val.cmd.args[i++] = tor_strndup(*s,next-*s); + *s = eat_whitespace_no_nl(next+1); + } + tok->val.cmd.n_args = i; + if (i >= MAX_ARGS) { + /* XXX free args[0..i] */ + tok->tp = _ERR; + tok->val.error = "Too many arguments"; return -1; + } + } else if (token_table[i].s == CONCAT_ARGS) { + /* The keyword takes the line as a single argument */ + *s = eat_whitespace_no_nl(next); + next = strchr(*s, '\n'); + if (!next) { + tok->tp = _ERR; + tok->val.error = "Unexpected EOF"; return -1; + } + tok->val.cmd.args[0] = tor_strndup(*s,next-*s); + tok->val.cmd.n_args = 1; + *s = eat_whitespace_no_nl(next+1); + } else { + /* The keyword takes no arguments. */ + *s = eat_whitespace_no_nl(next); + if (**s != '\n') { + tok->tp = _ERR; + tok->val.error = "Unexpected arguments"; return -1; + } + tok->val.cmd.n_args = 0; + *s = eat_whitespace_no_nl(*s+1); + } + return 0; + } + } + tok->val.error = "Unrecognized command"; return -1; + } +} + +#ifdef DEBUG_ROUTER_TOKENS +static void +router_dump_token(directory_token_t *tok) { + int i; + switch(tok->tp) + { + case _SIGNATURE: + puts("(signature)"); + return; + case _PUBLIC_KEY: + puts("(public key)"); + return; + case _ERR: + printf("(Error: %s\n)", tok->val.error); + return; + case _EOF: + puts("EOF"); + return; + case K_ACCEPT: printf("Accept"); break; + case K_DIRECTORY_SIGNATURE: printf("Directory-Signature"); break; + case K_REJECT: printf("Reject"); break; + case K_RECOMMENDED_SOFTWARE: printf("Server-Software"); break; + case K_ROUTER: printf("Router"); break; + case K_SIGNED_DIRECTORY: printf("Signed-Directory"); break; + case K_SIGNING_KEY: printf("Signing-Key"); break; + case K_ONION_KEY: printf("Onion-key"); break; + case K_LINK_KEY: printf("Link-key"); break; + case K_ROUTER_SIGNATURE: printf("Router-signature"); break; + case K_PUBLISHED: printf("Published"); break; + case K_RUNNING_ROUTERS: printf("Running-routers"); break; + case K_PLATFORM: printf("Platform"); break; + default: + printf("?????? %d\n", tok->tp); return; + } + for (i = 0; i < tok->val.cmd.n_args; ++i) { + printf(" \"%s\"", tok->val.cmd.args[i]); + } + printf("\n"); + return; +} +static int +router_get_next_token(const char **s, directory_token_t *tok) { + int i; + i = _router_get_next_token(s, tok); + router_dump_token(tok); + return i; +} +#else +#define router_get_next_token _router_get_next_token +#endif + +/* 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_SHA_digest(start, end-start, digest)) { + log_fn(LOG_WARN,"couldn't compute digest"); + return -1; + } + + return 0; +} + + + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/test.c b/tags/tor-0_0_2pre19/src/or/test.c new file mode 100644 index 0000000000..34fe2d6925 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/test.c @@ -0,0 +1,726 @@ +/* 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" + +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; + 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); + + /**** + * find_on_inbuf + ****/ + buf_free(buf); + buf = buf_new(); + s = open("/tmp/tor_test/data", O_RDONLY, 0); + eof = 0; + i = read_to_buf(s, 1024, buf, &eof); + test_eq(256, i); + close(s); + + test_eq(((int)'d') + 1, find_on_inbuf("abcd", 4, buf)); + test_eq(-1, find_on_inbuf("xyzzy", 5, buf)); + /* Make sure we don't look off the end of the buffef */ + ((char*)_buf_peek_raw_buffer(buf))[256] = 'A'; + ((char*)_buf_peek_raw_buffer(buf))[257] = 'X'; + test_eq(-1, find_on_inbuf("\xff" "A", 2, buf)); + test_eq(-1, find_on_inbuf("AX", 2, buf)); + /* Make sure we use the string length */ + test_eq(((int)'d')+1, find_on_inbuf("abcdX", 4, buf)); + + /**** + * 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[CRYPTO_DH_SIZE]; + char p2[CRYPTO_DH_SIZE]; + char s1[CRYPTO_DH_SIZE]; + char s2[CRYPTO_DH_SIZE]; + int s1len, s2len; + + dh1 = crypto_dh_new(); + dh2 = crypto_dh_new(); + test_eq(crypto_dh_get_bytes(dh1), CRYPTO_DH_SIZE); + test_eq(crypto_dh_get_bytes(dh2), CRYPTO_DH_SIZE); + + memset(p1, 0, CRYPTO_DH_SIZE); + memset(p2, 0, CRYPTO_DH_SIZE); + test_memeq(p1, p2, CRYPTO_DH_SIZE); + test_assert(! crypto_dh_get_public(dh1, p1, CRYPTO_DH_SIZE)); + test_memneq(p1, p2, CRYPTO_DH_SIZE); + test_assert(! crypto_dh_get_public(dh2, p2, CRYPTO_DH_SIZE)); + test_memneq(p1, p2, CRYPTO_DH_SIZE); + + memset(s1, 0, CRYPTO_DH_SIZE); + memset(s2, 0xFF, CRYPTO_DH_SIZE); + s1len = crypto_dh_compute_secret(dh1, p2, CRYPTO_DH_SIZE, s1, 50); + s2len = crypto_dh_compute_secret(dh2, p1, CRYPTO_DH_SIZE, 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; + FILE *f; + int i, j; + int str_ciphers[] = { CRYPTO_CIPHER_IDENTITY, + CRYPTO_CIPHER_DES, + CRYPTO_CIPHER_RC4, + CRYPTO_CIPHER_3DES, + CRYPTO_CIPHER_AES_CTR, + -1 }; + + 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); + + /* 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_set_iv(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); + + /* Now, test encryption and decryption with stream ciphers. */ + data1[0]='\0'; + for(i = 1023; i>0; i -= 35) + strncat(data1, "Now is the time for all good onions", i); + for(i=0; str_ciphers[i] >= 0; ++i) { + /* For each cipher... */ + memset(data2, 0, 1024); + memset(data3, 0, 1024); + env1 = crypto_new_cipher_env(str_ciphers[i]); + test_neq(env1, 0); + env2 = crypto_new_cipher_env(str_ciphers[i]); + test_neq(env2, 0); + j = crypto_cipher_generate_key(env1); + if (str_ciphers[i] != CRYPTO_CIPHER_IDENTITY) { + crypto_cipher_set_key(env2, crypto_cipher_get_key(env1)); + } + crypto_cipher_set_iv(env1, "12345678901234567890"); + crypto_cipher_set_iv(env2, "12345678901234567890"); + 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); + if (str_ciphers[i] == CRYPTO_CIPHER_IDENTITY) { + test_memeq(data1, data2, 512); + } else { + 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(str_ciphers[i]); + test_neq(env2, 0); + if (str_ciphers[i] != CRYPTO_CIPHER_IDENTITY) { + crypto_cipher_set_key(env2, crypto_cipher_get_key(env1)); + } + crypto_cipher_set_iv(env2, "12345678901234567890"); + 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_SHA_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(CRYPTO_PK_RSA); + pk2 = crypto_new_pk_env(CRYPTO_PK_RSA); + 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, + RSA_PKCS1_OAEP_PADDING)); + test_eq(128, crypto_pk_public_encrypt(pk1, "Hello whirled.", 15, data2, + RSA_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, + RSA_PKCS1_OAEP_PADDING)); + test_streq(data3, "Hello whirled."); + memset(data3, 0, 1024); + test_eq(15, crypto_pk_private_decrypt(pk1, data2, 128, data3, + RSA_PKCS1_OAEP_PADDING)); + test_streq(data3, "Hello whirled."); + /* Can't decrypt with public key. */ + test_eq(-1, crypto_pk_private_decrypt(pk2, data2, 128, data3, + RSA_PKCS1_OAEP_PADDING)); + /* 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, + RSA_PKCS1_OAEP_PADDING)); + + /* File operations: save and load private key */ + f = fopen("/tmp/tor_test/pkey1", "wb"); + test_assert(! crypto_pk_write_private_key_to_file(pk1, f)); + fclose(f); + f = fopen("/tmp/tor_test/pkey1", "rb"); + test_assert(! crypto_pk_read_private_key_from_file(pk2, f)); + fclose(f); + test_eq(15, crypto_pk_private_decrypt(pk2, data1, 128, data3, + RSA_PKCS1_OAEP_PADDING)); + test_assert(! crypto_pk_read_private_key_from_filename(pk2, + "/tmp/tor_test/pkey1")); + test_eq(15, crypto_pk_private_decrypt(pk2, data1, 128, data3, + RSA_PKCS1_OAEP_PADDING)); + + /* 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"); + /*XXXX test failed signing*/ + + 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'); + + + free(data1); + free(data2); + free(data3); + +} + +void +test_util() { + struct timeval start, end; + struct tm a_time; + + 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)); +} + +void test_onion() { +#if 0 + char **names; + int i,num; + + names = parse_nickname_list(" foo bar 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 1000\n" + "platform Tor "VERSION" on "); + strcat(buf2, get_uname()); + strcat(buf2, "\n" + "published 1970-01-01 00:00:00\n" + "onion-key\n"); + strcat(buf2, pk1_str); + strcat(buf2, "link-key\n"); + strcat(buf2, pk3_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_get_entry_from_string((const char**)&cp); + 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->bandwidth, r1.bandwidth); + test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0); + test_assert(crypto_pk_cmp_keys(rp1->link_pkey, pk3) == 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_get_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 + +#if 0 + /* XXX To re-enable this test, we need to separate directory generation + * XXX from the directory backend again. Do this the next time we have + * XXX directory trouble. */ + /* Okay, now for the directories. */ + dir1 = (directory_t*) tor_malloc(sizeof(directory_t)); + dir1->n_routers = 2; + dir1->routers = (routerinfo_t**) tor_malloc(sizeof(routerinfo_t*)*2); + dir1->routers[0] = &r1; + dir1->routers[1] = &r2; + test_assert(! dump_signed_directory_to_string_impl(buf, 4096, dir1, pk1)); + /* puts(buf); */ + + test_assert(! router_get_dir_from_string_impl(buf, &dir2, pk1)); + test_eq(2, dir2->n_routers); +#endif + + 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); /* And more !*/ + tor_free(dir2); /* And more !*/ + + /* make sure compare_recommended_versions() works */ + test_eq(0, compare_recommended_versions("abc", "abc")); + test_eq(0, compare_recommended_versions("abc", "ab,abd,abde,abc,abcde")); + test_eq(0, compare_recommended_versions("abc", "ab,abd,abde,abcde,abc")); + test_eq(0, compare_recommended_versions("abc", "abc,abd,abde,abc,abcde")); + test_eq(0, compare_recommended_versions("a", "a,ab,abd,abde,abc,abcde")); + test_eq(-1, compare_recommended_versions("a", "ab,abd,abde,abc,abcde")); + test_eq(-1, compare_recommended_versions("abb", "ab,abd,abde,abc,abcde")); + test_eq(-1, compare_recommended_versions("a", "")); +} + +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(); + puts("\n========================= Onion Skins ====================="); + test_onion(); + test_onion_handshake(); + puts("\n========================= Directory Formats ==============="); + test_dir_format(); + puts(""); + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/tags/tor-0_0_2pre19/src/or/tor_main.c b/tags/tor-0_0_2pre19/src/or/tor_main.c new file mode 100644 index 0000000000..28af141477 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/or/tor_main.c @@ -0,0 +1,18 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +int tor_main(int argc, char *argv[]); + +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_2pre19/src/or/tree.h b/tags/tor-0_0_2pre19/src/or/tree.h new file mode 100644 index 0000000000..927ca04cd7 --- /dev/null +++ b/tags/tor-0_0_2pre19/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_2pre19/src/win32/orconfig.h b/tags/tor-0_0_2pre19/src/win32/orconfig.h new file mode 100644 index 0000000000..3170f9e3e5 --- /dev/null +++ b/tags/tor-0_0_2pre19/src/win32/orconfig.h @@ -0,0 +1,147 @@ +/* $Id$ + * orconfig.h -- This file is *not* generated by autoconf. Instead, + * it has to be hand-edited to keep win32 happy. + */ +#define MS_WINDOWS +#define MS_WIN32 + +/* 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 HAVE_FCNTL_H + +#define HAVE_FTIME + +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if the system has the type `int16_t'. */ +#undef HAVE_INT16_T + +/* Define to 1 if the system has the type `int32_t'. */ +#undef HAVE_INT32_T + +/* Define to 1 if the system has the type `int64_t'. */ +#undef HAVE_INT64_T + +/* Define to 1 if the system has the type `int8_t'. */ +#undef HAVE_INT8_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_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. */ +#define HAVE_SIGNAL_H + +/* 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 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_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 */ +#define HAVE_TIME_H + +/* Define to 1 if the system has the type `uint16_t'. */ +#undef HAVE_UINT16_T + +/* Define to 1 if the system has the type `uint32_t'. */ +#undef HAVE_UINT32_T + +/* Define to 1 if the system has the type `uint64_t'. */ +#undef HAVE_UINT64_T + +/* Define to 1 if the system has the type `uint8_t'. */ +#undef HAVE_UINT8_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#define PACKAGE "tor" + +/* Define 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 `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 0 + +/* The size of a `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* 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 + +/* Version number of package */ +#define VERSION "0.0.2pre6" diff --git a/tags/tor-0_0_2pre19/tor.sh.in b/tags/tor-0_0_2pre19/tor.sh.in new file mode 100644 index 0000000000..ecf3cb32d8 --- /dev/null +++ b/tags/tor-0_0_2pre19/tor.sh.in @@ -0,0 +1,76 @@ +#!/bin/sh + +TORBIN=@BINDIR@/tor +TORPID=@LOCALSTATEDIR@/run/tor.pid +TORLOG=@LOCALSTATEDIR@/log/tor/tor.log +TORCONF=@CONFDIR@/torrc +TORARGS="--pidfile $TORPID --logfile $TORLOG --runasdaemon 1" +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..." + $TORBIN -f $TORCONF $TORARGS + 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 + ;; + + 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