diff --git a/tags/tor-0_0_2pre13/.cvsignore b/tags/tor-0_0_2pre13/.cvsignore new file mode 100644 index 0000000000..a48c2897d5 --- /dev/null +++ b/tags/tor-0_0_2pre13/.cvsignore @@ -0,0 +1,12 @@ +Makefile +Makefile.in +aclocal.m4 +configure +orconfig.h +orconfig.h.in +config.cache +config.log +config.status +conftest* +stamp-h +stamp-h1 diff --git a/tags/tor-0_0_2pre13/AUTHORS b/tags/tor-0_0_2pre13/AUTHORS new file mode 100644 index 0000000000..56d44fb0c2 --- /dev/null +++ b/tags/tor-0_0_2pre13/AUTHORS @@ -0,0 +1,23 @@ + +Main authors: +------------- + +Matej Pfajfar wrote the first version of the code +(called OR) in 2001-2002. + +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. + +Contributors: +------------- + +Aaron Turner contributed the first version of +the tor.sh initscripts shell script. + +Steven Hazel made 'make install' do the right +thing. + diff --git a/tags/tor-0_0_2pre13/COPYING b/tags/tor-0_0_2pre13/COPYING new file mode 100644 index 0000000000..ded21dabef --- /dev/null +++ b/tags/tor-0_0_2pre13/COPYING @@ -0,0 +1 @@ +Automake wants this file. See LICENSE instead. diff --git a/tags/tor-0_0_2pre13/ChangeLog b/tags/tor-0_0_2pre13/ChangeLog new file mode 100644 index 0000000000..f6464ecb20 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/INSTALL b/tags/tor-0_0_2pre13/INSTALL new file mode 100644 index 0000000000..26847de09c --- /dev/null +++ b/tags/tor-0_0_2pre13/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 in the README, 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. + (Once you're comfortable running it, you can run it with "-l warn", + which will only log things that you need to know.) +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 it 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_2pre13/LICENSE b/tags/tor-0_0_2pre13/LICENSE new file mode 100644 index 0000000000..4afb15965d --- /dev/null +++ b/tags/tor-0_0_2pre13/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2001-2003, Roger Dingledine, Matej Pfajfar, Naval +Research Lab. + +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_2pre13/Makefile.am b/tags/tor-0_0_2pre13/Makefile.am new file mode 100644 index 0000000000..47ddde068d --- /dev/null +++ b/tags/tor-0_0_2pre13/Makefile.am @@ -0,0 +1,9 @@ + +SUBDIRS = src doc + +DIST_SUBDIRS = src doc + +EXTRA_DIST = LICENSE + +install-data-local: + $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor diff --git a/tags/tor-0_0_2pre13/NEWS b/tags/tor-0_0_2pre13/NEWS new file mode 100644 index 0000000000..bd81a337d1 --- /dev/null +++ b/tags/tor-0_0_2pre13/NEWS @@ -0,0 +1,2 @@ +NEWS +---- \ No newline at end of file diff --git a/tags/tor-0_0_2pre13/README b/tags/tor-0_0_2pre13/README new file mode 100644 index 0000000000..fbe27b611d --- /dev/null +++ b/tags/tor-0_0_2pre13/README @@ -0,0 +1,38 @@ + +'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, move sample-server-torrc onto torrc, and edit it. Then run tor + to generate keys. One of the generated files is your 'fingerprint' file. + Mail it to arma@mit.edu. Remember that you won't be able to authenticate + to the other tor nodes until I've added you to the directory. + +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_2pre13/Win32Build/or/or.dsp b/tags/tor-0_0_2pre13/Win32Build/or/or.dsp new file mode 100644 index 0000000000..77bbf8916b --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/Win32Build/or/or.dsw b/tags/tor-0_0_2pre13/Win32Build/or/or.dsw new file mode 100644 index 0000000000..3ff4c8e840 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/Win32Build/tor/tor.dsw b/tags/tor-0_0_2pre13/Win32Build/tor/tor.dsw new file mode 100644 index 0000000000..ae4da0804f --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/Win32Build/tor/tor.ncb b/tags/tor-0_0_2pre13/Win32Build/tor/tor.ncb new file mode 100644 index 0000000000..535305ac99 Binary files /dev/null and b/tags/tor-0_0_2pre13/Win32Build/tor/tor.ncb differ diff --git a/tags/tor-0_0_2pre13/Win32Build/tor/tor.opt b/tags/tor-0_0_2pre13/Win32Build/tor/tor.opt new file mode 100644 index 0000000000..8ae08d4288 Binary files /dev/null and b/tags/tor-0_0_2pre13/Win32Build/tor/tor.opt differ diff --git a/tags/tor-0_0_2pre13/autogen.sh b/tags/tor-0_0_2pre13/autogen.sh new file mode 100755 index 0000000000..0b1eec9d8a --- /dev/null +++ b/tags/tor-0_0_2pre13/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +aclocal && autoheader && autoconf && automake +./configure --enable-debug + diff --git a/tags/tor-0_0_2pre13/configure.in b/tags/tor-0_0_2pre13/configure.in new file mode 100644 index 0000000000..33d9484167 --- /dev/null +++ b/tags/tor-0_0_2pre13/configure.in @@ -0,0 +1,188 @@ + +AC_INIT +AM_INIT_AUTOMAKE(tor, 0.0.2pre13) +AM_CONFIG_HEADER(orconfig.h) + +CFLAGS="$CFLAGS -Wall -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 -lcrypto -lssl" + + # 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 -lcrypto -lssl" + +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, , 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 src/config/sample-server-torrc src/Makefile doc/Makefile src/config/Makefile src/common/Makefile src/or/Makefile) + diff --git a/tags/tor-0_0_2pre13/depcomp b/tags/tor-0_0_2pre13/depcomp new file mode 100755 index 0000000000..3480ce4e96 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/doc/CLIENTS b/tags/tor-0_0_2pre13/doc/CLIENTS new file mode 100644 index 0000000000..8e98853615 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/doc/FAQ b/tags/tor-0_0_2pre13/doc/FAQ new file mode 100644 index 0000000000..87a70719d1 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/FAQ @@ -0,0 +1,123 @@ +The Onion Routing (TOR) Frequently Asked Questions +-------------------------------------------------- + +1. General. + +1.1. What is tor? + +Tor is an implementation of version 2 of Onion Routing. + +Onion Routing is a connection-oriented anonymizing communication +service. Users build a layered block of asymmetric encryptions +(an "onion") which describes a source-routed path through a set of +nodes. Those nodes build 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.) + +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 ("or") functions as both client and server, depending +on which ports are specified in the configuration file. You can specify: +* APPort: client applications (eg privoxy, Mozilla) can speak socks to + this port. +* OPPort: onion proxies (client onion routers) connect 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 (use the 'oprc' config file). +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 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 an 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? + +tagging: can change bytes in the cells, even through link encryption +end node can give back wrong data, even subtly wrong data. + + +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_2pre13/doc/HACKING b/tags/tor-0_0_2pre13/doc/HACKING new file mode 100644 index 0000000000..ddba876062 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/HACKING @@ -0,0 +1,532 @@ + 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-spec.txt, + tor-design.tex, 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 only by + dns.c. + + config.c -- Code to parse and validate the configuration file. + + [Background processing modules] + + cpuworker.c -- Implements a separate 'CPU worker' process 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 ACI 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_2pre13/doc/Makefile.am b/tags/tor-0_0_2pre13/doc/Makefile.am new file mode 100644 index 0000000000..5d4ab67610 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/Makefile.am @@ -0,0 +1,3 @@ + +EXTRA_DIST = tor-spec.txt CLIENTS FAQ HACKING rendezvous.txt tor-design.tex + diff --git a/tags/tor-0_0_2pre13/doc/TODO b/tags/tor-0_0_2pre13/doc/TODO new file mode 100644 index 0000000000..938e15c224 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/TODO @@ -0,0 +1,247 @@ + +make connection_flush_buf() more obviously obsolete +let hup reread the config file, eg so we can get new exit policies without restarting +use times(2) rather than gettimeofday to measure how long it takes to process a cell + +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 + +Short-term: + 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 + 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 +ARMA . find an application that uses half-open connections: openssh + o instruments ORs to report stats + o average cell fullness + o average bandwidth used + . integrate rep_ok functions, see what breaks + 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 +ARMA . Go through log messages, reduce confusing error messages. +ARMA . make the logs include more info (fd, etc) + o add log convention to the HACKING file + . 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 +ARMA - tor faq + list all other systems, why we're different. + o Move (most of) the router/directory code out of main.c + +Mid-term: + . 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 + . 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 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 +ARMA - Reduce streamid footprint from 7 bytes to 2 bytes + - Check for collisions in streamid (now possible with + just 2 bytes), and back up & replace with padding if so + - Use the 3 saved bytes to put pseudorandomness in each relay cell + - Use the 4 reserved bytes in each cell header to keep 1/5 + of a sha1 of the relay payload (move into stream header) + - Move length into the stream header too + - Spec the stream_id stuff. Clarify that nobody on the backward + stream should look at stream_id. +ARMA . Exit policies + o Spec how to write the exit policies + - Path selection algorithms + - Let user request certain nodes + - And disallow certain nodes + D Choose path by jurisdiction, etc? + - Make relay end cells have failure status and payload attached + - Streams that fail due to exit policy must reextend to new node + - Add extend_wait state to edge connections, thumb through them + when the AP get an extended cell. +SPEC!! D Non-clique topologies + D Implement our own memory management, at least for common structs + . 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 + - Handle cpuworkers dying + 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 (unused) 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 + 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? + . 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 + . Get tor to act like a socks server + o socks4, socks4a + o socks5 +SPEC!! - Handle socks commands other than connect, eg, bind? + . Develop rendezvous points + o Design + - Spec + - Implement + D Deploy and manage open source development site. + . Documentation + o Discussion of socks, tsocks, etc + o On-the-network protocol + o Onions + o Cells + . Better comments for functions! + - Tests + o Testing harness/infrastructure +NICK . Unit tests + 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) + D Deploy a widespread network + D Load balancing between router twins + D Keep track of load over links/nodes, to + know who's hosed +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 + . stdint.h + - Make a script to set up a local network on your machine + X Move away from openssl + o Abstract out crypto calls + X Look at nss, others? Just include code? + o Clearer bandwidth management + o Do we want to remove bandwidth from OR handshakes? + o What about OP handshakes? + - More flexibility in node addressing + D Support IPv6 rather than just 4 + - Handle multihomed servers (config variable to set IP) + . 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 + - choose exit nodes by exit policies + +Older (done) todo stuff: + + 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_2pre13/doc/rendezvous.txt b/tags/tor-0_0_2pre13/doc/rendezvous.txt new file mode 100644 index 0000000000..e05a7835cf --- /dev/null +++ b/tags/tor-0_0_2pre13/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 meeting 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_2pre13/doc/tor-design.bib b/tags/tor-0_0_2pre13/doc/tor-design.bib new file mode 100644 index 0000000000..d4b11345c9 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/tor-design.bib @@ -0,0 +1,875 @@ +@Misc{anonymizer, + key = {anonymizer}, + title = {The {Anonymizer}}, + note = {\url{http://www.anonymizer.com}} +} + +@Misc{anonnet, + key = {anonnet}, + title = {{AnonNet}}, + note = {\url{http://www.authnet.org/anonnet/}} +} + +@inproceedings{econymics, + title = {On the Economics of Anonymity}, + author = {Alessandro Acquisti and Roger Dingledine and Paul Syverson}, + booktitle = {Financial Cryptography, FC 2003}, + year = {2003}, + editor = {Rebecca N. Wright}, + publisher = {Springer-Verlag, LNCS 2742}, + note = {\url{http://freehaven.net/doc/fc03/econymics.pdf}}, +} + + +@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}}, + www_important = {1}, + www_section = {Anonymous communication}, +} + +@inproceedings{ rao-pseudonymity, + author = "Josyula R. Rao and Pankaj Rohatgi", + title = "Can Pseudonymity Really Guarantee Privacy?", + booktitle = "Proceedings of the Ninth USENIX Security Symposium", + year = {2000}, + month = Aug, + publisher = {USENIX}, + pages = "85--96", + note = {\url{http://www.usenix.org/publications/library/proceedings/sec2000/ +full_papers/rao/rao.pdf}}, +} + +@InProceedings{pfitzmann90how, + author = "Birgit Pfitzmann and Andreas Pfitzmann", + title = "How to Break the Direct {RSA}-Implementation of {MIXes}", + booktitle = {Eurocrypt 89}, + publisher = {Springer-Verlag, LNCS 434}, + year = {1990}, + note = {\url{http://citeseer.nj.nec.com/pfitzmann90how.html}}, +} + +@Misc{mixminion-spec, + author = {Mixminion}, + title = {Type {III} ({M}ixminion) Mix Protocol Specifications}, + note = {\newline \url{http://mixminion.net/minion-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, +} + +% Would a more recent reference for SPRPs be more useful? +@Article{sprp, + author = {Michael Luby and Charles Rackoff}, + title = {How to Construct Pseudorandom Permutations from + Pseudorandom Functions}, + journal = {SIAM Journal on Computing}, + year = {1988}, + volume = {17}, + number = {2}, + pages = {373--386}, +} + +@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{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 (forthcoming)}, +} + +@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{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-pet02, + 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{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}, + note = {\url{http://www.onion-router.net/Publications/IH-1996.ps.gz}} +} + +@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{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, FC'98}, + 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}}, +} + +@Article{mitzenm-loss, + author = {G. Louth and M. Mitzenmacher and F.P. Kelly}, + title = {Computational Complexity of Loss Networks}, + journal = {Theoretical Computer Science}, + year = {1994}, + volume = {125}, + pages = {45-59}, + note = {\newline \url{http://citeseer.nj.nec.com/louth94computational.html}}, +} + +@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 = {Proceedings of the Network and Distributed Security Symposium - {NDSS} '96}, + year = 1996, + month = {February}, + pages = {2--16}, + publisher = {IEEE}, + www_important = 1, + note = {\url{http://citeseer.nj.nec.com/2254.html}}, + www_section = {Anonymous communication}, +} + +@InProceedings{freenet, + author = {Ian Clarke and Oskar Sandberg and Brandon Wiley and Theodore W. Hong}, + title = {Freenet: {A} Distributed Anonymous Information Storage and Retrieval + System}, + booktitle = {Workshop on Design Issues in Anonymity and Unobservability}, + pages = {46--66}, + year = {2000}, + note = {\newline \url{http://citeseer.nj.nec.com/clarke00freenet.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/}}, +} + +@Misc{remailer-history-old, + author = {Tim May}, + title = {Description of early remailer history}, + howpublished = {E-mail archived at + \url{http://www.inet-one.com/cypherpunks/dir.1996.08.29-1996.09.04/ + msg00431.html}}, +} + +@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/}}, +} + +@Misc{timmay, + author = {Tim May}, + title = {Cyphernomicon}, + note = {\newline \url{http://www2.pro-ns.net/~crypto/cyphernomicon.html}}, +} + +@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{pidaho, + author = {Joel McNamara}, + title = {{P}rivate {I}daho}, + note = {\newline \url{http://www.eskimo.com/~joelm/pi.html}}, +} + +@misc{potato, + author = {RProcess}, + title = {{P}otato {S}oftware}, + note = {\newline \url{http://www.skuz.net/potatoware/}}, +} + +@misc{helsingius, + author = {J. Helsingius}, + title = {{\tt anon.penet.fi} press release}, + note = {\newline \url{http://www.penet.fi/press-english.html}}, +} + +@misc{mix-stats, + author = {Christian Mock}, + title = {Mixmaster Stats ({A}ustria)}, + note = {\newline \url{http://www.tahina.priv.at/~cm/stats/mlist2.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{freedom, + author = {Zero Knowledge Systems}, + title = {Freedom Version 2 White Papers}, + note = {\newline \url{http://www.freedom.net/info/whitepapers/}}, +} + + +@Misc{recovery, + author = {Miguel Castro and Barbara Liskov}, + title = {Proactive Recovery in a Byzantine-Fault-Tolerant System}, + note = {\newline \url{http://www.pmg.lcs.mit.edu/~castro/application/recovery.pdf}}, +} + +@Misc{advogato, + author = {Raph Levien}, + title = {Advogato's Trust Metric}, + note = {\newline \url{http://www.advogato.org/trust-metric.html}}, +} + +@Misc{rabin-ida, + author = {Michael O. Rabin}, + title = {Efficient Dispersal of Information for security, load balancing, + and fault tolerance}, + booktitle = {Journal of the ACM}, + year = {1989}, + volume = {36}, + number = {2}, + series = {335--348}, + month = {April}, +} + +@PhdThesis{malkin-thesis, + author = {Tal Malkin}, + school = {{MIT}}, + title = {Private {I}nformation {R}etrieval}, + year = {2000}, + note = {\newline \url{http://toc.lcs.mit.edu/~tal/pubs.html}} +} + +@Misc{zks, + title = {Zero {K}nowledge {S}ystems}, + note = {\newline \url{http://www.freedom.net/}}, +} + +@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}}, +} + +@Article{raghavan87randomized, + author = {P. Raghavan and C. Thompson}, + title = {Randomized rounding: A technique for provably good algorithms and algorithmic proofs}, + journal = {Combinatorica}, + volume = {7}, + pages = {365--374}, + year = {1987}, +} + +@InProceedings{leighton91fast, + author = {Frank Thomson Leighton and Fillia Makedon and Serge A. Plotkin and + Clifford Stein and Eva Tardos and Spyros Tragoudas}, + title = {Fast Approximation Algorithms for Multicommodity Flow Problems}, + booktitle = {{ACM} Symposium on Theory of Computing}, + pages = {101-111}, + year = {1991}, + note = {\newline \url{http://citeseer.nj.nec.com/91073.html}}, +} + +@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 (FC '02)}, + year = 2002, + editor = {Matt Blaze}, + publisher = {Springer-Verlag, LNCS (forthcoming)}, + 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{BEAR-LIONESS, + author = {Ross Anderson and Eli Biham}, + title = {Two Practical and Provably Secure Block Ciphers: {BEAR} and {LION}}, + booktitle = {International Workshop on Fast Software Encryption}, + year = {1996}, + publisher = {Springer-Verlag}, + note = {\url{http://citeseer.nj.nec.com/anderson96two.html}}, +} + +@Misc{SPC, + author = {Daniel Bleichenbacher and Anand Desai}, + title = {A Construction of a Super-Pseudorandom Cipher}, + howpublished = {Manuscript}, +} + + +@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 (forthcoming)} +} + +@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} +} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "tor-design" +%%% End: diff --git a/tags/tor-0_0_2pre13/doc/tor-design.tex b/tags/tor-0_0_2pre13/doc/tor-design.tex new file mode 100644 index 0000000000..421ec7c361 --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/tor-design.tex @@ -0,0 +1,521 @@ +\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} + +% 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: Design of a Next-Generation Onion Router} + +\author{Anonymous} +%\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 connection-based low-latency anonymous communication +system. It is intended as an update and replacement for onion routing +and addresses many limitations in the original onion routing design. +Tor works in a real-world Internet environment, +requires little synchronization or coordination between nodes, and +protects against known anonymity-breaking attacks as well +as or better than other systems with similar design parameters. +\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. Users choose a path through the network and +build a \emph{virtual circuit}, in which each node in the path knows its +predecessor and successor, but no others. Traffic flowing down the circuit +is sent in fixed-size \emph{cells}, which are unwrapped by a symmetric key +at each node, revealing the downstream node. The original onion routing +project published several design and analysis papers +\cite{or-jsac98,or-discex00,or-ih96,or-pet02}. While there was briefly +a wide area onion routing network, +the only long-running and publicly accessible +implementation was a fragile proof-of-concept that ran on a single +machine. Many critical design and deployment issues were never implemented, +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: + +\begin{tightlist} + +\item \textbf{Perfect forward secrecy:} The original onion routing +design is vulnerable to a single hostile node recording traffic and later +forcing successive nodes in the circuit to decrypt it. Rather than using +onions to lay the circuits, Tor uses an incremental or \emph{telescoping} +path-building design, where the initiator negotiates session keys with +each successive hop in the circuit. Onion replay detection is no longer +necessary, and the network as a whole is more reliable to boot, since +the initiator knows which hop failed and can try extending to a new node. + +\item \textbf{Applications talk to the onion proxy via Socks:} +The original onion routing design required a separate proxy for each +supported application protocol, resulting in a lot of extra code (most +of which was never written) and also meaning that a lot of TCP-based +applications were not supported. Tor uses the unified and standard Socks +\cite{socks4,socks5} interface, allowing us to support any TCP-based +program without modification. + +\item \textbf{Many applications can share one circuit:} The original +onion routing design built one circuit for each request. Aside from the +performance issues of doing public key operations for every request, it +also turns out that regular communications patterns mean building lots +of circuits, which can endanger anonymity \cite{wright03}. +%[XXX Was this +%supposed to be Wright02 or Wright03. In any case I am hesitant to cite +%that work in this context. While the point is valid in general, that +%work is predicated on assumptions that I don't think typically apply +%to onion routing (whether old or new design). -PS] +%[I had meant wright03, but I guess wright02 could work as well. +% If you don't think these attacks work on onion routing, you need to +% write up a convincing argument of this. Such an argument would +% be very worthwhile to include in this paper. -RD] +Tor multiplexes many +connections down each circuit, but still rotates the circuit periodically +to avoid too much linkability. + +\item \textbf{No mixing or traffic shaping:} The original onion routing +design called for full link padding both between onion routers and between +onion proxies (that is, users) and onion routers \cite{or-jsac98}. The +later analysis paper \cite{or-pet02} suggested \emph{traffic shaping} +to provide similar protection but use less bandwidth, but did not go +into detail. However, recent research \cite{econymics} and deployment +experience \cite{freedom} indicate that this level of resource +use is not practical or economical; and even full link padding is still +vulnerable to active attacks \cite{defensive-dropping}. +% [XXX what is being referenced here, Dogan? -PS] +%[An upcoming FC04 paper. I'll add a cite when it's out. -RD] + +\item \textbf{Leaky pipes:} Through in-band signalling within the circuit, +Tor initiators can direct traffic to nodes partway down the circuit. This +allows for long-range padding to frustrate timing attacks at the initiator +\cite{defensive-dropping}, but because circuits are used by more than +one application, it also allows traffic to exit the circuit from the +middle -- thus frustrating timing attacks based on observing exit points. +%Or something like that. hm. Tone this down maybe? Or support it. -RD + +\item \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. Our decentralized ack-based +congestion control maintains reasonable anonymity while allowing nodes +at the edges of the network to detect congestion or flooding attacks +and send less data until the congestion subsides. + +\item \textbf{Directory servers:} Rather than attempting to flood +link-state information through the network, which can be unreliable and +open to partitioning attacks or outright deception, Tor takes a simplified +view towards distributing link-state information. Certain more trusted +onion routers also serve as directory servers; they provide signed +\emph{directories} describing all routers they know about, and which +are currently up. Users periodically download these directories via HTTP. + +\item \textbf{End-to-end integrity checking:} Without integrity checking +on traffic going through the network, an onion router can change the +contents of cells as they pass by, e.g. by redirecting a connection on +the fly so it connects to a different webserver, or by tagging encrypted +traffic and looking for traffic at the network edges that has been +tagged \cite{minion-design}. + +\item \textbf{Robustness to node failure:} router twins + +\item \textbf{Exit policies:} +Tor provides a consistent mechanism for each node to specify and +advertise an exit policy. + +\item \textbf{Rendezvous points:} +location-protected servers + +\end{tightlist} + +We review previous work in Section \ref{sec:background}, 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:maintaining-anonymity}. We then summarize +how our design stands up to known attacks, and conclude with a list of +open problems. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\Section{Background and threat model} +\label{sec:background} + +\SubSection{Related work} +\label{sec:related-work} +Modern anonymity designs date to Chaum's Mix-Net\cite{chaum-mix} design of +1981. Chaum proposed hiding sender-recipient connections by wrapping +messages in several layers of public key cryptography, and relaying them +through a path composed of Mix servers. Mix servers in turn decrypt, delay, +and re-order messages, before relay them along the path towards their +destinations. + +Subsequent relay-based anonymity designs have diverged in two +principal directions. Some have attempted to maximize anonymity at +the cost of introducing comparatively large and variable latencies, +for example, Babel\cite{babel}, Mixmaster\cite{mixmaster-spec}, and +Mixminion\cite{minion-design}. Because of this +decision, such \emph{high-latency} networks are well-suited for anonymous +email, but introduce too much lag for interactive tasks such as web browsing, +internet chat, or SSH connections. + +Tor belongs to the second category: \emph{low-latency} designs that +attempt to anonymize interactive network traffic. Because such +traffic tends to involve a relatively large numbers of packets, it is +difficult to prevent an attacker who can eavesdrop entry and exit +points from correlating packets entering the anonymity network with +packets leaving it. Although some work has been done to frustrate +these attacks, most designs protect primarily against traffic analysis +rather than traffic confirmation \cite{or-jsac98}. One can pad and +limit communication to a constant rate or at least to control the +variation in traffic shape. This can have prohibitive bandwidth costs +and/or performance limitations. One can also use a cascade (fixed +shared route) with a relatively fixed set of users. This assumes a +degree of agreement and provides an easier target for an active +attacker since the endpoints are generally known. However, a practical +network with both of these features has been run for many years +\cite{web-mix}. + +they still... +[XXX go on to explain how the design choices implied in low-latency result in +significantly different designs.] + +The simplest low-latency designs are single-hop proxies such as the +Anonymizer \cite{anonymizer}, wherein a single trusted server removes +identifying users' data before relaying it. These designs are easy to +analyze, but require end-users to trust the anonymizing proxy. + +More complex are distributed-trust, channel-based anonymizing systems. In +these designs, a user establishes one or more medium-term bidirectional +end-to-end tunnels to exit servers, and uses those tunnels to deliver a +number of low-latency packets to and from one or more destinations per +tunnel. Establishing tunnels is comparatively expensive and typically +requires public-key cryptography, whereas relaying packets along a tunnel is +comparatively inexpensive. Because a tunnel crosses several servers, no +single server can learn the user's communication partners. + +Systems such as earlier versions of Freedom and onion routing +build the anonymous channel all at once (using an onion). Later +designs of each of these build the channel in stages as does AnonNet +\cite{anonnet}. Amongst other things, this makes perfect forward +secrecy feasible. + +Some systems, such as Crowds \cite{crowds-tissec}, do not rely on the +changing appearance of packets to hide the path; rather they employ +mechanisms so that an intermediary cannot be sure when it is +receiving/sending to the ultimate initiator. There is no public-key +encryption needed for Crowds, but the responder and all data are +visible to all nodes on the path so that anonymity of connection +initiator depends on filtering all identifying information from the +data stream. Crowds is also designed only for HTTP traffic. + +Hordes \cite{hordes-jcs} is based on Crowds but also uses multicast +responses to hide the initiator. Some systems go even further +requiring broadcast \cite{herbivore,p5} although tradeoffs are made to +make this more practical. Both Herbivore and P5 are designed primarily +for communication between communicating peers, although Herbivore +permits external connections by requesting a peer to serve as a proxy. +Allowing easy connections to nonparticipating responders or recipients +is a practical requirement for many users, e.g., to visit +nonparticipating Web sites or to send mail to nonparticipating +recipients. + +Distributed-trust anonymizing systems differ in how they prevent attackers +from controlling too many servers and thus compromising too many user paths. +Some protocols rely on a centrally maintained set of well-known anonymizing +servers. Others (such as Tarzan and MorphMix) allow unknown users to run +servers, while using a limited resource (DHT space for Tarzan; IP space for +MorphMix) to prevent an attacker from owning too much of the network. +[XXX what else? What does (say) crowds do?] + +All of the above systems Several systems with varying design goals +and capabilities but all of which require that communicants be +intentionally participating are mentioned here. + +Some involve multicast or more to work +herbivore + +There are also many systems which are intended for anonymous +and/or censorship resistant file sharing. [XXX Should we list all these +or just say it's out of scope for the paper? +eternity, gnunet, freenet, freehaven, publius, tangler, taz/rewebber] + + + +[XXX Should we add a paragraph dividing servers by all-at-once approach to + tunnel-building (OR1,Freedom1) versus piecemeal approach + (OR2,Anonnet?,Freedom2) ?] + + + +Channel-based anonymizing systems also differ in their use of dummy traffic. +[XXX] + +Finally, several systems provide low-latency anonymity without channel-based +communication. Crowds and [XXX] provide anonymity for HTTP requests; [...] + +[XXX Mention error recovery?] + +Web-MIXes \cite{web-mix} (also known as the Java Anon Proxy or JAP) +use a cascade architecture with relatively constant groups of users +sending and receiving at a constant rate. + +Some, such as Crowds \cite{crowds-tissec}, do nothing against such +confirmation but still make it difficult for nodes along a connection to +perform timing confirmations that would more easily identify when +the immediate predecessor is the initiator of a connection, which in +Crowds would reveal both initiator and responder to the attacker. + + +anonymizer +pipenet +freedom v1 +freedom v2 +onion routing v1 +isdn-mixes +crowds +real-time mixes, web mixes +anonnet (marc rennhard's stuff) +morphmix +P5 +gnunet +rewebbers +tarzan +herbivore +hordes +cebolla (?) + +[XXX Close by mentioning where Tor fits.] + +\SubSection{Our threat model} +\label{subsec:threat-model} + +\SubSection{Known attacks against low-latency anonymity systems} +\label{subsec:known-attacks} + +We discuss each of these attacks in more detail below, along with the +aspects of the Tor design that provide defense. We provide a summary +of the attacks and our defenses against them in Section \ref{sec:attacks}. + +Passive attacks: +simple observation, +timing correlation, +size correlation, +option distinguishability, + +Active attacks: +key compromise, +iterated subpoena, +run recipient, +run a hostile node, +compromise entire path, +selectively DOS servers, +introduce timing into messages, +directory attacks, +tagging attacks + +\Section{Design goals and assumptions} +\label{sec:assumptions} + +[XXX Perhaps the threat model belongs here.] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\Section{The Tor Design} +\label{sec:design} + + +\Section{Other design decisions} + +\SubSection{Exit policies and abuse} +\label{subsec:exitpolicies} + +\SubSection{Directory Servers} +\label{subsec:dir-servers} + +\Section{Rendezvous points for location privacy} +\label{sec:rendezvous} + +Rendezvous points are a building block for \emph{location-hidden services} +(that is, responder anonymity) in the Tor network. Location-hidden +services means Bob can offer a tcp service, such as an Apache webserver, +without revealing the IP of that service. + +We provide censorship resistance for Bob by allowing him to advertise +several onion routers (nodes known as his Introduction Points, see +Section \ref{subsec:intro-point}) as his public location. Alice, +the client, chooses a node known as a Meeting Point (see Section +\ref{subsec:meeting-point}). She connects to one of Bob's introduction +points, informs him about her meeting point, and then waits for him to +connect to her meeting point. This extra level of indirection is needed +so Bob's introduction points don't serve files directly (else they 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 +\ref{subsec:client-rendezvous}). Both Alice and Bob must run local +onion proxies (OPs) -- software that knows how to talk to the onion +routing network. + +The steps of a rendezvous: +\begin{tightlist} +\item Bob chooses some Introduction Points, and advertises them on a + Distributed Hash Table (DHT). +\item Bob establishes onion routing connections to each of his + Introduction Points, and waits. +\item 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. +\item Alice chooses and establishes a Meeting Point (MP) for this + transaction. +\item 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. +\item Bob chooses whether to ignore the blob, or to onion route to MP. + Let's assume the latter. +\item 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. +\item 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. +\item Data goes back and forth as usual. +\end{tightlist} + +Ian Goldberg developed a similar notion of rendezvous points for +low-latency anonymity systems \cite{goldberg-thesis}. His ``service tag'' +is the same concept as our ``hash of service's public key''. We make it +a hash of the public key so it can be self-authenticating, and so the +client can recognize the same service with confidence later on. +The main differences are: +* We force the client to use our software. This means + - the client can get anonymity for himself pretty easily, since he's + already running our onion proxy. + - the client can literally just click on a url in his Mozilla, paste it + into wget, etc, and it will just work. (The url is a long-term + service tag; like Ian's, it doesn't expire as the server changes + public addresses. But in Ian's scheme it seems the client must + manually hunt down a current location of the service via gnutella?) + - the client and server can share ephemeral DH keys, so at no point + in the path is the plaintext exposed. +* I fear that we would get *no* volunteers to run Ian's rendezvous points, + because they have to actually serve the Nazi propaganda (or whatever) + in plaintext. So we add another layer of indirection to the system: + the rendezvous service is divided into Introduction Points and + Meeting Points. The introduction points (the ones that the server is + publically associated with) do not output any bytes to the clients. And + the meeting points don't know the client, the server, or the stuff + being transmitted. The indirection scheme is also designed with + authentication/authorization in mind -- if the client doesn't include + the right cookie with its request for service, the server doesn't even + acknowledge its existence. + + +\subsubsection{Integration with user applications} + +\Section{Maintaining anonymity sets} +\label{sec:maintaining-anonymity} + +\SubSection{Using a circuit many times} +\label{subsec:many-messages} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\Section{Attacks and Defenses} +\label{sec:attacks} + +Below we summarize a variety of attacks and how well our design withstands +them. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\Section{Future Directions and Open Problems} +\label{sec:conclusion} + +Tor brings together many innovations from many different projects into +a unified deployable system. But there are still several attacks that +work quite well, as well as a number of sustainability and run-time +issues remaining to be ironed out. In particular: + +\begin{itemize} +\item foo +\end{itemize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\Section{Acknowledgments} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\bibliographystyle{latex8} +\bibliography{tor-design} + +\end{document} + +% Style guide: +% U.S. spelling +% avoid contractions (it's, can't, etc.) +% 'mix', 'mixes' (as noun) +% 'mix-net' +% 'mix', 'mixing' (as verb) +% 'Mixminion Project' +% 'Mixminion' (meaning the protocol suite or the network) +% 'Mixmaster' (meaning the protocol suite or the network) +% 'middleman' [Not with a hyphen; the hyphen has been optional +% since Middle English.] +% 'nymserver' +% 'Cypherpunk', 'Cypherpunks', 'Cypherpunk remailer' +% +% 'Whenever you are tempted to write 'Very', write 'Damn' instead, so +% your editor will take it out for you.' -- Misquoted from Mark Twain + diff --git a/tags/tor-0_0_2pre13/doc/tor-spec.txt b/tags/tor-0_0_2pre13/doc/tor-spec.txt new file mode 100644 index 0000000000..5536a1c76b --- /dev/null +++ b/tags/tor-0_0_2pre13/doc/tor-spec.txt @@ -0,0 +1,508 @@ +$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 + - Sendme w/stream0 is circuit sendme + - Integrate -NM and -RD comments + +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 + +Tor is a connection-oriented anonymizing communication service. Users +build a path known as 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. + +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 256 bytes long. Cells may be sent embedded in TLS + records of any size or divided across TLS records, but the framing + of TLS records should 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: + + ACI (anonymous circuit identifier) [2 bytes] + Command [1 byte] + Length [1 byte] + Sequence number (unused, set to 0) [4 bytes] + Payload (padded with 0 bytes) [248 bytes] + [Total size: 256 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 'Length' and 'Payload' depend on the type of + the cell. + PADDING: Neither field is used. + CREATE: Length is 144; the payload contains the first phase of the + DH handshake. + CREATED: Length is 128; the payload contains the second phase of + the DH handshake. + RELAY: Length is a value between 8 and 248; the first 'length' + bytes of payload contain useful data. + DESTROY: Neither field is used. + + Unused fields are filled with 0 bytes. 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 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. 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 the 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 two 16 byte keys, called Kf and Kb as + follows. First, the server represents g^xy as a big-endian + unsigned integer. Next, the server computes 40 bytes of key data + as K = SHA1(g^xy | [00]) | SHA1(g^xy | [01]) where "00" is a single + octet whose value is zero, and "01" is a single octet whose value + is one. The first 16 bytes of K form Kf, and the next 16 bytes of + K form Kb. + + Kf is used to encrypt the stream of data going from the OP to the + OR, whereas 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. + + 2. If not already connected to the first router in the chain, + open a new connection to that router. + + 3. Choose an ACI 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 ACI 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 back 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 ACI not + yet used on the connection between the two onion routers. (But see + section 4.3. above, concerning choosing ACIs.) + + 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 ACI, 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 ACI. + + 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 data cells + + When an OR receives a RELAY cell, it checks the cell's ACI 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 recognized 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 + + 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. + +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. + + A exit conneection 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. + + Either of these cells increment 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. + +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 APPort 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) +APPort ::= 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_2pre13/install-sh b/tags/tor-0_0_2pre13/install-sh new file mode 100755 index 0000000000..398a88e142 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/missing b/tags/tor-0_0_2pre13/missing new file mode 100755 index 0000000000..dd583709f5 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/mkinstalldirs b/tags/tor-0_0_2pre13/mkinstalldirs new file mode 100755 index 0000000000..994d71ce7a --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/.cvsignore b/tags/tor-0_0_2pre13/src/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/tags/tor-0_0_2pre13/src/Makefile.am b/tags/tor-0_0_2pre13/src/Makefile.am new file mode 100644 index 0000000000..ba296a6b0f --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/.cvsignore b/tags/tor-0_0_2pre13/src/common/.cvsignore new file mode 100644 index 0000000000..051d1bd50b --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/tags/tor-0_0_2pre13/src/common/Makefile.am b/tags/tor-0_0_2pre13/src/common/Makefile.am new file mode 100644 index 0000000000..7bd90b0bf4 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/aes.c b/tags/tor-0_0_2pre13/src/common/aes.c new file mode 100644 index 0000000000..ed3397ec40 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/aes.h b/tags/tor-0_0_2pre13/src/common/aes.h new file mode 100644 index 0000000000..9187708897 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/crypto.c b/tags/tor-0_0_2pre13/src/common/crypto.c new file mode 100644 index 0000000000..8da2916dcb --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/crypto.c @@ -0,0 +1,1049 @@ +/* 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 "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, 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_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(unsigned char *m, int len, unsigned char *digest) +{ + assert(m && digest); + return (SHA1(m,len,digest) == NULL); +} + +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); + 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); + if (secret_tmp) + 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); +} + +int crypto_pseudo_rand(unsigned int n, unsigned char *to) +{ + assert(to); + return (RAND_pseudo_bytes(to, n) == -1); +} + +/* errors */ +char *crypto_perror() +{ + return (char *)ERR_reason_error_string(ERR_get_error()); +} + +int +base64_encode(char *dest, int destlen, 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, src, srclen); + EVP_EncodeFinal(&ctx, dest+len, &ret); + ret += len; + return ret; +} +int +base64_decode(char *dest, int destlen, 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, src, srclen); + EVP_DecodeFinal(&ctx, dest, &ret); + ret += len; + return ret; +} + diff --git a/tags/tor-0_0_2pre13/src/common/crypto.h b/tags/tor-0_0_2pre13/src/common/crypto.h new file mode 100644 index 0000000000..31e995d3bd --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/crypto.h @@ -0,0 +1,109 @@ +/* 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 + +typedef struct crypto_pk_env_t crypto_pk_env_t; +typedef struct crypto_cipher_env_t crypto_cipher_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, 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, char *src, int srclen); +int base64_decode(char *dest, int destlen, 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_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(unsigned char *m, int len, unsigned char *digest); + +/* random numbers */ +int crypto_seed_rng(); +int crypto_rand(unsigned int n, unsigned char *to); +int crypto_pseudo_rand(unsigned int n, unsigned char *to); + +#define CRYPTO_PSEUDO_RAND_INT(v) crypto_pseudo_rand(sizeof(v),(char*)&(v)) + +/* errors */ +char *crypto_perror(); +#endif diff --git a/tags/tor-0_0_2pre13/src/common/fakepoll.c b/tags/tor-0_0_2pre13/src/common/fakepoll.c new file mode 100644 index 0000000000..cca6e44215 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/fakepoll.c @@ -0,0 +1,90 @@ +/* + * fakepoll.c + * + * On systems where 'poll' doesn't exist, fake it with 'select'. + * + * Nick Mathewson + */ + +#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 + +#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_2pre13/src/common/fakepoll.h b/tags/tor-0_0_2pre13/src/common/fakepoll.h new file mode 100644 index 0000000000..353e11ee4e --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/fakepoll.h @@ -0,0 +1,35 @@ +/* + * fakepoll.h + * + * On systems where 'poll' doesn't exist, fake it with 'select'. + * + * Nick Mathewson + */ + +#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_2pre13/src/common/log.c b/tags/tor-0_0_2pre13/src/common/log.c new file mode 100644 index 0000000000..3a60d0576d --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/log.c @@ -0,0 +1,162 @@ +/* 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 int loglevel = LOG_DEBUG; +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); + if (severity < loglevel) + return; + 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 */ + } +} + +void +log_set_severity(int severity) +{ + loglevel = severity; +} + +/* 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; +} + +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_2pre13/src/common/log.h b/tags/tor-0_0_2pre13/src/common/log.h new file mode 100644 index 0000000000..d5b8e9b260 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/log.h @@ -0,0 +1,49 @@ +/* + * log.h + * Logging facilities. + * + * Matej Pfajfar + */ + +#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 log_set_severity(int severity); + +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_2pre13/src/common/test.h b/tags/tor-0_0_2pre13/src/common/test.h new file mode 100644 index 0000000000..7411d8f812 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/test.h @@ -0,0 +1,127 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* 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_2pre13/src/common/torint.h b/tags/tor-0_0_2pre13/src/common/torint.h new file mode 100644 index 0000000000..96b1e83335 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/tortls.c b/tags/tor-0_0_2pre13/src/common/tortls.c new file mode 100644 index 0000000000..dafbbee6da --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/tortls.c @@ -0,0 +1,554 @@ +/* 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 + +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; + int err; + + 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 + 24*60*60*365; + 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; + + err = 0; + goto done; + error: + err = 1; + done: + if (x509 && err) + X509_free(x509); + 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); + 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; + int i; + + if (!(cert = SSL_get_peer_certificate(tls->ssl))) { + log_fn(LOG_WARN, "Peer has no certificate"); + return -1; + } + if (!(name = X509_get_subject_name(cert))) { + log_fn(LOG_WARN, "Peer certificate has no subject name"); + return -1; + } + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) + return -1; + + lenout = X509_NAME_get_text_by_NID(name, nid, buf, buflen); + if (lenout == -1) + return -1; + if (strspn(buf, LEGAL_NICKNAME_CHARACTERS) != lenout) { + log_fn(LOG_WARN, "Peer certificate nickname has illegal characters."); + return -1; + } + return 0; +} + +/* 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; + crypto_pk_env_t *r = NULL; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) + return NULL; + + now = time(NULL); + if (X509_cmp_time(X509_get_notBefore(cert), &now) > 0) { + log_fn(LOG_WARN,"X509_get_notBefore(cert) is in the future"); + goto done; + } + if (X509_cmp_time(X509_get_notAfter(cert), &now) < 0) { + log_fn(LOG_WARN,"X509_get_notAfter(cert) is in the past"); + 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_2pre13/src/common/tortls.h b/tags/tor-0_0_2pre13/src/common/tortls.h new file mode 100644 index 0000000000..fdfa161920 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/common/util.c b/tags/tor-0_0_2pre13/src/common/util.c new file mode 100644 index 0000000000..ccc130030e --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/util.c @@ -0,0 +1,533 @@ +/* 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_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; +} + +/* + * 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; +} + +/* + * 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 void *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, void *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, 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. */ + log_fn(LOG_WARN,"Line has keyword '%s' but no value. Skipping.",s); + 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; +} + diff --git a/tags/tor-0_0_2pre13/src/common/util.h b/tags/tor-0_0_2pre13/src/common/util.h new file mode 100644 index 0000000000..6358654bff --- /dev/null +++ b/tags/tor-0_0_2pre13/src/common/util.h @@ -0,0 +1,83 @@ +/* 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 + +#define xfree(p) do {if(p) {free(p); (p)=NULL;}} while(0) /* XXX use everywhere? */ +void *tor_malloc(size_t size); +void *tor_realloc(void *ptr, size_t size); +char *tor_strdup(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); + +int write_all(int fd, const void *buf, size_t count); +int read_all(int fd, void *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); + +/* 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_2pre13/src/config/Makefile.am b/tags/tor-0_0_2pre13/src/config/Makefile.am new file mode 100644 index 0000000000..8ff9132e7d --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/Makefile.am @@ -0,0 +1,5 @@ +confdir = $(sysconfdir)/tor + +EXTRA_DIST = dirservers + +conf_DATA = dirservers torrc sample-server-torrc diff --git a/tags/tor-0_0_2pre13/src/config/dirservers b/tags/tor-0_0_2pre13/src/config/dirservers new file mode 100644 index 0000000000..19a82692ef --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/config/moria1-orrc b/tags/tor-0_0_2pre13/src/config/moria1-orrc new file mode 100644 index 0000000000..b4a88c6902 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/moria1-orrc @@ -0,0 +1,13 @@ + +DataDirectory moria1 +Nickname moria1 + +ORPort 9001 +APPort 9021 +DirPort 9031 + +OnionRouter 1 + +# List of routers +RouterFile ../config/dirservers + diff --git a/tags/tor-0_0_2pre13/src/config/moria2-orrc b/tags/tor-0_0_2pre13/src/config/moria2-orrc new file mode 100644 index 0000000000..33353d56dc --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/moria2-orrc @@ -0,0 +1,13 @@ + +DataDirectory moria2 +Nickname moria2 + +ORPort 9002 +APPort 9022 +DirPort 9032 + +OnionRouter 1 + +# List of routers +RouterFile ../config/dirservers + diff --git a/tags/tor-0_0_2pre13/src/config/moria3-orrc b/tags/tor-0_0_2pre13/src/config/moria3-orrc new file mode 100644 index 0000000000..d1e34d6e79 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/moria3-orrc @@ -0,0 +1,13 @@ + +DataDirectory moria3 +Nickname moria3 + +ORPort 9003 +APPort 9023 +DirPort 9033 + +OnionRouter 1 + +# List of routers +RouterFile ../config/dirservers + diff --git a/tags/tor-0_0_2pre13/src/config/oprc b/tags/tor-0_0_2pre13/src/config/oprc new file mode 100644 index 0000000000..ce86f32adf --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/oprc @@ -0,0 +1,12 @@ +# Configuration file for a typical tor client +# (listen for applications only) + +# List of routers. Tor nodes start out knowing about the directory +# servers, and from them they get a list of currently up nodes. +RouterFile ../config/dirservers + +# Ports for various services. Comment out if you're not running that +# service. +#ORPort 9001 +APPort 9050 + diff --git a/tags/tor-0_0_2pre13/src/config/sample-orrc b/tags/tor-0_0_2pre13/src/config/sample-orrc new file mode 100644 index 0000000000..7a619710ae --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/sample-orrc @@ -0,0 +1,20 @@ +# Configuration file for a typical tor node + +# The directory for keeping all the config/data for this node +DataDirectory moria4 +# A unique handle for this node +Nickname moria4 + +# Ports for various services. Comment out or set to 0 if you're not +# offering that service. +ORPort 9004 # listening for cell-speaking connections +APPort 9024 # listening for socks-speaking connections +#DirPort 0 + +# Leave this set, or we'll be treated like a client. +OnionRouter 1 + +# List of routers. Tor nodes start out knowing about the directory +# servers, and from them they get a list of currently up nodes. +RouterFile ../config/dirservers + diff --git a/tags/tor-0_0_2pre13/src/config/sample-server-torrc.in b/tags/tor-0_0_2pre13/src/config/sample-server-torrc.in new file mode 100644 index 0000000000..3bb0f21629 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/sample-server-torrc.in @@ -0,0 +1,20 @@ +# Configuration file for a typical tor node + +# The directory for keeping all the config/data for this node +DataDirectory @LOCALSTATEDIR@/lib/tor +# A unique handle for this node +Nickname moria4 + +# Ports for various services. Comment out or set to 0 if you're not +# offering that service. +ORPort 9004 # listening for cell-speaking connections +APPort 9024 # listening for socks-speaking connections +#DirPort 0 + +# Leave this set, or we'll be treated like a client. +OnionRouter 1 + +# 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 + diff --git a/tags/tor-0_0_2pre13/src/config/torrc.in b/tags/tor-0_0_2pre13/src/config/torrc.in new file mode 100644 index 0000000000..361840e4e5 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/config/torrc.in @@ -0,0 +1,12 @@ +# Configuration file for a typical tor client +# (listen for applications only) + +# 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 + +# Ports for various services. Comment out if you're not running that +# service. +#ORPort 9001 +APPort 9050 + diff --git a/tags/tor-0_0_2pre13/src/or/.cvsignore b/tags/tor-0_0_2pre13/src/or/.cvsignore new file mode 100644 index 0000000000..14f9ecd6db --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +tor +test +.deps diff --git a/tags/tor-0_0_2pre13/src/or/Makefile.am b/tags/tor-0_0_2pre13/src/or/Makefile.am new file mode 100644 index 0000000000..cf33b14641 --- /dev/null +++ b/tags/tor-0_0_2pre13/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 routers.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 routers.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_2pre13/src/or/buffers.c b/tags/tor-0_0_2pre13/src/or/buffers.c new file mode 100644 index 0000000000..9a8e64ccaf --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/buffers.c @@ -0,0 +1,555 @@ +/* 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)) + return -1; + + /* this is the point where you would grow the buffer, if you want to */ + + if (string_len + buf->datalen > buf->len) { /* we're out of luck */ + log_fn(LOG_WARN, "buflen too small. Time to implement growing dynamic bufs."); + 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: + * copy 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) { + memcpy(headers_out,buf->mem,headerlen); + headers_out[headerlen] = 0; /* null terminate it */ + } + if(body_out) { + 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 addr_out, + * then pull the handshake off the buf, assign to addr_out and port_out, + * 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, char *socks_version, + char *reply, int *replylen, + char *addr_out, int max_addrlen, + uint16_t *port_out) { + 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(*socks_version != 5) { /* we need to negotiate a method */ + unsigned char nummethods = (unsigned char)*(buf->mem+1); + assert(!*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."); + *replylen = 2; /* 2 bytes of response */ + *reply = 5; /* socks5 reply */ + *(reply+1) = 0xFF; /* reject all methods */ + return -1; + } + buf_remove_from_front(buf,2+nummethods);/* remove packet from buf */ + + *replylen = 2; /* 2 bytes of response */ + *reply = 5; /* socks5 reply */ + *(reply+1) = 0; /* choose the 'no auth' method */ + *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_addrlen) { + log_fn(LOG_WARN,"socks5 IP takes %d bytes, which doesn't fit in %d", + strlen(tmpbuf)+1,max_addrlen); + return -1; + } + strcpy(addr_out,tmpbuf); + *port_out = 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_addrlen) { + log_fn(LOG_WARN,"socks5 hostname is %d bytes, which doesn't fit in %d", + len+1,max_addrlen); + return -1; + } + memcpy(addr_out,buf->mem+5,len); + addr_out[len] = 0; + *port_out = 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 */ + + *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; + } + + *port_out = ntohs(*(uint16_t*)(buf->mem+2)); + destip = ntohl(*(uint32_t*)(buf->mem+4)); + if(!*port_out || !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_addrlen) { + log_fn(LOG_WARN,"socks4 addr (%d bytes) too long.", 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_addrlen <= next-startaddr) { + log_fn(LOG_WARN,"Destaddr too long."); + return -1; + } + } + log_fn(LOG_DEBUG,"Everything is here. Success."); + strcpy(addr_out, 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_2pre13/src/or/circuit.c b/tags/tor-0_0_2pre13/src/or/circuit.c new file mode 100644 index 0000000000..be05caafd7 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/circuit.c @@ -0,0 +1,998 @@ +/* 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 void circuit_free_cpath(crypt_path_t *cpath); +static void circuit_free_cpath_node(crypt_path_t *victim); +static aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_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[] = { + "receiving the onion", /* 0 */ + "waiting to process create", /* 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(aci_t p_aci, connection_t *p_conn) { + circuit_t *circ; + + circ = (circuit_t *)tor_malloc(sizeof(circuit_t)); + memset(circ,0,sizeof(circuit_t)); /* zero it out */ + + circ->timestamp_created = time(NULL); + + circ->p_aci = p_aci; + circ->p_conn = p_conn; + + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + + /* ACIs */ + circ->p_aci = p_aci; + /* circ->n_aci remains 0 because we haven't identified the next hop yet */ + + circ->package_window = CIRCWINDOW_START; + circ->deliver_window = CIRCWINDOW_START; + + circuit_add(circ); + + return circ; +} + +void circuit_free(circuit_t *circ) { + if (circ->n_crypto) + crypto_free_cipher_env(circ->n_crypto); + if (circ->p_crypto) + crypto_free_cipher_env(circ->p_crypto); + circuit_free_cpath(circ->cpath); + free(circ); +} + +static void circuit_free_cpath(crypt_path_t *cpath) { + crypt_path_t *victim, *head=cpath; + + if(!cpath) + return; + + /* it's a doubly linked list, so we have to notice when we've + * gone through it once. */ + while(cpath->next && cpath->next != head) { + victim = cpath; + cpath = victim->next; + circuit_free_cpath_node(victim); + } + + circuit_free_cpath_node(cpath); +} + +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->handshake_state) + crypto_dh_free(victim->handshake_state); + free(victim); +} + +/* return 0 if can't get a unique aci. */ +static aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) { + aci_t test_aci; + connection_t *conn; + uint16_t high_bit; + + high_bit = (aci_type == ACI_TYPE_HIGHER) ? 1<<15 : 0; + conn = connection_exact_get_by_addr_port(addr,port); + /* XXX race condition: if conn is marked_for_close it won't be noticed */ + if (!conn) + return (1|high_bit); /* No connection exists; conflict is impossible. */ + + do { + /* Sequentially iterate over test_aci=1...1<<15-1 until we find an + * aci such that (high_bit|test_aci) is not already used. */ + /* XXX Will loop forever if all aci's in our range are used. + * This matters because it's an external DoS vulnerability. */ + test_aci = conn->next_aci++; + if (test_aci == 0 || test_aci >= 1<<15) { + test_aci = 1; + conn->next_aci = 2; + } + test_aci |= high_bit; + } while(circuit_get_by_aci_conn(test_aci, conn)); + return test_aci; +} + +circuit_t *circuit_enumerate_by_naddr_nport(circuit_t *circ, uint32_t naddr, uint16_t nport) { + + if(!circ) /* use circ if it's defined, else start from the beginning */ + circ = global_circuitlist; + else + circ = circ->next; + + for( ; circ; circ = circ->next) { + if(circ->n_addr == naddr && circ->n_port == nport) + return circ; + } + return NULL; +} + +circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_aci == aci) { + 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_aci == aci) { + 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; +} + +circuit_t *circuit_get_newest_open(void) { + circuit_t *circ, *bestcirc=NULL; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->cpath && circ->state == CIRCUIT_STATE_OPEN && circ->n_conn && (!bestcirc || + bestcirc->timestamp_created < circ->timestamp_created)) { + log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.", circ->n_conn->address, circ->n_port, circ->n_aci); + assert(circ->n_aci); + bestcirc = circ; + } + } + return bestcirc; +} + +int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction, crypt_path_t *layer_hint) { + connection_t *conn=NULL; + char recognized=0; + char buf[256]; + + assert(cell && circ); + assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN); + + buf[0] = cell->length; + memcpy(buf+1, cell->payload, CELL_PAYLOAD_SIZE); + + log_fn(LOG_DEBUG,"direction %d, streamid %d before crypt.", cell_direction, *(int*)(cell->payload+1)); + + if(relay_crypt(circ, buf, 1+CELL_PAYLOAD_SIZE, cell_direction, &layer_hint, &recognized, &conn) < 0) { + log_fn(LOG_WARN,"relay crypt failed. Dropping connection."); + return -1; + } + + cell->length = buf[0]; + memcpy(cell->payload, buf+1, CELL_PAYLOAD_SIZE); + + if(recognized) { + if(cell_direction == CELL_DIRECTION_OUT) { + ++stats_n_relay_cells_delivered; + log_fn(LOG_DEBUG,"Sending to exit."); + return connection_edge_process_relay_cell(cell, circ, conn, EDGE_EXIT, NULL); + } + if(cell_direction == CELL_DIRECTION_IN) { + ++stats_n_relay_cells_delivered; + log_fn(LOG_DEBUG,"Sending to AP."); + return connection_edge_process_relay_cell(cell, circ, conn, EDGE_AP, layer_hint); + } + } + + ++stats_n_relay_cells_relayed; + /* not recognized. pass it on. */ + if(cell_direction == CELL_DIRECTION_OUT) + conn = circ->n_conn; + else + conn = circ->p_conn; + + if(!conn) { //|| !connection_speaks_cells(conn)) { + log_fn(LOG_INFO,"Didn't recognize cell (%d), but circ stops here! Dropping.", *(int *)(cell->payload+1)); + return 0; + } + + log_fn(LOG_DEBUG,"Passing on unrecognized cell."); + connection_or_write_cell_to_buf(cell, conn); + return 0; +} + +int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction, + crypt_path_t **layer_hint, char *recognized, connection_t **conn) { + crypt_path_t *thishop; + char out[256]; + + assert(circ && in && recognized && conn); + + assert(inlen < 256); + + 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); + + log_fn(LOG_DEBUG,"before decrypt: %d",*(int*)(in+2)); + /* decrypt */ + if(crypto_cipher_decrypt(thishop->b_crypto, in, inlen, out)) { + log_fn(LOG_WARN,"Error performing onion decryption: %s", crypto_perror()); + return -1; + } + memcpy(in,out,inlen); + log_fn(LOG_DEBUG,"after decrypt: %d",*(int*)(in+2)); + + if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn))) { + *layer_hint = thishop; + return 0; + } + + thishop = thishop->next; + } while(thishop != circ->cpath && thishop->state == CPATH_STATE_OPEN); + log_fn(LOG_INFO,"in-cell at OP not recognized. Dropping."); + return 0; + } else { /* we're in the middle. Just one crypt. */ + + log_fn(LOG_DEBUG,"before encrypt: %d",*(int*)(in+2)); + if(crypto_cipher_encrypt(circ->p_crypto, in, inlen, out)) { + log_fn(LOG_WARN,"Onion encryption failed for ACI %u: %s", + circ->p_aci, crypto_perror()); + return -1; + } + memcpy(in,out,inlen); + log_fn(LOG_DEBUG,"after encrypt: %d",*(int*)(in+2)); + + log_fn(LOG_DEBUG,"Skipping recognized check, because we're not the OP."); + /* don't check for recognized. only the OP can recognize a stream on the way back. */ + + } + } else if(cell_direction == CELL_DIRECTION_OUT) { + if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */ + + thishop = *layer_hint; /* we already know which layer, from when we package_raw_inbuf'ed */ + /* moving from last to first hop */ + do { + assert(thishop); + + log_fn(LOG_DEBUG,"before encrypt: %d",*(int*)(in+2)); + if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, out)) { + log_fn(LOG_WARN,"Error performing encryption: %s", crypto_perror()); + return -1; + } + memcpy(in,out,inlen); + log_fn(LOG_DEBUG,"after encrypt: %d",*(int*)(in+2)); + + thishop = thishop->prev; + } while(thishop != circ->cpath->prev); + } else { /* we're in the middle. Just one crypt. */ + + if(crypto_cipher_decrypt(circ->n_crypto,in, inlen, out)) { + log_fn(LOG_WARN,"Decryption failed for ACI %u: %s", + circ->n_aci, crypto_perror()); + return -1; + } + memcpy(in,out,inlen); + + if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn))) + return 0; + + } + } else { + log_fn(LOG_ERR,"unknown cell direction %d.", cell_direction); + assert(0); + } + + return 0; +} + +int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, connection_t **conn) { +/* FIXME can optimize by passing thishop in */ + connection_t *tmpconn; + + if(!memcmp(stream,ZERO_STREAM,STREAM_ID_SIZE)) { + log_fn(LOG_DEBUG,"It's the zero stream. Recognized."); + return 1; /* the zero stream is always recognized */ + } + log_fn(LOG_DEBUG,"not the zero stream."); + + if(cell_direction == CELL_DIRECTION_OUT) + tmpconn = circ->n_streams; + else + tmpconn = circ->p_streams; + + if(!tmpconn) { + log_fn(LOG_DEBUG,"No conns. Not recognized."); + return 0; + } + + for( ; tmpconn; tmpconn=tmpconn->next_stream) { + if(!memcmp(stream,tmpconn->stream_id, STREAM_ID_SIZE)) { + log_fn(LOG_DEBUG,"recognized stream %d.", *(int*)stream); + *conn = tmpconn; + return 1; + } + log_fn(LOG_DEBUG,"considered stream %d, not it.",*(int*)tmpconn->stream_id); + } + + log_fn(LOG_DEBUG,"Didn't recognize on this iteration of decryption."); + return 0; + +} + +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; +} + +int circuit_consider_sending_sendme(circuit_t *circ, int edge_type, crypt_path_t *layer_hint) { + cell_t cell; + + assert(circ); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_SENDME); + SET_CELL_STREAM_ID(cell, ZERO_STREAM); + + cell.length = RELAY_HEADER_SIZE; + if(edge_type == EDGE_AP) { /* i'm the AP */ + cell.aci = circ->n_aci; + while(layer_hint->deliver_window < CIRCWINDOW_START-CIRCWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"deliver_window %d, Queueing sendme forward.", layer_hint->deliver_window); + layer_hint->deliver_window += CIRCWINDOW_INCREMENT; + if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_OUT, layer_hint) < 0) { + return -1; + } + } + } else if(edge_type == EDGE_EXIT) { /* i'm the exit */ + cell.aci = circ->p_aci; + while(circ->deliver_window < CIRCWINDOW_START-CIRCWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"deliver_window %d, Queueing sendme back.", circ->deliver_window); + circ->deliver_window += CIRCWINDOW_INCREMENT; + if(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_IN, layer_hint) < 0) { + return -1; + } + } + } + return 0; +} + +void circuit_close(circuit_t *circ) { + connection_t *conn; + circuit_t *youngest=NULL; + + assert(circ); + if(options.APPort) { + youngest = circuit_get_newest_open(); + log_fn(LOG_DEBUG,"youngest %d, circ %d.",(int)youngest, (int)circ); + } + circuit_remove(circ); + if(circ->n_conn) + connection_send_destroy(circ->n_aci, circ->n_conn); + for(conn=circ->n_streams; conn; conn=conn->next_stream) { + connection_send_destroy(circ->n_aci, conn); + } + if(circ->p_conn) + connection_send_destroy(circ->n_aci, circ->p_conn); + for(conn=circ->p_streams; conn; conn=conn->next_stream) { + connection_send_destroy(circ->p_aci, conn); + } + if(options.APPort && youngest == circ) { /* check this after we've sent the destroys, to reduce races */ + /* our current circuit just died. Launch another one pronto. */ + log_fn(LOG_INFO,"Youngest circuit dying. Launching a replacement."); + circuit_launch_new(1); + } + 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; + + if(!connection_speaks_cells(conn)) { + /* it's an edge conn. need to remove it from the linked list of + * conn's for this circuit. Send an 'end' relay command. + * But don't kill the circuit. + */ + + circ = circuit_get_by_conn(conn); + if(!circ) + return; + + if(conn == circ->p_streams) { + circ->p_streams = conn->next_stream; + goto send_end; + } + if(conn == circ->n_streams) { + circ->n_streams = conn->next_stream; + goto send_end; + } + 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; + goto send_end; + } + 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; + goto send_end; + } + log_fn(LOG_ERR,"edge conn not in circuit's list?"); + assert(0); /* should never get here */ +send_end: + connection_edge_send_command(conn, circ, RELAY_COMMAND_END, + NULL, 0, conn->cpath_layer); + return; + } + + /* this connection speaks cells. 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); + } +} + +/* FIXME this now leaves some out */ +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) + log(severity, "Conn %d has App-ward circuit: aci %d (other side %d), state %d (%s)", + conn->poll_index, circ->p_aci, circ->n_aci, circ->state, circuit_state_to_string[circ->state]); + for(tmpconn=circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + log(severity,"Conn %d has App-ward circuit: aci %d (other side %d), state %d (%s)", + conn->poll_index, circ->p_aci, circ->n_aci, circ->state, circuit_state_to_string[circ->state]); + } + } + if(circ->n_conn == conn) + log(severity,"Conn %d has Exit-ward circuit: aci %d (other side %d), state %d (%s)", + conn->poll_index, circ->n_aci, circ->p_aci, circ->state, circuit_state_to_string[circ->state]); + for(tmpconn=circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + log(severity,"Conn %d has Exit-ward circuit: aci %d (other side %d), state %d (%s)", + conn->poll_index, circ->n_aci, circ->p_aci, circ->state, circuit_state_to_string[circ->state]); + } + } + } +} + +void circuit_expire_unused_circuits(void) { + circuit_t *circ, *tmpcirc; + circuit_t *youngest; + + youngest = circuit_get_newest_open(); + + circ = global_circuitlist; + while(circ) { + tmpcirc = circ; + circ = circ->next; + if(tmpcirc != youngest && !tmpcirc->p_conn && !tmpcirc->p_streams) { + log_fn(LOG_DEBUG,"Closing n_aci %d",tmpcirc->n_aci); + circuit_close(tmpcirc); + } + } +} + +/* failure_status code: negative means reset failures to 0. Other values mean + * add that value to the current number of failures, then if we don't have too + * many failures on record, try to make a new circuit. + */ +void circuit_launch_new(int failure_status) { + static int failures=0; + + if(!options.APPort) /* we're not an application proxy. no need for circuits. */ + return; + + if(failure_status == -1) { /* I was called because a circuit succeeded */ + failures = 0; + return; + } + + failures += failure_status; + +retry_circuit: + + if(failures > 5) { + log_fn(LOG_INFO,"Giving up for now, %d failures.", failures); + return; + } + + if(circuit_establish_circuit() < 0) { + failures++; + goto retry_circuit; + } + + failures = 0; + return; +} + +int circuit_establish_circuit(void) { + routerinfo_t *firsthop; + connection_t *n_conn; + circuit_t *circ; + + circ = circuit_new(0, NULL); /* sets circ->p_aci and circ->p_conn */ + circ->state = CIRCUIT_STATE_OR_WAIT; + circ->cpath = onion_generate_cpath(&firsthop); + if(!circ->cpath) { + log_fn(LOG_INFO,"Generating cpath 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.OnionRouter) { /* we would be connected if he were up. but 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; + + log_fn(LOG_DEBUG,"Starting."); + circ = circuit_enumerate_by_naddr_nport(NULL, or_conn->addr, or_conn->port); + for(;;) { + if(!circ) + return; + + log_fn(LOG_DEBUG,"Found circ, sending onion skin."); + 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); + return; /* FIXME will want to try the other circuits too? */ + } + circ = circuit_enumerate_by_naddr_nport(circ, or_conn->addr, or_conn->port); + } +} + +int circuit_send_next_onion_skin(circuit_t *circ) { + cell_t cell; + crypt_path_t *hop; + routerinfo_t *router; + + assert(circ && circ->cpath); + + if(circ->cpath->state == CPATH_STATE_CLOSED) { + + log_fn(LOG_DEBUG,"First skin; sending create cell."); + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, ACI_TYPE_BOTH); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATE; + cell.aci = circ->n_aci; + cell.length = DH_ONIONSKIN_LEN; + + 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."); + for(hop=circ->cpath->next; + hop != circ->cpath && hop->state == CPATH_STATE_OPEN; + hop=hop->next) ; + if(hop == circ->cpath) { /* done building the circuit. whew. */ + circ->state = CIRCUIT_STATE_OPEN; + log_fn(LOG_INFO,"circuit built!"); + return 0; + } + + router = router_get_by_addr_port(hop->addr,hop->port); + if(!router) { + log_fn(LOG_WARN,"couldn't lookup router %d:%d",hop->addr,hop->port); + return -1; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + cell.aci = circ->n_aci; + SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_EXTEND); + SET_CELL_STREAM_ID(cell, ZERO_STREAM); + + cell.length = RELAY_HEADER_SIZE + 6 + DH_ONIONSKIN_LEN; + *(uint32_t*)(cell.payload+RELAY_HEADER_SIZE) = htonl(hop->addr); + *(uint16_t*)(cell.payload+RELAY_HEADER_SIZE+4) = htons(hop->port); + if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), cell.payload+RELAY_HEADER_SIZE+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(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION_OUT, hop->prev) < 0) { + log_fn(LOG_WARN,"failed to deliver extend cell. Closing."); + return -1; + } + 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; + aci_t aci_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_DEBUG,"Next router (%s:%d) not connected. Closing.", inet_ntoa(in), circ->n_port); + /* XXX later we should fail more gracefully here, like with a 'truncated' */ + return -1; + } + + 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); + + aci_type = decide_aci_type(options.Nickname, n_conn->nickname); + + log_fn(LOG_DEBUG,"aci_type = %u.",aci_type); + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type); + if(!circ->n_aci) { + log_fn(LOG_WARN,"failed to get unique aci."); + return -1; + } + log_fn(LOG_DEBUG,"Chosen ACI %u.",circ->n_aci); + + memset(&newcell, 0, sizeof(cell_t)); + newcell.command = CELL_CREATE; + newcell.aci = circ->n_aci; + newcell.length = DH_ONIONSKIN_LEN; + + memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+6, DH_ONIONSKIN_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[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, 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_DEBUG,"hop %d init cipher forward %d, backward %d.", (uint32_t)hop, *(uint32_t*)keys, *(uint32_t*)(keys+16)); + if (!(hop->f_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys,iv,1))) { + log(LOG_WARN,"forward cipher initialization failed."); + return -1; + } + + if (!(hop->b_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+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); + + 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.", *(int*)stream->stream_id); +/*ENDCLOSE*/ stream->marked_for_close = 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); + } else { + assert(c->n_crypto); + assert(c->p_crypto); + } + } + 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_2pre13/src/or/command.c b/tags/tor-0_0_2pre13/src/or/command.c new file mode 100644 index 0000000000..4e090909f6 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/command.c @@ -0,0 +1,226 @@ +/* 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_aci_conn(cell->aci, conn); + + if(circ) { + log_fn(LOG_WARN,"received CREATE cell (aci %d) for known circ. Dropping.", cell->aci); + return; + } + + circ = circuit_new(cell->aci, conn); + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + if(cell->length != DH_ONIONSKIN_LEN) { + log_fn(LOG_WARN,"Bad cell length %d. Dropping.", cell->length); + circuit_close(circ); + return; + } + + memcpy(circ->onionskin,cell->payload,cell->length); + + /* 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_aci_conn(cell->aci, conn); + + if(!circ) { + log_fn(LOG_INFO,"(aci %d) unknown circ (probably got a destroy earlier). Dropping.", cell->aci); + return; + } + + if(circ->n_aci != cell->aci) { + log_fn(LOG_WARN,"got created cell from OPward? Closing."); + circuit_close(circ); + return; + } + assert(cell->length == DH_KEY_LEN); + + 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_WARN,"circuit_send_next_onion_skin failed."); + circuit_close(circ); + 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, DH_KEY_LEN, NULL); + } +} + +static void command_process_relay_cell(cell_t *cell, connection_t *conn) { + circuit_t *circ; + + circ = circuit_get_by_aci_conn(cell->aci, conn); + + if(!circ) { + log_fn(LOG_INFO,"unknown circuit %d. Dropping.", cell->aci); + return; + } + + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + log_fn(LOG_WARN,"circuit in create_wait. Closing."); + circuit_close(circ); + return; + } + + if(cell->aci == circ->p_aci) { /* it's an outgoing cell */ + cell->aci = circ->n_aci; /* switch it */ + if(circuit_deliver_relay_cell(cell, circ, CELL_DIRECTION_OUT, conn->cpath_layer) < 0) { + log_fn(LOG_WARN,"circuit_deliver_relay_cell (forward) failed. Closing."); + circuit_close(circ); + return; + } + } else { /* it's an ingoing cell */ + cell->aci = circ->p_aci; /* switch it */ + if(circuit_deliver_relay_cell(cell, circ, CELL_DIRECTION_IN, NULL) < 0) { + log_fn(LOG_WARN,"circuit_deliver_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_aci_conn(cell->aci, conn); + + if(!circ) { + log_fn(LOG_INFO,"unknown circuit %d. Dropping.", cell->aci); + return; + } + + log_fn(LOG_DEBUG,"Received for aci %d.",cell->aci); + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + onion_pending_remove(circ); + } + + if(cell->aci == circ->p_aci || 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_2pre13/src/or/config.c b/tags/tor-0_0_2pre13/src/or/config.c new file mode 100644 index 0000000000..977af49816 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/config.c @@ -0,0 +1,334 @@ +/* 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: + *(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, "LogLevel", CONFIG_TYPE_STRING, &options->LogLevel) || + config_compare(list, "LogFile", CONFIG_TYPE_STRING, &options->LogFile) || + config_compare(list, "DebugLogFile", CONFIG_TYPE_STRING, &options->DebugLogFile) || + config_compare(list, "DataDirectory", CONFIG_TYPE_STRING, &options->DataDirectory) || + config_compare(list, "RouterFile", CONFIG_TYPE_STRING, &options->RouterFile) || + config_compare(list, "PidFile", CONFIG_TYPE_STRING, &options->PidFile) || + config_compare(list, "Nickname", CONFIG_TYPE_STRING, &options->Nickname) || + config_compare(list, "Address", CONFIG_TYPE_STRING, &options->Address) || + config_compare(list, "ExitPolicy", CONFIG_TYPE_STRING, &options->ExitPolicy) || + + /* int options */ + config_compare(list, "MaxConn", CONFIG_TYPE_INT, &options->MaxConn) || + config_compare(list, "APPort", CONFIG_TYPE_INT, &options->APPort) || + config_compare(list, "ORPort", CONFIG_TYPE_INT, &options->ORPort) || + config_compare(list, "DirPort", CONFIG_TYPE_INT, &options->DirPort) || + config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_INT, &options->DirFetchPostPeriod) || + config_compare(list, "KeepalivePeriod", CONFIG_TYPE_INT, &options->KeepalivePeriod) || + config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) || + config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) || + config_compare(list, "TotalBandwidth", CONFIG_TYPE_INT, &options->TotalBandwidth) || + config_compare(list, "NumCpus", CONFIG_TYPE_INT, &options->NumCpus) || + + config_compare(list, "OnionRouter", CONFIG_TYPE_BOOL, &options->OnionRouter) || + config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) || + config_compare(list, "LinkPadding", CONFIG_TYPE_BOOL, &options->LinkPadding) || + config_compare(list, "IgnoreVersion", CONFIG_TYPE_BOOL, &options->IgnoreVersion) || + config_compare(list, "RunAsDaemon", CONFIG_TYPE_BOOL, &options->RunAsDaemon) || + + /* float options */ + config_compare(list, "CoinWeight", CONFIG_TYPE_DOUBLE, &options->CoinWeight) + + ) { + /* then we're ok. it matched something. */ + } else { + log_fn(LOG_WARN,"Ignoring unknown keyword '%s'.",list->key); + } + + list = list->next; + } +} + +/* 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; + +/* give reasonable values for each option. Defaults to zero. */ + memset(options,0,sizeof(or_options_t)); + options->LogLevel = "info"; + options->ExitPolicy = "reject 127.0.0.1:*,reject 18.244.0.188:25,accept *:*"; + options->loglevel = LOG_INFO; + options->PidFile = "tor.pid"; + options->DataDirectory = NULL; + options->CoinWeight = 0.1; + options->MaxConn = 900; + options->DirFetchPostPeriod = 600; + options->KeepalivePeriod = 300; + options->MaxOnionsPending = 10; + options->NewCircuitPeriod = 60; /* once a minute */ + options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */ + options->NumCpus = 1; + +/* 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 */ + + 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->OnionRouter && options->ORPort == 0) { + log(LOG_WARN,"If OnionRouter is set, then ORPort must be positive."); + result = -1; + } + + if(options->OnionRouter && options->DataDirectory == NULL) { + log(LOG_WARN,"DataDirectory option required for OnionRouter, but not found."); + result = -1; + } + + if(options->OnionRouter && options->Nickname == NULL) { + log_fn(LOG_WARN,"Nickname required for OnionRouter, but not found."); + result = -1; + } + + if(options->APPort < 0) { + log(LOG_WARN,"APPort option can't be negative."); + result = -1; + } + + if(options->DirPort < 0) { + log(LOG_WARN,"DirPort option can't be negative."); + result = -1; + } + + if(options->APPort > 1 && + (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0)) { + log(LOG_WARN,"CoinWeight 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_2pre13/src/or/connection.c b/tags/tor-0_0_2pre13/src/or/connection.c new file mode 100644 index 0000000000..2c8a0032a0 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/connection.c @@ -0,0 +1,808 @@ +/* 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 OR connection", /* 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 = (connection_t *)tor_malloc(sizeof(connection_t)); + memset(conn,0,sizeof(connection_t)); /* zero it out to start */ + 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(); + } + + 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); + } + if(conn->address) + 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); + if (conn->nickname) + free(conn->nickname); + + if(conn->s >= 0) { + log_fn(LOG_INFO,"closing fd %d.",conn->s); + close(conn->s); + } + free(conn); +} + +int connection_create_listener(struct sockaddr_in *bindaddr, int type) { + connection_t *conn; + int s; + int one=1; + + s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) { + log_fn(LOG_WARN,"Socket creation failed."); + return -1; + } + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one)); + + if(bind(s,(struct sockaddr *)bindaddr,sizeof(*bindaddr)) < 0) { + log(LOG_WARN,"Could not bind to port %u: %s",ntohs(bindaddr->sin_port), + strerror(errno)); + return -1; + } + + if(listen(s,SOMAXCONN) < 0) { + log(LOG_WARN,"Could not listen on port %u: %s",ntohs(bindaddr->sin_port), + 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], ntohs(bindaddr->sin_port)); + + 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((void *)&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. */ + perror("connect"); + log_fn(LOG_INFO,"Connect() to %s:%u failed.",address,port); + 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; +} + +/* start all connections that should be up but aren't */ +int retry_all_connections(uint16_t or_listenport, uint16_t ap_listenport, uint16_t dir_listenport) { + struct sockaddr_in bindaddr; /* where to bind */ + + if(or_listenport) { + router_retry_connections(); + } + + memset(&bindaddr,0,sizeof(struct sockaddr_in)); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* anyone can connect */ + + if(or_listenport) { + bindaddr.sin_port = htons(or_listenport); + if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) { + connection_create_listener(&bindaddr, CONN_TYPE_OR_LISTENER); + } + } + + if(dir_listenport) { + bindaddr.sin_port = htons(dir_listenport); + if(!connection_get_by_type(CONN_TYPE_DIR_LISTENER)) { + connection_create_listener(&bindaddr, CONN_TYPE_DIR_LISTENER); + } + } + + if(ap_listenport) { + bindaddr.sin_port = htons(ap_listenport); + bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* the AP listens only on localhost! */ + if(!connection_get_by_type(CONN_TYPE_AP_LISTENER)) { + connection_create_listener(&bindaddr, CONN_TYPE_AP_LISTENER); + } + } + + 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 = 10*(CELL_NETWORK_SIZE); + } else { + at_most = 10*(CELL_PAYLOAD_SIZE - RELAY_HEADER_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); +} + +int connection_flush_buf(connection_t *conn) { + return flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); +} + +/* 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( (!connection_speaks_cells(conn)) || + (!connection_state_is_open(conn)) || + (options.LinkPadding == 0) ) { + /* connection types other than or, or or not in 'open' state, should flush immediately */ + /* also flush immediately if we're not doing LinkPadding, since otherwise it will never flush */ + connection_start_writing(conn); + conn->outbuf_flushlen += len; + } + + 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; + } +} + +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;imarked_for_close && + !crypto_pk_cmp_keys(conn->onion_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->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(aci_t aci, connection_t *conn) { + cell_t cell; + + assert(conn); + + if(!connection_speaks_cells(conn)) { + log_fn(LOG_INFO,"Aci %d: At an edge. Marking connection for close.", aci); +/*ENDCLOSE*/ conn->marked_for_close = 1; + return 0; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.aci = aci; + cell.command = CELL_DESTROY; + log_fn(LOG_INFO,"Sending destroy (aci %d).",aci); + 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 */ + 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[0]); + 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. */ + } + + 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); + 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_2pre13/src/or/connection_edge.c b/tags/tor-0_0_2pre13/src/or/connection_edge.c new file mode 100644 index 0000000000..b79da2d088 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/connection_edge.c @@ -0,0 +1,632 @@ +/* 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_ap_handshake_process_socks(connection_t *conn); +static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ, + char *destaddr, uint16_t destport); +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, + int edge_type); + +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) +/*ENDCLOSE*/ conn->marked_for_close = 1; + + /* XXX Factor out common logic here and in circuit_about_to_close NM */ + 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); +/*ENDCLOSE*/ return -1; +#endif + } + + switch(conn->state) { + case AP_CONN_STATE_SOCKS_WAIT: +/*ENDCLOSE*/ return connection_ap_handshake_process_socks(conn); + case AP_CONN_STATE_OPEN: + case EXIT_CONN_STATE_OPEN: + if(connection_edge_package_raw_inbuf(conn) < 0) +/*ENDCLOSE*/ 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; +} + +void 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; + int cell_direction; + int is_control_cell=0; + + if(!circ) { + log_fn(LOG_WARN,"no circ. Closing."); + return; + } + + if(!fromconn || relay_command == RELAY_COMMAND_BEGIN) /* XXX more */ + is_control_cell = 1; + + memset(&cell, 0, sizeof(cell_t)); + if(fromconn && fromconn->type == CONN_TYPE_AP) { + cell.aci = circ->n_aci; + cell_direction = CELL_DIRECTION_OUT; + } else { + /* NOTE: if !fromconn, we assume that it's heading towards the OP */ + cell.aci = circ->p_aci; + cell_direction = CELL_DIRECTION_IN; + } + + cell.command = CELL_RELAY; + SET_CELL_RELAY_COMMAND(cell, relay_command); + if(is_control_cell) + SET_CELL_STREAM_ID(cell, ZERO_STREAM); + else + SET_CELL_STREAM_ID(cell, fromconn->stream_id); + + cell.length = RELAY_HEADER_SIZE + payload_len; + 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_deliver_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) { + log_fn(LOG_WARN,"circuit_deliver_relay_cell failed. Closing."); + circuit_close(circ); + } +} + +/* 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) { + int relay_command; + static int num_seen=0; + + assert(cell && circ); + + relay_command = CELL_RELAY_COMMAND(*cell); +// log_fn(LOG_DEBUG,"command %d stream %d", relay_command, 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 && relay_command == RELAY_COMMAND_END) { + log_fn(LOG_INFO,"Exit got end before we're connected. Marking for close."); + conn->marked_for_close = 1; + 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); + } + return 0; + } else { + log_fn(LOG_WARN,"Got an unexpected relay cell, not in 'open' state. Closing."); + return -1; + } + } + + switch(relay_command) { + 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; + } + return connection_exit_begin_conn(cell, circ); + 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."); + return -1; + } + log_fn(LOG_DEBUG,"circ deliver_window now %d.", edge_type == EDGE_AP ? layer_hint->deliver_window : circ->deliver_window); + + if(circuit_consider_sending_sendme(circ, edge_type, layer_hint) < 0) + return -1; + + if(!conn) { + log_fn(LOG_INFO,"relay cell dropped, unknown stream %d.",*(int*)conn->stream_id); + 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. */ + } + +// printf("New text for buf (%d bytes): '%s'", cell->length - RELAY_HEADER_SIZE, cell->payload + RELAY_HEADER_SIZE); + stats_n_data_bytes_received += (cell->length - RELAY_HEADER_SIZE); + connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE, + cell->length - RELAY_HEADER_SIZE, conn); + connection_edge_consider_sending_sendme(conn, edge_type); + return 0; + case RELAY_COMMAND_END: + if(!conn) { + log_fn(LOG_INFO,"end cell dropped, unknown stream %d.",*(int*)conn->stream_id); + return 0; + } + log_fn(LOG_INFO,"end cell for stream %d. Removing stream.",*(int*)conn->stream_id); + +#ifdef HALF_OPEN + conn->done_sending = 1; + shutdown(conn->s, 1); /* XXX check return; refactor NM */ + if (conn->done_receiving) +/*ENDCLOSE*/ conn->marked_for_close = 1; +#endif +/*ENDCLOSE*/ conn->marked_for_close = 1; + break; + 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; + } + return circuit_send_next_onion_skin(circ); + 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_aci, 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; + } + return circuit_truncated(circ, layer_hint); + 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 %d.",*(int*)conn->stream_id); + break; + } + log_fn(LOG_INFO,"Connected! Notifying application."); + if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) { +/*ENDCLOSE*/ conn->marked_for_close = 1; + } + break; + 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 */ + break; + default: + log_fn(LOG_WARN,"unknown relay command %d.",relay_command); + return -1; + } + return 0; +} + +int connection_edge_finished_flushing(connection_t *conn) { + 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. */ + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, + NULL, 0, conn->cpath_layer); + return connection_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, conn->type); + return 0; + case AP_CONN_STATE_SOCKS_WAIT: + connection_stop_writing(conn); + return 0; + default: + log_fn(LOG_WARN,"BUG: called in unexpected 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 > CELL_PAYLOAD_SIZE - RELAY_HEADER_SIZE) { + length = CELL_PAYLOAD_SIZE - RELAY_HEADER_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)); + + connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA, + payload, length, conn->cpath_layer); + + 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; +} + +static void connection_edge_consider_sending_sendme(connection_t *conn, int edge_type) { + circuit_t *circ; + cell_t cell; + + 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; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + SET_CELL_RELAY_COMMAND(cell, RELAY_COMMAND_SENDME); + SET_CELL_STREAM_ID(cell, conn->stream_id); + cell.length += RELAY_HEADER_SIZE; + + if(edge_type == EDGE_EXIT) + cell.aci = circ->p_aci; + else + cell.aci = circ->n_aci; + + 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(circuit_deliver_relay_cell(&cell, circ, CELL_DIRECTION(edge_type), conn->cpath_layer) < 0) { + log_fn(LOG_WARN,"circuit_deliver_relay_cell failed. Closing."); + circuit_close(circ); + return; + } + } +} + +static int connection_ap_handshake_process_socks(connection_t *conn) { + circuit_t *circ; + char destaddr[200]; /* XXX why 200? but not 256, because it won't fit in a cell */ + char reply[256]; + uint16_t destport; + int replylen=0; + int sockshere; + + assert(conn); + + log_fn(LOG_DEBUG,"entered."); + + sockshere = fetch_from_buf_socks(conn->inbuf, &conn->socks_version, reply, &replylen, + destaddr, sizeof(destaddr), &destport); + if(sockshere == -1 || sockshere == 0) { + if(replylen) { /* we should send reply back */ + log_fn(LOG_DEBUG,"reply is already set for us. Using it."); + connection_ap_handshake_socks_reply(conn, reply, 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 */ + + /* find the circuit that we should use, if there is one. */ + circ = circuit_get_newest_open(); + + if(!circ) { + log_fn(LOG_INFO,"No circuit ready. Closing."); + return -1; + } + + circ->dirty = 1; + + /* add it into the linked list of streams on this circuit */ + log_fn(LOG_DEBUG,"attaching new conn to circ. n_aci %d.", circ->n_aci); + conn->next_stream = circ->p_streams; + circ->p_streams = conn; + + assert(circ->cpath && circ->cpath->prev); + assert(circ->cpath->prev->state == CPATH_STATE_OPEN); + conn->cpath_layer = circ->cpath->prev; + + if(connection_ap_handshake_send_begin(conn, circ, destaddr, destport) < 0) { + circuit_close(circ); + return -1; + } + + return 0; +} + +/* deliver the destaddr:destport in a relay cell */ +static int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ, + char *destaddr, uint16_t destport) { + char payload[CELL_PAYLOAD_SIZE]; + int payload_len; + + if(crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id) < 0) + return -1; + /* FIXME check for collisions */ + + memcpy(payload, ap_conn->stream_id, STREAM_ID_SIZE); + payload_len = STREAM_ID_SIZE + 1 + + snprintf(payload+STREAM_ID_SIZE,CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE, + "%s:%d", destaddr, destport); + + log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",*(int *)ap_conn->stream_id); + + connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_BEGIN, + payload, payload_len, ap_conn->cpath_layer); + + ap_conn->package_window = STREAMWINDOW_START; + ap_conn->deliver_window = STREAMWINDOW_START; + ap_conn->state = AP_CONN_STATE_OPEN; + log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_aci %d",ap_conn->s,circ->n_aci); + return 0; +} + +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 connection_flush_buf(conn); /* try to flush it */ + } + if(conn->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 connection_flush_buf(conn); /* try to flush it */ + } + if(conn->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); /* XXX set external addr/port to 0, see what breaks */ + connection_write_to_buf(buf,10,conn); + return connection_flush_buf(conn); /* try to flush it */ + } + return 0; /* if socks_version isn't 4 or 5, don't send anything */ +} + +/*ENDCLOSE*/ static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { + connection_t *n_stream; + char *colon; + + if(!memchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_SIZE,0, + cell->length-RELAY_HEADER_SIZE-STREAM_ID_SIZE)) { + log_fn(LOG_WARN,"relay begin cell has no \\0. Dropping."); + return 0; + } + colon = strchr(cell->payload+RELAY_HEADER_SIZE+STREAM_ID_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); + + memcpy(n_stream->stream_id, cell->payload + RELAY_HEADER_SIZE, STREAM_ID_SIZE); + n_stream->address = tor_strdup(cell->payload + RELAY_HEADER_SIZE + STREAM_ID_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 */ + if(connection_exit_connect(n_stream) >= 0) + return 0; + /* else fall through */ + case -1: /* resolve failed */ + log_fn(LOG_WARN,"Resolve or connect failed (%s).", n_stream->address); + connection_remove(n_stream); + connection_free(n_stream); + case 0: /* resolve added to pending list */ + ; + } + return 0; +} + +int connection_exit_connect(connection_t *conn) { + + if(router_compare_to_exit_policy(conn) < 0) { + log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port); + return -1; + } + + switch(connection_connect(conn, conn->address, conn->addr, conn->port)) { + case -1: + return -1; + 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 0; + /* 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. */ + connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, + NULL, 0, conn->cpath_layer); + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ + diff --git a/tags/tor-0_0_2pre13/src/or/connection_or.c b/tags/tor-0_0_2pre13/src/or/connection_or.c new file mode 100644 index 0000000000..bdaf16e23d --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/connection_or.c @@ -0,0 +1,302 @@ +/* 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->aci); + *(uint8_t*)(dest+2) = src->command; + *(uint8_t*)(dest+3) = src->length; + *(uint32_t*)(dest+4) = 0; /* Reserved */ + memcpy(dest+8, src->payload, CELL_PAYLOAD_SIZE); +} + +static void cell_unpack(cell_t *dest, const char *src) { + dest->aci = ntohs(*(uint16_t*)(src)); + dest->command = *(uint8_t*)(src+2); + dest->length = *(uint8_t*)(src+3); + dest->seq = ntohl(*(uint32_t*)(src+4)); + memcpy(dest->payload, src+8, 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); + if(conn->address) + 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[255]; + + conn->state = OR_CONN_STATE_OPEN; + directory_set_dirty(); + connection_watch_events(conn, POLLIN); + log_fn(LOG_DEBUG,"tls handshake done. verifying."); + if(options.OnionRouter) { /* I'm an OR */ + if(tor_tls_peer_has_cert(conn->tls)) { /* it's another OR */ + if (tor_tls_get_peer_cert_nickname(conn->tls, nickname, 256)) { + 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:%d) has a cert but it's invalid. Closing.", + 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:%d). Closing.", + 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' but he gave us a different key. Closing.", + router->nickname); + 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); + return -1; + } + connection_or_init_conn_from_router(conn, router); + } + crypto_free_pk_env(pk); + } else { /* it's an OP */ + conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; + } + } else { /* I'm a client */ + if(!tor_tls_peer_has_cert(conn->tls)) { /* it's a client too?! */ + log_fn(LOG_WARN,"Neither peer sent a cert! Closing."); + return -1; + } + pk = tor_tls_verify(conn->tls); + if(!pk) { + log_fn(LOG_WARN,"Other side (%s:%d) has a cert but it's invalid. Closing.", + 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:%d). Closing.", + conn->address, conn->port); + crypto_free_pk_env(pk); + return -1; + } + if(crypto_pk_cmp_keys(conn->link_pkey, pk)) { + log_fn(LOG_WARN,"We connected to '%s' but he gave us a different key. Closing.", + router->nickname); + crypto_free_pk_env(pk); + return -1; + } + log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good."); + crypto_free_pk_env(pk); + conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; + circuit_n_conn_open(conn); /* send the pending create */ + } + return 0; +} + +/* ********************************** */ + +void connection_or_write_cell_to_buf(const cell_t *cellp, connection_t *conn) { + char networkcell[CELL_NETWORK_SIZE]; + char *n = networkcell; + + assert(connection_speaks_cells(conn)); + + cell_pack(n, cellp); + + 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; + + 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); + + /* CLEAR Shouldn't this be connection_or_process_inbuf at least? Or maybe + just use a loop? If not, doc why not. */ + return connection_process_inbuf(conn); /* 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_2pre13/src/or/cpuworker.c b/tags/tor-0_0_2pre13/src/or/cpuworker.c new file mode 100644 index 0000000000..6fc0c2a0fb --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/cpuworker.c @@ -0,0 +1,284 @@ +/* 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 17 +#define MIN_CPUWORKERS 1 + +#define TAG_LEN 8 +#define LEN_ONION_QUESTION (1+TAG_LEN+DH_ONIONSKIN_LEN) +#define LEN_ONION_RESPONSE (1+TAG_LEN+DH_KEY_LEN+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, aci_t aci) { + *(uint32_t *)tag = addr; + *(uint16_t *)(tag+4) = port; + *(aci_t *)(tag+6) = aci; +} + +static void tag_unpack(char *tag, uint32_t *addr, uint16_t *port, aci_t *aci) { + struct in_addr in; + + *addr = *(uint32_t *)tag; + *port = *(uint16_t *)(tag+4); + *aci = *(aci_t *)(tag+6); + + in.s_addr = htonl(*addr); + log_fn(LOG_DEBUG,"onion was from %s:%d, aci %d.", inet_ntoa(in), *port, *aci); +} + +int connection_cpu_process_inbuf(connection_t *conn) { + unsigned char buf[LEN_ONION_RESPONSE]; + uint32_t addr; + uint16_t port; + aci_t aci; + 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 dying."); + if(conn->state != CPUWORKER_STATE_IDLE) { + /* XXX the circ associated with this cpuworker will wait forever. Oops. */ + num_cpuworkers_busy--; + } + num_cpuworkers--; + 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, &aci); + circ = NULL; + p_conn = connection_exact_get_by_addr_port(addr,port); + if(p_conn) + circ = circuit_get_by_aci_conn(aci, 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+DH_KEY_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[DH_ONIONSKIN_LEN]; + unsigned char question_type; + int *fdarray = data; + int fd; + + /* variables for onion processing */ + unsigned char keys[32]; + unsigned char reply_to_proxy[DH_KEY_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 */ + + 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, DH_ONIONSKIN_LEN) != DH_ONIONSKIN_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, 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,DH_KEY_LEN); + memcpy(buf+1+TAG_LEN+DH_KEY_LEN,keys,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, else + * (something else) + */ +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_aci); + + 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, DH_ONIONSKIN_LEN, cpuworker); + } + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ + diff --git a/tags/tor-0_0_2pre13/src/or/directory.c b/tags/tor-0_0_2pre13/src/or/directory.c new file mode 100644 index 0000000000..fb92bf75a4 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/directory.c @@ -0,0 +1,271 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* 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"; +static char the_directory[MAX_DIR_SIZE+1]; +static int directorylen=0; + +/********* END VARIABLES ************/ + +void directory_initiate_command(routerinfo_t *router, int command) { + connection_t *conn; + + if(!router) { /* i guess they didn't have one in mind for me to use */ + log_fn(LOG_WARN,"No running dirservers known. This is really bad."); + /* XXX never again will a directory fetch work. Should we exit here, or what? */ + return; + } + +#if 0 /* there's no problem with parallel get/posts now. whichever 'get' ends + last is the directory. */ + if(connection_get_by_type(CONN_TYPE_DIR)) { /* there's already a dir conn running */ + log_fn(LOG_DEBUG,"Canceling connect, dir conn already active."); + return; + } +#endif + + if(command == DIR_CONN_STATE_CONNECTING_FETCH) + log_fn(LOG_DEBUG,"initiating directory fetch"); + else + log_fn(LOG_DEBUG,"initiating directory upload"); + + 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); + if (router->identity_pkey) + conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); + else { + log_fn(LOG_WARN, "No signing key known for dirserver %s; signature won't be checked", conn->address); + conn->identity_pkey = NULL; + /* XXX is there really any situation where router doesn't have an 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", + 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) { + + 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 process the_directory and learn about new routers. */ + switch(fetch_from_buf_http(conn->inbuf, + NULL, 0, the_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(the_directory); + log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, the_directory); + if(directorylen == 0) { + log_fn(LOG_INFO,"Empty directory. Ignoring."); + return -1; + } + if(router_get_dir_from_string(the_directory, conn->identity_pkey) < 0){ + log_fn(LOG_INFO,"...but parsing failed. Ignoring."); + } else { + log_fn(LOG_INFO,"and got an %s directory; updated routers.", + conn->identity_pkey ? "authenticated" : "unauthenticated"); + } + if(options.OnionRouter) { /* connect to them all */ + router_retry_connections(); + } + 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(connection_t *conn) { + char headers[1024]; + char body[50000]; /* XXX */ + size_t dlen; + const char *cp; + + assert(conn && conn->type == CONN_TYPE_DIR); + + switch(fetch_from_buf_http(conn->inbuf, + headers, sizeof(headers), body, sizeof(body))) { + 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)) { + /* 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; + } + + if(!strncasecmp(headers,"POST",4)) { + /* 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; + } + + log_fn(LOG_WARN,"Got headers with unknown command. Closing."); + return -1; +} + +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_2pre13/src/or/dirserv.c b/tags/tor-0_0_2pre13/src/or/dirserv.c new file mode 100644 index 0000000000..14219beb1f --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/dirserv.c @@ -0,0 +1,447 @@ +/* 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 the_directory_is_dirty = 1; +static char *the_directory = NULL; +static int the_directory_len = -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. */ +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_WARN,"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 0; + } + 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 0; /* 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) +{ + if (desc->descriptor) + free(desc->descriptor); + if (desc->nickname) + 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 added; -1 if descriptor rejected. Updates *desc + * to point after the descriptor if the descriptor is OK. + */ +int +dirserv_add_descriptor(const char **desc) +{ + descriptor_entry_t **desc_ent_ptr; + routerinfo_t *ri = NULL; + int i; + char *start, *end; + char *desc_tmp = NULL, *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_malloc(desc_len+1); + strncpy(desc_tmp, start, desc_len); + desc_tmp[desc_len]='\0'; + + /* Check: is the descriptor syntactically valid? */ + ri = router_get_entry_from_string(&cp); + if (!ri) { + log(LOG_WARN, "Couldn't parse descriptor"); + goto err; + } + free(desc_tmp); desc_tmp = NULL; + /* Okay. Now check whether the fingerprint is recognized. */ + if (!dirserv_router_fingerprint_is_known(ri)) { + log(LOG_WARN, "Identity is unrecognized for descriptor"); + goto err; + } + /* 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. */ + if (desc_tmp) free(desc_tmp); + if (ri) 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++]; + } + + (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t)); + (*desc_ent_ptr)->nickname = 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; + the_directory_is_dirty = 1; + + routerinfo_free(ri); + return 0; + err: + if (desc_tmp) + 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); + 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); + + 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; +} + +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; + } + if (the_directory) + 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_get_dir_from_string(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_2pre13/src/or/dns.c b/tags/tor-0_0_2pre13/src/or/dns.c new file mode 100644 index 0000000000..7c0566c80b --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/dns.c @@ -0,0 +1,447 @@ +/* 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 *question, uint32_t answer); +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 question[MAX_ADDRESSLEN]; /* the hostname to be resolved */ + uint32_t answer; /* 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; +}; + +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->question, b->question, 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 %d)", 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); + } +} + +/* See if the question 'exitconn->address' has been answered. 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 'question' is already there. */ + strncpy(search.question, exitconn->address, MAX_ADDRESSLEN); + 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; + return 0; + case CACHE_STATE_VALID: + exitconn->addr = resolve->answer; + return 1; + case CACHE_STATE_FAILED: + return -1; + } + } else { /* need to add it */ + resolve = tor_malloc(sizeof(struct cached_resolve)); + memset(resolve, 0, sizeof(struct cached_resolve)); + resolve->state = CACHE_STATE_PENDING; + resolve->expire = now + 100; /* XXX for testing. when we're confident, switch it back */ +// resolve->expire = now + 86400; /* now + 1 day */ + strncpy(resolve->question, exitconn->address, MAX_ADDRESSLEN); + + /* 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); + } + + assert(0); + return 0; /* not reached; keep gcc happy */ +} + +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; + } + + 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 *question, connection_t *onlyconn) { + struct pending_connection_t *pend, *victim; + struct cached_resolve search; + struct cached_resolve *resolve, *tmp; + + strncpy(search.question, question, MAX_ADDRESSLEN); + + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(!resolve) { + log_fn(LOG_WARN,"Question '%s' is not pending. Dropping.", question); + 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 */ + return; + } else { + for( ; pend->next; pend = pend->next) { + if(pend->next->conn == onlyconn) { + victim = pend->next; + pend->next = victim->next; + free(victim); + return; /* more are pending */ + } + } + assert(0); /* not reachable unless onlyconn not in pending list */ + } + } else { + /* mark all pending connections to fail */ + while(resolve->pending_connections) { + pend = resolve->pending_connections; +/*ENDCLOSE*/ pend->conn->marked_for_close = 1; + 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 *question, uint32_t answer) { + struct pending_connection_t *pend; + struct cached_resolve search; + struct cached_resolve *resolve; + + strncpy(search.question, question, MAX_ADDRESSLEN); + + resolve = SPLAY_FIND(cache_tree, &cache_root, &search); + if(!resolve) { + log_fn(LOG_WARN,"Answer to unasked question '%s'? Dropping.", question); + return; + } + + assert(resolve->state == CACHE_STATE_PENDING); + /* XXX sometimes this still gets triggered. :( */ + + resolve->answer = ntohl(answer); + if(resolve->answer) + resolve->state = CACHE_STATE_VALID; + else + resolve->state = CACHE_STATE_FAILED; + + while(resolve->pending_connections) { + pend = resolve->pending_connections; + pend->conn->addr = resolve->answer; + if(resolve->state == CACHE_STATE_FAILED || connection_exit_connect(pend->conn) < 0) { +/*ENDCLOSE*/ pend->conn->marked_for_close = 1; + } + 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 answer; + + 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*)&answer,sizeof(answer),conn); + + dns_found_answer(conn->address, answer); + + free(conn->address); + conn->address = strdup(""); + conn->state = DNSWORKER_STATE_IDLE; + num_dnsworkers_busy--; + + return 0; +} + +int dnsworker_main(void *data) { + char question[MAX_ADDRESSLEN]; + unsigned char question_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 */ + + for(;;) { + + if(read(fd, &question_len, 1) != 1) { + log_fn(LOG_ERR,"read length failed. Child exiting."); + spawn_exit(); + } + assert(question_len > 0); + + if(read_all(fd, question, question_len) != question_len) { + log_fn(LOG_ERR,"read hostname failed. Child exiting."); + spawn_exit(); + } + question[question_len] = 0; /* null terminate it */ + + rent = gethostbyname(question); + if (!rent) { + log_fn(LOG_INFO,"Could not resolve dest addr %s. Returning nulls.",question); + 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,"Answered question '%s'.",question); + } + } + 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; + + 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); + + /* 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_needed+MAX_IDLE_DNSWORKERS) { /* too many idle? */ + /* cull excess workers */ + 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_2pre13/src/or/main.c b/tags/tor-0_0_2pre13/src/or/main.c new file mode 100644 index 0000000000..dd42865c4e --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/main.c @@ -0,0 +1,833 @@ +/* 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 */ + +/********* 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 */ + +/* private keys */ +static crypto_pk_env_t *onionkey=NULL; +static crypto_pk_env_t *linkkey=NULL; +static crypto_pk_env_t *identitykey=NULL; + +/********* END VARIABLES ************/ + +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; +} + +/**************************************************************************** +* +* 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)) /* not done flushing */ + log_fn(LOG_WARN,"Conn (socket %d) still wants to flush. Losing %d bytes!",conn->s, (int)buf_datalen(conn->inbuf)); + } + 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.OnionRouter && !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.OnionRouter) { + 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 NewCircuitPeriod seconds, we expire old circuits and make a + * new one as needed. + */ + if(options.APPort && time_to_new_circuit < now) { + circuit_expire_unused_circuits(); + circuit_launch_new(-1); /* tell it to forget about previous failures */ + circ = circuit_get_newest_open(); + if(!circ || circ->dirty) { + log_fn(LOG_INFO,"Youngest circuit %s; launching replacement.", circ ? "dirty" : "missing"); + circuit_launch_new(0); /* make an onion and lay the circuit */ + } + time_to_new_circuit = now + options.NewCircuitPeriod; + } + + /* 3. 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; + + + /* 4. We do houskeeping 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 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; +} + +static 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.OnionRouter) { + 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 int do_main_loop(void) { + int i; + int timeout; + int poll_result; + + /* load the routers file */ + if(router_get_list_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.OnionRouter) { + 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. + */ + retry_all_connections((uint16_t) options.ORPort, + (uint16_t) options.APPort, + (uint16_t) options.DirPort); + + for(;;) { +#ifndef MS_WIN32 /* 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) { + /* fetch a new directory */ + 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."); + } + + if(router_get_list_from_file(options.RouterFile) < 0) { + log(LOG_WARN,"Error reloading router list. Continuing with old list."); + } + } else { + directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH); + } + + /* close and reopen the log files */ + reset_logs(); + + 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*(CELL_PAYLOAD_SIZE-RELAY_HEADER_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*(CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE))) ); + + if (stats_n_seconds_reading) + log(severity,"Average bandwidth used: %d bytes/sec", + (int) (stats_n_bytes_read/stats_n_seconds_reading)); +} + +void daemonize(void) { +#ifndef MS_WINDOWS + /* Fork; parent exits. */ + if (fork()) + exit(0); + + /* Create new session; make sure we never get a terminal */ + setsid(); + if (fork()) + exit(0); + + chdir("/"); + umask(000); + + fclose(stdin); + fclose(stdout); + fclose(stderr); +#endif +} + +void write_pidfile(char *filename) { + 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); + } +} + +int tor_main(int argc, char *argv[]) { + + /* give it somewhere to log to initially */ + add_stream_log(LOG_INFO, "", stdout); + + if(getconfig(argc,argv,&options)) { + log_fn(LOG_ERR,"Reading config file failed. exiting."); + return -1; + } + log_set_severity(options.loglevel); /* assign logging severity level from options */ + close_logs(); /* close stdout, then open with correct loglevel if necessary */ + if(!options.LogFile && !options.RunAsDaemon) + add_stream_log(options.loglevel, "", stdout); + if(options.DebugLogFile) + add_file_log(LOG_DEBUG, options.DebugLogFile); + if(options.LogFile) + add_file_log(options.loglevel, options.LogFile); + + global_read_bucket = options.TotalBandwidth; /* start it at 1 second of traffic */ + stats_prev_global_read_bucket = global_read_bucket; + + /* write our pid to the pid file */ + write_pidfile(options.PidFile); + + if(options.RunAsDaemon) + daemonize(); + + if(options.OnionRouter) { /* only spawn dns handlers if we're a router */ + dns_init(); /* initialize the dns resolve tree, and spawn workers */ + } + +#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_2pre13/src/or/onion.c b/tags/tor-0_0_2pre13/src/or/onion.c new file mode 100644 index 0000000000..f0bf9aacc1 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/onion.c @@ -0,0 +1,566 @@ +/* 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_aci_type(char *local_nick, char *remote_nick) { + int result; + + assert(remote_nick); + if(!local_nick) + return ACI_TYPE_LOWER; + result = strcmp(local_nick, remote_nick); + assert(result); + if(result < 0) + return ACI_TYPE_LOWER; + return ACI_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)); + memset(tmp, 0, sizeof(struct onion_queue_t)); + tmp->circ = circ; + + 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); + if(!ol_list->circ->p_conn) { + log_fn(LOG_INFO,"ol_list->circ->p_conn null, must have died?"); + onion_pending_remove(ol_list->circ); + return onion_next_task(); /* recurse: how about the next one? */ + } + + 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_aci %d) not in list, probably at cpuworker.",circ->p_aci); + 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.aci = circ->p_aci; + cell.length = DH_KEY_LEN; + + circ->state = CIRCUIT_STATE_OPEN; + + log_fn(LOG_DEBUG,"Entering."); + + memcpy(cell.payload, payload, DH_KEY_LEN); + + log_fn(LOG_DEBUG,"init cipher forward %d, backward %d.", *(int*)keys, *(int*)(keys+16)); + + if (!(circ->n_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys,iv,0))) { + log_fn(LOG_WARN,"Cipher initialization failed (n)."); + return -1; + } + + if (!(circ->p_crypto = + crypto_create_init_cipher(CIRCUIT_CIPHER,keys+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; +} + +/* uses a weighted coin with weight cw to choose a route length */ +static int chooselen(double cw) { + int len = 2; + uint8_t coin; + + if ((cw < 0) || (cw >= 1)) /* invalid parameter */ + return -1; + + while(1) + { + if (CRYPTO_PSEUDO_RAND_INT(coin)) + return -1; + + if (coin > cw*255) /* don't extend */ + break; + else + len++; + } + + return len; +} + +/* returns an array of pointers to routent that define a new route through the OR network + * int cw is the coin weight to use when choosing the route + * order of routers is from last to first + */ +static unsigned int *new_route(double cw, routerinfo_t **rarray, int rarray_len, int *routelen) { + int i; + int num_acceptable_routers; + unsigned int *route; + unsigned int oldchoice, choice; + + assert((cw >= 0) && (cw < 1) && (rarray) && (routelen) ); /* valid parameters */ + + *routelen = chooselen(cw); + if (*routelen == -1) { + log_fn(LOG_WARN,"Choosing route length failed."); + return NULL; + } + 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 NULL; + } + + 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; + } + + if(*routelen < 1) { + log_fn(LOG_WARN,"Didn't find any acceptable routers. Failing."); + return NULL; + } + + /* allocate memory for the new route */ + route = (unsigned int *)tor_malloc(*routelen * sizeof(unsigned int)); + + oldchoice = rarray_len; + for(i=0;i<*routelen;i++) { +// log_fn(LOG_DEBUG,"Choosing hop %u.",i); + if (CRYPTO_PSEUDO_RAND_INT(choice)) { + free((void *)route); + return NULL; + } + + choice = choice % rarray_len; + log_fn(LOG_DEBUG,"Contemplating router %u.",choice); + if(choice == oldchoice || + (oldchoice < rarray_len && !crypto_pk_cmp_keys(rarray[choice]->onion_pkey, rarray[oldchoice]->onion_pkey)) || + (options.OnionRouter && !connection_twin_get_by_addr_port(rarray[choice]->addr, rarray[choice]->or_port))) { + /* Same router as last choice, or router twin, + * or no routers with that key are connected to us. + * Try again. */ + log_fn(LOG_DEBUG,"Picked a router %d that won't work as next hop.",choice); + i--; + continue; + } + log_fn(LOG_DEBUG,"Chosen router %u for hop %u.",choice,i); + oldchoice = choice; + route[i] = choice; + } + + return route; +} + +static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) { + int i, j; + int num=0; + connection_t *conn; + + for(i=0;iaddr, 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; +} + +crypt_path_t *onion_generate_cpath(routerinfo_t **firsthop) { + int routelen; /* length of the route */ + unsigned int *route; /* hops in the route as an array of indexes into rarray */ + crypt_path_t *cpath=NULL; + directory_t *dir; + routerinfo_t **rarray; + int rarray_len; + int i; + crypt_path_t *hop; + routerinfo_t *router; + struct in_addr netaddr; + + router_get_directory(&dir); + rarray = dir->routers; + rarray_len = dir->n_routers; + + /* choose a route */ + route = new_route(options.CoinWeight, rarray, rarray_len, &routelen); + if (!route) { + log_fn(LOG_INFO,"Error choosing a route through the OR network."); + return NULL; + } + log_fn(LOG_DEBUG,"Chosen a route of length %u: ",routelen); + + *firsthop = rarray[route[routelen-1]]; + assert(*firsthop); /* should always be defined */ + + for(i=0; iaddr); + + log_fn(LOG_DEBUG,"%u : %s:%u, %u/%u",routelen-i, + inet_ntoa(netaddr), + (rarray[route[i]])->or_port, + (int) (rarray[route[i]])->onion_pkey, + crypto_pk_keysize((rarray[route[i]])->onion_pkey)); + } + + /* create the cpath layer by layer, starting at the last hop */ + for (i=0;inext = cpath; + hop->prev = NULL; + hop->state = CPATH_STATE_CLOSED; + if(cpath) { + cpath->prev = hop; + } + cpath = hop; + + hop->port = rarray[route[i]]->or_port; + hop->addr = rarray[route[i]]->addr; + + hop->package_window = CIRCWINDOW_START; + hop->deliver_window = CIRCWINDOW_START; + + log_fn(LOG_DEBUG,"Building hop %u of crypt path.",i+1); + } + + /* now link cpath->prev to the end of cpath */ + for(hop=cpath; hop->next; hop=hop->next) ; + hop->next = cpath; + cpath->prev = hop; + + free(route); + return cpath; +} + +/*----------------------------------------------------------------------*/ + +/* Given a router's public key, generates a 144-byte encrypted DH pubkey, + * and stores it into onion_skin out. Stores the DH private key into + * handshake_state_out for later completion of the handshake. + * + * The encrypted pubkey is formed as follows: + * 16 bytes of symmetric key + * 128 bytes of g^x for DH. + * The first 128 bytes are RSA-encrypted with the server's public key, + * and the last 16 are encrypted with the symmetric key. + */ +int +onion_skin_create(crypto_pk_env_t *dest_router_key, + crypto_dh_env_t **handshake_state_out, + char *onion_skin_out) /* Must be DH_ONIONSKIN_LEN bytes long */ +{ + char iv[16]; + char *pubkey = 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, DH_ONIONSKIN_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+16 == DH_ONIONSKIN_LEN); + pubkey = (char *)tor_malloc(dhbytes+16); + + if (crypto_rand(16, pubkey)) + goto err; + + /* XXXX 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 'pubkey' to 0. This means that our symmetric key is really only + * 127 bits long, but since it shouldn't be necessary to encrypt + * DH public keys values in the first place, we should be fine. + */ + pubkey[0] &= 0x7f; + + if (crypto_dh_get_public(dh, pubkey+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_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 RECOMMENDED_SOFTWARE_VERSIONS "0.0.2pre8,0.0.2pre9,0.0.2pre10,0.0.2pre11,0.0.2pre12,0.0.2pre13,0.0.2pre14" + +#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 50000 /* XXX, big enough? */ + +#define ACI_TYPE_LOWER 0 +#define ACI_TYPE_HIGHER 1 +#define ACI_TYPE_BOTH 2 + +#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_OR_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_HEADER_SIZE 8 + +/* 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 + +#define CELL_PAYLOAD_SIZE 248 +#define CELL_NETWORK_SIZE 256 + +/* legal characters in a filename */ +#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/" +/* legal characters in a nickname */ +#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +/* structure of a socks client operation */ +typedef struct { + unsigned char version; /* socks version number */ + unsigned char command; /* command code */ + uint16_t destport; /* destination port, host order */ + uint32_t destip; /* destination address, host order */ + /* userid follows, terminated by a \0 */ + /* dest host follows, terminated by a \0 */ +} socks4_t; + +#define SOCKS4_NETWORK_LEN 8 + +typedef uint16_t aci_t; + +/* cell definition */ +typedef struct { + aci_t aci; /* Anonymous Connection Identifier */ + unsigned char command; + unsigned char length; /* of payload if relay cell */ + uint32_t seq; /* sequence number */ + + unsigned char payload[CELL_PAYLOAD_SIZE]; +} cell_t; +#define CELL_RELAY_COMMAND(c) (*(uint8_t*)((c).payload)) +#define SET_CELL_RELAY_COMMAND(c,cmd) (*(uint8_t*)((c).payload) = (cmd)) +#define STREAM_ID_SIZE 7 +#define SET_CELL_STREAM_ID(c,id) memcpy((c).payload+1,(id),STREAM_ID_SIZE) + +#define ZERO_STREAM "\0\0\0\0\0\0\0\0" + +typedef struct buf_t buf_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? */ + long 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? */ + long timestamp_lastwritten; /* when was the last time poll() said we could write? */ + + long 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_aci; /* Which ACI 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: */ + char socks_version; + char stream_id[STREAM_ID_SIZE]; + 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; +}; + +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; + char *address; + char *port; + + 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 ap_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; +} directory_t; + +struct crypt_path_t { + + /* crypto environments */ + crypto_cipher_env_t *f_crypto; + crypto_cipher_env_t *b_crypto; + + 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 DH_ONIONSKIN_LEN DH_KEY_LEN+16 + +typedef struct crypt_path_t crypt_path_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; + int package_window; + int deliver_window; + + aci_t p_aci; /* circuit identifiers */ + aci_t n_aci; + + crypto_cipher_env_t *p_crypto; /* used only for intermediate hops */ + crypto_cipher_env_t *n_crypto; + + crypt_path_t *cpath; + + char onionskin[DH_ONIONSKIN_LEN]; /* for storage while onionskin pending */ + long timestamp_created; + uint8_t dirty; /* whether this circuit has been used yet */ + + 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 *ExitPolicy; + double CoinWeight; + int ORPort; + int APPort; + int DirPort; + int MaxConn; + int OnionRouter; + int TrafficShaping; + int LinkPadding; + int IgnoreVersion; + int RunAsDaemon; + int DirRebuildPeriod; + int DirFetchPostPeriod; + int KeepalivePeriod; + int MaxOnionsPending; + int NewCircuitPeriod; + int TotalBandwidth; + int NumCpus; + int Role; + int loglevel; +} or_options_t; + + /* 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, char *socks_version, + char *reply, int *replylen, + char *addr_out, int max_addrlen, + uint16_t *port_out); + +/********************************* circuit.c ***************************/ + +void circuit_add(circuit_t *circ); +void circuit_remove(circuit_t *circ); +circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn); +void circuit_free(circuit_t *circ); + +circuit_t *circuit_enumerate_by_naddr_nport(circuit_t *start, uint32_t naddr, uint16_t nport); +circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn); +circuit_t *circuit_get_by_conn(connection_t *conn); +circuit_t *circuit_get_newest_open(void); + +int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction, crypt_path_t *layer_hint); +int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction, + crypt_path_t **layer_hint, char *recognized, connection_t **conn); +int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, connection_t **conn); + +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); +int 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); +void circuit_launch_new(int failure_status); +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); + +int connection_create_listener(struct sockaddr_in *bindaddr, int type); + +int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port); +int retry_all_connections(uint16_t or_listenport, uint16_t ap_listenport, uint16_t dir_listenport); + +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_flush_buf(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(aci_t aci, 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 ***************************/ + +int connection_edge_process_inbuf(connection_t *conn); +void 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); + +int connection_exit_connect(connection_t *conn); + +extern uint64_t stats_n_data_cells_packaged; +extern uint64_t stats_n_data_bytes_packaged; +extern uint64_t stats_n_data_cells_received; +extern uint64_t stats_n_data_bytes_received; + +/********************************* 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 *cellp, 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 ***************************/ + +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 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); + +const char *router_get_my_descriptor(void); + +int main(int argc, char *argv[]); + +/********************************* onion.c ***************************/ + +int decide_aci_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); + +crypt_path_t *onion_generate_cpath(routerinfo_t **firsthop); + +int onion_skin_create(crypto_pk_env_t *router_key, + crypto_dh_env_t **handshake_state_out, + char *onion_skin_out); /* Must be DH_ONIONSKIN_LEN bytes long */ + +int onion_skin_server_handshake(char *onion_skin, /* DH_ONIONSKIN_LEN bytes long */ + crypto_pk_env_t *private_key, + char *handshake_reply_out, /* DH_KEY_LEN bytes long */ + char *key_out, + int key_out_len); + +int onion_skin_client_handshake(crypto_dh_env_t *handshake_state, + char *handshake_reply,/* Must be DH_KEY_LEN bytes long*/ + char *key_out, + int key_out_len); + +/********************************* routers.c ***************************/ + +int learn_my_address(struct sockaddr_in *me); +void router_retry_connections(void); +routerinfo_t *router_pick_directory_server(void); +void router_upload_desc_to_dirservers(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_directory(directory_t **pdirectory); +int router_is_me(uint32_t addr, uint16_t port); +void router_mark_as_down(char *nickname); +int router_get_list_from_file(char *routerfile); +int router_get_router_hash(char *s, char *digest); +int router_get_dir_hash(char *s, char *digest); + +/* Reads a list of known routers, unsigned. */ +int router_get_list_from_string(char *s); +/* Exported for debugging */ +int router_get_list_from_string_impl(char **s, directory_t **dest, int n_good_nicknames, const char *good_nickname_lst[]); +/* Reads a signed directory. */ +int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey); +/* Exported or debugging */ +int router_get_dir_from_string_impl(char *s, directory_t **dest, + crypto_pk_env_t *pkey); +routerinfo_t *router_get_entry_from_string(char **s); +int router_compare_to_exit_policy(connection_t *conn); +void routerinfo_free(routerinfo_t *router); +int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, + crypto_pk_env_t *ident_key); +const routerinfo_t *router_get_desc_routerinfo(void); +const char *router_get_my_descriptor(void); +int router_rebuild_descriptor(void); + +/********************************* 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_2pre13/src/or/routers.c b/tags/tor-0_0_2pre13/src/or/routers.c new file mode 100644 index 0000000000..4ecd2fcb9c --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/routers.c @@ -0,0 +1,1282 @@ +/* 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 directory_t *directory = NULL; /* router array */ +static routerinfo_t *desc_routerinfo = NULL; /* my descriptor */ +static char descriptor[8192]; /* string representation of my descriptor */ + +extern or_options_t options; /* command-line and config-file options */ + +/****************************************************************************/ + +struct directory_token; +typedef struct directory_token directory_token_t; + +/* static function prototypes */ +void routerlist_free(routerinfo_t *list); +static char *eat_whitespace(char *s); +static char *eat_whitespace_no_nl(char *s); +static char *find_whitespace(char *s); +static int router_add_exit_policy_from_string(routerinfo_t *router, char *s); +static int router_add_exit_policy(routerinfo_t *router, + directory_token_t *tok); +static int router_resolve_directory(directory_t *dir); + +/****************************************************************************/ + +void router_retry_connections(void) { + int i; + routerinfo_t *router; + + for (i=0;in_routers;i++) { + router = directory->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); + } + } +} + +routerinfo_t *router_pick_directory_server(void) { + /* pick the first running router with a positive dir_port */ + int i; + routerinfo_t *router; + + if(!directory) + return NULL; + + for(i=0;in_routers;i++) { + router = directory->routers[i]; + if(router->dir_port > 0 && router->is_running) + return router; + } + + return NULL; +} + +void router_upload_desc_to_dirservers(void) { + int i; + routerinfo_t *router; + + if(!directory) + return; + + if (!router_get_my_descriptor()) { + log_fn(LOG_WARN, "No descriptor; skipping upload"); + return; + } + + for(i=0;in_routers;i++) { + router = directory->routers[i]; + if(router->dir_port > 0) + directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD); + } +} + +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + assert(directory); + + for(i=0;in_routers;i++) { + router = directory->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(directory); + + for(i=0;in_routers;i++) { + router = directory->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(directory); + + for(i=0;in_routers;i++) { + router = directory->routers[i]; + if (0 == strcmp(router->nickname, nickname)) + return router; + } + return NULL; +} + +void router_get_directory(directory_t **pdirectory) { + *pdirectory = directory; +} + +/* delete a router from memory */ +void routerinfo_free(routerinfo_t *router) +{ + struct exit_policy_t *e; + + if (!router) + return; + + if (router->address) + free(router->address); + if (router->nickname) + 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; + if (e->string) free(e->string); + if (e->address) free(e->address); + if (e->port) free(e->port); + free(e); + } + free(router); +} + +void directory_free(directory_t *dir) +{ + int i; + for (i = 0; i < dir->n_routers; ++i) + routerinfo_free(dir->routers[i]); + if (dir->routers) + free(dir->routers); + if(dir->software_versions) + free(dir->software_versions); + free(dir); +} + +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; +} + +/* load the router list */ +int router_get_list_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_get_list_from_string(string) < 0) { + log_fn(LOG_WARN,"The routerfile itself was corrupt."); + free(string); + return -1; + } + + free(string); + return 0; +} + + +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 +} directory_keyword; + +struct token_table_ent { char *t; int v; }; + +static struct token_table_ent token_table[] = { + { "accept", K_ACCEPT }, + { "directory-signature", K_DIRECTORY_SIGNATURE }, + { "reject", K_REJECT }, + { "router", K_ROUTER }, + { "recommended-software", K_RECOMMENDED_SOFTWARE }, + { "signed-directory", K_SIGNED_DIRECTORY }, + { "signing-key", K_SIGNING_KEY }, + { "onion-key", K_ONION_KEY }, + { "link-key", K_LINK_KEY }, + { "router-signature", K_ROUTER_SIGNATURE }, + { "published", K_PUBLISHED }, + { "running-routers", K_RUNNING_ROUTERS }, + { "platform", K_PLATFORM }, + { NULL, -1 } +}; + +#define MAX_ARGS 1024 +struct directory_token { + directory_keyword tp; + union { + struct { + char *args[MAX_ARGS+1]; + int n_args; + } cmd; + char *signature; + char *error; + crypto_pk_env_t *public_key; + } val; +}; + +/* Free any malloced resources allocated for a token. Don't call this if + you inherit the reference to those resources. + */ +static void +router_release_token(directory_token_t *tok) +{ + switch (tok->tp) + { + case _SIGNATURE: + free(tok->val.signature); + break; + case _PUBLIC_KEY: + crypto_free_pk_env(tok->val.public_key); + break; + default: + break; + } +} + +static int +_router_get_next_token(char **s, directory_token_t *tok) { + char *next; + crypto_pk_env_t *pkey = NULL; + char *signature = NULL; + int i, done; + + tok->tp = _ERR; + tok->val.error = ""; + + *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)) { + 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)) { + /* 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; + } + for (i = 0 ; token_table[i].t ; ++i) { + if (!strncmp(token_table[i].t, *s, next-*s)) { + tok->tp = token_table[i].v; + 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; + *next = 0; + tok->val.cmd.args[i++] = *s; + *s = eat_whitespace_no_nl(next+1); + }; + tok->val.cmd.n_args = i; + if (i > MAX_ARGS) { + tok->tp = _ERR; + tok->val.error = "Too many arguments"; return -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(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 + + +/* return the first char of s that is not whitespace and not a comment */ +static char *eat_whitespace(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; +} + +static char *eat_whitespace_no_nl(char *s) { + while(*s == ' ' || *s == '\t') + ++s; + return s; +} + +/* return the first char of s that is whitespace or '#' or '\0 */ +static char *find_whitespace(char *s) { + assert(s); + + while(*s && !isspace(*s) && *s != '#') + s++; + + return s; +} + +int router_get_list_from_string(char *s) +{ + if (router_get_list_from_string_impl(&s, &directory, -1, NULL)) { + log(LOG_WARN, "Error parsing router file"); + return -1; + } + if (router_resolve_directory(directory)) { + log(LOG_WARN, "Error resolving directory"); + return -1; + } + return 0; +} + +static int router_get_hash_impl(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; +} + +int router_get_dir_hash(char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "signed-directory","directory-signature"); +} +int router_get_router_hash(char *s, char *digest) +{ + return router_get_hash_impl(s,digest, + "router ","router-signature"); +} + +/* return 0 if myversion is in start. Else return -1. */ +int compare_recommended_versions(char *myversion, char *start) { + int len_myversion = strlen(myversion); + char *comma; + char *end = start + strlen(start); + + log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, start); + + for(;;) { + comma = strchr(start, ','); + if( ((comma ? comma : end) - start == len_myversion) && + !strncmp(start, myversion, len_myversion)) /* only do strncmp if the length matches */ + return 0; /* success, it's there */ + if(!comma) + return -1; /* nope */ + start = comma+1; + } +} + +int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey) +{ + if (router_get_dir_from_string_impl(s, &directory, pkey)) { + log_fn(LOG_WARN, "Couldn't parse directory."); + return -1; + } + if (router_resolve_directory(directory)) { + log_fn(LOG_WARN, "Error resolving directory"); + return -1; + } + if (compare_recommended_versions(VERSION, directory->software_versions) < 0) { + log(LOG_WARN, "You are running tor version %s, which is no longer supported.\nPlease upgrade to one of %s.", VERSION, directory->software_versions); + if(options.IgnoreVersion) { + log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so."); + } else { + log(LOG_ERR,"Set IgnoreVersion config variable if you want to proceed."); + fflush(0); + exit(0); + } + } + + return 0; +} + +int router_get_dir_from_string_impl(char *s, directory_t **dest, + crypto_pk_env_t *pkey) +{ + directory_token_t tok; + char digest[20]; + char signed_digest[128]; + directory_t *new_dir = NULL; + char *versions; + struct tm published; + time_t published_on; + const char *good_nickname_lst[1024]; + int n_good_nicknames; + +#define NEXT_TOK() \ + do { \ + if (router_get_next_token(&s, &tok)) { \ + log_fn(LOG_WARN, "Error reading directory: %s", tok.val.error);\ + return -1; \ + } } while (0) +#define TOK_IS(type,name) \ + do { \ + if (tok.tp != type) { \ + router_release_token(&tok); \ + log_fn(LOG_WARN, "Error reading directory: expected %s", name);\ + return -1; \ + } } while(0) + + 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"); + if (tok.val.cmd.n_args != 2) { + log_fn(LOG_WARN, "Invalid published line"); + goto err; + } + tok.val.cmd.args[1][-1] = ' '; + if (!strptime(tok.val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) { + log_fn(LOG_WARN, "Published time was unparseable"); goto err; + } + published_on = timegm(&published); + + NEXT_TOK(); + TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software"); + if (tok.val.cmd.n_args != 1) { + log_fn(LOG_WARN, "Invalid recommended-software line"); + goto err; + } + versions = tor_strdup(tok.val.cmd.args[0]); + + NEXT_TOK(); + TOK_IS(K_RUNNING_ROUTERS, "running-routers"); + n_good_nicknames = tok.val.cmd.n_args; + memcpy(good_nickname_lst, tok.val.cmd.args, n_good_nicknames*sizeof(char *)); + + if (router_get_list_from_string_impl(&s, &new_dir, + n_good_nicknames, good_nickname_lst)) { + log_fn(LOG_WARN, "Error reading routers from directory"); + goto err; + } + new_dir->software_versions = versions; + 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."); + free(tok.val.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."); + free(tok.val.signature); + goto err; + } + } + free(tok.val.signature); + + NEXT_TOK(); + TOK_IS(_EOF, "end of directory"); + + if (*dest) + directory_free(*dest); + *dest = new_dir; + + return 0; + + err: + if (new_dir) + directory_free(new_dir); + return -1; +#undef NEXT_TOK +#undef TOK_IS +} + +int router_get_list_from_string_impl(char **s, directory_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); + 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) + directory_free(*dest); + *dest = (directory_t *)tor_malloc(sizeof(directory_t)); + (*dest)->routers = rarray; + (*dest)->n_routers = rarray_len; + (*dest)->software_versions = NULL; + return 0; +} + +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; +} + +static int +router_resolve_directory(directory_t *dir) +{ + int i, max, remove; + if (!dir) + dir = directory; + + max = dir->n_routers; + for (i = 0; i < max; ++i) { + remove = 0; + if (router_resolve(dir->routers[i])) { + log_fn(LOG_WARN, "Couldn't resolve router %s; removing", + dir->routers[i]->address); + remove = 1; + } else if (options.Nickname && + !strcmp(dir->routers[i]->nickname, options.Nickname)) { + remove = 1; + } + if (remove) { + routerinfo_free(dir->routers[i]); + dir->routers[i] = dir->routers[--max]; + --dir->n_routers; + --i; + } + } + + return 0; +} + +/* reads a single router entry from s. + * updates s so it points to after the router it just read. + * mallocs a new router, returns it if all goes well, else returns NULL. + */ +routerinfo_t *router_get_entry_from_string(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; + +#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 + + 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) { + router_release_token(tok); + log_fn(LOG_WARN,"Entry does not start with \"router\""); + return NULL; + } + + router = tor_malloc(sizeof(routerinfo_t)); + memset(router,0,sizeof(routerinfo_t)); /* zero it out first */ + router->onion_pkey = router->identity_pkey = router->link_pkey = NULL; + + if (tok->val.cmd.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->ap_port */ + router->ap_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, ap_port %d, dir_port %d, bandwidth %d.", + router->or_port, router->ap_port, router->dir_port, 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; + } + if (tok->val.cmd.n_args != 2) { + log_fn(LOG_WARN, "Wrong number of arguments to published"); goto err; + } + ARGS[1][-1] = ' '; /* Re-insert space. */ + if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) { + log_fn(LOG_WARN, "Published time was unparseable"); goto err; + } + router->published_on = 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 NEXT_TOKEN +} + +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; + strcpy(line,s); + } else { + memcpy(line,s,e-s); + line[e-s] = 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; + } +} + +static int +router_add_exit_policy_from_string(routerinfo_t *router, + char *s) +{ + directory_token_t tok; + char *tmp, *cp; + int r; + int len, idx; + + len = strlen(s); + tmp = cp = 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; + } + r = router_add_exit_policy(router, &tok); + free(tmp); + return r; +} + +static int router_add_exit_policy(routerinfo_t *router, + directory_token_t *tok) { + struct exit_policy_t *tmpe, *newe; + char *arg, *colon; + + if (tok->val.cmd.n_args != 1) + return -1; + arg = tok->val.cmd.args[0]; + + newe = tor_malloc(sizeof(struct exit_policy_t)); + memset(newe,0,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 { + assert(tok->tp == K_ACCEPT); + strcpy(newe->string, "accept "); + newe->policy_type = EXIT_POLICY_ACCEPT; + } + strcat(newe->string, arg); + + colon = strchr(arg,':'); + if(!colon) + goto policy_read_failed; + *colon = 0; + newe->address = tor_strdup(arg); + newe->port = tor_strdup(colon+1); + + log_fn(LOG_DEBUG,"%s %s:%s", + newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept", + newe->address, newe->port); + + /* 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); + if(newe->string) + free(newe->string); + if(newe->address) + free(newe->address); + if(newe->port) + free(newe->port); + free(newe); + return -1; +} + +/* Return 0 if my exit policy says to allow connection to conn. + * Else return -1. + */ +int router_compare_to_exit_policy(connection_t *conn) { + struct exit_policy_t *tmpe; + struct in_addr in; + + assert(desc_routerinfo); + + for(tmpe=desc_routerinfo->exit_policy; tmpe; tmpe=tmpe->next) { + assert(tmpe->address); + assert(tmpe->port); + + log_fn(LOG_DEBUG,"Considering exit policy %s:%s",tmpe->address, tmpe->port); + if(strcmp(tmpe->address,"*") && + inet_aton(tmpe->address,&in) == 0) { /* malformed IP. reject. */ + log_fn(LOG_WARN,"Malformed IP %s in exit policy. Rejecting.",tmpe->address); + return -1; + } + if((!strcmp(tmpe->address,"*") || conn->addr == ntohl(in.s_addr)) && + (!strcmp(tmpe->port,"*") || atoi(tmpe->port) == conn->port)) { + log_fn(LOG_INFO,"Address '%s' matches '%s' and port '%s' matches '%d'. %s.", + tmpe->address, conn->address, + tmpe->port, conn->port, + tmpe->policy_type == EXIT_POLICY_ACCEPT ? "Accepting" : "Rejecting"); + if(tmpe->policy_type == EXIT_POLICY_ACCEPT) + return 0; + else + return -1; + } + } + return 0; /* accept all by default. */ +} + +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; +} +const routerinfo_t *router_get_desc_routerinfo(void) { + if (!desc_routerinfo) { + if (router_rebuild_descriptor()) + return NULL; + } + return desc_routerinfo; +} + +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->ap_port = options.APPort; + 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; +} + +#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; + 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; + 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->ap_port, + router->dir_port, + 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) { + result = snprintf(s+written, maxlen-written, "%s %s:%s\n", + tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject", + tmpe->address, tmpe->port); + if(result < 0 || result+written > maxlen) { + /* apparently different glibcs do different things on snprintf error.. so check both */ + return -1; + } + written += result; + } + 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 + s_tmp = s_dup = tor_strdup(s); + ri_tmp = router_get_entry_from_string(&s_tmp); + 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_2pre13/src/or/test.c b/tags/tor-0_0_2pre13/src/or/test.c new file mode 100644 index 0000000000..269022b5b3 --- /dev/null +++ b/tags/tor-0_0_2pre13/src/or/test.c @@ -0,0 +1,694 @@ +/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ +/* 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 640*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), 2*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), 2*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), MAX_BUF_SIZE); + 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), 32); + 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; + + 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)); + +} + +void +test_onion_handshake() { + /* client-side */ + crypto_dh_env_t *c_dh = NULL; + char c_buf[DH_ONIONSKIN_LEN]; + char c_keys[40]; + + /* server-side */ + char s_buf[DH_KEY_LEN]; + char s_keys[40]; + + /* shared */ + crypto_pk_env_t *pk = NULL; + + pk = crypto_new_pk_env(CRYPTO_PK_RSA); + test_assert(! crypto_pk_generate_key(pk)); + + /* client handshake 1. */ + memset(c_buf, 0, DH_ONIONSKIN_LEN); + test_assert(! onion_skin_create(pk, &c_dh, c_buf)); + + /* server handshake */ + memset(s_buf, 0, DH_KEY_LEN); + memset(s_keys, 0, 40); + test_assert(! onion_skin_server_handshake(c_buf, pk, s_buf, s_keys, 40)); + + /* client handshake 2 */ + memset(c_keys, 0, 40); + test_assert(! onion_skin_client_handshake(c_dh, s_buf, c_keys, 40)); + + crypto_dh_free(c_dh); + + if (memcmp(c_keys, s_keys, 40)) { + puts("Aiiiie"); + exit(1); + } + test_memeq(c_keys, s_keys, 40); + memset(s_buf, 0, 40); + test_memneq(c_keys, s_buf, 40); + crypto_free_pk_env(pk); +} + +/* from routers.c */ +int compare_recommended_versions(char *myversion, char *start); + +void +test_dir_format() +{ + char buf[8192], buf2[8192]; + char *pk1_str = NULL, *pk2_str = NULL, *pk3_str = NULL, *cp; + int pk1_str_len, pk2_str_len, pk3_str_len; + routerinfo_t r1, r2; + crypto_pk_env_t *pk1 = NULL, *pk2 = NULL, *pk3 = NULL; + routerinfo_t *rp1 = NULL, *rp2 = NULL; + struct exit_policy_t ex1, ex2; + directory_t *dir1 = NULL, *dir2 = NULL; + + test_assert( (pk1 = crypto_new_pk_env(CRYPTO_PK_RSA)) ); + test_assert( (pk2 = crypto_new_pk_env(CRYPTO_PK_RSA)) ); + test_assert( (pk3 = crypto_new_pk_env(CRYPTO_PK_RSA)) ); + test_assert(! crypto_pk_generate_key(pk1)); + test_assert(! crypto_pk_generate_key(pk2)); + test_assert(! crypto_pk_generate_key(pk3)); + + r1.address = "testaddr1.foo.bar"; + r1.addr = 0xc0a80001u; /* 192.168.0.1 */ + r1.published_on = 0; + r1.or_port = 9000; + r1.ap_port = 9002; + r1.dir_port = 9003; + r1.onion_pkey = pk1; + r1.identity_pkey = pk2; + r1.link_pkey = pk3; + r1.bandwidth = 1000; + r1.exit_policy = NULL; + r1.nickname = "Magri"; + + ex1.policy_type = EXIT_POLICY_ACCEPT; + ex1.string = NULL; + ex1.address = "*"; + ex1.port = "80"; + ex1.next = &ex2; + ex2.policy_type = EXIT_POLICY_REJECT; + ex2.address = "18.*"; + ex2.port = "24"; + ex2.next = NULL; + r2.address = "tor.tor.tor"; + r2.addr = 0x0a030201u; /* 10.3.2.1 */ + r2.published_on = 5; + r2.or_port = 9005; + r2.ap_port = 0; + r2.dir_port = 0; + r2.onion_pkey = pk2; + r2.identity_pkey = pk1; + r2.link_pkey = pk2; + r2.bandwidth = 3000; + r2.exit_policy = &ex1; + + test_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str, + &pk1_str_len)); + test_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str, + &pk2_str_len)); + test_assert(!crypto_pk_write_public_key_to_string(pk3 , &pk3_str, + &pk3_str_len)); + + memset(buf, 0, 2048); + log_set_severity(LOG_WARN); + test_assert(router_dump_router_to_string(buf, 2048, &r1, pk2)>0); + + 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(&cp); + test_assert(rp1); + test_streq(rp1->address, r1.address); + test_eq(rp1->or_port, r1.or_port); + test_eq(rp1->ap_port, r1.ap_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->ap_port, r2.ap_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 + + if (pk1_str) free(pk1_str); + if (pk2_str) 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); + if (dir1) free(dir1); /* And more !*/ + if (dir1) 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", "")); + test_eq(0, compare_recommended_versions(VERSION, RECOMMENDED_SOFTWARE_VERSIONS)); +} + +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 + log_set_severity(LOG_ERR); /* make logging quieter */ + + 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_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_2pre13/src/or/tor_main.c b/tags/tor-0_0_2pre13/src/or/tor_main.c new file mode 100644 index 0000000000..32f9b1d9de --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/or/tree.h b/tags/tor-0_0_2pre13/src/or/tree.h new file mode 100644 index 0000000000..927ca04cd7 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/src/win32/orconfig.h b/tags/tor-0_0_2pre13/src/win32/orconfig.h new file mode 100644 index 0000000000..3170f9e3e5 --- /dev/null +++ b/tags/tor-0_0_2pre13/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_2pre13/tor.sh.in b/tags/tor-0_0_2pre13/tor.sh.in new file mode 100644 index 0000000000..b47d82ae87 --- /dev/null +++ b/tags/tor-0_0_2pre13/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="-l warn" +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 >$TORLOG 2>&1 & + 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