This commit was manufactured by cvs2svn to create tag

'tor-0_0_2pre13'.

svn:r632
This commit is contained in:
(no author)
2003-10-19 05:45:22 +00:00
parent 0bb0a22f00
commit 30f0dcafd1
78 changed files with 19775 additions and 0 deletions
+12
View File
@@ -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
+23
View File
@@ -0,0 +1,23 @@
Main authors:
-------------
Matej Pfajfar <badbytes@freehaven.net> wrote the first version of the code
(called OR) in 2001-2002.
Roger Dingledine <arma@freehaven.net> overhauled all of the code, did
a bunch of new design work, etc.
Nick Mathewson <nickm@freehaven.net> wrote lots of stuff too, in
particular the router and descriptor parsing and the crypto and tls
wrappers.
Contributors:
-------------
Aaron Turner <aturner@netscreen.com> contributed the first version of
the tor.sh initscripts shell script.
Steven Hazel <sah@thalassocracy.org> made 'make install' do the right
thing.
+1
View File
@@ -0,0 +1 @@
Automake wants this file. See LICENSE instead.
+4
View File
@@ -0,0 +1,4 @@
hey, emacs, got something for you: -*- fundamental -*-
ChangeLog
---------
+51
View File
@@ -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.
+30
View File
@@ -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.
+9
View File
@@ -0,0 +1,9 @@
SUBDIRS = src doc
DIST_SUBDIRS = src doc
EXTRA_DIST = LICENSE
install-data-local:
$(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor
+2
View File
@@ -0,0 +1,2 @@
NEWS
----
+38
View File
@@ -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.
+218
View File
@@ -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
+29
View File
@@ -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>
{{{
}}}
###############################################################################
@@ -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>
{{{
}}}
###############################################################################
Binary file not shown.
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
aclocal && autoheader && autoconf && automake
./configure --enable-debug
+188
View File
@@ -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 <string.h>
#include <openssl/rand.h>
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)
+436
View File
@@ -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 <oliva@dcc.unicamp.br>.
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
+63
View File
@@ -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
+123
View File
@@ -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?
+532
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
EXTRA_DIST = tor-spec.txt CLIENTS FAQ HACKING rendezvous.txt tor-design.tex
+247
View File
@@ -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
+200
View File
@@ -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 <A,C>; Alice's end-to-end authentication is <A,B>.
[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.
+875
View File
@@ -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:
+521
View File
@@ -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
+508
View File
@@ -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:
+251
View File
@@ -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
+336
View File
@@ -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 <pinard@iro.umontreal.ca>, 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
+101
View File
@@ -0,0 +1,101 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# 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
+2
View File
@@ -0,0 +1,2 @@
Makefile
Makefile.in
+6
View File
@@ -0,0 +1,6 @@
# leave in dependency order, since common must be built first
SUBDIRS = common or config
DIST_SUBDIRS = common or config
@@ -0,0 +1,3 @@
Makefile
Makefile.in
.deps
@@ -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
File diff suppressed because it is too large Load Diff
+23
View File
@@ -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
File diff suppressed because it is too large Load Diff
+109
View File
@@ -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 <stdio.h>
#include <openssl/rsa.h>
#include <openssl/dh.h>
/* 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
+90
View File
@@ -0,0 +1,90 @@
/*
* fakepoll.c
*
* On systems where 'poll' doesn't exist, fake it with 'select'.
*
* Nick Mathewson <nickm@freehaven.net>
*/
#include "orconfig.h"
#include "fakepoll.h"
#ifdef USE_FAKE_POLL
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#if _MSC_VER > 1300
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(_MSC_VER)
#include <winsock.h>
#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
+35
View File
@@ -0,0 +1,35 @@
/*
* fakepoll.h
*
* On systems where 'poll' doesn't exist, fake it with 'select'.
*
* Nick Mathewson <nickm@freehaven.net>
*/
#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
+162
View File
@@ -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;
}
+49
View File
@@ -0,0 +1,49 @@
/*
* log.h
* Logging facilities.
*
* Matej Pfajfar <mp292@cam.ac.uk>
*/
#ifndef __LOG_H
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#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
+127
View File
@@ -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 <string.h>
#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:
*/
+156
View File
@@ -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 <stdint.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#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 */
+554
View File
@@ -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 <assert.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
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: <syscall 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);
}
+32
View File
@@ -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
+533
View File
@@ -0,0 +1,533 @@
/* Copyright 2003 Roger Dingledine */
/* See LICENSE for licensing information */
/* $Id$ */
#include "../or/or.h"
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#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;
}
+83
View File
@@ -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 <winsock2.h>
#include <ws2tcpip.h>
#elif defined(_MSC_VER)
#include <winsock.h>
#endif
#ifndef HAVE_GETTIMEOFDAY
#ifdef HAVE_FTIME
#define USING_FAKE_TIMEVAL
#include <sys/timeb.h>
#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
@@ -0,0 +1,5 @@
confdir = $(sysconfdir)/tor
EXTRA_DIST = dirservers
conf_DATA = dirservers torrc sample-server-torrc
+87
View File
@@ -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-----
@@ -0,0 +1,13 @@
DataDirectory moria1
Nickname moria1
ORPort 9001
APPort 9021
DirPort 9031
OnionRouter 1
# List of routers
RouterFile ../config/dirservers
@@ -0,0 +1,13 @@
DataDirectory moria2
Nickname moria2
ORPort 9002
APPort 9022
DirPort 9032
OnionRouter 1
# List of routers
RouterFile ../config/dirservers
@@ -0,0 +1,13 @@
DataDirectory moria3
Nickname moria3
ORPort 9003
APPort 9023
DirPort 9033
OnionRouter 1
# List of routers
RouterFile ../config/dirservers
+12
View File
@@ -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
@@ -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
@@ -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
+12
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
Makefile
Makefile.in
tor
test
.deps
+23
View File
@@ -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
+555
View File
@@ -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:
*/
+998
View File
@@ -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:
*/
+226
View File
@@ -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:
*/
+334
View File
@@ -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:
*/
+808
View File
@@ -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;i<n;i++) {
conn = carray[i];
if(conn->addr == 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;i<n;i++) {
conn = carray[i];
assert(conn);
if(connection_state_is_open(conn) &&
!conn->marked_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;i<n;i++) {
conn = carray[i];
if(conn->type == 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;i<n;i++) {
conn = carray[i];
if(conn->type == 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;i<n;i++) {
conn = carray[i];
if(conn->type == 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:
*/
@@ -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:
*/
+302
View File
@@ -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:
*/
+284
View File
@@ -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:
*/
+271
View File
@@ -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:
*/
+447
View File
@@ -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;i<n_fingerprints;++i) {
log_fn(LOG_DEBUG,"%s vs %s", router->nickname, 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; i<n_conns; ++i) {
conn = connection_array[i];
if (conn->type != 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; i<n; ++i) {
length += strlen(nickname_lst[i]);
}
*nicknames_out = tor_malloc(length);
cp = *nicknames_out;
memset(cp,0,length);
for (i = 0; i<n; ++i) {
if (i)
strcat(cp, " ");
strcat(cp, nickname_lst[i]);
while (*cp)
++cp;
}
return 0;
}
int
dirserv_dump_directory_to_string(char *s, int maxlen,
crypto_pk_env_t *private_key)
{
char *cp, *eos;
char digest[20];
char signature[128];
char published[33];
time_t published_on;
int i;
eos = s+maxlen;
if (list_running_servers(&cp))
return -1;
published_on = time(NULL);
strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on));
snprintf(s, maxlen,
"signed-directory\n"
"published %s\n"
"recommended-software "RECOMMENDED_SOFTWARE_VERSIONS"\n"
"running-routers %s\n\n", published, cp);
free(cp);
i = strlen(s);
cp = s+i;
for (i = 0; i < n_descriptors; ++i) {
strncat(cp, descriptor_list[i]->descriptor, 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;
}
+447
View File
@@ -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("<idle>");
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("<unused>");
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:
*/
+833
View File
@@ -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(i<nfds) { /* we just replaced the one at i with a new one. process it too. */
conn_read(i);
}
} else assert_connection_ok(conn, time(NULL));
}
static void conn_write(int i) {
connection_t *conn;
if(!(poll_array[i].revents & POLLOUT))
return; /* this conn doesn't want to write */
conn = connection_array[i];
log_fn(LOG_DEBUG,"socket %d wants to write.",conn->s);
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(i<nfds) { /* we just replaced the one at i with a new one. process it too. */
conn_write(i);
}
} else assert_connection_ok(conn, time(NULL));
}
static void conn_close_if_marked(int i) {
connection_t *conn;
conn = connection_array[i];
assert_connection_ok(conn, time(NULL));
if(conn->marked_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(i<nfds) { /* we just replaced the one at i with a new one.
process it too. */
conn_close_if_marked(i);
}
}
}
/* Perform regular maintenance tasks for a single connection. This
* function gets run once per second per connection by run_housekeeping.
*/
static void run_connection_housekeeping(int i, time_t now) {
cell_t cell;
connection_t *conn = connection_array[i];
if(connection_receiver_bucket_should_increase(conn)) {
conn->receiver_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<nfds;i++) {
run_connection_housekeeping(i, now);
}
/* 5. and blow away any connections that need to die. can't do this later
* because we might open up a circuit and not realize we're about to cull
* the connection it's running over.
*/
for(i=0;i<nfds;i++)
conn_close_if_marked(i);
}
static int prepare_for_poll(void) {
static long current_second = 0; /* from previous calls to gettimeofday */
connection_t *conn;
struct timeval now;
int i;
tor_gettimeofday(&now);
if(now.tv_sec > 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;i<nfds;i++) {
conn = connection_array[i];
if(connection_has_pending_tls_data(conn)) {
log_fn(LOG_DEBUG,"sock %d has pending bytes.",conn->s);
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;i<nfds;i++)
conn_read(i); /* this also blows away broken connections */
/* then do the writes */
for(i=0;i<nfds;i++)
conn_write(i);
/* any of the conns need to be closed now? */
for(i=0;i<nfds;i++)
conn_close_if_marked(i);
/* refilling buckets and sending cells happens at the beginning of the
* next iteration of the loop, inside prepare_for_poll()
*/
}
}
static void catch(int the_signal) {
#ifndef MS_WIN32 /* do signal stuff only on unix */
switch(the_signal) {
// case SIGABRT:
case SIGTERM:
case SIGINT:
log(LOG_ERR,"Catching signal %d, exiting cleanly.", the_signal);
/* we don't care if there was an error when we unlink,
nothing we could do about it anyways */
unlink(options.PidFile);
exit(0);
case SIGHUP:
please_reset = 1;
break;
case SIGUSR1:
please_dumpstats = 1;
break;
case SIGCHLD:
please_reap_children = 1;
break;
default:
log(LOG_WARN,"Caught signal %d that we can't handle??", the_signal);
}
#endif /* signal stuff */
}
static void dumpstats(int severity) {
int i;
connection_t *conn;
time_t now = time(NULL);
log(severity, "Dumping stats:");
for(i=0;i<nfds;i++) {
conn = connection_array[i];
log(severity, "Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago",
i, conn->s, 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>", 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>", 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:
*/
+566
View File
@@ -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;i<rarray_len;i++) {
log_fn(LOG_DEBUG,"Contemplating whether router %d is a new option...",i);
if(options.OnionRouter) {
conn = connection_exact_get_by_addr_port(rarray[i]->addr, rarray[i]->or_port);
if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) {
log_fn(LOG_DEBUG,"Nope, %d is not connected.",i);
goto next_i_loop;
}
}
for(j=0;j<i;j++) {
if(!crypto_pk_cmp_keys(rarray[i]->onion_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; i<routelen; i++) {
netaddr.s_addr = htonl((rarray[route[i]])->addr);
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;i<routelen;i++) {
router = rarray[route[i]];
/* build up the crypt_path */
hop = (crypt_path_t *)tor_malloc(sizeof(crypt_path_t));
memset(hop, 0, sizeof(crypt_path_t));
/* link hop into the cpath, at the front */
hop->next = 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<n; ++_i) printf("%02x ",((int)(a)[_i])&0xFF); }
printf("Client: client g^x:");
PA(pubkey+16,3);
printf("...");
PA(pubkey+141,3);
puts("");
printf("Client: client symkey:");
PA(pubkey+0,16);
puts("");
#endif
cipher = crypto_create_init_cipher(ONION_CIPHER, pubkey, iv, 1);
if (!cipher)
goto err;
if (crypto_pk_public_encrypt(dest_router_key, pubkey, pkbytes,
onion_skin_out, RSA_NO_PADDING)==-1)
goto err;
if (crypto_cipher_encrypt(cipher, pubkey+pkbytes, dhbytes+16-pkbytes,
onion_skin_out+pkbytes))
goto err;
free(pubkey);
crypto_free_cipher_env(cipher);
*handshake_state_out = dh;
return 0;
err:
if (pubkey) free(pubkey);
if (dh) crypto_dh_free(dh);
if (cipher) crypto_free_cipher_env(cipher);
return -1;
}
/* Given an encrypted DH public key as generated by onion_skin_create,
* and the private key for this onion router, generate the 128-byte DH
* reply, and key_out_len bytes of key material, stored in key_out.
*/
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)
{
char buf[DH_ONIONSKIN_LEN];
char iv[16];
crypto_dh_env_t *dh = NULL;
crypto_cipher_env_t *cipher = NULL;
int pkbytes;
int len;
memset(iv, 0, 16);
pkbytes = crypto_pk_keysize(private_key);
if (crypto_pk_private_decrypt(private_key,
onion_skin, pkbytes,
buf, RSA_NO_PADDING) == -1)
goto err;
#ifdef DEBUG_ONION_SKINS
printf("Server: client symkey:");
PA(buf+0,16);
puts("");
#endif
cipher = crypto_create_init_cipher(ONION_CIPHER, buf, iv, 0);
if (crypto_cipher_decrypt(cipher, onion_skin+pkbytes, DH_ONIONSKIN_LEN-pkbytes,
buf+pkbytes))
goto err;
#ifdef DEBUG_ONION_SKINS
printf("Server: client g^x:");
PA(buf+16,3);
printf("...");
PA(buf+141,3);
puts("");
#endif
dh = crypto_dh_new();
if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN))
goto err;
#ifdef DEBUG_ONION_SKINS
printf("Server: server g^y:");
PA(handshake_reply_out+0,3);
printf("...");
PA(handshake_reply_out+125,3);
puts("");
#endif
len = crypto_dh_compute_secret(dh, buf+16, DH_KEY_LEN, key_out, key_out_len);
if (len < 0)
goto err;
#ifdef DEBUG_ONION_SKINS
printf("Server: key material:");
PA(buf, DH_KEY_LEN);
puts("");
printf("Server: keys out:");
PA(key_out, key_out_len);
puts("");
#endif
crypto_free_cipher_env(cipher);
crypto_dh_free(dh);
return 0;
err:
if (cipher) crypto_free_cipher_env(cipher);
if (dh) crypto_dh_free(dh);
return -1;
}
/* Finish the client side of the DH handshake.
* Given the 128 byte DH reply as generated by onion_skin_server_handshake
* and the handshake state generated by onion_skin_create, generate
* key_out_len bytes of shared key material and store them in key_out.
*
* After the invocation, call crypto_dh_free on handshake_state.
*/
int
onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
char *handshake_reply,/* Must be DH_KEY_LEN bytes long*/
char *key_out,
int key_out_len)
{
int len;
assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
#ifdef DEBUG_ONION_SKINS
printf("Client: server g^y:");
PA(handshake_reply+0,3);
printf("...");
PA(handshake_reply+125,3);
puts("");
#endif
len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN,
key_out, key_out_len);
if (len < 0)
return -1;
#ifdef DEBUG_ONION_SKINS
printf("Client: keys out:");
PA(key_out, key_out_len);
puts("");
#endif
return 0;
}
/*
Local Variables:
mode:c
indent-tabs-mode:nil
c-basic-offset:2
End:
*/
+743
View File
@@ -0,0 +1,743 @@
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */
#ifndef __OR_H
#define __OR_H
#include "orconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "../common/torint.h"
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#elif HAVE_POLL_H
#include <poll.h>
#else
#include "../common/fakepoll.h"
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> /* Must be included before sys/stat.h for Ultrix */
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_ASSERT_H
#include <assert.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#endif
#if _MSC_VER > 1300
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(_MSC_VER)
#include <winsock.h>
#endif
#ifdef MS_WINDOWS
#include <io.h>
#include <process.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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:
*/
File diff suppressed because it is too large Load Diff
+694
View File
@@ -0,0 +1,694 @@
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */
#include <stdio.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef MS_WINDOWS
/* For mkdir() */
#include <direct.h>
#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<len;++i) {
for (j=1;j>=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:
*/
+18
View File
@@ -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:
*/
+677
View File
@@ -0,0 +1,677 @@
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* 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_ */
+147
View File
@@ -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 <arpa/inet.h> header file. */
#undef HAVE_ARPA_INET_H
/* Define to 1 if you have the <assert.h> header file. */
#define HAVE_ASSERT_H
/* Define to 1 if you have the <ctype.h> header file. */
#define HAVE_CTYPE_H
/* Define to 1 if you have the <errno.h> 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 <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet/in.h> header file. */
#undef HAVE_NETINET_IN_H
/* Define to 1 if you have the <poll.h> header file. */
#undef HAVE_POLL_H
/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H
/* Define to 1 if you have the <sys/fcntl.h> header file. */
#undef HAVE_SYS_FCNTL_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/poll.h> header file. */
#undef HAVE_SYS_POLL_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H
/* Define to 1 if you have the <time.h> 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 <unistd.h> 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"
+76
View File
@@ -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