mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
This commit was manufactured by cvs2svn to create tag
'tor-0_0_2pre19'. svn:r980
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
configure
|
||||
orconfig.h
|
||||
orconfig.h.in
|
||||
config.cache
|
||||
config.log
|
||||
config.status
|
||||
conftest*
|
||||
stamp-h
|
||||
stamp-h1
|
||||
tor.sh
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
Main authors:
|
||||
-------------
|
||||
|
||||
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.
|
||||
|
||||
Matej Pfajfar <badbytes@freehaven.net> wrote the first version of the code
|
||||
(called OR) in 2001-2002.
|
||||
|
||||
Contributors:
|
||||
-------------
|
||||
|
||||
Christian Grothoff <grothoff@cs.purdue.edu> contributed better daemonizing
|
||||
behavior.
|
||||
|
||||
Steven Hazel <sah@thalassocracy.org> made 'make install' do the right
|
||||
thing.
|
||||
|
||||
Jason Holt <jason@lunkwill.org> contributed patches to the instructions
|
||||
and the man page.
|
||||
|
||||
Aaron Turner <aturner@netscreen.com> contributed the first version of
|
||||
the tor.sh initscripts shell script.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
hey, emacs, got something for you: -*- fundamental -*-
|
||||
|
||||
ChangeLog
|
||||
---------
|
||||
@@ -0,0 +1,51 @@
|
||||
Quickstart version for users:
|
||||
|
||||
0) Download the absolute newest version. No, really.
|
||||
http://freehaven.net/tor/dist/
|
||||
1) tar xvf it, and then cd into the directory.
|
||||
2) ./configure (or do the two-line version below, if you're on bsd)
|
||||
3) make
|
||||
4) make install
|
||||
5) tor (if it doesn't work, give it the whole path or fix your path)
|
||||
You don't need to run this as root, and you probably shouldn't.
|
||||
(If you're having problems, try running it with "-l info" to get
|
||||
more details.)
|
||||
6) point your browser to socks4 or socks5 proxy at localhost port
|
||||
9050. In mozilla, this is in edit|preferences|advanced|proxies. This
|
||||
allows you to test to make sure tor is installed correctly.
|
||||
(If you have a personal firewall, be sure to allow connections to
|
||||
localhost port 9050.)
|
||||
(If you're using Safari as your browser, keep in mind that OS X before
|
||||
10.3 claims to support socks but does not. You must do step 8.)
|
||||
7) make sure you've set it up correctly: go to
|
||||
http://www.junkbusters.com/cgi-bin/privacy and see what IP it says
|
||||
you're coming from. If it works, you should probably go on to step 8,
|
||||
to get better privacy.
|
||||
|
||||
8) Optionally, install privoxy (www.privoxy.org), and add the line
|
||||
"forward-socks4a / localhost:9050 ." (without the quotes -- don't forget
|
||||
the dot) to its config file. Then change your mozilla to http proxy
|
||||
at localhost port 8118 (and no socks proxy). This step will give you
|
||||
good html scrubbing as well.
|
||||
(See doc/CLIENTS for why direct socks gives you less anonymity.)
|
||||
|
||||
*****If this works for you, you can stop reading here******
|
||||
|
||||
If you got the source from cvs:
|
||||
|
||||
Run "./autogen.sh", which will run the various auto* programs and then
|
||||
run ./configure for you. From there, start at step 3 in the quickstart
|
||||
list above.
|
||||
|
||||
If the quickstart doesn't work for you:
|
||||
|
||||
If you have problems finding libraries, try
|
||||
CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" \
|
||||
./configure
|
||||
rather than simply ./configure.
|
||||
|
||||
Check out the list archives at http://archives.seul.org/or/dev/ and see
|
||||
if somebody else has reported your problem. If not, please subscribe
|
||||
and let us know what you did to fix it, or give us the details and
|
||||
we'll see what we can do.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
Copyright (c) 2001-2003, Roger Dingledine
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the names of the copyright owners nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,11 @@
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
# else it keeps trying to put COPYING back in
|
||||
|
||||
SUBDIRS = src doc
|
||||
|
||||
DIST_SUBDIRS = src doc
|
||||
|
||||
EXTRA_DIST = INSTALL README AUTHORS LICENSE
|
||||
|
||||
#install-data-local:
|
||||
# $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
'tor' is an implementation of The Onion Routing system, as
|
||||
described in a bit more detail at http://www.onion-router.net/. You
|
||||
can read list archives, and subscribe to the mailing list, at
|
||||
http://archives.seul.org/or/dev/.
|
||||
|
||||
Is your question in the FAQ? Should it be?
|
||||
|
||||
**************************************************************************
|
||||
See the INSTALL file for a quickstart. That is all you will probably need.
|
||||
**************************************************************************
|
||||
|
||||
**************************************************************************
|
||||
You only need to look beyond this point if the quickstart in the INSTALL
|
||||
doesn't work for you.
|
||||
**************************************************************************
|
||||
|
||||
Do you want to run a tor server?
|
||||
|
||||
First, edit the bottom part of your torrc. Create the DataDirectory,
|
||||
and make sure it's owned by whoever will be running tor. Fix your system
|
||||
clock so it's not too far off. Make sure name resolution works. Make
|
||||
sure other people can reliably resolve the Address you chose.
|
||||
|
||||
Then run tor to generate keys. One of the files generated
|
||||
in your DataDirectory is your 'fingerprint' file. Mail it to
|
||||
arma@mit.edu.
|
||||
|
||||
NOTE: You won't be able to use tor as a client or server
|
||||
in this configuration until you've been added to the directory
|
||||
and can authenticate to the other nodes.
|
||||
|
||||
Configuring tsocks:
|
||||
|
||||
If you want to use Tor for protocols that can't use Privoxy, or
|
||||
with applications that are not socksified, then download tsocks
|
||||
(tsocks.sourceforge.net) and configure it to talk to localhost:9050
|
||||
as a socks4 server. My /etc/tsocks.conf simply has:
|
||||
server_port = 9050
|
||||
server = 127.0.0.1
|
||||
(I had to "cd /usr/lib; ln -s /lib/libtsocks.so" to get the tsocks
|
||||
library working after install, since my libpath didn't include /lib.)
|
||||
Then you can do "tsocks ssh arma@moria.mit.edu". But note that if
|
||||
ssh is suid root, you either need to do this as root, or cp a local
|
||||
version of ssh that isn't suid.
|
||||
|
||||
@@ -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
|
||||
@@ -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.
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Run this to generate all the initial makefiles, etc.
|
||||
aclocal && autoheader && autoconf && automake
|
||||
./configure
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
|
||||
AC_INIT
|
||||
AM_INIT_AUTOMAKE(tor, 0.0.2pre19)
|
||||
AM_CONFIG_HEADER(orconfig.h)
|
||||
|
||||
CFLAGS="$CFLAGS -Wall -g -O2 -I/usr/kerberos/include"
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug compiles with debugging info],
|
||||
[if test x$enableval = xyes; then
|
||||
CFLAGS="$CFLAGS -g"
|
||||
fi])
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_RANLIB
|
||||
|
||||
# The big search for OpenSSL
|
||||
# copied from openssh's configure.ac
|
||||
AC_ARG_WITH(ssl-dir,
|
||||
[ --with-ssl-dir=PATH Specify path to OpenSSL installation ],
|
||||
[
|
||||
if test "x$withval" != "xno" ; then
|
||||
tryssldir=$withval
|
||||
fi
|
||||
]
|
||||
)
|
||||
|
||||
saved_LIBS="$LIBS"
|
||||
saved_LDFLAGS="$LDFLAGS"
|
||||
saved_CPPFLAGS="$CPPFLAGS"
|
||||
if test "x$prefix" != "xNONE" ; then
|
||||
tryssldir="$tryssldir $prefix"
|
||||
fi
|
||||
AC_CACHE_CHECK([for OpenSSL directory], ac_cv_openssldir, [
|
||||
for ssldir in $tryssldir "" /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/pkg /opt /opt/openssl ; do
|
||||
CPPFLAGS="$saved_CPPFLAGS"
|
||||
LDFLAGS="$saved_LDFLAGS"
|
||||
LIBS="$saved_LIBS -lssl -lcrypto"
|
||||
|
||||
# Skip directories if they don't exist
|
||||
if test ! -z "$ssldir" -a ! -d "$ssldir" ; then
|
||||
continue;
|
||||
fi
|
||||
if test ! -z "$ssldir" -a "x$ssldir" != "x/usr"; then
|
||||
# Try to use $ssldir/lib if it exists, otherwise
|
||||
# $ssldir
|
||||
if test -d "$ssldir/lib" ; then
|
||||
LDFLAGS="-L$ssldir/lib $saved_LDFLAGS"
|
||||
if test ! -z "$need_dash_r" ; then
|
||||
LDFLAGS="-R$ssldir/lib $LDFLAGS"
|
||||
fi
|
||||
else
|
||||
LDFLAGS="-L$ssldir $saved_LDFLAGS"
|
||||
if test ! -z "$need_dash_r" ; then
|
||||
LDFLAGS="-R$ssldir $LDFLAGS"
|
||||
fi
|
||||
fi
|
||||
# Try to use $ssldir/include if it exists, otherwise
|
||||
# $ssldir
|
||||
if test -d "$ssldir/include" ; then
|
||||
CPPFLAGS="-I$ssldir/include $saved_CPPFLAGS"
|
||||
else
|
||||
CPPFLAGS="-I$ssldir $saved_CPPFLAGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Basic test to check for compatible version and correct linking
|
||||
# *does not* test for RSA - that comes later.
|
||||
AC_TRY_RUN(
|
||||
[
|
||||
#include <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 -lssl -lcrypto"
|
||||
|
||||
dnl The warning message here is no longer strictly accurate.
|
||||
|
||||
AC_CHECK_HEADERS(unistd.h string.h signal.h netdb.h ctype.h poll.h sys/stat.h sys/poll.h sys/types.h sys/fcntl.h sys/ioctl.h sys/socket.h sys/time.h netinet/in.h arpa/inet.h errno.h assert.h time.h pwd.h grp.h, , AC_MSG_WARN(some headers were not found, compilation may fail))
|
||||
|
||||
dnl These headers are not essential
|
||||
|
||||
AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/wait.h netinet/in.h arpa/inet.h)
|
||||
|
||||
AC_CHECK_FUNCS(gettimeofday ftime socketpair uname)
|
||||
|
||||
dnl In case we aren't given a working stdint.h, we'll need to grow our own.
|
||||
dnl Watch out.
|
||||
|
||||
AC_CHECK_SIZEOF(int8_t)
|
||||
AC_CHECK_SIZEOF(int16_t)
|
||||
AC_CHECK_SIZEOF(int32_t)
|
||||
AC_CHECK_SIZEOF(int64_t)
|
||||
AC_CHECK_SIZEOF(uint8_t)
|
||||
AC_CHECK_SIZEOF(uint16_t)
|
||||
AC_CHECK_SIZEOF(uint32_t)
|
||||
AC_CHECK_SIZEOF(uint64_t)
|
||||
|
||||
dnl AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t])
|
||||
|
||||
AC_CHECK_SIZEOF(char)
|
||||
AC_CHECK_SIZEOF(short)
|
||||
AC_CHECK_SIZEOF(int)
|
||||
AC_CHECK_SIZEOF(long)
|
||||
AC_CHECK_SIZEOF(long long)
|
||||
AC_CHECK_SIZEOF(__int64)
|
||||
|
||||
# $prefix stores the value of the --prefix command line option, or
|
||||
# NONE if the option wasn't set. In the case that it wasn't set, make
|
||||
# it be the default, so that we can use it to expand directories now.
|
||||
if test "x$prefix" = "xNONE"; then
|
||||
prefix=$ac_default_prefix
|
||||
fi
|
||||
|
||||
# and similarly for $exec_prefix
|
||||
if test "x$exec_prefix" = "xNONE"; then
|
||||
exec_prefix=$prefix
|
||||
fi
|
||||
|
||||
CONFDIR=`eval echo $sysconfdir/tor`
|
||||
AC_SUBST(CONFDIR)
|
||||
AC_DEFINE_UNQUOTED(CONFDIR,"$CONFDIR")
|
||||
AC_DEFINE([CONFDIR], [], [tor's configuration directory])
|
||||
|
||||
BINDIR=`eval echo $bindir`
|
||||
AC_SUBST(BINDIR)
|
||||
|
||||
LOCALSTATEDIR=`eval echo $localstatedir`
|
||||
AC_SUBST(LOCALSTATEDIR)
|
||||
|
||||
echo "confdir: $CONFDIR"
|
||||
|
||||
AC_OUTPUT(Makefile tor.sh src/config/torrc doc/tor.1 src/Makefile doc/Makefile src/config/Makefile src/common/Makefile src/or/Makefile)
|
||||
|
||||
Executable
+436
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
The Onion Routing (TOR) Frequently Asked Questions
|
||||
--------------------------------------------------
|
||||
|
||||
1. General.
|
||||
|
||||
1.1. What is Tor?
|
||||
|
||||
Tor is an implementation of version 2 of Onion Routing.
|
||||
Go read the tor-design.pdf for the details.
|
||||
|
||||
In brief, Onion Routing is a connection-oriented anonymizing communication
|
||||
service. Users choose a source-routed path through a set of nodes, and
|
||||
negotiate a "virtual circuit" through the network, in which each node
|
||||
knows its predecessor and successor, but no others. Traffic flowing down
|
||||
the circuit is unwrapped by a symmetric key at each node, which reveals
|
||||
the downstream node.
|
||||
|
||||
Basically Tor provides a distributed network of servers ("onion
|
||||
routers"). Users bounce their tcp streams (web traffic, ftp, ssh, etc)
|
||||
around the routers, and recipients, observers, and even the routers
|
||||
themselves have difficulty tracking the source of the stream.
|
||||
|
||||
1.2. Why's it called Tor?
|
||||
|
||||
Because Tor is the onion routing system. I kept telling people I was
|
||||
working on onion routing, and they said "Neat. Which one?" Even if onion
|
||||
routing has become a standard household term, this is the actual onion
|
||||
routing project, started out of the Naval Research Lab.
|
||||
|
||||
(Theories about recursive acronyms are ok too. It's also got a fine
|
||||
translation into German.)
|
||||
|
||||
1.3 Is there a backdoor in Tor?
|
||||
|
||||
Not right now, but if this answer changes we probably won't be allowed
|
||||
to tell you. You should always check the source (or at least the diffs
|
||||
since the last release) for suspicious things; and if we don't give you
|
||||
source, that's a sure sign something funny could be going on.
|
||||
|
||||
2. Compiling and installing.
|
||||
|
||||
[Read the README file for now; check back here once we've got packages/etc
|
||||
for you.]
|
||||
|
||||
|
||||
3. Running Tor.
|
||||
|
||||
3.1. What kind of server should I run?
|
||||
|
||||
The same executable functions as both client and server, depending on
|
||||
which ports are specified in the configuration file. You can specify:
|
||||
* SocksPort: client applications (eg privoxy, Mozilla) can speak socks to
|
||||
this port.
|
||||
* ORPort: other onion routers connect to this port
|
||||
* DirPort: onion proxies and onion routers speak http to this port, to
|
||||
pull down a directory of which nodes are currently available.
|
||||
|
||||
3.2. So I can just run a full onion router and join the network?
|
||||
|
||||
No. Users should run just an onion proxy. If you start up a full onion
|
||||
router, the rest of the routers in the system won't recognize you,
|
||||
so they will reject your handshake attempts.
|
||||
|
||||
3.3. How do I join the network then?
|
||||
|
||||
If you just want to use the onion routing network, you can run a proxy
|
||||
and you're all set. If you want to run a router, you must convince
|
||||
the directory server operators (currently arma@mit.edu) that you're a
|
||||
trustworthy and reliable person. From there, the operators add you to
|
||||
the directory, which propagates out to the rest of the network. All
|
||||
nodes will know about you within a half hour.
|
||||
|
||||
3.4. I want to run a directory server too.
|
||||
|
||||
If you run a very reliable node, you plan to be around for a long time,
|
||||
and you want to spend some time ensuring that router operators are
|
||||
people we know and like, we may want you to run a directory server
|
||||
too. We must manually add you to the 'dirservers' file that's part of
|
||||
the distribution; users will only know about you when they upgrade to
|
||||
a new version. Of course, you can always just start up your router as a
|
||||
directory server too --- but users won't know to ask you for directories,
|
||||
and more importantly, you'll never learn from the real directory servers
|
||||
about recently joined routers.
|
||||
|
||||
|
||||
4. Development.
|
||||
|
||||
4.1. Who's doing this?
|
||||
|
||||
4.2. Can I help?
|
||||
|
||||
4.3. I've got a bug.
|
||||
|
||||
|
||||
5. Anonymity.
|
||||
|
||||
5.1. So I'm totally anonymous if I use Tor?
|
||||
|
||||
|
||||
|
||||
5.2. Where can I learn more about anonymity?
|
||||
|
||||
5.3. What attacks remain against onion routing?
|
||||
|
||||
|
||||
|
||||
6. Comparison to related projects.
|
||||
|
||||
6.1. Onion Routing.
|
||||
|
||||
Tor *is* onion routing.
|
||||
|
||||
6.2. Freedom.
|
||||
|
||||
|
||||
7. Protocol and application support.
|
||||
|
||||
7.1. http? ftp? udp? socks? mozilla?
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
Guide to Hacking Tor
|
||||
|
||||
(As of 8 October 2003, this was all accurate. If you're reading this in
|
||||
the distant future, stuff may have changed.)
|
||||
|
||||
0. Intro and required reading
|
||||
|
||||
Onion Routing is still very much in development stages. This document
|
||||
aims to get you started in the right direction if you want to understand
|
||||
the code, add features, fix bugs, etc.
|
||||
|
||||
Read the README file first, so you can get familiar with the basics of
|
||||
installing and running an onion router.
|
||||
|
||||
Then, skim some of the introductory materials in tor-design.pdf,
|
||||
tor-spec.txt, and the Tor FAQ to learn more about how the Tor protocol
|
||||
is supposed to work. This document will assume you know about Cells,
|
||||
Circuits, Streams, Connections, Onion Routers, and Onion Proxies.
|
||||
|
||||
1. Code organization
|
||||
|
||||
1.1. The modules
|
||||
|
||||
The code is divided into two directories: ./src/common and ./src/or.
|
||||
The "common" directory contains general purpose utility functions not
|
||||
specific to onion routing. The "or" directory implements all
|
||||
onion-routing and onion-proxy specific functionality.
|
||||
|
||||
Files in ./src/common:
|
||||
|
||||
aes.[ch] -- Implements the AES cipher (with 128-bit keys and blocks),
|
||||
and a counter-mode stream cipher on top of AES. This code is
|
||||
taken from the main Rijndael distribution. (We include this
|
||||
because many people are running older versions of OpenSSL without
|
||||
AES support.)
|
||||
|
||||
crypto.[ch] -- Wrapper functions to present a consistent interface to
|
||||
public-key and symmetric cryptography operations from OpenSSL.
|
||||
|
||||
fakepoll.[ch] -- Used on systems that don't have a poll() system call;
|
||||
reimplements() poll using the select() system call.
|
||||
|
||||
log.[ch] -- Tor's logging subsystem.
|
||||
|
||||
test.h -- Macros used by unit tests.
|
||||
|
||||
torint.h -- Provides missing [u]int*_t types for environments that
|
||||
don't have stdint.h.
|
||||
|
||||
tortls.[ch] -- Wrapper functions to present a consistent interface to
|
||||
TLS, SSL, and X.509 functions from OpenSSL.
|
||||
|
||||
util.[ch] -- Miscellaneous portability and convenience functions.
|
||||
|
||||
Files in ./src/or:
|
||||
|
||||
[General-purpose modules]
|
||||
|
||||
or.h -- Common header file: include everything, define everything.
|
||||
|
||||
buffers.c -- Implements a generic buffer interface. Buffers are
|
||||
fairly opaque string holders that can read to or flush from:
|
||||
memory, file descriptors, or TLS connections.
|
||||
|
||||
Also implements parsing functions to read HTTP and SOCKS commands
|
||||
from buffers.
|
||||
|
||||
tree.h -- A splay tree implementation by Niels Provos. Used by
|
||||
dns.c for dns caching at exits, and by connection_edge.c for dns
|
||||
caching at clients.
|
||||
|
||||
config.c -- Code to parse and validate the configuration file.
|
||||
|
||||
[Background processing modules]
|
||||
|
||||
cpuworker.c -- Implements a farm of 'CPU worker' processes to perform
|
||||
CPU-intensive tasks in the background, so as not interrupt the
|
||||
onion router. (OR only)
|
||||
|
||||
dns.c -- Implements a farm of 'DNS worker' processes to perform DNS
|
||||
lookups for onion routers and cache the results. [This needs to
|
||||
be done in the background because of the lack of a good,
|
||||
ubiquitous asynchronous DNS implementation.] (OR only)
|
||||
|
||||
[Directory-related functionality.]
|
||||
|
||||
directory.c -- Code to send and fetch directories and router
|
||||
descriptors via HTTP. Directories use dirserv.c to generate the
|
||||
results; clients use routers.c to parse them.
|
||||
|
||||
dirserv.c -- Code to manage directory contents and generate
|
||||
directories. [Directory server only]
|
||||
|
||||
routers.c -- Code to parse directories and router descriptors; and to
|
||||
generate a router descriptor corresponding to this OR's
|
||||
capabilities. Also presents some high-level interfaces for
|
||||
managing an OR or OP's view of the directory.
|
||||
|
||||
[Circuit-related modules.]
|
||||
|
||||
circuit.c -- Code to create circuits, manage circuits, and route
|
||||
relay cells along circuits.
|
||||
|
||||
onion.c -- Code to generate and respond to "onion skins".
|
||||
|
||||
[Core protocol implementation.]
|
||||
|
||||
connection.c -- Code used in common by all connection types. See
|
||||
1.2. below for more general information about connections.
|
||||
|
||||
connection_edge.c -- Code used only by edge connections.
|
||||
|
||||
command.c -- Code to handle specific cell types.
|
||||
|
||||
connection_or.c -- Code to implement cell-speaking connections.
|
||||
|
||||
[Toplevel modules.]
|
||||
|
||||
main.c -- Toplevel module. Initializes keys, handles signals,
|
||||
multiplexes between connections, implements main loop, and drives
|
||||
scheduled events.
|
||||
|
||||
tor_main.c -- Stub module containing a main() function. Allows unit
|
||||
test binary to link against main.c
|
||||
|
||||
[Unit tests]
|
||||
|
||||
test.c -- Contains unit tests for many pieces of the lower level Tor
|
||||
modules.
|
||||
|
||||
1.2. All about connections
|
||||
|
||||
All sockets in Tor are handled as different types of nonblocking
|
||||
'connections'. (What the Tor spec calls a "Connection", the code refers
|
||||
to as a "Cell-speaking" or "OR" connection.)
|
||||
|
||||
Connections are implemented by the connection_t struct, defined in or.h.
|
||||
Not every kind of connection uses all the fields in connection_t; see
|
||||
the comments in or.h and the assertions in assert_connection_ok() for
|
||||
more information.
|
||||
|
||||
Every connection has a type and a state. Connections never change their
|
||||
type, but can go through many state changes in their lifetime.
|
||||
|
||||
The connection types break down as follows:
|
||||
|
||||
[Cell-speaking connections]
|
||||
CONN_TYPE_OR -- A bidirectional TLS connection transmitting a
|
||||
sequence of cells. May be from an OR to an OR, or from an OP to
|
||||
an OR.
|
||||
|
||||
[Edge connections]
|
||||
CONN_TYPE_EXIT -- A TCP connection from an onion router to a
|
||||
Stream's destination. [OR only]
|
||||
CONN_TYPE_AP -- A SOCKS proxy connection from the end user
|
||||
application to the onion proxy. [OP only]
|
||||
|
||||
[Listeners]
|
||||
CONN_TYPE_OR_LISTENER [OR only]
|
||||
CONN_TYPE_AP_LISTENER [OP only]
|
||||
CONN_TYPE_DIR_LISTENER [Directory server only]
|
||||
-- Bound network sockets, waiting for incoming connections.
|
||||
|
||||
[Internal]
|
||||
CONN_TYPE_DNSWORKER -- Connection from the main process to a DNS
|
||||
worker process. [OR only]
|
||||
|
||||
CONN_TYPE_CPUWORKER -- Connection from the main process to a CPU
|
||||
worker process. [OR only]
|
||||
|
||||
Connection states are documented in or.h.
|
||||
|
||||
Every connection has two associated input and output buffers.
|
||||
Listeners don't use them. For non-listener connections, incoming
|
||||
data is appended to conn->inbuf, and outgoing data is taken from the
|
||||
front of conn->outbuf. Connections differ primarily in the functions
|
||||
called to fill and drain these buffers.
|
||||
|
||||
1.3. All about circuits.
|
||||
|
||||
A circuit_t structure fills two roles. First, a circuit_t links two
|
||||
connections together: either an edge connection and an OR connection,
|
||||
or two OR connections. (When joined to an OR connection, a circuit_t
|
||||
affects only cells sent to a particular circID on that connection. When
|
||||
joined to an edge connection, a circuit_t affects all data.)
|
||||
|
||||
Second, a circuit_t holds the cipher keys and state for sending data
|
||||
along a given circuit. At the OP, it has a sequence of ciphers, each
|
||||
of which is shared with a single OR along the circuit. Separate
|
||||
ciphers are used for data going "forward" (away from the OP) and
|
||||
"backward" (towards the OP). At the OR, a circuit has only two stream
|
||||
ciphers: one for data going forward, and one for data going backward.
|
||||
|
||||
1.4. Asynchronous IO and the main loop.
|
||||
|
||||
Tor uses the poll(2) system call (or it wraps select(2) to act like
|
||||
poll, if poll is not available) to handle nonblocking (asynchronous)
|
||||
IO. If you're not familiar with nonblocking IO, check out the links
|
||||
at the end of this document.
|
||||
|
||||
All asynchronous logic is handled in main.c. The functions
|
||||
'connection_add', 'connection_set_poll_socket', and 'connection_remove'
|
||||
manage an array of connection_t*, and keep in synch with the array of
|
||||
struct pollfd required by poll(2). (This array of connection_t* is
|
||||
accessible via get_connection_array, but users should generally call
|
||||
one of the 'connection_get_by_*' functions in connection.c to look up
|
||||
individual connections.)
|
||||
|
||||
To trap read and write events, connections call the functions
|
||||
'connection_{is|stop|start}_{reading|writing}'. If you want
|
||||
to completely reset the events you're watching for, use
|
||||
'connection_watch_events'.
|
||||
|
||||
Every time poll() finishes, main.c calls conn_read and conn_write on
|
||||
every connection. These functions dispatch events that have something
|
||||
to read to connection_handle_read, and events that have something to
|
||||
write to connection_handle_write, respectively.
|
||||
|
||||
When connections need to be closed, they can respond in two ways. Most
|
||||
simply, they can make connection_handle_* return an error (-1),
|
||||
which will make conn_{read|write} close them. But if it's not
|
||||
convenient to return -1 (for example, processing one connection causes
|
||||
you to realize that a second one should close), then you can also
|
||||
mark a connection to close by setting conn->marked_for_close. Marked
|
||||
connections will be closed at the end of the current iteration of
|
||||
the main loop.
|
||||
|
||||
The main loop handles several other operations: First, it checks
|
||||
whether any signals have been received that require a response (HUP,
|
||||
KILL, USR1, CHLD). Second, it calls prepare_for_poll to handle recurring
|
||||
tasks and compute the necessary poll timeout. These recurring tasks
|
||||
include periodically fetching the directory, timing out unused
|
||||
circuits, incrementing flow control windows and re-enabling connections
|
||||
that were blocking for more bandwidth, and maintaining statistics.
|
||||
|
||||
A word about TLS: Using TLS on OR connections complicates matters in
|
||||
two ways.
|
||||
First, a TLS stream has its own read buffer independent of the
|
||||
connection's read buffer. (TLS needs to read an entire frame from
|
||||
the network before it can decrypt any data. Thus, trying to read 1
|
||||
byte from TLS can require that several KB be read from the network
|
||||
and decrypted. The extra data is stored in TLS's decrypt buffer.)
|
||||
Because the data hasn't been read by tor (it's still inside the TLS),
|
||||
this means that sometimes a connection "has stuff to read" even when
|
||||
poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is
|
||||
used in main.c to detect TLS objects with non-empty internal buffers.
|
||||
Second, the TLS stream's events do not correspond directly to network
|
||||
events: sometimes, before a TLS stream can read, the network must be
|
||||
ready to write -- or vice versa.
|
||||
|
||||
1.5. How data flows (An illustration.)
|
||||
|
||||
Suppose an OR receives 256 bytes along an OR connection. These 256
|
||||
bytes turn out to be a data relay cell, which gets decrypted and
|
||||
delivered to an edge connection. Here we give a possible call sequence
|
||||
for the delivery of this data.
|
||||
|
||||
(This may be outdated quickly.)
|
||||
|
||||
do_main_loop -- Calls poll(2), receives a POLLIN event on a struct
|
||||
pollfd, then calls:
|
||||
conn_read -- Looks up the corresponding connection_t, and calls:
|
||||
connection_handle_read -- Calls:
|
||||
connection_read_to_buf -- Notices that it has an OR connection so:
|
||||
read_to_buf_tls -- Pulls data from the TLS stream onto conn->inbuf.
|
||||
connection_process_inbuf -- Notices that it has an OR connection so:
|
||||
connection_or_process_inbuf -- Checks whether conn is open, and calls:
|
||||
connection_process_cell_from_inbuf -- Notices it has enough data for
|
||||
a cell, then calls:
|
||||
connection_fetch_from_buf -- Pulls the cell from the buffer.
|
||||
cell_unpack -- Decodes the raw cell into a cell_t
|
||||
command_process_cell -- Notices it is a relay cell, so calls:
|
||||
command_process_relay_cell -- Looks up the circuit for the cell,
|
||||
makes sure the circuit is live, then passes the cell to:
|
||||
circuit_deliver_relay_cell -- Passes the cell to each of:
|
||||
relay_crypt -- Strips a layer of encryption from the cell and
|
||||
notices that the cell is for local delivery.
|
||||
connection_edge_process_relay_cell -- extracts the cell's
|
||||
relay command, and makes sure the edge connection is
|
||||
open. Since it has a DATA cell and an open connection,
|
||||
calls:
|
||||
circuit_consider_sending_sendme -- check if the total number
|
||||
of cells received by all streams on this circuit is
|
||||
enough that we should send back an acknowledgement
|
||||
(requesting that more cells be sent to any stream).
|
||||
connection_write_to_buf -- To place the data on the outgoing
|
||||
buffer of the correct edge connection, by calling:
|
||||
connection_start_writing -- To tell the main poll loop about
|
||||
the pending data.
|
||||
write_to_buf -- To actually place the outgoing data on the
|
||||
edge connection.
|
||||
connection_consider_sending_sendme -- if the outbuf waiting
|
||||
to flush to the exit connection is not too full, check
|
||||
if the total number of cells received on this stream
|
||||
is enough that we should send back an acknowledgement
|
||||
(requesting that more cells be sent to this stream).
|
||||
|
||||
In a subsequent iteration, main notices that the edge connection is
|
||||
ready for writing:
|
||||
|
||||
do_main_loop -- Calls poll(2), receives a POLLOUT event on a struct
|
||||
pollfd, then calls:
|
||||
conn_write -- Looks up the corresponding connection_t, and calls:
|
||||
connection_handle_write -- This isn't a TLS connection, so calls:
|
||||
flush_buf -- Delivers data from the edge connection's outbuf to the
|
||||
network.
|
||||
connection_wants_to_flush -- Reports that all data has been flushed.
|
||||
connection_finished_flushing -- Notices the connection is an exit,
|
||||
and calls:
|
||||
connection_edge_finished_flushing -- The connection is open, so it
|
||||
calls:
|
||||
connection_stop_writing -- Tells the main poll loop that this
|
||||
connection has no more data to write.
|
||||
connection_consider_sending_sendme -- now that the outbuf
|
||||
is empty, check again if the total number of cells
|
||||
received on this stream is enough that we should send
|
||||
back an acknowledgement (requesting that more cells be
|
||||
sent to this stream).
|
||||
|
||||
|
||||
1.6. Routers, descriptors, and directories
|
||||
|
||||
All Tor processes need to keep track of a list of onion routers, for
|
||||
several reasons:
|
||||
- OPs need to establish connections and circuits to ORs.
|
||||
- ORs need to establish connections to other ORs.
|
||||
- OPs and ORs need to fetch directories from a directory server.
|
||||
- ORs need to upload their descriptors to directory servers.
|
||||
- Directory servers need to know which ORs are allowed onto the
|
||||
network, what the descriptors are for those ORs, and which of
|
||||
those ORs are currently live.
|
||||
|
||||
Thus, every Tor process keeps track of a list of all the ORs it knows
|
||||
in a static variable 'directory' in the routers.c module. This
|
||||
variable contains a routerinfo_t object for each known OR. On startup,
|
||||
the directory is initialized to a list of known directory servers (via
|
||||
router_get_list_from_file()). Later, the directory is updated via
|
||||
router_get_dir_from_string(). (OPs and ORs retrieve fresh directories
|
||||
from directory servers; directory servers generate their own.)
|
||||
|
||||
Every OR must periodically regenerate a router descriptor for itself.
|
||||
The descriptor and the corresponding routerinfo_t are stored in the
|
||||
'desc_routerinfo' and 'descriptor' static variables in routers.c.
|
||||
|
||||
Additionally, a directory server keeps track of a list of the
|
||||
router descriptors it knows in a separate list in dirserv.c. It
|
||||
uses this list, checking which OR connections are open, to build
|
||||
directories.
|
||||
|
||||
1.7. Data model
|
||||
|
||||
[XXX]
|
||||
|
||||
1.8. Flow control
|
||||
|
||||
[XXX]
|
||||
|
||||
2. Coding conventions
|
||||
|
||||
2.1. Details
|
||||
|
||||
Use tor_malloc, tor_strdup, and tor_gettimeofday instead of their
|
||||
generic equivalents. (They always succeed or exit.)
|
||||
|
||||
Use INLINE instead of 'inline', so that we work properly on windows.
|
||||
|
||||
2.2. Calling and naming conventions
|
||||
|
||||
Whenever possible, functions should return -1 on error and and 0 on
|
||||
success.
|
||||
|
||||
For multi-word identifiers, use lowercase words combined with
|
||||
underscores. (e.g., "multi_word_identifier"). Use ALL_CAPS for macros and
|
||||
constants.
|
||||
|
||||
Typenames should end with "_t".
|
||||
|
||||
Function names should be prefixed with a module name or object name. (In
|
||||
general, code to manipulate an object should be a module with the same
|
||||
name as the object, so it's hard to tell which convention is used.)
|
||||
|
||||
Functions that do things should have imperative-verb names
|
||||
(e.g. buffer_clear, buffer_resize); functions that return booleans should
|
||||
have predicate names (e.g. buffer_is_empty, buffer_needs_resizing).
|
||||
|
||||
2.3. What To Optimize
|
||||
|
||||
Don't optimize anything if it's not in the critical path. Right now,
|
||||
the critical path seems to be AES, logging, and the network itself.
|
||||
Feel free to do your own profiling to determine otherwise.
|
||||
|
||||
2.4. Log conventions
|
||||
|
||||
Log convention: use only these four log severities.
|
||||
|
||||
ERR is if something fatal just happened.
|
||||
WARN if something bad happened, but we're still running. The
|
||||
bad thing is either a bug in the code, an attack or buggy
|
||||
protocol/implementation of the remote peer, etc. The operator should
|
||||
examine the bad thing and try to correct it.
|
||||
(No error or warning messages should be expected during normal OR or OP
|
||||
operation. I expect most people to run on -l warn eventually. If a
|
||||
library function is currently called such that failure always means
|
||||
ERR, then the library function should log WARN and let the caller
|
||||
log ERR.)
|
||||
INFO means something happened (maybe bad, maybe ok), but there's nothing
|
||||
you need to (or can) do about it.
|
||||
DEBUG is for everything louder than INFO.
|
||||
|
||||
[XXX Proposed convention: every messages of severity INFO or higher should
|
||||
either (A) be intelligible to end-users who don't know the Tor source; or
|
||||
(B) somehow inform the end-users that they aren't expected to understand
|
||||
the message (perhaps with a string like "internal error"). Option (A) is
|
||||
to be preferred to option (B). -NM]
|
||||
|
||||
3. References
|
||||
|
||||
About Tor
|
||||
|
||||
See http://freehaven.net/tor/
|
||||
http://freehaven.net/tor/cvs/doc/tor-spec.txt
|
||||
http://freehaven.net/tor/cvs/doc/tor-design.tex
|
||||
http://freehaven.net/tor/cvs/doc/FAQ
|
||||
|
||||
About anonymity
|
||||
|
||||
See http://freehaven.net/anonbib/
|
||||
|
||||
About nonblocking IO
|
||||
|
||||
[XXX insert references]
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# Old HACKING document; merge into the above, move into tor-design.tex,
|
||||
# or delete.
|
||||
# ======================================================================
|
||||
The pieces.
|
||||
|
||||
Routers. Onion routers, as far as the 'tor' program is concerned,
|
||||
are a bunch of data items that are loaded into the router_array when
|
||||
the program starts. Periodically it downloads a new set of routers
|
||||
from a directory server, and updates the router_array. When a new OR
|
||||
connection is started (see below), the relevant information is copied
|
||||
from the router struct to the connection struct.
|
||||
|
||||
Connections. A connection is a long-standing tcp socket between
|
||||
nodes. A connection is named based on what it's connected to -- an "OR
|
||||
connection" has an onion router on the other end, an "OP connection" has
|
||||
an onion proxy on the other end, an "exit connection" has a website or
|
||||
other server on the other end, and an "AP connection" has an application
|
||||
proxy (and thus a user) on the other end.
|
||||
|
||||
Circuits. A circuit is a path over the onion routing
|
||||
network. Applications can connect to one end of the circuit, and can
|
||||
create exit connections at the other end of the circuit. AP and exit
|
||||
connections have only one circuit associated with them (and thus these
|
||||
connection types are closed when the circuit is closed), whereas OP and
|
||||
OR connections multiplex many circuits at once, and stay standing even
|
||||
when there are no circuits running over them.
|
||||
|
||||
Streams. Streams are specific conversations between an AP and an exit.
|
||||
Streams are multiplexed over circuits.
|
||||
|
||||
Cells. Some connections, specifically OR and OP connections, speak
|
||||
"cells". This means that data over that connection is bundled into 256
|
||||
byte packets (8 bytes of header and 248 bytes of payload). Each cell has
|
||||
a type, or "command", which indicates what it's for.
|
||||
|
||||
Robustness features.
|
||||
|
||||
[XXX no longer up to date]
|
||||
Bandwidth throttling. Each cell-speaking connection has a maximum
|
||||
bandwidth it can use, as specified in the routers.or file. Bandwidth
|
||||
throttling can occur on both the sender side and the receiving side. If
|
||||
the LinkPadding option is on, the sending side sends cells at regularly
|
||||
spaced intervals (e.g., a connection with a bandwidth of 25600B/s would
|
||||
queue a cell every 10ms). The receiving side protects against misbehaving
|
||||
servers that send cells more frequently, by using a simple token bucket:
|
||||
|
||||
Each connection has a token bucket with a specified capacity. Tokens are
|
||||
added to the bucket each second (when the bucket is full, new tokens
|
||||
are discarded.) Each token represents permission to receive one byte
|
||||
from the network --- to receive a byte, the connection must remove a
|
||||
token from the bucket. Thus if the bucket is empty, that connection must
|
||||
wait until more tokens arrive. The number of tokens we add enforces a
|
||||
longterm average rate of incoming bytes, yet we still permit short-term
|
||||
bursts above the allowed bandwidth. Currently bucket sizes are set to
|
||||
ten seconds worth of traffic.
|
||||
|
||||
The bandwidth throttling uses TCP to push back when we stop reading.
|
||||
We extend it with token buckets to allow more flexibility for traffic
|
||||
bursts.
|
||||
|
||||
Data congestion control. Even with the above bandwidth throttling,
|
||||
we still need to worry about congestion, either accidental or intentional.
|
||||
If a lot of people make circuits into same node, and they all come out
|
||||
through the same connection, then that connection may become saturated
|
||||
(be unable to send out data cells as quickly as it wants to). An adversary
|
||||
can make a 'put' request through the onion routing network to a webserver
|
||||
he owns, and then refuse to read any of the bytes at the webserver end
|
||||
of the circuit. These bottlenecks can propagate back through the entire
|
||||
network, mucking up everything.
|
||||
|
||||
(See the tor-spec.txt document for details of how congestion control
|
||||
works.)
|
||||
|
||||
In practice, all the nodes in the circuit maintain a receive window
|
||||
close to maximum except the exit node, which stays around 0, periodically
|
||||
receiving a sendme and reading more data cells from the webserver.
|
||||
In this way we can use pretty much all of the available bandwidth for
|
||||
data, but gracefully back off when faced with multiple circuits (a new
|
||||
sendme arrives only after some cells have traversed the entire network),
|
||||
stalled network connections, or attacks.
|
||||
|
||||
We don't need to reimplement full tcp windows, with sequence numbers,
|
||||
the ability to drop cells when we're full etc, because the tcp streams
|
||||
already guarantee in-order delivery of each cell. Rather than trying
|
||||
to build some sort of tcp-on-tcp scheme, we implement this minimal data
|
||||
congestion control; so far it's enough.
|
||||
|
||||
Router twins. In many cases when we ask for a router with a given
|
||||
address and port, we really mean a router who knows a given key. Router
|
||||
twins are two or more routers that share the same private key. We thus
|
||||
give routers extra flexibility in choosing the next hop in the circuit: if
|
||||
some of the twins are down or slow, it can choose the more available ones.
|
||||
|
||||
Currently the code tries for the primary router first, and if it's down,
|
||||
chooses the first available twin.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
EXTRA_DIST = tor-spec.txt CLIENTS FAQ HACKING rendezvous.txt tor-design.tex
|
||||
|
||||
man_MANS = tor.1
|
||||
@@ -0,0 +1,307 @@
|
||||
|
||||
Legend:
|
||||
SPEC!! - Not specified
|
||||
SPEC - Spec not finalized
|
||||
NICK - nick claims
|
||||
ARMA - arma claims
|
||||
- Not done
|
||||
* Top priority
|
||||
. Partially done
|
||||
o Done
|
||||
D Deferred
|
||||
X Abandoned
|
||||
|
||||
For 0.0.2pre17:
|
||||
o Put a H(K | handshake) into the onionskin response
|
||||
o Make cells 512 bytes
|
||||
o Reduce streamid footprint from 7 bytes to 2 bytes
|
||||
X Check for collisions in streamid (now possible with
|
||||
just 2 bytes), and back up & replace with padding if so
|
||||
o Use the 4 reserved bytes in each cell header to keep 1/5
|
||||
of a sha1 of the ongoing relay payload (move into stream header)
|
||||
o Move length into the stream header too
|
||||
o Make length 2 bytes
|
||||
D increase DH key length
|
||||
D increase RSA key length
|
||||
D Spec the stream_id stuff. Clarify that nobody on the backward
|
||||
stream should look at stream_id.
|
||||
|
||||
Cell:
|
||||
ACI (anonymous circuit identifier) [2 bytes]
|
||||
Command [1 byte]
|
||||
Payload (padded with 0 bytes) [509 bytes]
|
||||
|
||||
Relay payload:
|
||||
Relay command [1 byte]
|
||||
Stream ID [7 bytes]
|
||||
Partial SHA-1 [4 bytes]
|
||||
Length [2 bytes]
|
||||
Relay payload [495 bytes]
|
||||
|
||||
For 0.0.2pre15:
|
||||
o don't pick exit nodes which will certainly reject all things.
|
||||
o don't pick nodes that the directory says are down
|
||||
o choose randomly from running dirservers, not just first one
|
||||
o install the man page
|
||||
o warn when client-side tries an address/port which no router in the dir accepts.
|
||||
|
||||
For 0.0.2pre14:
|
||||
o More flexible exit policies (18.*, 18.0.0.0/8)
|
||||
o Work to succeed in the precense of exit policy violation
|
||||
o Replace desired_path_len with opaque path-selection specifier
|
||||
o Client-side DNS caching
|
||||
o Add entries to client DNS cache based on END cells
|
||||
o Remove port from END_REASON_EXITPOLICY cells
|
||||
o Start building new circuits when we get an exit-policy
|
||||
failure. (Defer exiting from the middle of existing
|
||||
circuits or extending existing circuits for later.)
|
||||
o Implement function to check whether a routerinfo_t
|
||||
supports a given exit addr.
|
||||
o Choose the exit node of an in-progress circuit based on
|
||||
pending AP connections.
|
||||
o Choose the exit node _first_, then beginning, then
|
||||
middle nodes.
|
||||
|
||||
Short-term:
|
||||
- improve how it behaves when i remove a line from the approved-routers files
|
||||
- Make tls connections tls_close intentionally
|
||||
o Rename ACI to circID
|
||||
. integrate rep_ok functions, see what breaks
|
||||
- update tor faq
|
||||
o obey SocksBindAddress, ORBindAddress
|
||||
o warn if we're running as root
|
||||
o make connection_flush_buf() more obviously obsolete
|
||||
o let hup reread the config file, eg so we can get new exit
|
||||
policies without restarting
|
||||
o Put recommended_versions in a config entry
|
||||
X use times(2) rather than gettimeofday to measure how long it
|
||||
takes to process a cell
|
||||
o Separate trying to rebuild a circuit because you have none from trying
|
||||
to rebuild a circuit because the current one is stale
|
||||
X Continue reading from socks port even while waiting for connect.
|
||||
o Exit policies
|
||||
o Spec how to write the exit policies
|
||||
o Path selection algorithms
|
||||
o Choose path more incrementally
|
||||
o Let user request first/last node
|
||||
o And disallow certain nodes
|
||||
D Choose path by jurisdiction, etc?
|
||||
o Make relay end cells have failure status and payload attached
|
||||
X let non-approved routers handshake.
|
||||
- Dirserver shouldn't put you in running-routers list if you haven't
|
||||
uploaded a descriptor recently
|
||||
. migrate to using nickname rather than addr:port for routers
|
||||
o decide_aci_type
|
||||
- generate onion skins
|
||||
- circuit_send_next_onion_skin
|
||||
- circuit_extend
|
||||
- onion_generate_cpath
|
||||
- get_unique_aci_by_addr_port
|
||||
- circ->n_addr and circ->n_port
|
||||
- circuit_enumerate_by_naddr_nport
|
||||
- cpath layers
|
||||
- connection_or_connect
|
||||
- connection_exact_get_by_addr_port
|
||||
- connection_twin_get_by_addr_port
|
||||
- router_get_by_addr_port
|
||||
- connection_or_init_conn_from_router
|
||||
- tag_pack, tag_unpack, connection_cpu_process_inbuf
|
||||
- directory_initiate_command
|
||||
. Move from onions to ephemeral DH
|
||||
o incremental path building
|
||||
o transition circuit-level sendmes to hop-level sendmes
|
||||
o implement truncate, truncated
|
||||
o move from 192byte DH to 128byte DH, so it isn't so damn slow
|
||||
- exiting from not-last hop
|
||||
- OP logic to decide to extend/truncate a path
|
||||
- make sure exiting from the not-last hop works
|
||||
- logic to find last *open* hop, not last hop, in cpath
|
||||
o Remember address and port when beginning.
|
||||
- Extend by nickname/hostname/something, not by IP.
|
||||
- Need a relay teardown cell, separate from one-way ends.
|
||||
|
||||
On-going
|
||||
. Better comments for functions!
|
||||
. Go through log messages, reduce confusing error messages.
|
||||
. make the logs include more info (fd, etc)
|
||||
. Unit tests
|
||||
. Update the spec so it matches the code
|
||||
|
||||
Mid-term:
|
||||
- Rotate tls-level connections -- make new ones, expire old ones.
|
||||
So we get actual key rotation, not just symmetric key rotation
|
||||
o Are there anonymity issues with sequential streamIDs? Sequential
|
||||
circIDs? Eg an attacker can learn how many there have been.
|
||||
The fix is to initialize them randomly rather than at 1.
|
||||
- Look at having smallcells and largecells
|
||||
. Redo scheduler
|
||||
o fix SSL_read bug for buffered records
|
||||
- make round-robining more fair
|
||||
- What happens when a circuit's length is 1? What breaks?
|
||||
. streams / circuits
|
||||
o Implement streams
|
||||
o Rotate circuits after N minutes?
|
||||
X Circuits should expire when circuit->expire triggers
|
||||
NICK . Handle half-open connections
|
||||
o openssh is an application that uses half-open connections
|
||||
o Figure out what causes connections to close, standardize
|
||||
when we mark a connection vs when we tear it down
|
||||
o Look at what ssl does to keep from mutating data streams
|
||||
o Put CPU workers in separate processes
|
||||
o Handle multiple cpu workers (one for each cpu, plus one)
|
||||
o Queue for pending tasks if all workers full
|
||||
o Support the 'process this onion' task
|
||||
D Merge dnsworkers and cpuworkers to some extent
|
||||
o Handle cpuworkers dying
|
||||
. Scrubbing proxies
|
||||
- Find an smtp proxy?
|
||||
- Check the old smtp proxy code
|
||||
o Find an ftp proxy? wget --passive
|
||||
D Wait until there are packet redirectors for Linux
|
||||
. Get socks4a support into Mozilla
|
||||
. Develop rendezvous points
|
||||
X Handle socks commands other than connect, eg, bind?
|
||||
o Design
|
||||
- Spec
|
||||
- Implement
|
||||
. Tests
|
||||
o Testing harness/infrastructure
|
||||
D System tests (how?)
|
||||
- Performance tests, so we know when we've improved
|
||||
. webload infrastructure (Bruce)
|
||||
. httperf infrastructure (easy to set up)
|
||||
. oprofile (installed in RH >8.0)
|
||||
NICK . Daemonize and package
|
||||
o Teach it to fork and background
|
||||
- Red Hat spec file
|
||||
- Debian spec file equivalent
|
||||
. Portability
|
||||
. Which .h files are we actually using?
|
||||
. Port to:
|
||||
o Linux
|
||||
o BSD
|
||||
. Solaris
|
||||
o Cygwin
|
||||
. Win32
|
||||
o OS X
|
||||
- deal with pollhup / reached_eof on all platforms
|
||||
o openssl randomness
|
||||
o inet_ntoa
|
||||
o stdint.h
|
||||
- Make a script to set up a local network on your machine
|
||||
o More flexibility in node addressing
|
||||
D Support IPv6 rather than just 4
|
||||
o Handle multihomed servers (config variable to set IP)
|
||||
|
||||
In the distant future:
|
||||
D Load balancing between router twins
|
||||
D Keep track of load over links/nodes, to
|
||||
know who's hosed
|
||||
SPEC!! D Non-clique topologies
|
||||
D Implement our own memory management, at least for common structs
|
||||
(Not ever necessary?)
|
||||
D Advanced directory servers
|
||||
D Automated reputation management
|
||||
SPEC!! D Figure out how to do threshold directory servers
|
||||
D jurisdiction info in dirserver entries? other info?
|
||||
|
||||
Older (done) todo stuff:
|
||||
|
||||
o Get tor to act like a socks server
|
||||
o socks4, socks4a
|
||||
o socks5
|
||||
o routers have identity key, link key, onion key.
|
||||
o link key certs are
|
||||
D signed by identity key
|
||||
D not in descriptor
|
||||
o not in config
|
||||
D not on disk
|
||||
o identity and onion keys are in descriptor (and disk)
|
||||
o upon boot, if it doesn't find identity key, generate it and write it.
|
||||
o also write a file with the identity key fingerprint in it
|
||||
o router generates descriptor: flesh out router_get_my_descriptor()
|
||||
o Routers sign descriptors with identity key
|
||||
o routers put version number in descriptor
|
||||
o routers should maybe have `uname -a` in descriptor?
|
||||
o Give nicknames to routers
|
||||
o in config
|
||||
o in descriptors
|
||||
o router posts descriptor
|
||||
o when it boots
|
||||
o every DirFetchPostPeriod seconds
|
||||
D when it changes
|
||||
o change tls stuff so certs don't get written to disk, or read from disk
|
||||
o make directory.c 'thread'safe
|
||||
o dirserver parses descriptor
|
||||
o dirserver checks signature
|
||||
D client checks signature?
|
||||
o dirserver writes directory to file
|
||||
o reads that file upon boot
|
||||
o directory includes all routers, up and down
|
||||
o add "up" line to directory, listing nicknames
|
||||
o instruments ORs to report stats
|
||||
o average cell fullness
|
||||
o average bandwidth used
|
||||
o configure log files. separate log file, separate severities.
|
||||
o what assumptions break if we fclose(0) when we daemonize?
|
||||
o make buffer struct elements opaque outside buffers.c
|
||||
o add log convention to the HACKING file
|
||||
o make 'make install' do the right thing
|
||||
o change binary name to tor
|
||||
o change config files so you look at commandline, else look in
|
||||
/etc/torrc. no cascading.
|
||||
o have an absolute datadir with fixed names for files, and fixed-name
|
||||
keydir under that with fixed names
|
||||
o Move (most of) the router/directory code out of main.c
|
||||
o Simple directory servers
|
||||
o Include key in source; sign directories
|
||||
o Signed directory backend
|
||||
o Document
|
||||
o Integrate
|
||||
o Add versions to code
|
||||
o Have directories list recommended-versions
|
||||
o Include line in directories
|
||||
o Check for presence of line.
|
||||
o Quit if running the wrong version
|
||||
o Command-line option to override quit
|
||||
o Add more information to directory server entries
|
||||
o Exit policies
|
||||
o Clearer bandwidth management
|
||||
o Do we want to remove bandwidth from OR handshakes?
|
||||
o What about OP handshakes?
|
||||
X Move away from openssl
|
||||
o Abstract out crypto calls
|
||||
X Look at nss, others? Just include code?
|
||||
o Use a stronger cipher
|
||||
o aes now, by including the code ourselves
|
||||
X On the fly compression of each stream
|
||||
o Clean up the event loop (optimize and sanitize)
|
||||
o Remove that awful concept of 'roles'
|
||||
o Terminology
|
||||
o Circuits, topics, cells stay named that
|
||||
o 'Connection' gets divided, or renamed, or something?
|
||||
o DNS farm
|
||||
o Distribute queries onto the farm, get answers
|
||||
o Preemptively grow a new worker before he's needed
|
||||
o Prune workers when too many are idle
|
||||
o DNS cache
|
||||
o Clear DNS cache over time
|
||||
D Honor DNS TTL info (how??)
|
||||
o Have strategy when all workers are busy
|
||||
o Keep track of which connections are in dns_wait
|
||||
o Need to cache positives/negatives on the tor side
|
||||
o Keep track of which queries have been asked
|
||||
o Better error handling when
|
||||
o An address doesn't resolve
|
||||
o We have max workers running
|
||||
o Consider taking the master out of the loop?
|
||||
X Implement reply onions
|
||||
o Total rate limiting
|
||||
o Look at OR handshake in more detail
|
||||
o Spec it
|
||||
o Merge OR and OP handshakes
|
||||
o rearrange connection_or so it doesn't suck so much to read
|
||||
D Periodic link key rotation. Spec?
|
||||
o wrap malloc with something that explodes when it fails
|
||||
o Clean up the number of places that get to look at prkey
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
|
||||
|
||||
% ---------------------------------------------------------------
|
||||
%
|
||||
% $Id$
|
||||
%
|
||||
% by Paolo.Ienne@di.epfl.ch
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
%
|
||||
% no guarantee is given that the format corresponds perfectly to
|
||||
% IEEE 8.5" x 11" Proceedings, but most features should be ok.
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
% with LaTeX2e:
|
||||
% =============
|
||||
%
|
||||
% use as
|
||||
% \documentclass[times,10pt,twocolumn]{article}
|
||||
% \usepackage{latex8}
|
||||
% \usepackage{times}
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
% with LaTeX 2.09:
|
||||
% ================
|
||||
%
|
||||
% use as
|
||||
% \documentstyle[times,art10,twocolumn,latex8]{article}
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
% with both versions:
|
||||
% ===================
|
||||
%
|
||||
% specify \pagestyle{empty} to omit page numbers in the final
|
||||
% version
|
||||
%
|
||||
% specify references as
|
||||
% \bibliographystyle{latex8}
|
||||
% \bibliography{...your files...}
|
||||
%
|
||||
% use Section{} and SubSection{} instead of standard section{}
|
||||
% and subsection{} to obtain headings in the form
|
||||
% "1.3. My heading"
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
|
||||
\typeout{IEEE 8.5 x 11-Inch Proceedings Style `latex8.sty'.}
|
||||
|
||||
% ten point helvetica bold required for captions
|
||||
% in some sites the name of the helvetica bold font may differ,
|
||||
% change the name here:
|
||||
\font\tenhv = phvb at 10pt
|
||||
%\font\tenhv = phvb7t at 10pt
|
||||
|
||||
% eleven point times bold required for second-order headings
|
||||
% \font\elvbf = cmbx10 scaled 1100
|
||||
\font\elvbf = ptmb scaled 1100
|
||||
|
||||
% set dimensions of columns, gap between columns, and paragraph indent
|
||||
\setlength{\textheight}{8.875in}
|
||||
\setlength{\textwidth}{6.875in}
|
||||
\setlength{\columnsep}{0.3125in}
|
||||
%\setlength{\columnsep}{0.26in}
|
||||
\setlength{\topmargin}{0in}
|
||||
\setlength{\headheight}{0in}
|
||||
\setlength{\headsep}{.5in}
|
||||
\setlength{\parindent}{1pc}
|
||||
\setlength{\oddsidemargin}{-.304in}
|
||||
\setlength{\evensidemargin}{-.304in}
|
||||
|
||||
% memento from size10.clo
|
||||
% \normalsize{\@setfontsize\normalsize\@xpt\@xiipt}
|
||||
% \small{\@setfontsize\small\@ixpt{11}}
|
||||
% \footnotesize{\@setfontsize\footnotesize\@viiipt{9.5}}
|
||||
% \scriptsize{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||
% \tiny{\@setfontsize\tiny\@vpt\@vipt}
|
||||
% \large{\@setfontsize\large\@xiipt{14}}
|
||||
% \Large{\@setfontsize\Large\@xivpt{18}}
|
||||
% \LARGE{\@setfontsize\LARGE\@xviipt{22}}
|
||||
% \huge{\@setfontsize\huge\@xxpt{25}}
|
||||
% \Huge{\@setfontsize\Huge\@xxvpt{30}}
|
||||
|
||||
\def\@maketitle
|
||||
{
|
||||
\newpage
|
||||
\null
|
||||
\vskip .375in
|
||||
\begin{center}
|
||||
{\Large \bf \@title \par}
|
||||
% additional two empty lines at the end of the title
|
||||
\vspace*{24pt}
|
||||
{
|
||||
\large
|
||||
\lineskip .5em
|
||||
\begin{tabular}[t]{c}
|
||||
\@author
|
||||
\end{tabular}
|
||||
\par
|
||||
}
|
||||
% additional small space at the end of the author name
|
||||
\vskip .5em
|
||||
{
|
||||
\large
|
||||
\begin{tabular}[t]{c}
|
||||
\@affiliation
|
||||
\end{tabular}
|
||||
\par
|
||||
\ifx \@empty \@email
|
||||
\else
|
||||
\begin{tabular}{r@{~}l}
|
||||
E-mail: & {\tt \@email}
|
||||
\end{tabular}
|
||||
\par
|
||||
\fi
|
||||
}
|
||||
% additional empty line at the end of the title block
|
||||
\vspace*{12pt}
|
||||
\end{center}
|
||||
}
|
||||
|
||||
\def\abstract
|
||||
{%
|
||||
\centerline{\large\bf Abstract}%
|
||||
\vspace*{12pt}%
|
||||
\it%
|
||||
}
|
||||
|
||||
\def\endabstract
|
||||
{
|
||||
% additional empty line at the end of the abstract
|
||||
\vspace*{12pt}
|
||||
}
|
||||
|
||||
\def\affiliation#1{\gdef\@affiliation{#1}} \gdef\@affiliation{}
|
||||
|
||||
\def\email#1{\gdef\@email{#1}}
|
||||
\gdef\@email{}
|
||||
|
||||
\newlength{\@ctmp}
|
||||
\newlength{\@figindent}
|
||||
\setlength{\@figindent}{1pc}
|
||||
|
||||
\long\def\@makecaption#1#2{
|
||||
\vskip 10pt
|
||||
\setbox\@tempboxa\hbox{\tenhv\noindent #1.~#2}
|
||||
\setlength{\@ctmp}{\hsize}
|
||||
\addtolength{\@ctmp}{-\@figindent}\addtolength{\@ctmp}{-\@figindent}
|
||||
% IF longer than one indented paragraph line
|
||||
\ifdim \wd\@tempboxa >\@ctmp
|
||||
% THEN set as an indented paragraph
|
||||
\begin{list}{}{\leftmargin\@figindent \rightmargin\leftmargin}
|
||||
\item[]\tenhv #1.~#2\par
|
||||
\end{list}
|
||||
\else
|
||||
% ELSE center
|
||||
\hbox to\hsize{\hfil\box\@tempboxa\hfil}
|
||||
\fi}
|
||||
|
||||
% correct heading spacing and type
|
||||
\def\section{\@startsection {section}{1}{\z@}
|
||||
{14pt plus 2pt minus 2pt}{14pt plus 2pt minus 2pt} {\large\bf}}
|
||||
\def\subsection{\@startsection {subsection}{2}{\z@}
|
||||
{13pt plus 2pt minus 2pt}{13pt plus 2pt minus 2pt} {\elvbf}}
|
||||
|
||||
% add the period after section numbers
|
||||
\newcommand{\Section}[1]{\section{\hskip -1em.~#1}}
|
||||
\newcommand{\SubSection}[1]{\subsection{\hskip -1em.~#1}}
|
||||
|
||||
% end of file latex8.sty
|
||||
% ---------------------------------------------------------------
|
||||
@@ -0,0 +1,200 @@
|
||||
How to make rendezvous points work
|
||||
|
||||
0. Overview
|
||||
|
||||
Rendezvous points are an implementation of location-hidden services
|
||||
(server anonymity) in the onion routing network. Location-hidden
|
||||
services means Bob can offer a tcp service (say, a webserver) via the
|
||||
onion routing network, without revealing the IP of that service.
|
||||
|
||||
The basic idea is to provide censorship resistance for Bob by allowing
|
||||
him to advertise a variety of onion routers as his public location
|
||||
(nodes known as his Introduction Points, see Section 1). Alice,
|
||||
the client, chooses a node known as a Meeting Point (see Section
|
||||
2). This extra level of indirection is needed so Bob doesn't serve
|
||||
files directly from his public locations (so these nodes don't open
|
||||
themselves up to abuse, eg from serving Nazi propaganda in France). The
|
||||
extra level of indirection also allows Bob to choose which requests
|
||||
to respond to, and which to ignore.
|
||||
|
||||
We provide the necessary glue code so that Alice can view webpages
|
||||
on a location-hidden webserver, and Bob can run a location-hidden
|
||||
server, with minimal invasive changes (see Section 3). Both Alice
|
||||
and Bob must run local onion proxies (OPs) -- software that knows
|
||||
how to talk to the onion routing network.
|
||||
|
||||
The big picture follows. We direct the reader to the rest of the
|
||||
document for more details and explanation.
|
||||
|
||||
1) Bob chooses some Introduction Points, and advertises them on a
|
||||
Distributed Hash Table (DHT).
|
||||
2) Bob establishes onion routing connections to each of his
|
||||
Introduction Points, and waits.
|
||||
3) Alice learns about Bob's service out of band (perhaps Bob gave her
|
||||
a pointer, or she found it on a website). She looks up the details
|
||||
of Bob's service from the DHT.
|
||||
4) Alice chooses and establishes a Meeting Point for this transaction.
|
||||
5) Alice goes to one of Bob's Introduction Points, and gives it a blob
|
||||
(encrypted for Bob) which tells him about herself and the Meeting
|
||||
Point she chose. The Introduction Point sends the blob to Bob.
|
||||
6) Bob chooses whether to ignore the blob, or to onion route to MP.
|
||||
Let's assume the latter.
|
||||
7) MP plugs together Alice and Bob. Note that MP doesn't know (or care)
|
||||
who Alice is, or who Bob is; and it can't read anything they
|
||||
transmit either, because they share a session key.
|
||||
8) Alice sends a 'begin' cell along the circuit. It makes its way
|
||||
to Bob's onion proxy. Bob's onion proxy connects to Bob's webserver.
|
||||
9) Data goes back and forth as usual.
|
||||
|
||||
1. Introduction service
|
||||
|
||||
Bob wants to learn about client requests for communication, but
|
||||
wants to avoid responding unnecessarily to unauthorized clients.
|
||||
Bob's proxy opens a circuit, and tells some onion router on that
|
||||
circuit to expect incoming connections, and notify Bob of them.
|
||||
|
||||
When establishing such an introduction point, Bob provides the onion
|
||||
router with a public "introduction" key. The hash of this public
|
||||
key identifies a unique Bob, and (since Bob is required to sign his
|
||||
messages) prevents anybody else from usurping Bob's introduction
|
||||
point in the future. Additionally, Bob can use the same public key
|
||||
to establish an introduction point on another onion router (OR),
|
||||
and Alice can still be confident that Bob is the same server.
|
||||
|
||||
(The set-up-an-introduction-point command should come via a
|
||||
RELAY_BIND_INTRODUCTION cell. This cell creates a new stream on the
|
||||
circuit from Bob to the introduction point.)
|
||||
|
||||
ORs that support introduction run an introduction service on a
|
||||
separate port. When Alice wants to notify Bob of a meeting point,
|
||||
she connects (directly or via Tor) to the introduction port, and
|
||||
sends the following:
|
||||
|
||||
MEETING REQUEST
|
||||
RSA-OAEP encrypted with server's public key:
|
||||
[20 bytes] Hash of Bob's public key (identifies which Bob to notify)
|
||||
[ 0 bytes] Initial authentication [optional]
|
||||
RSA encrypted with Bob's public key:
|
||||
[16 bytes] Symmetric key for encrypting blob past RSA
|
||||
[ 6 bytes] Meeting point (IP/port)
|
||||
[ 8 bytes] Meeting cookie
|
||||
[ 0 bytes] End-to-end authentication [optional]
|
||||
[98 bytes] g^x part 1 (inside the RSA)
|
||||
[30 bytes] g^x part 2 (symmetrically encrypted)
|
||||
|
||||
The meeting point and meeting cookie allow Bob to contact Alice and
|
||||
prove his identity; the end-to-end authentication enables Bob to
|
||||
decide whether to talk to Alice; the initial authentication enables
|
||||
the introduction point to pre-screen introduction requests before
|
||||
sending them to Bob. (See Section 2 for a discussion of meeting
|
||||
points; see Section 1.1 for an example authentication mechanism.)
|
||||
|
||||
The authentication steps are the appropriate places for the
|
||||
introduction server or Bob to do replay prevention, if desired.
|
||||
|
||||
When the introduction point receives a valid meeting request, it
|
||||
sends the portion intended for Bob along the stream
|
||||
created by Bob's RELAY_BIND_INTRODUCTION. Bob then, at his
|
||||
discretion, connects to Alice's meeting point.
|
||||
|
||||
1.1. An example authentication scheme for introduction services
|
||||
|
||||
Bob makes two short-term secrets SB and SN, and tells the
|
||||
introduction point about SN. Bob gives Alice a cookie consisting
|
||||
of A,B,C such that H(A|SB)=B and H(A|SN)=C. Alice's initial
|
||||
authentication is <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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,522 @@
|
||||
$Id$
|
||||
|
||||
Tor Spec
|
||||
|
||||
Note: This is an attempt to specify Tor as it exists as implemented in
|
||||
early June, 2003. It is not recommended that others implement this
|
||||
design as it stands; future versions of Tor will implement improved
|
||||
protocols.
|
||||
|
||||
TODO: (very soon)
|
||||
- Specify truncate/truncated payloads?
|
||||
- Specify RELAY_END payloads. [It's 1 byte of reason, then X bytes of
|
||||
data, right?]
|
||||
- Sendme w/stream0 is circuit sendme
|
||||
- Integrate -NM and -RD comments
|
||||
- EXTEND cells should have hostnames or nicknames, so that OPs never
|
||||
resolve OR hostnames. Else DNS servers can give different answers to
|
||||
different OPs, and compromise their anonymity.
|
||||
|
||||
EVEN LATER:
|
||||
- Do TCP-style sequencing and ACKing of DATA cells so that we can afford
|
||||
to lose some data cells.
|
||||
|
||||
0. Notation:
|
||||
|
||||
PK -- a public key.
|
||||
SK -- a private key
|
||||
K -- a key for a symmetric cypher
|
||||
|
||||
a|b -- concatenation of 'a' with 'b'.
|
||||
|
||||
All numeric values are encoded in network (big-endian) order.
|
||||
|
||||
Unless otherwise specified, all symmetric ciphers are AES in counter
|
||||
mode, with an IV of all 0 bytes. Asymmetric ciphers are either RSA
|
||||
with 1024-bit keys and exponents of 65537, or DH with the safe prime
|
||||
from rfc2409, section 6.2, whose hex representation is:
|
||||
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
|
||||
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
|
||||
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
|
||||
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
|
||||
"49286651ECE65381FFFFFFFFFFFFFFFF"
|
||||
|
||||
|
||||
1. System overview
|
||||
|
||||
Onion Routing is a distributed overlay network designed to anonymize
|
||||
low-latency TCP-based applications such as web browsing, secure shell,
|
||||
and instant messaging. Clients choose a path through the network and
|
||||
build a ``circuit'', in which each node (or ``onion router'' or ``OR'')
|
||||
in the path knows its predecessor and successor, but no other nodes in
|
||||
the circuit. Traffic flowing down the circuit is sent in fixed-size
|
||||
``cells'', which are unwrapped by a symmetric key at each node (like
|
||||
the layers of an onion) and relayed downstream.
|
||||
|
||||
2. Connections
|
||||
|
||||
There are two ways to connect to an onion router (OR). The first is
|
||||
as an onion proxy (OP), which allows the OP to authenticate the OR
|
||||
without authenticating itself. The second is as another OR, which
|
||||
allows mutual authentication.
|
||||
|
||||
Tor uses TLS for link encryption, using the cipher suite
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA". An OR always sends a
|
||||
self-signed X.509 certificate whose commonName is the server's
|
||||
nickname, and whose public key is in the server directory.
|
||||
|
||||
All parties receiving certificates must confirm that the public
|
||||
key is as it appears in the server directory, and close the
|
||||
connection if it is not.
|
||||
|
||||
Once a TLS connection is established, the two sides send cells
|
||||
(specified below) to one another. Cells are sent serially. All
|
||||
cells are 512 bytes long. Cells may be sent embedded in TLS
|
||||
records of any size or divided across TLS records, but the framing
|
||||
of TLS records must not leak information about the type or
|
||||
contents of the cells.
|
||||
|
||||
OR-to-OR connections are never deliberately closed. An OP should
|
||||
close a connection to an OR if there are no circuits running over
|
||||
the connection, and an amount of time (KeepalivePeriod, defaults to
|
||||
5 minutes) has passed.
|
||||
|
||||
3. Cell Packet format
|
||||
|
||||
The basic unit of communication for onion routers and onion
|
||||
proxies is a fixed-width "cell". Each cell contains the following
|
||||
fields:
|
||||
|
||||
CircID [2 bytes]
|
||||
Command [1 byte]
|
||||
Payload (padded with 0 bytes) [509 bytes]
|
||||
[Total size: 512 bytes]
|
||||
|
||||
The 'Command' field holds one of the following values:
|
||||
0 -- PADDING (Padding) (See Sec 6.2)
|
||||
1 -- CREATE (Create a circuit) (See Sec 4)
|
||||
2 -- CREATED (Acknowledge create) (See Sec 4)
|
||||
3 -- RELAY (End-to-end data) (See Sec 5)
|
||||
4 -- DESTROY (Stop using a circuit) (See Sec 4)
|
||||
|
||||
The interpretation of 'Payload' depends on the type of the cell.
|
||||
PADDING: Unused.
|
||||
CREATE: Payload contains the handshake challenge.
|
||||
CREATED: Payload contains the handshake response.
|
||||
RELAY: Payload contains the relay header and relay body.
|
||||
DESTROY: Unused.
|
||||
|
||||
The payload is padded with 0 bytes.
|
||||
|
||||
PADDING cells are currently used to implement connection
|
||||
keepalive. ORs and OPs send one another a PADDING cell every few
|
||||
minutes.
|
||||
|
||||
CREATE, CREATED, and DESTROY cells are used to manage circuits;
|
||||
see section 4 below.
|
||||
|
||||
RELAY cells are used to send commands and data along a circuit; see
|
||||
section 5 below.
|
||||
|
||||
|
||||
4. Circuit management
|
||||
|
||||
4.1. CREATE and CREATED cells
|
||||
|
||||
Users set up circuits incrementally, one hop at a time. To create a
|
||||
new circuit, users send a CREATE cell to the first node, with the
|
||||
first half of the DH handshake; that node responds with a CREATED
|
||||
cell with the second half of the DH handshake plus the first 20 bytes
|
||||
of derivative key data (see section 4.2). To extend a circuit past
|
||||
the first hop, the user sends an EXTEND relay cell (see section 5)
|
||||
which instructs the last node in the circuit to send a CREATE cell
|
||||
to extend the circuit.
|
||||
|
||||
The payload for a CREATE cell is an 'onion skin', consisting of:
|
||||
RSA-encrypted data [128 bytes]
|
||||
Symmetrically-encrypted data [16 bytes]
|
||||
|
||||
The RSA-encrypted portion contains:
|
||||
Symmetric key [16 bytes]
|
||||
First part of DH data (g^x) [112 bytes]
|
||||
The symmetrically encrypted portion contains:
|
||||
Second part of DH data (g^x) [16 bytes]
|
||||
|
||||
The two parts of DH data, once decrypted and concatenated, form
|
||||
g^x as calculated by the client.
|
||||
|
||||
The relay payload for an EXTEND relay cell consists of:
|
||||
Address [4 bytes]
|
||||
Port [2 bytes]
|
||||
Onion skin [144 bytes]
|
||||
|
||||
The port and address field denote the IPV4 address and port of the
|
||||
next onion router in the circuit.
|
||||
|
||||
4.2. Setting circuit keys
|
||||
|
||||
Once the handshake between the OP and an OR is completed, both
|
||||
servers can now calculate g^xy with ordinary DH. From the base key
|
||||
material g^xy, they compute derivative key material as follows.
|
||||
First, the server represents g^xy as a big-endian unsigned integer.
|
||||
Next, the server computes 60 bytes of key data as K = SHA1(g^xy |
|
||||
[00]) | SHA1(g^xy | [01]) | SHA1(g^xy | [02]) where "00" is a single
|
||||
octet whose value is zero, "01" is a single octet whose value is
|
||||
one, etc. The first 20 bytes of K form KH, the next 16 bytes of K
|
||||
form Kf, and the next 16 bytes of K form Kb.
|
||||
|
||||
KH is used in the handshake response to demonstrate knowledge of the
|
||||
computed shared key. Kf is used to encrypt the stream of data going
|
||||
from the OP to the OR, and Kb is used to encrypt the stream of data
|
||||
going from the OR to the OP.
|
||||
|
||||
4.3. Creating circuits
|
||||
|
||||
When creating a circuit through the network, the circuit creator
|
||||
performs the following steps:
|
||||
|
||||
1. Choose a chain of N onion routers (R_1...R_N) to constitute
|
||||
the path, such that no router appears in the path twice.
|
||||
[this is wrong, see October 2003 discussion on or-dev]
|
||||
|
||||
2. If not already connected to the first router in the chain,
|
||||
open a new connection to that router.
|
||||
|
||||
3. Choose a circID not already in use on the connection with the
|
||||
first router in the chain. If we are an onion router and our
|
||||
nickname is lexicographically greater than the nickname of the
|
||||
other side, then let the high bit of the circID be 1, else 0.
|
||||
|
||||
4. Send a CREATE cell along the connection, to be received by
|
||||
the first onion router.
|
||||
|
||||
5. Wait until a CREATED cell is received; finish the handshake
|
||||
and extract the forward key Kf_1 and the backward key Kb_1.
|
||||
|
||||
6. For each subsequent onion router R (R_2 through R_N), extend
|
||||
the circuit to R.
|
||||
|
||||
To extend the circuit by a single onion router R_M, the circuit
|
||||
creator performs these steps:
|
||||
|
||||
1. Create an onion skin, encrypting the RSA-encrypted part with
|
||||
R's public key.
|
||||
|
||||
2. Encrypt and send the onion skin in a relay EXTEND cell along
|
||||
the circuit (see section 5).
|
||||
|
||||
3. When a relay EXTENDED cell is received, calculate the shared
|
||||
keys. The circuit is now extended.
|
||||
|
||||
When an onion router receives an EXTEND relay cell, it sends a
|
||||
CREATE cell to the next onion router, with the enclosed onion skin
|
||||
as its payload. The initiating onion router chooses some circID not
|
||||
yet used on the connection between the two onion routers. (But see
|
||||
section 4.3. above, concerning choosing circIDs.)
|
||||
|
||||
As an extension (called router twins), if the desired next onion
|
||||
router R in the circuit is down, and some other onion router R'
|
||||
has the same key as R, then it's ok to extend to R' rather than R.
|
||||
|
||||
When an onion router receives a CREATE cell, if it already has a
|
||||
circuit on the given connection with the given circID, it drops the
|
||||
cell. Otherwise, sometime after receiving the CREATE cell, it completes
|
||||
the DH handshake, and replies with a CREATED cell, containing g^y
|
||||
as its [128 byte] payload. Upon receiving a CREATED cell, an onion
|
||||
router packs it payload into an EXTENDED relay cell (see section 5),
|
||||
and sends that cell up the circuit. Upon receiving the EXTENDED
|
||||
relay cell, the OP can retrieve g^y.
|
||||
|
||||
(As an optimization, OR implementations may delay processing onions
|
||||
until a break in traffic allows time to do so without harming
|
||||
network latency too greatly.)
|
||||
|
||||
4.4. Tearing down circuits
|
||||
|
||||
Circuits are torn down when an unrecoverable error occurs along
|
||||
the circuit, or when all streams on a circuit are closed and the
|
||||
circuit's intended lifetime is over. Circuits may be torn down
|
||||
either completely or hop-by-hop.
|
||||
|
||||
To tear down a circuit completely, an OR or OP sends a DESTROY
|
||||
cell to the adjacent nodes on that circuit, using the appropriate
|
||||
direction's circID.
|
||||
|
||||
Upon receiving an outgoing DESTROY cell, an OR frees resources
|
||||
associated with the corresponding circuit. If it's not the end of
|
||||
the circuit, it sends a DESTROY cell for that circuit to the next OR
|
||||
in the circuit. If the node is the end of the circuit, then it tears
|
||||
down any associated edge connections (see section 5.1).
|
||||
|
||||
After a DESTROY cell has been processed, an OR ignores all data or
|
||||
destroy cells for the corresponding circuit.
|
||||
|
||||
To tear down part of a circuit, the OP sends a RELAY_TRUNCATE cell
|
||||
signaling a given OR (Stream ID zero). That OR sends a DESTROY
|
||||
cell to the next node in the circuit, and replies to the OP with a
|
||||
RELAY_TRUNCATED cell.
|
||||
|
||||
When an unrecoverable error occurs along one connection in a
|
||||
circuit, the nodes on either side of the connection should, if they
|
||||
are able, act as follows: the node closer to the OP should send a
|
||||
RELAY_TRUNCATED cell towards the OP; the node farther from the OP
|
||||
should send a DESTROY cell down the circuit.
|
||||
|
||||
[We'll have to reevaluate this section once we figure out cleaner
|
||||
circuit/connection killing conventions. -RD]
|
||||
|
||||
4.5. Routing relay cells
|
||||
|
||||
When an OR receives a RELAY cell, it checks the cell's circID and
|
||||
determines whether it has a corresponding circuit along that
|
||||
connection. If not, the OR drops the RELAY cell.
|
||||
|
||||
Otherwise, if the OR is not at the OP edge of the circuit (that is,
|
||||
either an 'exit node' or a non-edge node), it de/encrypts the length
|
||||
field and the payload with AES/CTR, as follows:
|
||||
'Forward' relay cell (same direction as CREATE):
|
||||
Use Kf as key; encrypt.
|
||||
'Back' relay cell (opposite direction from CREATE):
|
||||
Use Kb as key; decrypt.
|
||||
If the OR recognizes the stream ID on the cell (it is either the ID
|
||||
of an open stream or the signaling (zero) ID), the OR processes the
|
||||
contents of the relay cell. Otherwise, it passes the decrypted
|
||||
relay cell along the circuit if the circuit continues, or drops the
|
||||
cell if it's the end of the circuit. [Getting an unrecognized
|
||||
relay cell at the end of the circuit must be allowed for now;
|
||||
we can reexamine this once we've designed full tcp-style close
|
||||
handshakes. -RD]
|
||||
|
||||
Otherwise, if the data cell is coming from the OP edge of the
|
||||
circuit, the OP decrypts the length and payload fields with AES/CTR as
|
||||
follows:
|
||||
OP sends data cell to node R_M:
|
||||
For I=1...M, decrypt with Kf_I.
|
||||
|
||||
Otherwise, if the data cell is arriving at the OP edge if the
|
||||
circuit, the OP encrypts the length and payload fields with AES/CTR as
|
||||
follows:
|
||||
OP receives data cell:
|
||||
For I=N...1,
|
||||
Encrypt with Kb_I. If the stream ID is a recognized
|
||||
stream for R_I, or if the stream ID is the signaling
|
||||
ID (zero), then stop and process the payload.
|
||||
|
||||
For more information, see section 5 below.
|
||||
|
||||
5. Application connections and stream management
|
||||
|
||||
5.1. Streams
|
||||
|
||||
Within a circuit, the OP and the exit node use the contents of
|
||||
RELAY packets to tunnel end-to-end commands and TCP connections
|
||||
("Streams") across circuits. End-to-end commands can be initiated
|
||||
by either edge; streams are initiated by the OP.
|
||||
|
||||
The first 8 bytes of each relay cell are reserved as follows:
|
||||
Relay command [1 byte]
|
||||
Stream ID [7 bytes]
|
||||
|
||||
The relay commands are:
|
||||
1 -- RELAY_BEGIN
|
||||
2 -- RELAY_DATA
|
||||
3 -- RELAY_END
|
||||
4 -- RELAY_CONNECTED
|
||||
5 -- RELAY_SENDME
|
||||
6 -- RELAY_EXTEND
|
||||
7 -- RELAY_EXTENDED
|
||||
8 -- RELAY_TRUNCATE
|
||||
9 -- RELAY_TRUNCATED
|
||||
10 -- RELAY_DROP
|
||||
|
||||
All RELAY cells pertaining to the same tunneled stream have the
|
||||
same stream ID. Stream ID's are chosen randomly by the OP. A
|
||||
stream ID is considered "recognized" on a circuit C by an OP or an
|
||||
OR if it already has an existing stream established on that
|
||||
circuit, or if the stream ID is equal to the signaling stream ID,
|
||||
which is all zero: [00 00 00 00 00 00 00]
|
||||
|
||||
To create a new anonymized TCP connection, the OP sends a
|
||||
RELAY_BEGIN data cell with a payload encoding the address and port
|
||||
of the destination host. The stream ID is zero. The payload format is:
|
||||
NEWSTREAMID | ADDRESS | ':' | PORT | '\000'
|
||||
where NEWSTREAMID is the newly generated Stream ID to use for
|
||||
this stream, ADDRESS may be a DNS hostname, or an IPv4 address in
|
||||
dotted-quad format; and where PORT is encoded in decimal.
|
||||
|
||||
Upon receiving this packet, the exit node resolves the address as
|
||||
necessary, and opens a new TCP connection to the target port. If
|
||||
the address cannot be resolved, or a connection can't be
|
||||
established, the exit node replies with a RELAY_END cell.
|
||||
Otherwise, the exit node replies with a RELAY_CONNECTED cell.
|
||||
|
||||
The OP waits for a RELAY_CONNECTED cell before sending any data.
|
||||
Once a connection has been established, the OP and exit node
|
||||
package stream data in RELAY_DATA cells, and upon receiving such
|
||||
cells, echo their contents to the corresponding TCP stream.
|
||||
|
||||
Relay RELAY_DROP cells are long-range dummies; upon receiving such
|
||||
a cell, the OR or OP must drop it.
|
||||
|
||||
5.2. Closing streams
|
||||
|
||||
[Note -- TCP streams can only be half-closed for reading. Our
|
||||
Bickford's conversation was incorrect. -NM]
|
||||
|
||||
Because TCP connections can be half-open, we follow an equivalent
|
||||
to TCP's FIN/FIN-ACK/ACK protocol to close streams.
|
||||
|
||||
An exit connection can have a TCP stream in one of three states:
|
||||
'OPEN', 'DONE_PACKAGING', and 'DONE_DELIVERING'. For the purposes
|
||||
of modeling transitions, we treat 'CLOSED' as a fourth state,
|
||||
although connections in this state are not, in fact, tracked by the
|
||||
onion router.
|
||||
|
||||
A stream begins in the 'OPEN' state. Upon receiving a 'FIN' from
|
||||
the corresponding TCP connection, the edge node sends a 'RELAY_END'
|
||||
cell along the circuit and changes its state to 'DONE_PACKAGING'.
|
||||
Upon receiving a 'RELAY_END' cell, an edge node sends a 'FIN' to
|
||||
the corresponding TCP connection (e.g., by calling
|
||||
shutdown(SHUT_WR)) and changing its state to 'DONE_DELIVERING'.
|
||||
|
||||
When a stream in already in 'DONE_DELIVERING' receives a 'FIN', it
|
||||
also sends a 'RELAY_END' along the circuit, and changes its state
|
||||
to 'CLOSED'. When a stream already in 'DONE_PACKAGING' receives a
|
||||
'RELAY_END' cell, it sends a 'FIN' and changes its state to
|
||||
'CLOSED'.
|
||||
|
||||
[Note: Please rename 'RELAY_END2'. :) -NM ]
|
||||
|
||||
If an edge node encounters an error on any stram, it sends a
|
||||
'RELAY_END2' cell along the circuit (if possible) and closes the
|
||||
TCP connection immediately. If an edge node receives a
|
||||
'RELAY_END2' cell for any stream, it closes the TCP connection
|
||||
completely, and sends nothing along the circuit.
|
||||
|
||||
6. Flow control
|
||||
|
||||
6.1. Link throttling
|
||||
|
||||
Each node should do appropriate bandwidth throttling to keep its
|
||||
user happy.
|
||||
|
||||
Communicants rely on TCP's default flow control to push back when they
|
||||
stop reading.
|
||||
|
||||
6.2. Link padding
|
||||
|
||||
Currently nodes are not required to do any sort of link padding or
|
||||
dummy traffic. Because strong attacks exist even with link padding,
|
||||
and because link padding greatly increases the bandwidth requirements
|
||||
for running a node, we plan to leave out link padding until this
|
||||
tradeoff is better understood.
|
||||
|
||||
6.3. Circuit-level flow control
|
||||
|
||||
To control a circuit's bandwidth usage, each OR keeps track of
|
||||
two 'windows', consisting of how many RELAY_DATA cells it is
|
||||
allowed to package for transmission, and how many RELAY_DATA cells
|
||||
it is willing to deliver to streams outside the network.
|
||||
Each 'window' value is initially set to 1000 data cells
|
||||
in each direction (cells that are not data cells do not affect
|
||||
the window). When an OR is willing to deliver more cells, it sends a
|
||||
RELAY_SENDME cell towards the OP, with Stream ID zero. When an OR
|
||||
receives a RELAY_SENDME cell with stream ID zero, it increments its
|
||||
packaging window.
|
||||
|
||||
Each of these cells increments the corresponding window by 100.
|
||||
|
||||
The OP behaves identically, except that it must track a packaging
|
||||
window and a delivery window for every OR in the circuit.
|
||||
|
||||
An OR or OP sends cells to increment its delivery window when the
|
||||
corresponding window value falls under some threshold (900).
|
||||
|
||||
If a packaging window reaches 0, the OR or OP stops reading from
|
||||
TCP connections for all streams on the corresponding circuit, and
|
||||
sends no more RELAY_DATA cells until receiving a RELAY_SENDME cell.
|
||||
[this stuff is badly worded; copy in the tor-design section -RD]
|
||||
|
||||
6.4. Stream-level flow control
|
||||
|
||||
Edge nodes use RELAY_SENDME cells to implement end-to-end flow
|
||||
control for individual connections across circuits. Similarly to
|
||||
circuit-level flow control, edge nodes begin with a window of cells
|
||||
(500) per stream, and increment the window by a fixed value (50)
|
||||
upon receiving a RELAY_SENDME cell. Edge nodes initiate RELAY_SENDME
|
||||
cells when both a) the window is <= 450, and b) there are less than
|
||||
ten cell payloads remaining to be flushed at that edge.
|
||||
|
||||
|
||||
7. Directories and routers
|
||||
|
||||
7.1. Router descriptor format.
|
||||
|
||||
(Unless otherwise noted, tokens on the same line are space-separated.)
|
||||
|
||||
Router ::= Router-Line Date-Line Onion-Key Link-Key Signing-Key Exit-Policy Router-Signature NL
|
||||
Router-Line ::= "router" nickname address ORPort SocksPort DirPort bandwidth NL
|
||||
Date-Line ::= "published" YYYY-MM-DD HH:MM:SS NL
|
||||
Onion-key ::= "onion-key" NL a public key in PEM format NL
|
||||
Link-key ::= "link-key" NL a public key in PEM format NL
|
||||
Signing-Key ::= "signing-key" NL a public key in PEM format NL
|
||||
Exit-Policy ::= Exit-Line*
|
||||
Exit-Line ::= ("accept"|"reject") string NL
|
||||
Router-Signature ::= "router-signature" NL Signature
|
||||
Signature ::= "-----BEGIN SIGNATURE-----" NL
|
||||
Base-64-encoded-signature NL "-----END SIGNATURE-----" NL
|
||||
|
||||
ORport ::= port where the router listens for routers/proxies (speaking cells)
|
||||
SocksPort ::= where the router listens for applications (speaking socks)
|
||||
DirPort ::= where the router listens for directory download requests
|
||||
bandwidth ::= maximum bandwidth, in bytes/s
|
||||
|
||||
nickname ::= between 1 and 32 alphanumeric characters. case-insensitive.
|
||||
|
||||
Example:
|
||||
router moria1 moria.mit.edu 9001 9021 9031 100000
|
||||
published 2003-09-24 19:36:05
|
||||
-----BEGIN RSA PUBLIC KEY-----
|
||||
MIGJAoGBAMBBuk1sYxEg5jLAJy86U3GGJ7EGMSV7yoA6mmcsEVU3pwTUrpbpCmwS
|
||||
7BvovoY3z4zk63NZVBErgKQUDkn3pp8n83xZgEf4GI27gdWIIwaBjEimuJlEY+7K
|
||||
nZ7kVMRoiXCbjL6VAtNa4Zy1Af/GOm0iCIDpholeujQ95xew7rQnAgMA//8=
|
||||
-----END RSA PUBLIC KEY-----
|
||||
signing-key
|
||||
-----BEGIN RSA PUBLIC KEY-----
|
||||
7BvovoY3z4zk63NZVBErgKQUDkn3pp8n83xZgEf4GI27gdWIIwaBjEimuJlEY+7K
|
||||
MIGJAoGBAMBBuk1sYxEg5jLAJy86U3GGJ7EGMSV7yoA6mmcsEVU3pwTUrpbpCmwS
|
||||
f/GOm0iCIDpholeujQ95xew7rnZ7kVMRoiXCbjL6VAtNa4Zy1AQnAgMA//8=
|
||||
-----END RSA PUBLIC KEY-----
|
||||
reject 18.0.0.0/24
|
||||
|
||||
Note: The extra newline at the end of the router block is intentional.
|
||||
|
||||
7.2. Directory format
|
||||
|
||||
Directory ::= Directory-Header Directory-Router Router* Signature
|
||||
Directory-Header ::= "signed-directory" NL Software-Line NL
|
||||
Software-Line: "recommended-software" comma-separated-version-list
|
||||
Directory-Router ::= Router
|
||||
Directory-Signature ::= "directory-signature" NL Signature
|
||||
Signature ::= "-----BEGIN SIGNATURE-----" NL
|
||||
Base-64-encoded-signature NL "-----END SIGNATURE-----" NL
|
||||
|
||||
Note: The router block for the directory server must appear first.
|
||||
The signature is computed by computing the SHA-1 hash of the
|
||||
directory, from the characters "signed-directory", through the newline
|
||||
after "directory-signature". This digest is then padded with PKCS.1,
|
||||
and signed with the directory server's signing key.
|
||||
|
||||
7.3. Behavior of a directory server
|
||||
|
||||
lists nodes that are connected currently
|
||||
speaks http on a socket, spits out directory on request
|
||||
|
||||
-----------
|
||||
(for emacs)
|
||||
Local Variables:
|
||||
mode:text
|
||||
indent-tabs-mode:nil
|
||||
fill-column:77
|
||||
End:
|
||||
@@ -0,0 +1,139 @@
|
||||
.TH TOR 1 "November 2003" "TOR"
|
||||
.SH NAME
|
||||
tor \- The second-generation onion router
|
||||
.SH SYNOPSIS
|
||||
.B tor
|
||||
[\fIOPTION value\fR]...
|
||||
.SH DESCRIPTION
|
||||
.I tor
|
||||
is a connection-oriented anonymizing communication
|
||||
service. Users choose a source-routed path through a set of nodes, and
|
||||
negotiate a "virtual circuit" through the network, in which each node
|
||||
knows its predecessor and successor, but no others. Traffic flowing down
|
||||
the circuit is unwrapped by a symmetric key at each node, which reveals
|
||||
the downstream node.
|
||||
.PP
|
||||
Basically \fItor\fR provides a distributed network of servers ("onion
|
||||
routers"). Users bounce their tcp streams -- web traffic, ftp, ssh, etc --
|
||||
around the routers, and recipients, observers, and even the routers
|
||||
themselves have difficulty tracking the source of the stream.
|
||||
.SH OPTIONS
|
||||
\fB-h, -help\fP
|
||||
Display a short help message and exit.
|
||||
.TP
|
||||
\fB-f \fR\fIFILE\fP
|
||||
FILE contains further "option value" pairs. (Default: @CONFDIR@/torrc)
|
||||
.TP
|
||||
Other options can be specified either on the commandline (\fI--option value\fR), or in the configuration file (\fIoption value\fR).
|
||||
.TP
|
||||
\fBloglevel debug|info|warn|err\fP
|
||||
Set the verboseness level of the primary log. (Default: warn)
|
||||
.TP
|
||||
\fBlogfile \fR\fIFILE\fP
|
||||
Rather than logging to stdout, log to FILE.
|
||||
.TP
|
||||
\fBdebuglogfile \fR\fIFILE\fP
|
||||
In addition to other logging, we will log to FILE at log-level debug.
|
||||
.TP
|
||||
\fBgroup \fR\fIGID\fP
|
||||
On startup, setgid to this user.
|
||||
.TP
|
||||
\fBkeepaliveperiod \fR\fINUM\fP
|
||||
To keep firewalls from expiring connections, send a padding keepalive cell on open connections every NUM seconds. (Default: 300)
|
||||
.TP
|
||||
\fBpidfile \fR\fIFILE\fP
|
||||
On startup, write our PID to FILE. On clean shutdown, remove FILE.
|
||||
.TP
|
||||
\fBrouterfile \fR\fIFILE\fP
|
||||
FILE contains a list of directory servers, to bootstrap into the network. (Default: @CONFDIR@/dirservers)
|
||||
.TP
|
||||
\fBrunasdaemon \fR\fI0|1\fP
|
||||
If 1, Tor forks and daemonizes to the background. (Default: 0)
|
||||
.TP
|
||||
\fBtotalbandwidth \fR\fINUM\fP
|
||||
A token bucket limits the average incoming bandwidth on this node to NUM bytes per second. (Default: 800000)
|
||||
.TP
|
||||
\fBuser \fR\fIUID\fP
|
||||
On startup, setuid to this user.
|
||||
|
||||
.SH CLIENT OPTIONS
|
||||
.PP
|
||||
The following options are useful only for clients (that is, if \fBsocksport\fP is non-zero):
|
||||
.TP
|
||||
\fBnewcircuitperiod \fR\fINUM\fP
|
||||
Every NUM seconds consider whether to build a new circuit. (Default: 60)
|
||||
.TP
|
||||
\fBpathlencoinweight \fR\fI0.0-1.0\fP
|
||||
Paths are 3 hops plus a geometric distribution centered around this coinweight. Must be >=0.0 and <1.0. (Default: 0.3)
|
||||
.TP
|
||||
\fBsocksport \fR\fIPORT\fP
|
||||
Bind to this port to listen for connections from socks-speaking applications.
|
||||
.TP
|
||||
\fBsocksbindaddress \fR\fIIP\fP
|
||||
Bind to this address to listen for connections from socks-speaking applications. (Default: 127.0.0.1)
|
||||
|
||||
.SH SERVER OPTIONS
|
||||
.PP
|
||||
The following options are useful only for servers (that is, if \fBorport\fP is non-zero):
|
||||
.TP
|
||||
\fBaddress address\fP
|
||||
The fqdn of this server (e.g. moria.mit.edu).
|
||||
.TP
|
||||
\fBdatadirectory \fR\fIDIR\fP
|
||||
Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor)
|
||||
.TP
|
||||
\fBexitpolicy \fR\fIpolicy,policy,...\fP
|
||||
Set an exit policy for this server. Each policy is of the form "reject ADDR/MASK:PORT". For example,
|
||||
"reject 127.0.0.1:*,reject 192.168.1.0/24:*,accept *:*" would reject any traffic destined for
|
||||
localhost and any 192.168.1.* address, but accept anything else.
|
||||
.TP
|
||||
\fBmaxonionspending \fR\fINUM\fP
|
||||
If you have more than this number of onionskins queued for decrypt, reject new ones. (Default: 100)
|
||||
.TP
|
||||
\fBnickname \fR\fIname\fP
|
||||
Set the server's nickname to 'name'.
|
||||
.TP
|
||||
\fBnumcpus \fR\fInum\fP
|
||||
How many processes to use at once for decrypting onionskins. (Default: 1)
|
||||
.TP
|
||||
\fBorport \fR\fIPORT\fP
|
||||
Bind to this port to listen for connections from Tor clients and servers.
|
||||
.TP
|
||||
\fBorbindaddress \fR\fIIP\fP
|
||||
Bind to this address to listen for connections from Tor clients and servers. (Default: 0.0.0.0)
|
||||
|
||||
.SH DIRECTORY SERVER OPTIONS
|
||||
.PP
|
||||
The following options are useful only for directory servers (that is, if \fBdirport\fP is non-zero):
|
||||
.TP
|
||||
\fBdirport \fR\fIPORT\fP
|
||||
Bind the directory service to this port.
|
||||
.TP
|
||||
\fBdirbindaddress \fR\fIIP\fP
|
||||
Bind the directory service to this address. (Default: 0.0.0.0)
|
||||
.TP
|
||||
\fBrecommendedversions \fR\fISTRING\fP
|
||||
STRING is a command-separated list of Tor versions currently believed to be safe. The list is included in each directory, and nodes which pull down the directory learn whether they need to upgrade.
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
.I @CONFDIR@/torrc
|
||||
The configuration file, which contains "option value" pairs.
|
||||
.TP
|
||||
.I @CONFDIR@/dirservers
|
||||
A list of directory servers, to bootstrap into the network.
|
||||
.TP
|
||||
.I @LOCALSTATEDIR@/lib/tor/
|
||||
The tor server stores keys/etc here.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR privoxy (1),
|
||||
.BR tsocks (1)
|
||||
|
||||
.BR http://freehaven.net/tor/
|
||||
|
||||
.SH BUGS
|
||||
Plenty, probably. It's still in alpha. Please report them.
|
||||
.SH AUTHORS
|
||||
Roger Dingledine <arma@mit.edu>.
|
||||
|
||||
Executable
+251
@@ -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
|
||||
Executable
+336
@@ -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
|
||||
Executable
+101
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
@@ -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
@@ -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
@@ -0,0 +1,121 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef __CRYPTO_H
|
||||
#define __CRYPTO_H
|
||||
|
||||
#include <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
|
||||
|
||||
#define CRYPTO_SHA1_DIGEST 0
|
||||
|
||||
typedef struct crypto_pk_env_t crypto_pk_env_t;
|
||||
typedef struct crypto_cipher_env_t crypto_cipher_env_t;
|
||||
typedef struct crypto_digest_env_t crypto_digest_env_t;
|
||||
|
||||
/* global state */
|
||||
int crypto_global_init();
|
||||
int crypto_global_cleanup();
|
||||
|
||||
/* environment setup */
|
||||
crypto_pk_env_t *crypto_new_pk_env(int type);
|
||||
void crypto_free_pk_env(crypto_pk_env_t *env);
|
||||
|
||||
crypto_cipher_env_t *crypto_new_cipher_env(int type);
|
||||
void crypto_free_cipher_env(crypto_cipher_env_t *env);
|
||||
|
||||
/* public key crypto */
|
||||
int crypto_pk_generate_key(crypto_pk_env_t *env);
|
||||
|
||||
int crypto_pk_read_private_key_from_file(crypto_pk_env_t *env, FILE *src);
|
||||
int crypto_pk_read_public_key_from_file(crypto_pk_env_t *env, FILE *src);
|
||||
int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int *len);
|
||||
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, int len);
|
||||
int crypto_pk_write_private_key_to_file(crypto_pk_env_t *env, FILE *dest);
|
||||
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname);
|
||||
int crypto_pk_write_public_key_to_file(crypto_pk_env_t *env, FILE *dest);
|
||||
int crypto_pk_check_key(crypto_pk_env_t *env);
|
||||
int crypto_pk_read_private_key_from_filename(crypto_pk_env_t *env, const char *keyfile);
|
||||
|
||||
int crypto_pk_set_key(crypto_pk_env_t *env, unsigned char *key);
|
||||
int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b);
|
||||
crypto_pk_env_t *crypto_pk_dup_key(crypto_pk_env_t *orig);
|
||||
int crypto_pk_keysize(crypto_pk_env_t *env);
|
||||
|
||||
int crypto_pk_public_encrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
|
||||
int crypto_pk_private_decrypt(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to, int padding);
|
||||
int crypto_pk_private_sign(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
|
||||
int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fromlen, unsigned char *to);
|
||||
#define FINGERPRINT_LEN 49
|
||||
int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out);
|
||||
int crypto_pk_check_fingerprint_syntax(const char *s);
|
||||
|
||||
int base64_encode(char *dest, int destlen, const char *src, int srclen);
|
||||
int base64_decode(char *dest, int destlen, const char *src, int srclen);
|
||||
|
||||
/* Key negotiation */
|
||||
typedef struct crypto_dh_env_st {
|
||||
DH *dh;
|
||||
} crypto_dh_env_t;
|
||||
|
||||
/* #define CRYPTO_DH_SIZE (1536 / 8) */
|
||||
#define CRYPTO_DH_SIZE (1024 / 8)
|
||||
crypto_dh_env_t *crypto_dh_new();
|
||||
int crypto_dh_get_bytes(crypto_dh_env_t *dh);
|
||||
int crypto_dh_get_public(crypto_dh_env_t *dh, char *pubkey_out,
|
||||
int pubkey_out_len);
|
||||
int crypto_dh_compute_secret(crypto_dh_env_t *dh,
|
||||
char *pubkey, int pubkey_len,
|
||||
char *secret_out, int secret_out_len);
|
||||
void crypto_dh_free(crypto_dh_env_t *dh);
|
||||
|
||||
/* symmetric crypto */
|
||||
int crypto_cipher_generate_key(crypto_cipher_env_t *env);
|
||||
int crypto_cipher_set_iv(crypto_cipher_env_t *env, unsigned char *iv);
|
||||
int crypto_cipher_set_key(crypto_cipher_env_t *env, unsigned char *key);
|
||||
int crypto_cipher_encrypt_init_cipher(crypto_cipher_env_t *env);
|
||||
int crypto_cipher_decrypt_init_cipher(crypto_cipher_env_t *env);
|
||||
unsigned char *crypto_cipher_get_key(crypto_cipher_env_t *env);
|
||||
|
||||
int crypto_cipher_encrypt(crypto_cipher_env_t *env, unsigned char *from, unsigned int fromlen, unsigned char *to);
|
||||
int crypto_cipher_decrypt(crypto_cipher_env_t *env, unsigned char *from, unsigned int fromlen, unsigned char *to);
|
||||
|
||||
/* only implemented for CRYPTO_CIPHER_AES_CTR */
|
||||
int crypto_cipher_rewind(crypto_cipher_env_t *env, long delta);
|
||||
int crypto_cipher_advance(crypto_cipher_env_t *env, long delta);
|
||||
|
||||
/* convenience function: wraps crypto_create_crypto_env, set_key, set_iv, and init. */
|
||||
crypto_cipher_env_t *crypto_create_init_cipher(int cipher_type, char *key, char *iv, int encrypt_mode);
|
||||
|
||||
/* SHA-1 */
|
||||
int crypto_SHA_digest(const unsigned char *m, int len, unsigned char *digest);
|
||||
crypto_digest_env_t *crypto_new_digest_env(int type);
|
||||
void crypto_free_digest_env(crypto_digest_env_t *digest);
|
||||
void crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data,
|
||||
size_t len);
|
||||
void crypto_digest_get_digest(crypto_digest_env_t *digest,
|
||||
char *out, size_t out_len);
|
||||
crypto_digest_env_t *crypto_digest_dup(const crypto_digest_env_t *digest);
|
||||
void crypto_digest_assign(crypto_digest_env_t *into,
|
||||
const crypto_digest_env_t *from);
|
||||
|
||||
/* random numbers */
|
||||
int crypto_seed_rng();
|
||||
int crypto_rand(unsigned int n, unsigned char *to);
|
||||
void crypto_pseudo_rand(unsigned int n, unsigned char *to);
|
||||
int crypto_pseudo_rand_int(unsigned int max);
|
||||
|
||||
/* errors */
|
||||
char *crypto_perror();
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
/* On systems where 'poll' doesn't exist, fake it with 'select'. */
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "fakepoll.h"
|
||||
|
||||
#ifdef USE_FAKE_POLL
|
||||
#include <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
|
||||
|
||||
/* by default, windows handles only 64 fd's */
|
||||
#if defined(MS_WINDOWS) && !defined(FD_SETSIZE)
|
||||
#define FD_SETSIZE MAXCONNECTIONS
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int
|
||||
poll(struct pollfd *ufds, unsigned int nfds, int timeout)
|
||||
{
|
||||
int idx, maxfd, fd;
|
||||
int r;
|
||||
#ifdef MS_WINDOWS
|
||||
int any_fds_set = 0;
|
||||
#endif
|
||||
fd_set readfds, writefds, exceptfds;
|
||||
#ifdef USING_FAKE_TIMEVAL
|
||||
#undef timeval
|
||||
#undef tv_sec
|
||||
#undef tv_usec
|
||||
#endif
|
||||
struct timeval _timeout;
|
||||
_timeout.tv_sec = timeout/1000;
|
||||
_timeout.tv_usec = (timeout%1000)*1000;
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
|
||||
maxfd = -1;
|
||||
for (idx = 0; idx < nfds; ++idx) {
|
||||
ufds[idx].revents = 0;
|
||||
fd = ufds[idx].fd;
|
||||
if (fd > maxfd) {
|
||||
maxfd = fd;
|
||||
#ifdef MS_WINDOWS
|
||||
any_fds_set = 1;
|
||||
#endif
|
||||
}
|
||||
if (ufds[idx].events & POLLIN)
|
||||
FD_SET(fd, &readfds);
|
||||
if (ufds[idx].events & POLLOUT)
|
||||
FD_SET(fd, &writefds);
|
||||
FD_SET(fd, &exceptfds);
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
if (!any_fds_set) {
|
||||
Sleep(timeout);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
r = select(maxfd+1, &readfds, &writefds, &exceptfds,
|
||||
timeout == -1 ? NULL : &_timeout);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
r = 0;
|
||||
for (idx = 0; idx < nfds; ++idx) {
|
||||
fd = ufds[idx].fd;
|
||||
if (FD_ISSET(fd, &readfds))
|
||||
ufds[idx].revents |= POLLIN;
|
||||
if (FD_ISSET(fd, &writefds))
|
||||
ufds[idx].revents |= POLLOUT;
|
||||
if (FD_ISSET(fd, &exceptfds))
|
||||
ufds[idx].revents |= POLLERR;
|
||||
if (ufds[idx].revents)
|
||||
++r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright 2002,2003 Nick Mathewson, Roger Dingledine. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef __FAKEPOLL_H
|
||||
#define __FAKEPOLL_H
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifndef HAVE_POLL_H
|
||||
#ifndef HAVE_SYS_POLL_H
|
||||
#define USE_FAKE_POLL
|
||||
|
||||
struct pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
#define POLLIN 0x0001
|
||||
#define POLLPRI 0x0002
|
||||
#define POLLOUT 0x0004
|
||||
#define POLLERR 0x0008
|
||||
#define POLLHUP 0x0010
|
||||
#define POLLNVAL 0x0020
|
||||
|
||||
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "../or/or.h"
|
||||
|
||||
struct logfile_t;
|
||||
typedef struct logfile_t {
|
||||
struct logfile_t *next;
|
||||
const char *filename;
|
||||
FILE *file;
|
||||
int needs_close;
|
||||
int loglevel;
|
||||
int max_loglevel;
|
||||
} logfile_t;
|
||||
|
||||
static INLINE const char *sev_to_string(int severity) {
|
||||
switch(severity) {
|
||||
case LOG_DEBUG: return "debug";
|
||||
case LOG_INFO: return "info";
|
||||
case LOG_WARN: return "warn";
|
||||
case LOG_ERR: return "err";
|
||||
default: assert(0); return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static logfile_t *logfiles = NULL;
|
||||
|
||||
/* Format a log message into a fixed-sized buffer. (This is factored out
|
||||
* of 'logv' so that we never format a message more than once.
|
||||
*/
|
||||
static INLINE void format_msg(char *buf, size_t buf_len,
|
||||
int severity, const char *funcname,
|
||||
const char *format, va_list ap)
|
||||
{
|
||||
time_t t;
|
||||
struct timeval now;
|
||||
int n;
|
||||
|
||||
buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */
|
||||
|
||||
tor_gettimeofday(&now);
|
||||
t = (time_t)now.tv_sec;
|
||||
|
||||
n = strftime(buf, buf_len, "%b %d %H:%M:%S", localtime(&t));
|
||||
n += snprintf(buf+n, buf_len-n,
|
||||
".%.3ld [%s] ",
|
||||
(long)now.tv_usec / 1000, sev_to_string(severity));
|
||||
if(n > buf_len)
|
||||
n = buf_len-1; /* the *nprintf funcs return how many bytes they
|
||||
* _would_ print, if the output is truncated.
|
||||
* Subtract one because the count doesn't include the \0 */
|
||||
|
||||
if (funcname) {
|
||||
n += snprintf(buf+n, buf_len-n, "%s(): ", funcname);
|
||||
if(n > buf_len)
|
||||
n = buf_len-1;
|
||||
}
|
||||
|
||||
n += vsnprintf(buf+n,buf_len-n,format,ap);
|
||||
if(n > buf_len)
|
||||
n = buf_len-1;
|
||||
buf[n]='\n';
|
||||
buf[n+1]='\0';
|
||||
}
|
||||
|
||||
static void
|
||||
logv(int severity, const char *funcname, const char *format, va_list ap)
|
||||
{
|
||||
char buf[10024];
|
||||
int formatted = 0;
|
||||
logfile_t *lf;
|
||||
|
||||
assert(format);
|
||||
for (lf = logfiles; lf; lf = lf->next) {
|
||||
if (severity < lf->loglevel || severity > lf->max_loglevel)
|
||||
continue;
|
||||
if (!lf->file)
|
||||
continue;
|
||||
|
||||
if (!formatted) {
|
||||
format_msg(buf, 10024, severity, funcname, format, ap);
|
||||
formatted = 1;
|
||||
}
|
||||
fputs(buf, lf->file);
|
||||
fflush(lf->file);
|
||||
/* XXX check for EOF */
|
||||
}
|
||||
}
|
||||
|
||||
/* Outputs a message to stdout */
|
||||
void _log(int severity, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,format);
|
||||
logv(severity, NULL, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _log_fn(int severity, const char *fn, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,format);
|
||||
logv(severity, fn, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void close_logs()
|
||||
{
|
||||
logfile_t *victim;
|
||||
while(logfiles) {
|
||||
victim = logfiles;
|
||||
logfiles = logfiles->next;
|
||||
if (victim->needs_close)
|
||||
fclose(victim->file);
|
||||
free(victim);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_logs()
|
||||
{
|
||||
logfile_t *lf;
|
||||
for (lf = logfiles; lf; lf = lf->next) {
|
||||
if (lf->needs_close) {
|
||||
fclose(lf->file);
|
||||
lf->file = fopen(lf->filename, "a");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_stream_log(int loglevel, const char *name, FILE *stream)
|
||||
{
|
||||
logfile_t *lf;
|
||||
lf = tor_malloc(sizeof(logfile_t));
|
||||
lf->filename = name;
|
||||
lf->needs_close = 0;
|
||||
lf->loglevel = loglevel;
|
||||
lf->max_loglevel = LOG_ERR;
|
||||
lf->file = stream;
|
||||
lf->next = logfiles;
|
||||
logfiles = lf;
|
||||
}
|
||||
|
||||
/*
|
||||
* If opening the logfile fails, -1 is returned and
|
||||
* errno is set appropriately (by fopen)
|
||||
*/
|
||||
int add_file_log(int loglevel, const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
f = fopen(filename, "a");
|
||||
if (!f) return -1;
|
||||
add_stream_log(loglevel, filename, f);
|
||||
logfiles->needs_close = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef __LOG_H
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <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 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
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine. */
|
||||
/* 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:
|
||||
*/
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,573 @@
|
||||
/* Copyright 2003 Roger Dingledine. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
/* TLS wrappers for The Onion Router. (Unlike other tor functions, these
|
||||
* are prefixed with tor_ in order to avoid conflicting with OpenSSL
|
||||
* functions and variables.)
|
||||
*/
|
||||
|
||||
#include "./crypto.h"
|
||||
#include "./tortls.h"
|
||||
#include "./util.h"
|
||||
#include "./log.h"
|
||||
|
||||
/* Copied from or.h */
|
||||
#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
#include <assert.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/tls1.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bio.h>
|
||||
|
||||
/* How long do certificates live? (sec) */
|
||||
#define CERT_LIFETIME (365*24*60*60)
|
||||
/* How much clock skew do we tolerate when checking certificates? (sec) */
|
||||
#define CERT_ALLOW_SKEW (30*60)
|
||||
|
||||
struct tor_tls_context_st {
|
||||
SSL_CTX *ctx;
|
||||
};
|
||||
|
||||
struct tor_tls_st {
|
||||
SSL *ssl;
|
||||
int socket;
|
||||
enum {
|
||||
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
|
||||
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED
|
||||
} state;
|
||||
int isServer;
|
||||
int wantwrite_n; /* 0 normally, >0 if we returned wantwrite last time */
|
||||
};
|
||||
|
||||
static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa,
|
||||
const char *nickname);
|
||||
|
||||
/* global tls context, keep it here because nobody else needs to touch it */
|
||||
static tor_tls_context *global_tls_context = NULL;
|
||||
static int tls_library_is_initialized = 0;
|
||||
|
||||
#define _TOR_TLS_SYSCALL -6
|
||||
#define _TOR_TLS_ZERORETURN -5
|
||||
|
||||
|
||||
/* These functions are declared in crypto.c but not exported. */
|
||||
EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env);
|
||||
crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa);
|
||||
|
||||
static void
|
||||
tls_log_errors(int severity, const char *doing)
|
||||
{
|
||||
int err;
|
||||
const char *msg, *lib, *func;
|
||||
while ((err = ERR_get_error()) != 0) {
|
||||
msg = (const char*)ERR_reason_error_string(err);
|
||||
lib = (const char*)ERR_lib_error_string(err);
|
||||
func = (const char*)ERR_func_error_string(err);
|
||||
if (!msg) msg = "(null)";
|
||||
if (doing) {
|
||||
log(severity, "TLS error while %s: %s (in %s:%s)", doing, msg, lib,func);
|
||||
} else {
|
||||
log(severity, "TLS error: %s (in %s:%s)", msg, lib, func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CATCH_SYSCALL 1
|
||||
#define CATCH_ZERO 2
|
||||
|
||||
static int
|
||||
tor_tls_get_error(tor_tls *tls, int r, int extra,
|
||||
const char *doing, int severity)
|
||||
{
|
||||
int err = SSL_get_error(tls->ssl, r);
|
||||
switch (err) {
|
||||
case SSL_ERROR_NONE:
|
||||
return TOR_TLS_DONE;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return TOR_TLS_WANTREAD;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return TOR_TLS_WANTWRITE;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
if (extra&CATCH_SYSCALL)
|
||||
return _TOR_TLS_SYSCALL;
|
||||
log(severity, "TLS error: <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;
|
||||
|
||||
tor_tls_init();
|
||||
|
||||
start_time = time(NULL);
|
||||
|
||||
assert(rsa && nickname);
|
||||
if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa)))
|
||||
return NULL;
|
||||
if (!(x509 = X509_new()))
|
||||
goto error;
|
||||
if (!(X509_set_version(x509, 2)))
|
||||
goto error;
|
||||
if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time)))
|
||||
goto error;
|
||||
|
||||
if (!(name = X509_NAME_new()))
|
||||
goto error;
|
||||
if ((nid = OBJ_txt2nid("organizationName")) == NID_undef) goto error;
|
||||
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
|
||||
"TOR", -1, -1, 0))) goto error;
|
||||
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
|
||||
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
|
||||
(char*)nickname, -1, -1, 0))) goto error;
|
||||
|
||||
if (!(X509_set_issuer_name(x509, name)))
|
||||
goto error;
|
||||
if (!(X509_set_subject_name(x509, name)))
|
||||
goto error;
|
||||
if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
|
||||
goto error;
|
||||
end_time = start_time + CERT_LIFETIME;
|
||||
if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
|
||||
goto error;
|
||||
if (!X509_set_pubkey(x509, pkey))
|
||||
goto error;
|
||||
if (!X509_sign(x509, pkey, EVP_sha1()))
|
||||
goto error;
|
||||
|
||||
goto done;
|
||||
error:
|
||||
if (x509) {
|
||||
X509_free(x509);
|
||||
x509 = NULL;
|
||||
}
|
||||
done:
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (name)
|
||||
X509_NAME_free(name);
|
||||
return x509;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EVERYONE_HAS_AES
|
||||
/* Everybody is running OpenSSL 0.9.7 or later, so no backward compatibiliy
|
||||
* is needed. */
|
||||
#define CIPHER_LIST TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
|
||||
#elif defined(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
|
||||
/* Some people are running OpenSSL before 0.9.7, but we aren't.
|
||||
* We can support AES and 3DES.
|
||||
*/
|
||||
#define CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
|
||||
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
|
||||
#else
|
||||
/* We're running OpenSSL before 0.9.7. We only support 3DES. */
|
||||
#define CIPHER_LIST SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
|
||||
#endif
|
||||
|
||||
/* Create a new TLS context. If we are going to be using it as a
|
||||
* server, it must have isServer set to true, certfile set to a
|
||||
* filename for a certificate file, and RSA set to the private key
|
||||
* used for that certificate. Return -1 if failure, else 0.
|
||||
*/
|
||||
int
|
||||
tor_tls_context_new(crypto_pk_env_t *rsa,
|
||||
int isServer, const char *nickname)
|
||||
{
|
||||
crypto_dh_env_t *dh = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
tor_tls_context *result;
|
||||
X509 *cert = NULL;
|
||||
|
||||
tor_tls_init();
|
||||
|
||||
if (rsa) {
|
||||
cert = tor_tls_create_certificate(rsa, nickname);
|
||||
if (!cert) {
|
||||
log(LOG_WARN, "Error creating certificate");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
result = tor_malloc(sizeof(tor_tls_context));
|
||||
result->ctx = NULL;
|
||||
#ifdef EVERYONE_HAS_AES
|
||||
/* Tell OpenSSL to only use TLS1 */
|
||||
if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
|
||||
goto error;
|
||||
#else
|
||||
/* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */
|
||||
if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
|
||||
goto error;
|
||||
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
|
||||
#endif
|
||||
if (!SSL_CTX_set_cipher_list(result->ctx, CIPHER_LIST))
|
||||
goto error;
|
||||
if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
|
||||
goto error;
|
||||
SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
|
||||
if (rsa) {
|
||||
if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa)))
|
||||
goto error;
|
||||
if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
|
||||
goto error;
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
if (cert) {
|
||||
if (!SSL_CTX_check_private_key(result->ctx))
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
dh = crypto_dh_new();
|
||||
SSL_CTX_set_tmp_dh(result->ctx, dh->dh);
|
||||
crypto_dh_free(dh);
|
||||
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
|
||||
always_accept_verify_cb);
|
||||
/* let us realloc bufs that we're writing from */
|
||||
SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
||||
/* Free the old context if one exists. */
|
||||
if (global_tls_context) {
|
||||
/* This is safe even if there are open connections: OpenSSL does
|
||||
* reference counting with SSL and SSL_CTX objects. */
|
||||
SSL_CTX_free(global_tls_context->ctx);
|
||||
free(global_tls_context);
|
||||
}
|
||||
global_tls_context = result;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (dh)
|
||||
crypto_dh_free(dh);
|
||||
if (result && result->ctx)
|
||||
SSL_CTX_free(result->ctx);
|
||||
if (result)
|
||||
free(result);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create a new TLS object from a TLS context, a filedescriptor, and
|
||||
* a flag to determine whether it is functioning as a server.
|
||||
*/
|
||||
tor_tls *
|
||||
tor_tls_new(int sock, int isServer)
|
||||
{
|
||||
tor_tls *result = tor_malloc(sizeof(tor_tls));
|
||||
assert(global_tls_context); /* make sure somebody made it first */
|
||||
if (!(result->ssl = SSL_new(global_tls_context->ctx)))
|
||||
return NULL;
|
||||
result->socket = sock;
|
||||
SSL_set_fd(result->ssl, sock);
|
||||
result->state = TOR_TLS_ST_HANDSHAKE;
|
||||
result->isServer = isServer;
|
||||
result->wantwrite_n = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Release resources associated with a TLS object. Does not close the
|
||||
* underlying file descriptor.
|
||||
*/
|
||||
void
|
||||
tor_tls_free(tor_tls *tls)
|
||||
{
|
||||
SSL_free(tls->ssl);
|
||||
free(tls);
|
||||
}
|
||||
|
||||
/* Underlying function for TLS reading. Reads up to 'len' characters
|
||||
* from 'tls' into 'cp'. On success, returns the number of characters
|
||||
* read. On failure, returns TOR_TLS_ERROR, TOR_TLS_CLOSE,
|
||||
* TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
|
||||
*/
|
||||
int
|
||||
tor_tls_read(tor_tls *tls, char *cp, int len)
|
||||
{
|
||||
int r, err;
|
||||
assert(tls && tls->ssl);
|
||||
assert(tls->state == TOR_TLS_ST_OPEN);
|
||||
r = SSL_read(tls->ssl, cp, len);
|
||||
if (r > 0)
|
||||
return r;
|
||||
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_INFO);
|
||||
if (err == _TOR_TLS_ZERORETURN) {
|
||||
tls->state = TOR_TLS_ST_CLOSED;
|
||||
return TOR_TLS_CLOSE;
|
||||
} else {
|
||||
assert(err != TOR_TLS_DONE);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Underlying function for TLS writing. Write up to 'n' characters
|
||||
* from 'cp' onto 'tls'. On success, returns the number of characters
|
||||
* written. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
|
||||
* or TOR_TLS_WANTWRITE.
|
||||
*/
|
||||
int
|
||||
tor_tls_write(tor_tls *tls, char *cp, int n)
|
||||
{
|
||||
int r, err;
|
||||
assert(tls && tls->ssl);
|
||||
assert(tls->state == TOR_TLS_ST_OPEN);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if(tls->wantwrite_n) {
|
||||
/* if WANTWRITE last time, we must use the _same_ n as before */
|
||||
assert(n >= tls->wantwrite_n);
|
||||
log_fn(LOG_INFO,"resuming pending-write, (%d to flush, reusing %d)",
|
||||
n, tls->wantwrite_n);
|
||||
n = tls->wantwrite_n;
|
||||
tls->wantwrite_n = 0;
|
||||
}
|
||||
r = SSL_write(tls->ssl, cp, n);
|
||||
err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO);
|
||||
if (err == TOR_TLS_DONE) {
|
||||
return r;
|
||||
}
|
||||
if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) {
|
||||
log_fn(LOG_INFO,"wantwrite or wantread. remembering the number %d.",n);
|
||||
tls->wantwrite_n = n;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Perform initial handshake on 'tls'. When finished, returns
|
||||
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
|
||||
* or TOR_TLS_WANTWRITE.
|
||||
*/
|
||||
int
|
||||
tor_tls_handshake(tor_tls *tls)
|
||||
{
|
||||
int r;
|
||||
assert(tls && tls->ssl);
|
||||
assert(tls->state == TOR_TLS_ST_HANDSHAKE);
|
||||
if (tls->isServer) {
|
||||
r = SSL_accept(tls->ssl);
|
||||
} else {
|
||||
r = SSL_connect(tls->ssl);
|
||||
}
|
||||
r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO);
|
||||
if (r == TOR_TLS_DONE) {
|
||||
tls->state = TOR_TLS_ST_OPEN;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Shut down an open tls connection 'tls'. When finished, returns
|
||||
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
|
||||
* or TOR_TLS_WANTWRITE.
|
||||
*/
|
||||
int
|
||||
tor_tls_shutdown(tor_tls *tls)
|
||||
{
|
||||
int r, err;
|
||||
char buf[128];
|
||||
assert(tls && tls->ssl);
|
||||
|
||||
while (1) {
|
||||
if (tls->state == TOR_TLS_ST_SENTCLOSE) {
|
||||
/* If we've already called shutdown once to send a close message,
|
||||
* we read until the other side has closed too.
|
||||
*/
|
||||
do {
|
||||
r = SSL_read(tls->ssl, buf, 128);
|
||||
} while (r>0);
|
||||
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down",
|
||||
LOG_INFO);
|
||||
if (err == _TOR_TLS_ZERORETURN) {
|
||||
tls->state = TOR_TLS_ST_GOTCLOSE;
|
||||
/* fall through... */
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
r = SSL_shutdown(tls->ssl);
|
||||
if (r == 1) {
|
||||
/* If shutdown returns 1, the connection is entirely closed. */
|
||||
tls->state = TOR_TLS_ST_CLOSED;
|
||||
return TOR_TLS_DONE;
|
||||
}
|
||||
err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down",
|
||||
LOG_INFO);
|
||||
if (err == _TOR_TLS_SYSCALL) {
|
||||
/* The underlying TCP connection closed while we were shutting down. */
|
||||
tls->state = TOR_TLS_ST_CLOSED;
|
||||
return TOR_TLS_DONE;
|
||||
} else if (err == _TOR_TLS_ZERORETURN) {
|
||||
/* The TLS connection says that it sent a shutdown record, but
|
||||
* isn't done shutting down yet. Make sure that this hasn't
|
||||
* happened before, then go back to the start of the function
|
||||
* and try to read.
|
||||
*/
|
||||
if (tls->state == TOR_TLS_ST_GOTCLOSE ||
|
||||
tls->state == TOR_TLS_ST_SENTCLOSE) {
|
||||
log(LOG_WARN,
|
||||
"TLS returned \"half-closed\" value while already half-closed");
|
||||
return TOR_TLS_ERROR;
|
||||
}
|
||||
tls->state = TOR_TLS_ST_SENTCLOSE;
|
||||
/* fall through ... */
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
} /* end loop */
|
||||
}
|
||||
|
||||
/* Return true iff this TLS connection is authenticated.
|
||||
*/
|
||||
int
|
||||
tor_tls_peer_has_cert(tor_tls *tls)
|
||||
{
|
||||
X509 *cert;
|
||||
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
|
||||
return 0;
|
||||
X509_free(cert);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_get_peer_cert_nickname(tor_tls *tls, char *buf, int buflen)
|
||||
{
|
||||
X509 *cert = NULL;
|
||||
X509_NAME *name = NULL;
|
||||
int nid;
|
||||
int lenout;
|
||||
|
||||
if (!(cert = SSL_get_peer_certificate(tls->ssl))) {
|
||||
log_fn(LOG_WARN, "Peer has no certificate");
|
||||
goto error;
|
||||
}
|
||||
if (!(name = X509_get_subject_name(cert))) {
|
||||
log_fn(LOG_WARN, "Peer certificate has no subject name");
|
||||
goto error;
|
||||
}
|
||||
if ((nid = OBJ_txt2nid("commonName")) == NID_undef)
|
||||
goto error;
|
||||
|
||||
lenout = X509_NAME_get_text_by_NID(name, nid, buf, buflen);
|
||||
if (lenout == -1)
|
||||
goto error;
|
||||
if (strspn(buf, LEGAL_NICKNAME_CHARACTERS) != lenout) {
|
||||
log_fn(LOG_WARN, "Peer certificate nickname has illegal characters.");
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
error:
|
||||
if (cert)
|
||||
X509_free(cert);
|
||||
if (name)
|
||||
X509_NAME_free(name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If the provided tls connection is authenticated and has a
|
||||
* certificate that is currently valid and is correctly self-signed,
|
||||
* return its public key. Otherwise return NULL.
|
||||
*/
|
||||
crypto_pk_env_t *
|
||||
tor_tls_verify(tor_tls *tls)
|
||||
{
|
||||
X509 *cert = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
RSA *rsa = NULL;
|
||||
time_t now, t;
|
||||
crypto_pk_env_t *r = NULL;
|
||||
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
|
||||
return NULL;
|
||||
|
||||
now = time(NULL);
|
||||
t = now + CERT_ALLOW_SKEW;
|
||||
if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
|
||||
log_fn(LOG_WARN,"Certificate becomes valid in the future: possible clock skew.");
|
||||
goto done;
|
||||
}
|
||||
t = now - CERT_ALLOW_SKEW;
|
||||
if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
|
||||
log_fn(LOG_WARN,"Certificate already expired; possible clock skew.");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Get the public key. */
|
||||
if (!(pkey = X509_get_pubkey(cert))) {
|
||||
log_fn(LOG_WARN,"X509_get_pubkey returned null");
|
||||
goto done;
|
||||
}
|
||||
if (X509_verify(cert, pkey) <= 0) {
|
||||
log_fn(LOG_WARN,"X509_verify on cert and pkey returned <= 0");
|
||||
goto done;
|
||||
}
|
||||
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
if (!rsa) {
|
||||
log_fn(LOG_WARN,"EVP_PKEY_get1_RSA(pkey) returned null");
|
||||
goto done;
|
||||
}
|
||||
|
||||
r = _crypto_new_pk_env_rsa(rsa);
|
||||
rsa = NULL;
|
||||
|
||||
done:
|
||||
if (cert)
|
||||
X509_free(cert);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (rsa)
|
||||
RSA_free(rsa);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_get_pending_bytes(tor_tls *tls)
|
||||
{
|
||||
assert(tls);
|
||||
return SSL_pending(tls->ssl);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,854 @@
|
||||
/* 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_malloc_zero(size_t size) {
|
||||
void *result = tor_malloc(size);
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
void *tor_realloc(void *ptr, size_t size) {
|
||||
void *result;
|
||||
|
||||
result = realloc(ptr, size);
|
||||
if (!result) {
|
||||
log_fn(LOG_ERR, "Out of memory. Dying.");
|
||||
exit(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *tor_strdup(const char *s) {
|
||||
char *dup;
|
||||
assert(s);
|
||||
|
||||
dup = strdup(s);
|
||||
if(!dup) {
|
||||
log_fn(LOG_ERR,"Out of memory. Dying.");
|
||||
exit(1);
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
|
||||
char *tor_strndup(const char *s, size_t n) {
|
||||
char *dup;
|
||||
assert(s);
|
||||
dup = tor_malloc(n+1);
|
||||
strncpy(dup, s, n);
|
||||
dup[n] = 0;
|
||||
return dup;
|
||||
}
|
||||
|
||||
/*
|
||||
* A simple smartlist interface to make an unordered list of acceptable
|
||||
* nodes and then choose a random one.
|
||||
* smartlist_create() mallocs the list, _free() frees the list,
|
||||
* _add() adds an element, _remove() removes an element if it's there,
|
||||
* _choose() returns a random element.
|
||||
*/
|
||||
|
||||
smartlist_t *smartlist_create(int max_elements) {
|
||||
smartlist_t *sl = tor_malloc(sizeof(smartlist_t));
|
||||
sl->list = tor_malloc(sizeof(void *) * max_elements);
|
||||
sl->num_used = 0;
|
||||
sl->max = max_elements;
|
||||
return sl;
|
||||
}
|
||||
|
||||
void smartlist_free(smartlist_t *sl) {
|
||||
free(sl->list);
|
||||
free(sl);
|
||||
}
|
||||
|
||||
/* add element to the list, but only if there's room */
|
||||
void smartlist_add(smartlist_t *sl, void *element) {
|
||||
if(sl->num_used < sl->max)
|
||||
sl->list[sl->num_used++] = element;
|
||||
else
|
||||
log_fn(LOG_WARN,"We've already got %d elements, discarding.",sl->max);
|
||||
}
|
||||
|
||||
void smartlist_remove(smartlist_t *sl, void *element) {
|
||||
int i;
|
||||
if(element == NULL)
|
||||
return;
|
||||
for(i=0; i < sl->num_used; i++)
|
||||
if(sl->list[i] == element) {
|
||||
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
|
||||
i--; /* so we process the new i'th element */
|
||||
}
|
||||
}
|
||||
|
||||
int smartlist_isin(smartlist_t *sl, void *element) {
|
||||
int i;
|
||||
for(i=0; i < sl->num_used; i++)
|
||||
if(sl->list[i] == element)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smartlist_overlap(smartlist_t *sl1, smartlist_t *sl2) {
|
||||
int i;
|
||||
for(i=0; i < sl2->num_used; i++)
|
||||
if(smartlist_isin(sl1, sl2->list[i]))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove elements of sl1 that aren't in sl2 */
|
||||
void smartlist_intersect(smartlist_t *sl1, smartlist_t *sl2) {
|
||||
int i;
|
||||
for(i=0; i < sl1->num_used; i++)
|
||||
if(!smartlist_isin(sl2, sl1->list[i])) {
|
||||
sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */
|
||||
i--; /* so we process the new i'th element */
|
||||
}
|
||||
}
|
||||
|
||||
/* remove all elements of sl2 from sl1 */
|
||||
void smartlist_subtract(smartlist_t *sl1, smartlist_t *sl2) {
|
||||
int i;
|
||||
for(i=0; i < sl2->num_used; i++)
|
||||
smartlist_remove(sl1, sl2->list[i]);
|
||||
}
|
||||
|
||||
void *smartlist_choose(smartlist_t *sl) {
|
||||
if(sl->num_used)
|
||||
return sl->list[crypto_pseudo_rand_int(sl->num_used)];
|
||||
return NULL; /* no elements to choose from */
|
||||
}
|
||||
|
||||
/*
|
||||
* String manipulation
|
||||
*/
|
||||
|
||||
/* return the first char of s that is not whitespace and not a comment */
|
||||
const char *eat_whitespace(const char *s) {
|
||||
assert(s);
|
||||
|
||||
while(isspace(*s) || *s == '#') {
|
||||
while(isspace(*s))
|
||||
s++;
|
||||
if(*s == '#') { /* read to a \n or \0 */
|
||||
while(*s && *s != '\n')
|
||||
s++;
|
||||
if(!*s)
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const char *eat_whitespace_no_nl(const char *s) {
|
||||
while(*s == ' ' || *s == '\t')
|
||||
++s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* return the first char of s that is whitespace or '#' or '\0 */
|
||||
const char *find_whitespace(const char *s) {
|
||||
assert(s);
|
||||
|
||||
while(*s && !isspace(*s) && *s != '#')
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Time
|
||||
*/
|
||||
|
||||
void tor_gettimeofday(struct timeval *timeval) {
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
if (gettimeofday(timeval, NULL)) {
|
||||
log_fn(LOG_ERR, "gettimeofday failed.");
|
||||
/* If gettimeofday dies, we have either given a bad timezone (we didn't),
|
||||
or segfaulted.*/
|
||||
exit(1);
|
||||
}
|
||||
#elif defined(HAVE_FTIME)
|
||||
ftime(timeval);
|
||||
#else
|
||||
#error "No way to get time."
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
long
|
||||
tv_udiff(struct timeval *start, struct timeval *end)
|
||||
{
|
||||
long udiff;
|
||||
long secdiff = end->tv_sec - start->tv_sec;
|
||||
|
||||
if (secdiff+1 > LONG_MAX/1000000) {
|
||||
log_fn(LOG_WARN, "comparing times too far apart.");
|
||||
return LONG_MAX;
|
||||
}
|
||||
|
||||
udiff = secdiff*1000000L + (end->tv_usec - start->tv_usec);
|
||||
if(udiff < 0) {
|
||||
log_fn(LOG_INFO, "start (%ld.%ld) is after end (%ld.%ld). Returning 0.",
|
||||
(long)start->tv_sec, (long)start->tv_usec, (long)end->tv_sec, (long)end->tv_usec);
|
||||
return 0;
|
||||
}
|
||||
return udiff;
|
||||
}
|
||||
|
||||
int tv_cmp(struct timeval *a, struct timeval *b) {
|
||||
if (a->tv_sec > b->tv_sec)
|
||||
return 1;
|
||||
if (a->tv_sec < b->tv_sec)
|
||||
return -1;
|
||||
if (a->tv_usec > b->tv_usec)
|
||||
return 1;
|
||||
if (a->tv_usec < b->tv_usec)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tv_add(struct timeval *a, struct timeval *b) {
|
||||
a->tv_usec += b->tv_usec;
|
||||
a->tv_sec += b->tv_sec + (a->tv_usec / 1000000);
|
||||
a->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
void tv_addms(struct timeval *a, long ms) {
|
||||
a->tv_usec += (ms * 1000) % 1000000;
|
||||
a->tv_sec += ((ms * 1000) / 1000000) + (a->tv_usec / 1000000);
|
||||
a->tv_usec %= 1000000;
|
||||
}
|
||||
|
||||
|
||||
#define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400)))
|
||||
static int n_leapdays(int y1, int y2) {
|
||||
--y1;
|
||||
--y2;
|
||||
return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
|
||||
}
|
||||
static const int days_per_month[] =
|
||||
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
time_t tor_timegm (struct tm *tm) {
|
||||
/* This is a pretty ironclad timegm implementation, snarfed from Python2.2.
|
||||
* It's way more brute-force than fiddling with tzset().
|
||||
*/
|
||||
time_t ret;
|
||||
unsigned long year, days, hours, minutes;
|
||||
int i;
|
||||
year = tm->tm_year + 1900;
|
||||
assert(year >= 1970);
|
||||
assert(tm->tm_mon >= 0 && tm->tm_mon <= 11);
|
||||
days = 365 * (year-1970) + n_leapdays(1970,year);
|
||||
for (i = 0; i < tm->tm_mon; ++i)
|
||||
days += days_per_month[i];
|
||||
if (tm->tm_mon > 1 && IS_LEAPYEAR(year))
|
||||
++days;
|
||||
days += tm->tm_mday - 1;
|
||||
hours = days*24 + tm->tm_hour;
|
||||
|
||||
minutes = hours*60 + tm->tm_min;
|
||||
ret = minutes*60 + tm->tm_sec;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Low-level I/O.
|
||||
*/
|
||||
|
||||
/* a wrapper for write(2) that makes sure to write all count bytes.
|
||||
* Only use if fd is a blocking fd. */
|
||||
int write_all(int fd, const char *buf, size_t count) {
|
||||
int written = 0;
|
||||
int result;
|
||||
|
||||
while(written != count) {
|
||||
result = write(fd, buf+written, count-written);
|
||||
if(result<0)
|
||||
return -1;
|
||||
written += result;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* a wrapper for read(2) that makes sure to read all count bytes.
|
||||
* Only use if fd is a blocking fd. */
|
||||
int read_all(int fd, char *buf, size_t count) {
|
||||
int numread = 0;
|
||||
int result;
|
||||
|
||||
while(numread != count) {
|
||||
result = read(fd, buf+numread, count-numread);
|
||||
if(result<=0)
|
||||
return -1;
|
||||
numread += result;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void set_socket_nonblocking(int socket)
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
/* Yes means no and no means yes. Do you not want to be nonblocking? */
|
||||
int nonblocking = 0;
|
||||
ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking);
|
||||
#else
|
||||
fcntl(socket, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Process control
|
||||
*/
|
||||
|
||||
/* Minimalist interface to run a void function in the background. On
|
||||
* unix calls fork, on win32 calls beginthread. Returns -1 on failure.
|
||||
* func should not return, but rather should call spawn_exit.
|
||||
*/
|
||||
int spawn_func(int (*func)(void *), void *data)
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
int rv;
|
||||
rv = _beginthread(func, 0, data);
|
||||
if (rv == (unsigned long) -1)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
if (pid<0)
|
||||
return -1;
|
||||
if (pid==0) {
|
||||
/* Child */
|
||||
func(data);
|
||||
assert(0); /* Should never reach here. */
|
||||
return 0; /* suppress "control-reaches-end-of-non-void" warning. */
|
||||
} else {
|
||||
/* Parent */
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void spawn_exit()
|
||||
{
|
||||
#ifdef MS_WINDOWS
|
||||
_endthread();
|
||||
#else
|
||||
exit(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Windows compatibility.
|
||||
*/
|
||||
int
|
||||
tor_socketpair(int family, int type, int protocol, int fd[2])
|
||||
{
|
||||
#ifdef HAVE_SOCKETPAIR_XXXX
|
||||
/* For testing purposes, we never fall back to real socketpairs. */
|
||||
return socketpair(family, type, protocol, fd);
|
||||
#else
|
||||
int listener = -1;
|
||||
int connector = -1;
|
||||
int acceptor = -1;
|
||||
struct sockaddr_in listen_addr;
|
||||
struct sockaddr_in connect_addr;
|
||||
int size;
|
||||
|
||||
if (protocol
|
||||
#ifdef AF_UNIX
|
||||
|| family != AF_UNIX
|
||||
#endif
|
||||
) {
|
||||
#ifdef MS_WINDOWS
|
||||
errno = WSAEAFNOSUPPORT;
|
||||
#else
|
||||
errno = EAFNOSUPPORT;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if (!fd) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
listener = socket(AF_INET, type, 0);
|
||||
if (listener == -1)
|
||||
return -1;
|
||||
memset (&listen_addr, 0, sizeof (listen_addr));
|
||||
listen_addr.sin_family = AF_INET;
|
||||
listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
||||
listen_addr.sin_port = 0; /* kernel choses port. */
|
||||
if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
|
||||
== -1)
|
||||
goto tidy_up_and_fail;
|
||||
if (listen(listener, 1) == -1)
|
||||
goto tidy_up_and_fail;
|
||||
|
||||
connector = socket(AF_INET, type, 0);
|
||||
if (connector == -1)
|
||||
goto tidy_up_and_fail;
|
||||
/* We want to find out the port number to connect to. */
|
||||
size = sizeof (connect_addr);
|
||||
if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
|
||||
goto tidy_up_and_fail;
|
||||
if (size != sizeof (connect_addr))
|
||||
goto abort_tidy_up_and_fail;
|
||||
if (connect(connector, (struct sockaddr *) &connect_addr,
|
||||
sizeof (connect_addr)) == -1)
|
||||
goto tidy_up_and_fail;
|
||||
|
||||
size = sizeof (listen_addr);
|
||||
acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
|
||||
if (acceptor == -1)
|
||||
goto tidy_up_and_fail;
|
||||
if (size != sizeof(listen_addr))
|
||||
goto abort_tidy_up_and_fail;
|
||||
close(listener);
|
||||
/* Now check we are talking to ourself by matching port and host on the
|
||||
two sockets. */
|
||||
if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
|
||||
goto tidy_up_and_fail;
|
||||
if (size != sizeof (connect_addr)
|
||||
|| listen_addr.sin_family != connect_addr.sin_family
|
||||
|| listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
|
||||
|| listen_addr.sin_port != connect_addr.sin_port) {
|
||||
goto abort_tidy_up_and_fail;
|
||||
}
|
||||
fd[0] = connector;
|
||||
fd[1] = acceptor;
|
||||
return 0;
|
||||
|
||||
abort_tidy_up_and_fail:
|
||||
#ifdef MS_WINDOWS
|
||||
errno = WSAECONNABORTED;
|
||||
#else
|
||||
errno = ECONNABORTED; /* I hope this is portable and appropriate. */
|
||||
#endif
|
||||
tidy_up_and_fail:
|
||||
{
|
||||
int save_errno = errno;
|
||||
if (listener != -1)
|
||||
close(listener);
|
||||
if (connector != -1)
|
||||
close(connector);
|
||||
if (acceptor != -1)
|
||||
close(acceptor);
|
||||
errno = save_errno;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
int correct_socket_errno(int s)
|
||||
{
|
||||
int optval, optvallen=sizeof(optval);
|
||||
assert(errno == WSAEWOULDBLOCK);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&optval, &optvallen))
|
||||
return errno;
|
||||
if (optval)
|
||||
return optval;
|
||||
return WSAEWOULDBLOCK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Filesystem operations.
|
||||
*/
|
||||
|
||||
/* Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't
|
||||
* exist, FN_FILE if it is a regular file, or FN_DIR if it's a
|
||||
* directory. */
|
||||
file_status_t file_status(const char *fname)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(fname, &st)) {
|
||||
if (errno == ENOENT) {
|
||||
return FN_NOENT;
|
||||
}
|
||||
return FN_ERROR;
|
||||
}
|
||||
if (st.st_mode & S_IFDIR)
|
||||
return FN_DIR;
|
||||
else if (st.st_mode & S_IFREG)
|
||||
return FN_FILE;
|
||||
else
|
||||
return FN_ERROR;
|
||||
}
|
||||
|
||||
/* Check whether dirname exists and is private. If yes returns
|
||||
0. Else returns -1. */
|
||||
int check_private_dir(const char *dirname, int create)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(dirname, &st)) {
|
||||
if (errno != ENOENT) {
|
||||
log(LOG_WARN, "Directory %s cannot be read: %s", dirname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!create) {
|
||||
log(LOG_WARN, "Directory %s does not exist.", dirname);
|
||||
return -1;
|
||||
}
|
||||
log(LOG_INFO, "Creating directory %s", dirname);
|
||||
if (mkdir(dirname, 0700)) {
|
||||
log(LOG_WARN, "Error creating directory %s: %s", dirname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!(st.st_mode & S_IFDIR)) {
|
||||
log(LOG_WARN, "%s is not a directory", dirname);
|
||||
return -1;
|
||||
}
|
||||
if (st.st_uid != getuid()) {
|
||||
log(LOG_WARN, "%s is not owned by this UID (%d)", dirname, (int)getuid());
|
||||
return -1;
|
||||
}
|
||||
if (st.st_mode & 0077) {
|
||||
log(LOG_WARN, "Fixing permissions on directory %s", dirname);
|
||||
if (chmod(dirname, 0700)) {
|
||||
log(LOG_WARN, "Could not chmod directory %s: %s", dirname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
write_str_to_file(const char *fname, const char *str)
|
||||
{
|
||||
char tempname[1024];
|
||||
int fd;
|
||||
FILE *file;
|
||||
if (strlen(fname) > 1000) {
|
||||
log(LOG_WARN, "Filename %s is too long.", fname);
|
||||
return -1;
|
||||
}
|
||||
strcpy(tempname,fname);
|
||||
strcat(tempname,".tmp");
|
||||
if ((fd = open(tempname, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
|
||||
log(LOG_WARN, "Couldn't open %s for writing: %s", tempname,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (!(file = fdopen(fd, "w"))) {
|
||||
log(LOG_WARN, "Couldn't fdopen %s for writing: %s", tempname,
|
||||
strerror(errno));
|
||||
close(fd); return -1;
|
||||
}
|
||||
if (fputs(str,file) == EOF) {
|
||||
log(LOG_WARN, "Error writing to %s: %s", tempname, strerror(errno));
|
||||
fclose(file); return -1;
|
||||
}
|
||||
fclose(file);
|
||||
if (rename(tempname, fname)) {
|
||||
log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *read_file_to_str(const char *filename) {
|
||||
int fd; /* router file */
|
||||
struct stat statbuf;
|
||||
char *string;
|
||||
|
||||
assert(filename);
|
||||
|
||||
if(strcspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) {
|
||||
log_fn(LOG_WARN,"Filename %s contains illegal characters.",filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(stat(filename, &statbuf) < 0) {
|
||||
log_fn(LOG_INFO,"Could not stat %s.",filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fd = open(filename,O_RDONLY,0);
|
||||
if (fd<0) {
|
||||
log_fn(LOG_WARN,"Could not open %s.",filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string = tor_malloc(statbuf.st_size+1);
|
||||
|
||||
if(read_all(fd,string,statbuf.st_size) != statbuf.st_size) {
|
||||
log_fn(LOG_WARN,"Couldn't read all %ld bytes of file '%s'.",
|
||||
(long)statbuf.st_size,filename);
|
||||
free(string);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
string[statbuf.st_size] = 0; /* null terminate it */
|
||||
return string;
|
||||
}
|
||||
|
||||
/* read lines from f (no more than maxlen-1 bytes each) until we
|
||||
* get one with a well-formed "key value".
|
||||
* point *key to the first word in line, point *value to the second.
|
||||
* Put a \0 at the end of key, remove everything at the end of value
|
||||
* that is whitespace or comment.
|
||||
* Return 1 if success, 0 if no more lines, -1 if error.
|
||||
*/
|
||||
int parse_line_from_file(char *line, int maxlen, FILE *f, char **key_out, char **value_out) {
|
||||
char *s, *key, *end, *value;
|
||||
|
||||
try_next_line:
|
||||
if(!fgets(line, maxlen, f)) {
|
||||
if(feof(f))
|
||||
return 0;
|
||||
return -1; /* real error */
|
||||
}
|
||||
|
||||
if((s = strchr(line,'#'))) /* strip comments */
|
||||
*s = 0; /* stop the line there */
|
||||
|
||||
/* remove end whitespace */
|
||||
s = strchr(line, 0); /* now we're at the null */
|
||||
do {
|
||||
*s = 0;
|
||||
s--;
|
||||
} while (s >= line && isspace(*s));
|
||||
|
||||
key = line;
|
||||
while(isspace(*key))
|
||||
key++;
|
||||
if(*key == 0)
|
||||
goto try_next_line; /* this line has nothing on it */
|
||||
end = key;
|
||||
while(*end && !isspace(*end))
|
||||
end++;
|
||||
value = end;
|
||||
while(*value && isspace(*value))
|
||||
value++;
|
||||
|
||||
if(!*end || !*value) { /* only a key on this line. no value. */
|
||||
*end = 0;
|
||||
log_fn(LOG_WARN,"Line has keyword '%s' but no value. Skipping.",key);
|
||||
goto try_next_line;
|
||||
}
|
||||
*end = 0; /* null it out */
|
||||
|
||||
log_fn(LOG_DEBUG,"got keyword '%s', value '%s'", key, value);
|
||||
*key_out = key, *value_out = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char uname_result[256];
|
||||
static int uname_result_is_set = 0;
|
||||
|
||||
const char *
|
||||
get_uname(void)
|
||||
{
|
||||
#ifdef HAVE_UNAME
|
||||
struct utsname u;
|
||||
#endif
|
||||
if (!uname_result_is_set) {
|
||||
#ifdef HAVE_UNAME
|
||||
if (!uname((&u))) {
|
||||
snprintf(uname_result, 255, "%s %s %s %s %s",
|
||||
u.sysname, u.nodename, u.release, u.version, u.machine);
|
||||
uname_result[255] = '\0';
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
strcpy(uname_result, "Unknown platform");
|
||||
}
|
||||
uname_result_is_set = 1;
|
||||
}
|
||||
return uname_result;
|
||||
}
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
/* Based on code contributed by christian grothoff */
|
||||
static int start_daemon_called = 0;
|
||||
static int finish_daemon_called = 0;
|
||||
static int daemon_filedes[2];
|
||||
void start_daemon(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (start_daemon_called)
|
||||
return;
|
||||
start_daemon_called = 1;
|
||||
|
||||
/* Don't hold the wrong FS mounted */
|
||||
if (chdir("/") < 0) {
|
||||
perror("chdir");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pipe(daemon_filedes);
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (pid) { /* Parent */
|
||||
int ok;
|
||||
char c;
|
||||
|
||||
close(daemon_filedes[1]); /* we only read */
|
||||
ok = -1;
|
||||
while (0 < read(daemon_filedes[0], &c, sizeof(char))) {
|
||||
if (c == '.')
|
||||
ok = 1;
|
||||
}
|
||||
fflush(stdout);
|
||||
if (ok == 1)
|
||||
exit(0);
|
||||
else
|
||||
exit(1); /* child reported error */
|
||||
} else { /* Child */
|
||||
close(daemon_filedes[0]); /* we only write */
|
||||
|
||||
pid = setsid(); /* Detach from controlling terminal */
|
||||
/*
|
||||
* Fork one more time, so the parent (the session group leader) can exit.
|
||||
* This means that we, as a non-session group leader, can never regain a
|
||||
* controlling terminal. This part is recommended by Stevens's
|
||||
* _Advanced Programming in the Unix Environment_.
|
||||
*/
|
||||
if (fork() != 0) {
|
||||
exit(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void finish_daemon(void)
|
||||
{
|
||||
int nullfd;
|
||||
char c = '.';
|
||||
if (finish_daemon_called)
|
||||
return;
|
||||
if (!start_daemon_called)
|
||||
start_daemon();
|
||||
finish_daemon_called = 1;
|
||||
|
||||
nullfd = open("/dev/null",
|
||||
O_CREAT | O_RDWR | O_APPEND);
|
||||
if (nullfd < 0) {
|
||||
perror("/dev/null");
|
||||
exit(1);
|
||||
}
|
||||
/* close fds linking to invoking terminal, but
|
||||
* close usual incoming fds, but redirect them somewhere
|
||||
* useful so the fds don't get reallocated elsewhere.
|
||||
*/
|
||||
if (dup2(nullfd,0) < 0 ||
|
||||
dup2(nullfd,1) < 0 ||
|
||||
dup2(nullfd,2) < 0) {
|
||||
perror("dup2"); /* Should never happen... */
|
||||
exit(1);
|
||||
}
|
||||
write(daemon_filedes[1], &c, sizeof(char)); /* signal success */
|
||||
close(daemon_filedes[1]);
|
||||
}
|
||||
#else
|
||||
/* defined(MS_WINDOWS) */
|
||||
void start_daemon(void) {}
|
||||
void finish_daemon(void) {}
|
||||
#endif
|
||||
|
||||
void write_pidfile(char *filename) {
|
||||
#ifndef MS_WINDOWS
|
||||
FILE *pidfile;
|
||||
|
||||
if ((pidfile = fopen(filename, "w")) == NULL) {
|
||||
log_fn(LOG_WARN, "unable to open %s for writing: %s", filename,
|
||||
strerror(errno));
|
||||
} else {
|
||||
fprintf(pidfile, "%d", getpid());
|
||||
fclose(pidfile);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int switch_id(char *user, char *group) {
|
||||
#ifndef MS_WINDOWS
|
||||
struct passwd *pw = NULL;
|
||||
struct group *gr = NULL;
|
||||
|
||||
if (user) {
|
||||
pw = getpwnam(user);
|
||||
if (pw == NULL) {
|
||||
log_fn(LOG_ERR,"User '%s' not found.", user);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* switch the group first, while we still have the privileges to do so */
|
||||
if (group) {
|
||||
gr = getgrnam(group);
|
||||
if (gr == NULL) {
|
||||
log_fn(LOG_ERR,"Group '%s' not found.", group);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setgid(gr->gr_gid) != 0) {
|
||||
log_fn(LOG_ERR,"Error setting GID: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
} else if (user) {
|
||||
if (setgid(pw->pw_gid) != 0) {
|
||||
log_fn(LOG_ERR,"Error setting GID: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* now that the group is switched, we can switch users and lose
|
||||
privileges */
|
||||
if (user) {
|
||||
if (setuid(pw->pw_uid) != 0) {
|
||||
log_fn(LOG_ERR,"Error setting UID: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
log_fn(LOG_ERR,
|
||||
"User or group specified, but switching users is not supported.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Copyright 2003 Roger Dingledine */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef __UTIL_H
|
||||
#define __UTIL_H
|
||||
|
||||
#include "../or/or.h"
|
||||
|
||||
#if _MSC_VER > 1300
|
||||
#include <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
|
||||
|
||||
void *tor_malloc(size_t size);
|
||||
void *tor_malloc_zero(size_t size);
|
||||
void *tor_realloc(void *ptr, size_t size);
|
||||
char *tor_strdup(const char *s);
|
||||
char *tor_strndup(const char *s, size_t n);
|
||||
#define tor_free(p) do {if(p) {free(p); (p)=NULL;}} while(0)
|
||||
|
||||
typedef struct {
|
||||
void **list;
|
||||
int num_used;
|
||||
int max;
|
||||
} smartlist_t;
|
||||
|
||||
smartlist_t *smartlist_create(int max_elements);
|
||||
void smartlist_free(smartlist_t *sl);
|
||||
void smartlist_add(smartlist_t *sl, void *element);
|
||||
void smartlist_remove(smartlist_t *sl, void *element);
|
||||
int smartlist_isin(smartlist_t *sl, void *element);
|
||||
int smartlist_overlap(smartlist_t *sl1, smartlist_t *sl2);
|
||||
void smartlist_intersect(smartlist_t *sl1, smartlist_t *sl2);
|
||||
void smartlist_subtract(smartlist_t *sl1, smartlist_t *sl2);
|
||||
void *smartlist_choose(smartlist_t *sl);
|
||||
|
||||
const char *eat_whitespace(const char *s);
|
||||
const char *eat_whitespace_no_nl(const char *s);
|
||||
const char *find_whitespace(const char *s);
|
||||
|
||||
void tor_gettimeofday(struct timeval *timeval);
|
||||
long tv_udiff(struct timeval *start, struct timeval *end);
|
||||
void tv_addms(struct timeval *a, long ms);
|
||||
void tv_add(struct timeval *a, struct timeval *b);
|
||||
int tv_cmp(struct timeval *a, struct timeval *b);
|
||||
time_t tor_timegm (struct tm *tm);
|
||||
|
||||
int write_all(int fd, const char *buf, size_t count);
|
||||
int read_all(int fd, char *buf, size_t count);
|
||||
|
||||
void set_socket_nonblocking(int socket);
|
||||
|
||||
typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR} file_status_t;
|
||||
|
||||
file_status_t file_status(const char *filename);
|
||||
int check_private_dir(const char *dirname, int create);
|
||||
int write_str_to_file(const char *fname, const char *str);
|
||||
char *read_file_to_str(const char *filename);
|
||||
int parse_line_from_file(char *line, int maxlen, FILE *f, char **key_out, char **value_out);
|
||||
|
||||
int spawn_func(int (*func)(void *), void *data);
|
||||
void spawn_exit();
|
||||
|
||||
int tor_socketpair(int family, int type, int protocol, int fd[2]);
|
||||
|
||||
const char *get_uname(void);
|
||||
|
||||
/* Start putting the process into daemon mode: fork and drop all resources
|
||||
* except standard fds. The parent process never returns, but stays around
|
||||
* until finish_daemon is called. (Note: it's safe to call this more
|
||||
* than once: calls after the first are ignored.)
|
||||
*/
|
||||
void start_daemon(void);
|
||||
/* Finish putting the process into daemon mode: drop standard fds, and tell
|
||||
* the parent process to exit. (Note: it's safe to call this more than once:
|
||||
* calls after the first are ignored. Calls start_daemon first if it hasn't
|
||||
* been called already.)
|
||||
*/
|
||||
void finish_daemon(void);
|
||||
|
||||
void write_pidfile(char *filename);
|
||||
int switch_id(char *user, char *group);
|
||||
|
||||
/* For stupid historical reasons, windows sockets have an independent set of
|
||||
* errnos which they use as the fancy strikes them.
|
||||
*/
|
||||
#ifdef MS_WINDOWS
|
||||
#define ERRNO_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK)
|
||||
#define ERRNO_EINPROGRESS(e) ((e) == WSAEINPROGRESS)
|
||||
#define ERRNO_CONN_EINPROGRESS(e) ((e) == WSAEINPROGRESS || (e) == WSAEINVAL)
|
||||
int correct_socket_errno(int s);
|
||||
#else
|
||||
#define ERRNO_EAGAIN(e) ((e) == EAGAIN)
|
||||
#define ERRNO_EINPROGRESS(e) ((e) == EINPROGRESS)
|
||||
#define ERRNO_CONN_EINPROGRESS(e) ((e) == EINPROGRESS)
|
||||
#define correct_socket_errno(s) (errno)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
sample-server-torrc
|
||||
torrc
|
||||
@@ -0,0 +1,5 @@
|
||||
confdir = $(sysconfdir)/tor
|
||||
|
||||
EXTRA_DIST = dirservers
|
||||
|
||||
conf_DATA = dirservers torrc
|
||||
@@ -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,12 @@
|
||||
|
||||
DataDirectory moria1
|
||||
Nickname moria1
|
||||
|
||||
ORPort 9001
|
||||
DirPort 9031
|
||||
|
||||
# List of routers
|
||||
RouterFile ../config/dirservers
|
||||
|
||||
ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:*
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
DataDirectory moria2
|
||||
Nickname moria2
|
||||
|
||||
ORPort 9002
|
||||
DirPort 9032
|
||||
|
||||
# List of routers
|
||||
RouterFile ../config/dirservers
|
||||
|
||||
ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:*
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
DataDirectory moria3
|
||||
Nickname moria3
|
||||
|
||||
ORPort 9003
|
||||
DirPort 9033
|
||||
|
||||
# List of routers
|
||||
RouterFile ../config/dirservers
|
||||
|
||||
ExitPolicy reject 0.0.0.0/8,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:*
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Configuration file for a typical tor user
|
||||
|
||||
# List of routers. Tor nodes start out knowing about the directory
|
||||
# servers, and from them they get a list of currently up nodes.
|
||||
RouterFile @CONFDIR@/dirservers
|
||||
|
||||
# Comment out if you don't want to allow applications to connect.
|
||||
SocksPort 9050
|
||||
SocksBindAddress 127.0.0.1 # accept connections only from localhost
|
||||
|
||||
##################### Below is just for servers #####################
|
||||
|
||||
## The directory for keeping all the keys/etc for this server
|
||||
#DataDirectory @LOCALSTATEDIR@/lib/tor
|
||||
|
||||
#Nickname moria # A unique handle for this server
|
||||
#Address moria.seul.org # The fqdn for this server
|
||||
|
||||
#ORPort 9001 # where to listen for cell-speaking connections
|
||||
#ORBindAddress 0.0.0.0 # accept connections from anywhere
|
||||
|
||||
## A comma-separated list of exit policies. They're considered in
|
||||
## order, first match wins.
|
||||
#ExitPolicy reject 0.0.0.0/8,reject 169.254.0.0/16,reject 127.0.0.0/8,reject 192.168.0.0/16,reject 10.0.0.0/8,reject 172.16.0.0/12,accept *:20-22,accept *:53,accept *:79-80,accept *:110,accept *:143,accept *:443,accept *:873,accept *:1024-65535,reject *:*
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
tor
|
||||
test
|
||||
.deps
|
||||
@@ -0,0 +1,23 @@
|
||||
TESTS = test
|
||||
|
||||
noinst_PROGRAMS = test
|
||||
|
||||
bin_PROGRAMS = tor
|
||||
|
||||
tor_SOURCES = buffers.c circuit.c command.c connection.c \
|
||||
connection_or.c config.c dirserv.c \
|
||||
onion.c router.c routerlist.c directory.c dns.c connection_edge.c \
|
||||
cpuworker.c main.c tor_main.c
|
||||
|
||||
tor_LDADD = ../common/libor.a
|
||||
|
||||
test_SOURCES = buffers.c circuit.c command.c connection.c \
|
||||
connection_or.c config.c dirserv.c \
|
||||
onion.c router.c routerlist.c directory.c dns.c connection_edge.c \
|
||||
cpuworker.c main.c test.c
|
||||
|
||||
test_LDADD = ../common/libor.a
|
||||
|
||||
noinst_HEADERS = or.h tree.h
|
||||
|
||||
|
||||
@@ -0,0 +1,550 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
/* buffers.c */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
struct buf_t {
|
||||
char *mem;
|
||||
size_t len;
|
||||
size_t datalen;
|
||||
};
|
||||
|
||||
/* Size, in bytes, for newly allocated buffers. Should be a power of 2. */
|
||||
#define INITIAL_BUF_SIZE (4*1024)
|
||||
/* Maximum size, in bytes, for resized buffers. */
|
||||
#define MAX_BUF_SIZE (1024*1024)
|
||||
/* Size, in bytes, for minimum 'shrink' size for buffers. Buffers may start
|
||||
* out smaller than this, but they will never autoshrink to less
|
||||
* than this size. */
|
||||
#define MIN_BUF_SHRINK_SIZE (16*1024)
|
||||
#define BUF_OK(b) ((b) && (b)->mem && (b)->datalen <= (b)->len)
|
||||
|
||||
/* Change a buffer's capacity. Must only be called when */
|
||||
static INLINE void buf_resize(buf_t *buf, size_t new_capacity)
|
||||
{
|
||||
assert(buf->datalen <= new_capacity);
|
||||
assert(new_capacity);
|
||||
buf->mem = tor_realloc(buf->mem, new_capacity);
|
||||
buf->len = new_capacity;
|
||||
}
|
||||
|
||||
/* If the buffer is not large enough to hold "capacity" bytes, resize
|
||||
* it so that it can. (The new size will be a power of 2 times the old
|
||||
* size.)
|
||||
*/
|
||||
static INLINE int buf_ensure_capacity(buf_t *buf, size_t capacity)
|
||||
{
|
||||
size_t new_len;
|
||||
if (buf->len >= capacity) /* Don't grow if we're already big enough. */
|
||||
return 0;
|
||||
if (capacity > MAX_BUF_SIZE) /* Don't grow past the maximum. */
|
||||
return -1;
|
||||
/* Find the smallest new_len equal to (2**X)*len for some X; such that
|
||||
* new_len is at least capacity.
|
||||
*/
|
||||
new_len = buf->len*2;
|
||||
while (new_len < capacity)
|
||||
new_len *= 2;
|
||||
/* Resize the buffer. */
|
||||
log_fn(LOG_DEBUG,"Growing buffer from %d to %d bytes.",
|
||||
(int)buf->len, (int)new_len);
|
||||
buf_resize(buf,new_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the buffer is at least 2*MIN_BUF_SHRINK_SIZE bytes in capacity,
|
||||
* and if the buffer is less than 1/4 full, shrink the buffer until
|
||||
* one of the above no longer holds. (We shrink the buffer by
|
||||
* dividing by powers of 2.)
|
||||
*/
|
||||
static INLINE void buf_shrink_if_underfull(buf_t *buf) {
|
||||
size_t new_len;
|
||||
/* If the buffer is at least .25 full, or if shrinking the buffer would
|
||||
* put it onder MIN_BUF_SHRINK_SIZE, don't do it. */
|
||||
if (buf->datalen >= buf->len/4 || buf->len < 2*MIN_BUF_SHRINK_SIZE)
|
||||
return;
|
||||
/* Shrink new_len by powers of 2 until: datalen is at least 1/4 of
|
||||
* new_len, OR shrinking new_len more would put it under
|
||||
* MIN_BUF_SHRINK_SIZE.
|
||||
*/
|
||||
new_len = buf->len / 2;
|
||||
while (buf->datalen < new_len/4 && new_len/2 > MIN_BUF_SHRINK_SIZE)
|
||||
new_len /= 2;
|
||||
log_fn(LOG_DEBUG,"Shrinking buffer from %d to %d bytes.",
|
||||
(int)buf->len, (int)new_len);
|
||||
buf_resize(buf, new_len);
|
||||
}
|
||||
|
||||
/* Remove the first 'n' bytes from buf.
|
||||
*/
|
||||
static INLINE void buf_remove_from_front(buf_t *buf, size_t n) {
|
||||
assert(buf->datalen >= n);
|
||||
buf->datalen -= n;
|
||||
memmove(buf->mem, buf->mem+n, buf->datalen);
|
||||
buf_shrink_if_underfull(buf);
|
||||
}
|
||||
|
||||
/* Find the first instance of str on buf. If none exists, return -1.
|
||||
* Otherwise, return index of the first character in buf _after_ the
|
||||
* first instance of str.
|
||||
*/
|
||||
static int find_str_in_str(const char *str, int str_len,
|
||||
const char *buf, int buf_len)
|
||||
{
|
||||
const char *location;
|
||||
const char *last_possible = buf + buf_len - str_len;
|
||||
|
||||
assert(str && str_len > 0 && buf);
|
||||
|
||||
if(buf_len < str_len)
|
||||
return -1;
|
||||
|
||||
for(location = buf; location <= last_possible; location++)
|
||||
if((*location == *str) && !memcmp(location+1, str+1, str_len-1))
|
||||
return location-buf+str_len;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int find_on_inbuf(char *string, int string_len, buf_t *buf) {
|
||||
return find_str_in_str(string, string_len, buf->mem, buf->datalen);
|
||||
}
|
||||
|
||||
/* Create and return a new buf of size 'size'
|
||||
*/
|
||||
buf_t *buf_new_with_capacity(size_t size) {
|
||||
buf_t *buf;
|
||||
buf = (buf_t*)tor_malloc(sizeof(buf_t));
|
||||
buf->mem = (char *)tor_malloc(size);
|
||||
buf->len = size;
|
||||
buf->datalen = 0;
|
||||
// memset(buf->mem,0,size);
|
||||
|
||||
assert(BUF_OK(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
buf_t *buf_new()
|
||||
{
|
||||
return buf_new_with_capacity(INITIAL_BUF_SIZE);
|
||||
}
|
||||
|
||||
|
||||
size_t buf_datalen(const buf_t *buf)
|
||||
{
|
||||
return buf->datalen;
|
||||
}
|
||||
|
||||
size_t buf_capacity(const buf_t *buf)
|
||||
{
|
||||
return buf->len;
|
||||
}
|
||||
|
||||
const char *_buf_peek_raw_buffer(const buf_t *buf)
|
||||
{
|
||||
return buf->mem;
|
||||
}
|
||||
|
||||
void buf_free(buf_t *buf) {
|
||||
assert(buf && buf->mem);
|
||||
free(buf->mem);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* read from socket s, writing onto end of buf.
|
||||
* read at most 'at_most' bytes, and in any case don't read more than will fit based on buflen.
|
||||
* If read() returns 0, set *reached_eof to 1 and return 0. If you want to tear
|
||||
* down the connection return -1, else return the number of bytes read.
|
||||
*/
|
||||
int read_to_buf(int s, int at_most, buf_t *buf, int *reached_eof) {
|
||||
|
||||
int read_result;
|
||||
#ifdef MS_WINDOWS
|
||||
int e;
|
||||
#endif
|
||||
|
||||
assert(BUF_OK(buf) && reached_eof && (s>=0));
|
||||
|
||||
if (buf_ensure_capacity(buf,buf->datalen+at_most))
|
||||
return -1;
|
||||
|
||||
if(at_most > buf->len - buf->datalen)
|
||||
at_most = buf->len - buf->datalen; /* take the min of the two */
|
||||
|
||||
if(at_most == 0)
|
||||
return 0; /* we shouldn't read anything */
|
||||
|
||||
// log_fn(LOG_DEBUG,"reading at most %d bytes.",at_most);
|
||||
read_result = read(s, buf->mem+buf->datalen, at_most);
|
||||
if (read_result < 0) {
|
||||
if(!ERRNO_EAGAIN(errno)) { /* it's a real error */
|
||||
return -1;
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
e = correct_socket_errno(s);
|
||||
if(!ERRNO_EAGAIN(e)) { /* no, it *is* a real error! */
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
} else if (read_result == 0) {
|
||||
log_fn(LOG_DEBUG,"Encountered eof");
|
||||
*reached_eof = 1;
|
||||
return 0;
|
||||
} else { /* we read some bytes */
|
||||
buf->datalen += read_result;
|
||||
log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf.",read_result,
|
||||
(int)buf->datalen);
|
||||
return read_result;
|
||||
}
|
||||
}
|
||||
|
||||
int read_to_buf_tls(tor_tls *tls, int at_most, buf_t *buf) {
|
||||
int r;
|
||||
assert(tls && BUF_OK(buf));
|
||||
|
||||
if (buf_ensure_capacity(buf, at_most+buf->datalen))
|
||||
return -1;
|
||||
|
||||
if (at_most > buf->len - buf->datalen)
|
||||
at_most = buf->len - buf->datalen;
|
||||
|
||||
if (at_most == 0)
|
||||
return 0;
|
||||
|
||||
r = tor_tls_read(tls, buf->mem+buf->datalen, at_most);
|
||||
if (r<0)
|
||||
return r;
|
||||
buf->datalen += r;
|
||||
log_fn(LOG_DEBUG,"Read %d bytes. %d on inbuf.",r, (int)buf->datalen);
|
||||
return r;
|
||||
}
|
||||
|
||||
int flush_buf(int s, buf_t *buf, int *buf_flushlen)
|
||||
{
|
||||
|
||||
/* push from buf onto s
|
||||
* then memmove to front of buf
|
||||
* return -1 or how many bytes remain to be flushed */
|
||||
|
||||
int write_result;
|
||||
#ifdef MS_WINDOWS
|
||||
int e;
|
||||
#endif
|
||||
|
||||
assert(BUF_OK(buf) && buf_flushlen && (s>=0) && (*buf_flushlen <= buf->datalen));
|
||||
|
||||
if(*buf_flushlen == 0) /* nothing to flush */
|
||||
return 0;
|
||||
|
||||
write_result = write(s, buf->mem, *buf_flushlen);
|
||||
if (write_result < 0) {
|
||||
if(!ERRNO_EAGAIN(errno)) { /* it's a real error */
|
||||
return -1;
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
e = correct_socket_errno(s);
|
||||
if(!ERRNO_EAGAIN(e)) { /* no, it *is* a real error! */
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
log_fn(LOG_DEBUG,"write() would block, returning.");
|
||||
return 0;
|
||||
} else {
|
||||
*buf_flushlen -= write_result;
|
||||
buf_remove_from_front(buf, write_result);
|
||||
log_fn(LOG_DEBUG,"%d: flushed %d bytes, %d ready to flush, %d remain.",
|
||||
s,write_result,*buf_flushlen,(int)buf->datalen);
|
||||
|
||||
return *buf_flushlen;
|
||||
/* XXX USE_TLS should change to return write_result like any sane function would */
|
||||
}
|
||||
}
|
||||
|
||||
int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen)
|
||||
{
|
||||
int r;
|
||||
assert(tls && BUF_OK(buf) && buf_flushlen);
|
||||
|
||||
/* we want to let tls write even if flushlen is zero, because it might
|
||||
* have a partial record pending */
|
||||
r = tor_tls_write(tls, buf->mem, *buf_flushlen);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
*buf_flushlen -= r;
|
||||
buf_remove_from_front(buf, r);
|
||||
log_fn(LOG_DEBUG,"flushed %d bytes, %d ready to flush, %d remain.",
|
||||
r,*buf_flushlen,(int)buf->datalen);
|
||||
return r;
|
||||
}
|
||||
|
||||
int write_to_buf(const char *string, int string_len, buf_t *buf) {
|
||||
|
||||
/* append string to buf (growing as needed, return -1 if "too big")
|
||||
* return total number of bytes on the buf
|
||||
*/
|
||||
|
||||
assert(string && BUF_OK(buf));
|
||||
|
||||
if (buf_ensure_capacity(buf, buf->datalen+string_len)) {
|
||||
log_fn(LOG_WARN, "buflen too small, can't hold %d bytes.", (int)buf->datalen+string_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf->mem+buf->datalen, string, string_len);
|
||||
buf->datalen += string_len;
|
||||
log_fn(LOG_DEBUG,"added %d bytes to buf (now %d total).",string_len, (int)buf->datalen);
|
||||
return buf->datalen;
|
||||
}
|
||||
|
||||
int fetch_from_buf(char *string, int string_len, buf_t *buf) {
|
||||
|
||||
/* There must be string_len bytes in buf; write them onto string,
|
||||
* then memmove buf back (that is, remove them from buf).
|
||||
*
|
||||
* Return the number of bytes still on the buffer. */
|
||||
|
||||
assert(string && BUF_OK(buf));
|
||||
assert(string_len <= buf->datalen); /* make sure we don't ask for too much */
|
||||
|
||||
memcpy(string,buf->mem,string_len);
|
||||
buf_remove_from_front(buf, string_len);
|
||||
return buf->datalen;
|
||||
}
|
||||
|
||||
/* There is a (possibly incomplete) http statement on *buf, of the
|
||||
* form "%s\r\n\r\n%s", headers, body.
|
||||
* If a) the headers include a Content-Length field and all bytes in
|
||||
* the body are present, or b) there's no Content-Length field and
|
||||
* all headers are present, then:
|
||||
* strdup headers and body into the supplied args (and null terminate
|
||||
* them), remove them from buf, and return 1.
|
||||
* (If headers or body is NULL, discard that part of the buf.)
|
||||
* If a headers or body doesn't fit in the arg, return -1.
|
||||
*
|
||||
* Else, change nothing and return 0.
|
||||
*/
|
||||
int fetch_from_buf_http(buf_t *buf,
|
||||
char **headers_out, int max_headerlen,
|
||||
char **body_out, int max_bodylen) {
|
||||
char *headers, *body;
|
||||
int i;
|
||||
int headerlen, bodylen, contentlen;
|
||||
|
||||
assert(BUF_OK(buf));
|
||||
|
||||
headers = buf->mem;
|
||||
i = find_on_inbuf("\r\n\r\n", 4, buf);
|
||||
if(i < 0) {
|
||||
log_fn(LOG_DEBUG,"headers not all here yet.");
|
||||
return 0;
|
||||
}
|
||||
body = buf->mem+i;
|
||||
headerlen = body-headers; /* includes the CRLFCRLF */
|
||||
bodylen = buf->datalen - headerlen;
|
||||
log_fn(LOG_DEBUG,"headerlen %d, bodylen %d.", headerlen, bodylen);
|
||||
|
||||
if(headers_out && max_headerlen <= headerlen) {
|
||||
log_fn(LOG_WARN,"headerlen %d larger than %d. Failing.", headerlen, max_headerlen-1);
|
||||
return -1;
|
||||
}
|
||||
if(body_out && max_bodylen <= bodylen) {
|
||||
log_fn(LOG_WARN,"bodylen %d larger than %d. Failing.", bodylen, max_bodylen-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define CONTENT_LENGTH "\r\nContent-Length: "
|
||||
i = find_str_in_str(CONTENT_LENGTH, strlen(CONTENT_LENGTH),
|
||||
headers, headerlen);
|
||||
if(i > 0) {
|
||||
contentlen = atoi(headers+i);
|
||||
/* XXX What if content-length is malformed? */
|
||||
log_fn(LOG_DEBUG,"Got a contentlen of %d.",contentlen);
|
||||
if(bodylen < contentlen) {
|
||||
log_fn(LOG_DEBUG,"body not all here yet.");
|
||||
return 0; /* not all there yet */
|
||||
}
|
||||
bodylen = contentlen;
|
||||
log_fn(LOG_DEBUG,"bodylen reduced to %d.",bodylen);
|
||||
}
|
||||
/* all happy. copy into the appropriate places, and return 1 */
|
||||
if(headers_out) {
|
||||
*headers_out = tor_malloc(headerlen+1);
|
||||
memcpy(*headers_out,buf->mem,headerlen);
|
||||
(*headers_out)[headerlen] = 0; /* null terminate it */
|
||||
}
|
||||
if(body_out) {
|
||||
*body_out = tor_malloc(bodylen+1);
|
||||
memcpy(*body_out,buf->mem+headerlen,bodylen);
|
||||
(*body_out)[bodylen] = 0; /* null terminate it */
|
||||
}
|
||||
buf_remove_from_front(buf, headerlen+bodylen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* There is a (possibly incomplete) socks handshake on buf, of one
|
||||
* of the forms
|
||||
* socks4: "socksheader username\0"
|
||||
* socks4a: "socksheader username\0 destaddr\0"
|
||||
* socks5 phase one: "version #methods methods"
|
||||
* socks5 phase two: "version command 0 addresstype..."
|
||||
* If it's a complete and valid handshake, and destaddr fits in
|
||||
* MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
|
||||
* assign to *req, and return 1.
|
||||
* If it's invalid or too big, return -1.
|
||||
* Else it's not all there yet, leave buf alone and return 0.
|
||||
* If you want to specify the socks reply, write it into *reply
|
||||
* and set *replylen, else leave *replylen alone.
|
||||
* If returning 0 or -1, *addr_out and *port_out are undefined.
|
||||
*/
|
||||
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) {
|
||||
unsigned char len;
|
||||
char *tmpbuf=NULL;
|
||||
uint32_t destip;
|
||||
enum {socks4, socks4a} socks4_prot = socks4a;
|
||||
char *next, *startaddr;
|
||||
struct in_addr in;
|
||||
|
||||
if(buf->datalen < 2) /* version and another byte */
|
||||
return 0;
|
||||
switch(*(buf->mem)) { /* which version of socks? */
|
||||
|
||||
case 5: /* socks5 */
|
||||
|
||||
if(req->socks_version != 5) { /* we need to negotiate a method */
|
||||
unsigned char nummethods = (unsigned char)*(buf->mem+1);
|
||||
assert(!req->socks_version);
|
||||
log_fn(LOG_DEBUG,"socks5: learning offered methods");
|
||||
if(buf->datalen < 2+nummethods)
|
||||
return 0;
|
||||
if(!nummethods || !memchr(buf->mem+2, 0, nummethods)) {
|
||||
log_fn(LOG_WARN,"socks5: offered methods don't include 'no auth'. Rejecting.");
|
||||
req->replylen = 2; /* 2 bytes of response */
|
||||
req->reply[0] = 5; /* socks5 reply */
|
||||
req->reply[1] = 0xFF; /* reject all methods */
|
||||
return -1;
|
||||
}
|
||||
buf_remove_from_front(buf,2+nummethods);/* remove packet from buf */
|
||||
|
||||
req->replylen = 2; /* 2 bytes of response */
|
||||
req->reply[0] = 5; /* socks5 reply */
|
||||
req->reply[1] = 0; /* choose the 'no auth' method */
|
||||
req->socks_version = 5; /* remember that we've already negotiated auth */
|
||||
log_fn(LOG_DEBUG,"socks5: accepted method 0");
|
||||
return 0;
|
||||
}
|
||||
/* we know the method; read in the request */
|
||||
log_fn(LOG_DEBUG,"socks5: checking request");
|
||||
if(buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */
|
||||
return 0; /* not yet */
|
||||
if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
|
||||
log_fn(LOG_WARN,"socks5: command %d not '1'.",*(buf->mem+1));
|
||||
return -1;
|
||||
}
|
||||
switch(*(buf->mem+3)) { /* address type */
|
||||
case 1: /* IPv4 address */
|
||||
log_fn(LOG_DEBUG,"socks5: ipv4 address type");
|
||||
if(buf->datalen < 10) /* ip/port there? */
|
||||
return 0; /* not yet */
|
||||
destip = ntohl(*(uint32_t*)(buf->mem+4));
|
||||
in.s_addr = htonl(destip);
|
||||
tmpbuf = inet_ntoa(in);
|
||||
if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
|
||||
log_fn(LOG_WARN,"socks5 IP takes %d bytes, which doesn't fit in %d",
|
||||
(int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN);
|
||||
return -1;
|
||||
}
|
||||
strcpy(req->address,tmpbuf);
|
||||
req->port = ntohs(*(uint16_t*)(buf->mem+8));
|
||||
buf_remove_from_front(buf, 10);
|
||||
return 1;
|
||||
case 3: /* fqdn */
|
||||
log_fn(LOG_DEBUG,"socks5: fqdn address type");
|
||||
len = (unsigned char)*(buf->mem+4);
|
||||
if(buf->datalen < 7+len) /* addr/port there? */
|
||||
return 0; /* not yet */
|
||||
if(len+1 > MAX_SOCKS_ADDR_LEN) {
|
||||
log_fn(LOG_WARN,"socks5 hostname is %d bytes, which doesn't fit in %d",
|
||||
len+1,MAX_SOCKS_ADDR_LEN);
|
||||
return -1;
|
||||
}
|
||||
memcpy(req->address,buf->mem+5,len);
|
||||
req->address[len] = 0;
|
||||
req->port = ntohs(*(uint16_t*)(buf->mem+5+len));
|
||||
buf_remove_from_front(buf, 5+len+2);
|
||||
return 1;
|
||||
default: /* unsupported */
|
||||
log_fn(LOG_WARN,"socks5: unsupported address type %d",*(buf->mem+3));
|
||||
return -1;
|
||||
}
|
||||
assert(0);
|
||||
case 4: /* socks4 */
|
||||
|
||||
req->socks_version = 4;
|
||||
if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
|
||||
return 0; /* not yet */
|
||||
|
||||
if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */
|
||||
log_fn(LOG_WARN,"socks4: command %d not '1'.",*(buf->mem+1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
req->port = ntohs(*(uint16_t*)(buf->mem+2));
|
||||
destip = ntohl(*(uint32_t*)(buf->mem+4));
|
||||
if(!req->port || !destip) {
|
||||
log_fn(LOG_WARN,"socks4: Port or DestIP is zero.");
|
||||
return -1;
|
||||
}
|
||||
if(destip >> 8) {
|
||||
log_fn(LOG_DEBUG,"socks4: destip not in form 0.0.0.x.");
|
||||
in.s_addr = htonl(destip);
|
||||
tmpbuf = inet_ntoa(in);
|
||||
if(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
|
||||
log_fn(LOG_WARN,"socks4 addr (%d bytes) too long.",
|
||||
(int)strlen(tmpbuf));
|
||||
return -1;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"socks4: successfully read destip (%s)", tmpbuf);
|
||||
socks4_prot = socks4;
|
||||
}
|
||||
|
||||
next = memchr(buf->mem+SOCKS4_NETWORK_LEN, 0, buf->datalen);
|
||||
if(!next) {
|
||||
log_fn(LOG_DEBUG,"Username not here yet.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
startaddr = next+1;
|
||||
if(socks4_prot == socks4a) {
|
||||
next = memchr(startaddr, 0, buf->mem+buf->datalen-startaddr);
|
||||
if(!next) {
|
||||
log_fn(LOG_DEBUG,"Destaddr not here yet.");
|
||||
return 0;
|
||||
}
|
||||
if(MAX_SOCKS_ADDR_LEN <= next-startaddr) {
|
||||
log_fn(LOG_WARN,"Destaddr too long.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
log_fn(LOG_DEBUG,"Everything is here. Success.");
|
||||
strcpy(req->address, socks4_prot == socks4 ? tmpbuf : startaddr);
|
||||
buf_remove_from_front(buf, next-buf->mem+1); /* next points to the final \0 on inbuf */
|
||||
return 1;
|
||||
|
||||
default: /* version is not socks4 or socks5 */
|
||||
log_fn(LOG_WARN,"Socks version %d not recognized. (Tor is not an httpd proxy.)",*(buf->mem));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
unsigned long stats_n_padding_cells_processed = 0;
|
||||
unsigned long stats_n_create_cells_processed = 0;
|
||||
unsigned long stats_n_created_cells_processed = 0;
|
||||
unsigned long stats_n_relay_cells_processed = 0;
|
||||
unsigned long stats_n_destroy_cells_processed = 0;
|
||||
|
||||
static void command_process_create_cell(cell_t *cell, connection_t *conn);
|
||||
static void command_process_created_cell(cell_t *cell, connection_t *conn);
|
||||
static void command_process_relay_cell(cell_t *cell, connection_t *conn);
|
||||
static void command_process_destroy_cell(cell_t *cell, connection_t *conn);
|
||||
|
||||
static void command_time_process_cell(cell_t *cell, connection_t *conn,
|
||||
int *num, int *time,
|
||||
void (*func)(cell_t *, connection_t *)) {
|
||||
struct timeval start, end;
|
||||
long time_passed;
|
||||
|
||||
*num += 1;
|
||||
|
||||
tor_gettimeofday(&start);
|
||||
|
||||
(*func)(cell, conn);
|
||||
|
||||
tor_gettimeofday(&end);
|
||||
time_passed = tv_udiff(&start, &end) ;
|
||||
|
||||
if (time_passed > 5000) { /* more than 5ms */
|
||||
log_fn(LOG_INFO,"That call just took %ld ms.",time_passed/1000);
|
||||
}
|
||||
*time += time_passed;
|
||||
}
|
||||
|
||||
void command_process_cell(cell_t *cell, connection_t *conn) {
|
||||
static int num_create=0, num_created=0, num_relay=0, num_destroy=0;
|
||||
static int create_time=0, created_time=0, relay_time=0, destroy_time=0;
|
||||
static time_t current_second = 0; /* from previous calls to time */
|
||||
time_t now = time(NULL);
|
||||
|
||||
if(now > current_second) { /* the second has rolled over */
|
||||
/* print stats */
|
||||
log(LOG_INFO,"At end of second:");
|
||||
log(LOG_INFO,"Create: %d (%d ms)", num_create, create_time/1000);
|
||||
log(LOG_INFO,"Created: %d (%d ms)", num_created, created_time/1000);
|
||||
log(LOG_INFO,"Relay: %d (%d ms)", num_relay, relay_time/1000);
|
||||
log(LOG_INFO,"Destroy: %d (%d ms)", num_destroy, destroy_time/1000);
|
||||
|
||||
/* zero out stats */
|
||||
num_create = num_created = num_relay = num_destroy = 0;
|
||||
create_time = created_time = relay_time = destroy_time = 0;
|
||||
|
||||
/* remember which second it is, for next time */
|
||||
current_second = now;
|
||||
}
|
||||
|
||||
switch(cell->command) {
|
||||
case CELL_PADDING:
|
||||
++stats_n_padding_cells_processed;
|
||||
/* do nothing */
|
||||
break;
|
||||
case CELL_CREATE:
|
||||
++stats_n_create_cells_processed;
|
||||
command_time_process_cell(cell, conn, &num_create, &create_time,
|
||||
command_process_create_cell);
|
||||
break;
|
||||
case CELL_CREATED:
|
||||
++stats_n_created_cells_processed;
|
||||
command_time_process_cell(cell, conn, &num_created, &created_time,
|
||||
command_process_created_cell);
|
||||
break;
|
||||
case CELL_RELAY:
|
||||
++stats_n_relay_cells_processed;
|
||||
command_time_process_cell(cell, conn, &num_relay, &relay_time,
|
||||
command_process_relay_cell);
|
||||
break;
|
||||
case CELL_DESTROY:
|
||||
++stats_n_destroy_cells_processed;
|
||||
command_time_process_cell(cell, conn, &num_destroy, &destroy_time,
|
||||
command_process_destroy_cell);
|
||||
break;
|
||||
default:
|
||||
log_fn(LOG_WARN,"Cell of unknown type (%d) received. Dropping.", cell->command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void command_process_create_cell(cell_t *cell, connection_t *conn) {
|
||||
circuit_t *circ;
|
||||
|
||||
circ = circuit_get_by_circ_id_conn(cell->circ_id, conn);
|
||||
|
||||
if(circ) {
|
||||
log_fn(LOG_WARN,"received CREATE cell (circID %d) for known circ. Dropping.", cell->circ_id);
|
||||
return;
|
||||
}
|
||||
|
||||
circ = circuit_new(cell->circ_id, conn);
|
||||
circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING;
|
||||
|
||||
memcpy(circ->onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN);
|
||||
|
||||
/* hand it off to the cpuworkers, and then return */
|
||||
if(assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) {
|
||||
log_fn(LOG_WARN,"Failed to hand off onionskin. Closing.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
log_fn(LOG_INFO,"success: handed off onionskin.");
|
||||
}
|
||||
|
||||
static void command_process_created_cell(cell_t *cell, connection_t *conn) {
|
||||
circuit_t *circ;
|
||||
|
||||
circ = circuit_get_by_circ_id_conn(cell->circ_id, conn);
|
||||
|
||||
if(!circ) {
|
||||
log_fn(LOG_INFO,"(circID %d) unknown circ (probably got a destroy earlier). Dropping.", cell->circ_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if(circ->n_circ_id != cell->circ_id) {
|
||||
log_fn(LOG_WARN,"got created cell from OPward? Closing.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
|
||||
if(circ->cpath) { /* we're the OP. Handshake this. */
|
||||
log_fn(LOG_DEBUG,"at OP. Finishing handshake.");
|
||||
if(circuit_finish_handshake(circ, cell->payload) < 0) {
|
||||
log_fn(LOG_WARN,"circuit_finish_handshake failed.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"Moving to next skin.");
|
||||
if(circuit_send_next_onion_skin(circ) < 0) {
|
||||
log_fn(LOG_INFO,"circuit_send_next_onion_skin failed.");
|
||||
circuit_close(circ); /* XXX push this circuit_close lower */
|
||||
return;
|
||||
}
|
||||
} else { /* pack it into an extended relay cell, and send it. */
|
||||
log_fn(LOG_INFO,"Converting created cell to extended relay cell, sending.");
|
||||
connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTENDED,
|
||||
cell->payload, ONIONSKIN_REPLY_LEN, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void command_process_relay_cell(cell_t *cell, connection_t *conn) {
|
||||
circuit_t *circ;
|
||||
|
||||
circ = circuit_get_by_circ_id_conn(cell->circ_id, conn);
|
||||
|
||||
if(!circ) {
|
||||
log_fn(LOG_INFO,"unknown circuit %d. Dropping.", cell->circ_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
|
||||
log_fn(LOG_WARN,"circuit in create_wait. Closing.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
|
||||
if(cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */
|
||||
if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_OUT) < 0) {
|
||||
log_fn(LOG_WARN,"circuit_receive_relay_cell (forward) failed. Closing.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
} else { /* it's an ingoing cell */
|
||||
if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_IN) < 0) {
|
||||
log_fn(LOG_WARN,"circuit_receive_relay_cell (backward) failed. Closing.");
|
||||
circuit_close(circ);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void command_process_destroy_cell(cell_t *cell, connection_t *conn) {
|
||||
circuit_t *circ;
|
||||
|
||||
circ = circuit_get_by_circ_id_conn(cell->circ_id, conn);
|
||||
|
||||
if(!circ) {
|
||||
log_fn(LOG_INFO,"unknown circuit %d. Dropping.", cell->circ_id);
|
||||
return;
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG,"Received for circID %d.",cell->circ_id);
|
||||
if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
|
||||
onion_pending_remove(circ);
|
||||
}
|
||||
|
||||
if(cell->circ_id == circ->p_circ_id || circ->cpath) {
|
||||
/* either the destroy came from behind, or we're the AP */
|
||||
circ->p_conn = NULL;
|
||||
circuit_close(circ);
|
||||
} else { /* the destroy came from ahead */
|
||||
circ->n_conn = NULL;
|
||||
log_fn(LOG_DEBUG, "Delivering 'truncated' back.");
|
||||
connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,439 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/* enumeration of types which option values can take */
|
||||
#define CONFIG_TYPE_STRING 0
|
||||
#define CONFIG_TYPE_CHAR 1
|
||||
#define CONFIG_TYPE_INT 2
|
||||
#define CONFIG_TYPE_LONG 3
|
||||
#define CONFIG_TYPE_DOUBLE 4
|
||||
#define CONFIG_TYPE_BOOL 5
|
||||
|
||||
#define CONFIG_LINE_MAXLEN 4096
|
||||
|
||||
struct config_line {
|
||||
char *key;
|
||||
char *value;
|
||||
struct config_line *next;
|
||||
};
|
||||
|
||||
static FILE *config_open(const unsigned char *filename);
|
||||
static int config_close(FILE *f);
|
||||
static struct config_line *config_get_commandlines(int argc, char **argv);
|
||||
static struct config_line *config_get_lines(FILE *f);
|
||||
static void config_free_lines(struct config_line *front);
|
||||
static int config_compare(struct config_line *c, char *key, int type, void *arg);
|
||||
static void config_assign(or_options_t *options, struct config_line *list);
|
||||
|
||||
/* open configuration file for reading */
|
||||
static FILE *config_open(const unsigned char *filename) {
|
||||
assert(filename);
|
||||
if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) {
|
||||
/* filename has illegal letters */
|
||||
return NULL;
|
||||
}
|
||||
return fopen(filename, "r");
|
||||
}
|
||||
|
||||
/* close configuration file */
|
||||
static int config_close(FILE *f) {
|
||||
assert(f);
|
||||
return fclose(f);
|
||||
}
|
||||
|
||||
static struct config_line *config_get_commandlines(int argc, char **argv) {
|
||||
struct config_line *new;
|
||||
struct config_line *front = NULL;
|
||||
char *s;
|
||||
int i = 1;
|
||||
|
||||
while(i < argc-1) {
|
||||
if(!strcmp(argv[i],"-f")) {
|
||||
// log(LOG_DEBUG,"Commandline: skipping over -f.");
|
||||
i+=2; /* this is the config file option. ignore it. */
|
||||
continue;
|
||||
}
|
||||
|
||||
new = tor_malloc(sizeof(struct config_line));
|
||||
s = argv[i];
|
||||
while(*s == '-')
|
||||
s++;
|
||||
new->key = tor_strdup(s);
|
||||
new->value = tor_strdup(argv[i+1]);
|
||||
|
||||
log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
|
||||
new->key, new->value);
|
||||
new->next = front;
|
||||
front = new;
|
||||
i += 2;
|
||||
}
|
||||
return front;
|
||||
}
|
||||
|
||||
/* parse the config file and strdup into key/value strings. Return list,
|
||||
* or NULL if parsing the file failed.
|
||||
* Warn and ignore mangled lines. */
|
||||
static struct config_line *config_get_lines(FILE *f) {
|
||||
struct config_line *new;
|
||||
struct config_line *front = NULL;
|
||||
char line[CONFIG_LINE_MAXLEN];
|
||||
int result;
|
||||
char *key, *value;
|
||||
|
||||
while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) {
|
||||
new = tor_malloc(sizeof(struct config_line));
|
||||
new->key = tor_strdup(key);
|
||||
new->value = tor_strdup(value);
|
||||
|
||||
new->next = front;
|
||||
front = new;
|
||||
}
|
||||
if(result < 0)
|
||||
return NULL;
|
||||
return front;
|
||||
}
|
||||
|
||||
static void config_free_lines(struct config_line *front) {
|
||||
struct config_line *tmp;
|
||||
|
||||
while(front) {
|
||||
tmp = front;
|
||||
front = tmp->next;
|
||||
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static int config_compare(struct config_line *c, char *key, int type, void *arg) {
|
||||
int i;
|
||||
|
||||
if(strncasecmp(c->key,key,strlen(c->key)))
|
||||
return 0;
|
||||
|
||||
/* it's a match. cast and assign. */
|
||||
log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value);
|
||||
|
||||
switch(type) {
|
||||
case CONFIG_TYPE_INT:
|
||||
*(int *)arg = atoi(c->value);
|
||||
break;
|
||||
case CONFIG_TYPE_BOOL:
|
||||
i = atoi(c->value);
|
||||
if (i != 0 && i != 1) {
|
||||
log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1", c->key);
|
||||
return 0;
|
||||
}
|
||||
*(int *)arg = i;
|
||||
break;
|
||||
case CONFIG_TYPE_STRING:
|
||||
tor_free(*(char **)arg);
|
||||
*(char **)arg = tor_strdup(c->value);
|
||||
break;
|
||||
case CONFIG_TYPE_DOUBLE:
|
||||
*(double *)arg = atof(c->value);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void config_assign(or_options_t *options, struct config_line *list) {
|
||||
|
||||
/* iterate through list. for each item convert as appropriate and assign to 'options'. */
|
||||
|
||||
while(list) {
|
||||
if(
|
||||
|
||||
/* order matters here! abbreviated arguments use the first match. */
|
||||
|
||||
/* string options */
|
||||
config_compare(list, "Address", CONFIG_TYPE_STRING, &options->Address) ||
|
||||
|
||||
config_compare(list, "DebugLogFile", CONFIG_TYPE_STRING, &options->DebugLogFile) ||
|
||||
config_compare(list, "DataDirectory", CONFIG_TYPE_STRING, &options->DataDirectory) ||
|
||||
config_compare(list, "DirPort", CONFIG_TYPE_INT, &options->DirPort) ||
|
||||
config_compare(list, "DirBindAddress", CONFIG_TYPE_STRING, &options->DirBindAddress) ||
|
||||
config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_INT, &options->DirFetchPostPeriod) ||
|
||||
|
||||
config_compare(list, "ExitNodes", CONFIG_TYPE_STRING, &options->ExitNodes) ||
|
||||
config_compare(list, "EntryNodes", CONFIG_TYPE_STRING, &options->EntryNodes) ||
|
||||
config_compare(list, "ExitPolicy", CONFIG_TYPE_STRING, &options->ExitPolicy) ||
|
||||
config_compare(list, "ExcludedNodes", CONFIG_TYPE_STRING, &options->ExcludedNodes) ||
|
||||
|
||||
config_compare(list, "Group", CONFIG_TYPE_STRING, &options->Group) ||
|
||||
|
||||
config_compare(list, "IgnoreVersion", CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
|
||||
|
||||
config_compare(list, "KeepalivePeriod",CONFIG_TYPE_INT, &options->KeepalivePeriod) ||
|
||||
|
||||
config_compare(list, "LogLevel", CONFIG_TYPE_STRING, &options->LogLevel) ||
|
||||
config_compare(list, "LogFile", CONFIG_TYPE_STRING, &options->LogFile) ||
|
||||
config_compare(list, "LinkPadding", CONFIG_TYPE_BOOL, &options->LinkPadding) ||
|
||||
|
||||
config_compare(list, "MaxConn", CONFIG_TYPE_INT, &options->MaxConn) ||
|
||||
config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
|
||||
|
||||
config_compare(list, "Nickname", CONFIG_TYPE_STRING, &options->Nickname) ||
|
||||
config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
|
||||
config_compare(list, "NumCpus", CONFIG_TYPE_INT, &options->NumCpus) ||
|
||||
|
||||
config_compare(list, "ORPort", CONFIG_TYPE_INT, &options->ORPort) ||
|
||||
config_compare(list, "ORBindAddress", CONFIG_TYPE_STRING, &options->ORBindAddress) ||
|
||||
|
||||
config_compare(list, "PidFile", CONFIG_TYPE_STRING, &options->PidFile) ||
|
||||
config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
|
||||
|
||||
config_compare(list, "RouterFile", CONFIG_TYPE_STRING, &options->RouterFile) ||
|
||||
config_compare(list, "RunAsDaemon", CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
|
||||
config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) ||
|
||||
|
||||
config_compare(list, "SocksPort", CONFIG_TYPE_INT, &options->SocksPort) ||
|
||||
config_compare(list, "SocksBindAddress",CONFIG_TYPE_STRING,&options->SocksBindAddress) ||
|
||||
|
||||
config_compare(list, "TotalBandwidth", CONFIG_TYPE_INT, &options->TotalBandwidth) ||
|
||||
config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) ||
|
||||
|
||||
config_compare(list, "User", CONFIG_TYPE_STRING, &options->User)
|
||||
) {
|
||||
/* then we're ok. it matched something. */
|
||||
} else {
|
||||
log_fn(LOG_WARN,"Ignoring unknown keyword '%s'.",list->key);
|
||||
}
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* prints the usage of tor. */
|
||||
void print_usage(void) {
|
||||
printf("tor -f <torrc> [args]\n"
|
||||
"-d <file>\t\tDebug file\n"
|
||||
"-m <max>\t\tMax number of connections\n"
|
||||
"-l <level>\t\tLog level\n"
|
||||
"-t <bandwidth>\t\tTotal bandwidth\n"
|
||||
"-r <file>\t\tList of known routers\n");
|
||||
printf("\nClient options:\n"
|
||||
"-e \"nick1 nick2 ...\"\t\tExit nodes\n"
|
||||
"-s <IP>\t\t\tPort to bind to for Socks\n"
|
||||
);
|
||||
printf("\nServer options:\n"
|
||||
"-n <nick>\t\tNickname of router\n"
|
||||
"-o <port>\t\tOR port to bind to\n"
|
||||
"-p <file>\t\tPID file\n"
|
||||
);
|
||||
}
|
||||
|
||||
void free_options(or_options_t *options) {
|
||||
tor_free(options->LogLevel);
|
||||
tor_free(options->LogFile);
|
||||
tor_free(options->DebugLogFile);
|
||||
tor_free(options->DataDirectory);
|
||||
tor_free(options->RouterFile);
|
||||
tor_free(options->Nickname);
|
||||
tor_free(options->Address);
|
||||
tor_free(options->PidFile);
|
||||
tor_free(options->ExitNodes);
|
||||
tor_free(options->EntryNodes);
|
||||
tor_free(options->ExcludedNodes);
|
||||
tor_free(options->ExitPolicy);
|
||||
tor_free(options->SocksBindAddress);
|
||||
tor_free(options->ORBindAddress);
|
||||
tor_free(options->DirBindAddress);
|
||||
tor_free(options->RecommendedVersions);
|
||||
tor_free(options->User);
|
||||
tor_free(options->Group);
|
||||
}
|
||||
|
||||
void init_options(or_options_t *options) {
|
||||
/* give reasonable values for each option. Defaults to zero. */
|
||||
memset(options,0,sizeof(or_options_t));
|
||||
options->LogLevel = tor_strdup("warn");
|
||||
options->ExitNodes = tor_strdup("");
|
||||
options->EntryNodes = tor_strdup("");
|
||||
options->ExcludedNodes = tor_strdup("");
|
||||
options->ExitPolicy = tor_strdup("reject *:25,reject 127.0.0.0/8:*,reject 0.0.0.0/8,accept *:*");
|
||||
options->SocksBindAddress = tor_strdup("127.0.0.1");
|
||||
options->ORBindAddress = tor_strdup("0.0.0.0");
|
||||
options->DirBindAddress = tor_strdup("0.0.0.0");
|
||||
options->RecommendedVersions = tor_strdup("none");
|
||||
options->loglevel = LOG_INFO;
|
||||
options->PidFile = NULL; // tor_strdup("tor.pid");
|
||||
options->DataDirectory = NULL;
|
||||
options->PathlenCoinWeight = 0.3;
|
||||
options->MaxConn = 900;
|
||||
options->DirFetchPostPeriod = 600;
|
||||
options->KeepalivePeriod = 300;
|
||||
options->MaxOnionsPending = 100;
|
||||
options->NewCircuitPeriod = 60; /* once a minute */
|
||||
options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */
|
||||
options->NumCpus = 1;
|
||||
}
|
||||
|
||||
/* return 0 if success, <0 if failure. */
|
||||
int getconfig(int argc, char **argv, or_options_t *options) {
|
||||
struct config_line *cl;
|
||||
FILE *cf;
|
||||
char *fname;
|
||||
int i;
|
||||
int result = 0;
|
||||
static int first_load = 1;
|
||||
static char **backup_argv;
|
||||
static int backup_argc;
|
||||
char *previous_pidfile = NULL;
|
||||
int previous_runasdaemon = 0;
|
||||
|
||||
if(first_load) { /* first time we're called. save commandline args */
|
||||
backup_argv = argv;
|
||||
backup_argc = argc;
|
||||
first_load = 0;
|
||||
} else { /* we're reloading. need to clean up old ones first. */
|
||||
argv = backup_argv;
|
||||
argc = backup_argc;
|
||||
|
||||
/* record some previous values, so we can fail if they change */
|
||||
if(options->PidFile)
|
||||
previous_pidfile = tor_strdup(options->PidFile);
|
||||
previous_runasdaemon = options->RunAsDaemon;
|
||||
free_options(options);
|
||||
}
|
||||
init_options(options);
|
||||
|
||||
if(argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* learn config file name, get config lines, assign them */
|
||||
i = 1;
|
||||
while(i < argc-1 && strcmp(argv[i],"-f")) {
|
||||
i++;
|
||||
}
|
||||
if(i < argc-1) { /* we found one */
|
||||
fname = argv[i+1];
|
||||
} else { /* didn't find one, try CONFDIR */
|
||||
fname = CONFDIR "/torrc";
|
||||
}
|
||||
log(LOG_DEBUG,"Opening config file '%s'",fname);
|
||||
|
||||
cf = config_open(fname);
|
||||
if(!cf) {
|
||||
log(LOG_WARN, "Unable to open configuration file '%s'.",fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cl = config_get_lines(cf);
|
||||
if(!cl) return -1;
|
||||
config_assign(options,cl);
|
||||
config_free_lines(cl);
|
||||
config_close(cf);
|
||||
|
||||
/* go through command-line variables too */
|
||||
cl = config_get_commandlines(argc,argv);
|
||||
config_assign(options,cl);
|
||||
config_free_lines(cl);
|
||||
|
||||
/* Validate options */
|
||||
|
||||
/* first check if any of the previous options have changed but aren't allowed to */
|
||||
if(previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
|
||||
log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
|
||||
previous_pidfile, options->PidFile);
|
||||
return -1;
|
||||
}
|
||||
tor_free(previous_pidfile);
|
||||
|
||||
if(previous_runasdaemon && !options->RunAsDaemon) {
|
||||
log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(options->LogLevel) {
|
||||
if(!strcmp(options->LogLevel,"err"))
|
||||
options->loglevel = LOG_ERR;
|
||||
else if(!strcmp(options->LogLevel,"warn"))
|
||||
options->loglevel = LOG_WARN;
|
||||
else if(!strcmp(options->LogLevel,"info"))
|
||||
options->loglevel = LOG_INFO;
|
||||
else if(!strcmp(options->LogLevel,"debug"))
|
||||
options->loglevel = LOG_DEBUG;
|
||||
else {
|
||||
log(LOG_WARN,"LogLevel must be one of err|warn|info|debug.");
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(options->RouterFile == NULL) {
|
||||
log(LOG_WARN,"RouterFile option required, but not found.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->ORPort < 0) {
|
||||
log(LOG_WARN,"ORPort option can't be negative.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->ORPort && options->DataDirectory == NULL) {
|
||||
log(LOG_WARN,"DataDirectory option required if ORPort is set, but not found.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->ORPort && options->Nickname == NULL) {
|
||||
log_fn(LOG_WARN,"Nickname required if ORPort is set, but not found.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->SocksPort < 0) {
|
||||
log(LOG_WARN,"SocksPort option can't be negative.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->SocksPort == 0 && options->ORPort == 0) {
|
||||
log(LOG_WARN,"SocksPort and ORPort are both undefined? Quitting.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->DirPort < 0) {
|
||||
log(LOG_WARN,"DirPort option can't be negative.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->SocksPort > 1 &&
|
||||
(options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
|
||||
log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->MaxConn < 1) {
|
||||
log(LOG_WARN,"MaxConn option must be a non-zero positive integer.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->MaxConn >= MAXCONNECTIONS) {
|
||||
log(LOG_WARN,"MaxConn option must be less than %d.", MAXCONNECTIONS);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->DirFetchPostPeriod < 1) {
|
||||
log(LOG_WARN,"DirFetchPostPeriod option must be positive.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if(options->KeepalivePeriod < 1) {
|
||||
log(LOG_WARN,"KeepalivePeriod option must be positive.");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,838 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/********* START VARIABLES **********/
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
extern int global_read_bucket;
|
||||
|
||||
char *conn_type_to_string[] = {
|
||||
"", /* 0 */
|
||||
"OP listener", /* 1 */
|
||||
"OP", /* 2 */
|
||||
"OR listener", /* 3 */
|
||||
"OR", /* 4 */
|
||||
"Exit", /* 5 */
|
||||
"App listener",/* 6 */
|
||||
"App", /* 7 */
|
||||
"Dir listener",/* 8 */
|
||||
"Dir", /* 9 */
|
||||
"DNS worker", /* 10 */
|
||||
"CPU worker", /* 11 */
|
||||
};
|
||||
|
||||
char *conn_state_to_string[][_CONN_TYPE_MAX+1] = {
|
||||
{ NULL }, /* no type associated with 0 */
|
||||
{ NULL }, /* op listener, obsolete */
|
||||
{ NULL }, /* op, obsolete */
|
||||
{ "ready" }, /* or listener, 0 */
|
||||
{ "", /* OR, 0 */
|
||||
"connect()ing", /* 1 */
|
||||
"handshaking", /* 2 */
|
||||
"open" }, /* 3 */
|
||||
{ "", /* exit, 0 */
|
||||
"waiting for dest info", /* 1 */
|
||||
"connecting", /* 2 */
|
||||
"open" }, /* 3 */
|
||||
{ "ready" }, /* app listener, 0 */
|
||||
{ "", /* 0 */
|
||||
"", /* 1 */
|
||||
"", /* 2 */
|
||||
"", /* 3 */
|
||||
"awaiting dest info", /* app, 4 */
|
||||
"waiting for safe circuit", /* 5 */
|
||||
"open" }, /* 6 */
|
||||
{ "ready" }, /* dir listener, 0 */
|
||||
{ "", /* dir, 0 */
|
||||
"connecting (fetch)", /* 1 */
|
||||
"connecting (upload)", /* 2 */
|
||||
"client sending fetch", /* 3 */
|
||||
"client sending upload", /* 4 */
|
||||
"client reading fetch", /* 5 */
|
||||
"client reading upload", /* 6 */
|
||||
"awaiting command", /* 7 */
|
||||
"writing" }, /* 8 */
|
||||
{ "", /* dns worker, 0 */
|
||||
"idle", /* 1 */
|
||||
"busy" }, /* 2 */
|
||||
{ "", /* cpu worker, 0 */
|
||||
"idle", /* 1 */
|
||||
"busy with onion", /* 2 */
|
||||
"busy with handshake" }, /* 3 */
|
||||
};
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
|
||||
static int connection_init_accepted_conn(connection_t *conn);
|
||||
static int connection_handle_listener_read(connection_t *conn, int new_type);
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
connection_t *connection_new(int type) {
|
||||
connection_t *conn;
|
||||
time_t now = time(NULL);
|
||||
|
||||
conn = tor_malloc_zero(sizeof(connection_t));
|
||||
conn->s = -1; /* give it a default of 'not used' */
|
||||
|
||||
conn->type = type;
|
||||
if(!connection_is_listener(conn)) { /* listeners never use their buf */
|
||||
conn->inbuf = buf_new();
|
||||
conn->outbuf = buf_new();
|
||||
}
|
||||
if (type == CONN_TYPE_AP) {
|
||||
conn->socks_request = tor_malloc_zero(sizeof(socks_request_t));
|
||||
}
|
||||
|
||||
conn->next_circ_id = crypto_pseudo_rand_int(1<<15);
|
||||
|
||||
conn->timestamp_created = now;
|
||||
conn->timestamp_lastread = now;
|
||||
conn->timestamp_lastwritten = now;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void connection_free(connection_t *conn) {
|
||||
assert(conn);
|
||||
|
||||
if(!connection_is_listener(conn)) {
|
||||
buf_free(conn->inbuf);
|
||||
buf_free(conn->outbuf);
|
||||
}
|
||||
tor_free(conn->address);
|
||||
|
||||
if(connection_speaks_cells(conn)) {
|
||||
directory_set_dirty();
|
||||
if (conn->tls)
|
||||
tor_tls_free(conn->tls);
|
||||
}
|
||||
|
||||
if (conn->onion_pkey)
|
||||
crypto_free_pk_env(conn->onion_pkey);
|
||||
if (conn->link_pkey)
|
||||
crypto_free_pk_env(conn->link_pkey);
|
||||
if (conn->identity_pkey)
|
||||
crypto_free_pk_env(conn->identity_pkey);
|
||||
tor_free(conn->nickname);
|
||||
tor_free(conn->socks_request);
|
||||
|
||||
if(conn->s >= 0) {
|
||||
log_fn(LOG_INFO,"closing fd %d.",conn->s);
|
||||
close(conn->s);
|
||||
}
|
||||
free(conn);
|
||||
}
|
||||
|
||||
void connection_free_all(void) {
|
||||
int i, n;
|
||||
connection_t **carray;
|
||||
|
||||
get_connection_array(&carray,&n);
|
||||
for(i=0;i<n;i++)
|
||||
connection_free(carray[i]);
|
||||
}
|
||||
|
||||
int connection_create_listener(char *bindaddress, uint16_t bindport, int type) {
|
||||
struct sockaddr_in bindaddr; /* where to bind */
|
||||
struct hostent *rent;
|
||||
connection_t *conn;
|
||||
int s; /* the socket we're going to make */
|
||||
int one=1;
|
||||
|
||||
memset(&bindaddr,0,sizeof(struct sockaddr_in));
|
||||
bindaddr.sin_family = AF_INET;
|
||||
bindaddr.sin_port = htons(bindport);
|
||||
rent = gethostbyname(bindaddress);
|
||||
if (!rent) {
|
||||
log_fn(LOG_WARN,"Can't resolve BindAddress %s",bindaddress);
|
||||
return -1;
|
||||
}
|
||||
if(rent->h_length != 4)
|
||||
return -1; /* XXX complain */
|
||||
memcpy(&(bindaddr.sin_addr.s_addr),rent->h_addr,rent->h_length);
|
||||
|
||||
s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
|
||||
if (s < 0) {
|
||||
log_fn(LOG_WARN,"Socket creation failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
|
||||
if(bind(s,(struct sockaddr *)&bindaddr,sizeof(bindaddr)) < 0) {
|
||||
log_fn(LOG_WARN,"Could not bind to port %u: %s",bindport,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(listen(s,SOMAXCONN) < 0) {
|
||||
log_fn(LOG_WARN,"Could not listen on port %u: %s",bindport,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_socket_nonblocking(s);
|
||||
|
||||
conn = connection_new(type);
|
||||
conn->s = s;
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
log_fn(LOG_WARN,"connection_add failed. Giving up.");
|
||||
connection_free(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG,"%s listening on port %u.",conn_type_to_string[type], bindport);
|
||||
|
||||
conn->state = LISTENER_STATE_READY;
|
||||
connection_start_reading(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_handle_listener_read(connection_t *conn, int new_type) {
|
||||
int news; /* the new socket */
|
||||
connection_t *newconn;
|
||||
struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */
|
||||
int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */
|
||||
#ifdef MS_WINDOWS
|
||||
int e;
|
||||
#endif
|
||||
|
||||
news = accept(conn->s,(struct sockaddr *)&remote,&remotelen);
|
||||
if (news == -1) { /* accept() error */
|
||||
if(ERRNO_EAGAIN(errno)) {
|
||||
#ifdef MS_WINDOWS
|
||||
e = correct_socket_errno(conn->s);
|
||||
if (ERRNO_EAGAIN(e))
|
||||
return 0;
|
||||
#else
|
||||
return 0; /* he hung up before we could accept(). that's fine. */
|
||||
#endif
|
||||
}
|
||||
/* else there was a real error. */
|
||||
log_fn(LOG_WARN,"accept() failed. Closing listener.");
|
||||
return -1;
|
||||
}
|
||||
log(LOG_INFO,"Connection accepted on socket %d (child of fd %d).",news, conn->s);
|
||||
|
||||
set_socket_nonblocking(news);
|
||||
|
||||
newconn = connection_new(new_type);
|
||||
newconn->s = news;
|
||||
|
||||
newconn->address = tor_strdup(inet_ntoa(remote.sin_addr)); /* remember the remote address */
|
||||
newconn->addr = ntohl(remote.sin_addr.s_addr);
|
||||
newconn->port = ntohs(remote.sin_port);
|
||||
|
||||
if(connection_add(newconn) < 0) { /* no space, forget it */
|
||||
connection_free(newconn);
|
||||
return 0; /* no need to tear down the parent */
|
||||
}
|
||||
|
||||
if(connection_init_accepted_conn(newconn) < 0) {
|
||||
newconn->marked_for_close = 1;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_init_accepted_conn(connection_t *conn) {
|
||||
|
||||
connection_start_reading(conn);
|
||||
|
||||
switch(conn->type) {
|
||||
case CONN_TYPE_OR:
|
||||
return connection_tls_start_handshake(conn, 1);
|
||||
case CONN_TYPE_AP:
|
||||
conn->state = AP_CONN_STATE_SOCKS_WAIT;
|
||||
break;
|
||||
case CONN_TYPE_DIR:
|
||||
conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* take conn, make a nonblocking socket; try to connect to
|
||||
* addr:port (they arrive in *host order*). If fail, return -1. Else
|
||||
* assign s to conn->s: if connected return 1, if eagain return 0.
|
||||
* address is used to make the logs useful.
|
||||
*/
|
||||
int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port) {
|
||||
int s;
|
||||
struct sockaddr_in dest_addr;
|
||||
|
||||
s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
|
||||
if (s < 0) {
|
||||
log_fn(LOG_WARN,"Error creating network socket.");
|
||||
return -1;
|
||||
}
|
||||
set_socket_nonblocking(s);
|
||||
|
||||
memset(&dest_addr,0,sizeof(dest_addr));
|
||||
dest_addr.sin_family = AF_INET;
|
||||
dest_addr.sin_port = htons(port);
|
||||
dest_addr.sin_addr.s_addr = htonl(addr);
|
||||
|
||||
log_fn(LOG_DEBUG,"Connecting to %s:%u.",address,port);
|
||||
|
||||
if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0) {
|
||||
if(!ERRNO_CONN_EINPROGRESS(errno)) {
|
||||
/* yuck. kill it. */
|
||||
log_fn(LOG_INFO,"Connect() to %s:%u failed: %s",address,port,strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
} else {
|
||||
/* it's in progress. set state appropriately and return. */
|
||||
conn->s = s;
|
||||
log_fn(LOG_DEBUG,"connect in progress, socket %d.",s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* it succeeded. we're connected. */
|
||||
log_fn(LOG_INFO,"Connection to %s:%u established.",address,port);
|
||||
conn->s = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void listener_close_if_present(int type) {
|
||||
connection_t *conn = connection_get_by_type(type);
|
||||
if (conn) {
|
||||
close(conn->s);
|
||||
conn->s = -1;
|
||||
conn->marked_for_close = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* start all connections that should be up but aren't */
|
||||
int retry_all_connections(void) {
|
||||
|
||||
if(options.ORPort) {
|
||||
router_retry_connections();
|
||||
}
|
||||
|
||||
if(options.ORPort) {
|
||||
listener_close_if_present(CONN_TYPE_OR_LISTENER);
|
||||
if(connection_create_listener(options.ORBindAddress, options.ORPort,
|
||||
CONN_TYPE_OR_LISTENER) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(options.DirPort) {
|
||||
listener_close_if_present(CONN_TYPE_DIR_LISTENER);
|
||||
if(connection_create_listener(options.DirBindAddress, options.DirPort,
|
||||
CONN_TYPE_DIR_LISTENER) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(options.SocksPort) {
|
||||
listener_close_if_present(CONN_TYPE_AP_LISTENER);
|
||||
if(connection_create_listener(options.SocksBindAddress, options.SocksPort,
|
||||
CONN_TYPE_AP_LISTENER) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_handle_read(connection_t *conn) {
|
||||
|
||||
conn->timestamp_lastread = time(NULL);
|
||||
|
||||
switch(conn->type) {
|
||||
case CONN_TYPE_OR_LISTENER:
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_OR);
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_AP);
|
||||
case CONN_TYPE_DIR_LISTENER:
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_DIR);
|
||||
}
|
||||
|
||||
if(connection_read_to_buf(conn) < 0) {
|
||||
if(conn->type == CONN_TYPE_DIR &&
|
||||
(conn->state == DIR_CONN_STATE_CONNECTING_FETCH ||
|
||||
conn->state == DIR_CONN_STATE_CONNECTING_UPLOAD)) {
|
||||
/* it's a directory server and connecting failed: forget about this router */
|
||||
/* XXX I suspect pollerr may make Windows not get to this point. :( */
|
||||
router_mark_as_down(conn->nickname);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if(connection_process_inbuf(conn) < 0) {
|
||||
// log_fn(LOG_DEBUG,"connection_process_inbuf returned -1.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return -1 if we want to break conn, else return 0 */
|
||||
int connection_read_to_buf(connection_t *conn) {
|
||||
int result;
|
||||
int at_most;
|
||||
|
||||
if(options.LinkPadding) {
|
||||
at_most = global_read_bucket;
|
||||
} else {
|
||||
/* do a rudimentary round-robin so one connection can't hog a thickpipe */
|
||||
if(connection_speaks_cells(conn)) {
|
||||
at_most = 30*(CELL_NETWORK_SIZE);
|
||||
} else {
|
||||
at_most = 30*(RELAY_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
if(at_most > global_read_bucket)
|
||||
at_most = global_read_bucket;
|
||||
}
|
||||
|
||||
if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) {
|
||||
if(conn->state == OR_CONN_STATE_HANDSHAKING)
|
||||
return connection_tls_continue_handshake(conn);
|
||||
|
||||
/* else open, or closing */
|
||||
if(at_most > conn->receiver_bucket)
|
||||
at_most = conn->receiver_bucket;
|
||||
result = read_to_buf_tls(conn->tls, at_most, conn->inbuf);
|
||||
|
||||
switch(result) {
|
||||
case TOR_TLS_ERROR:
|
||||
case TOR_TLS_CLOSE:
|
||||
log_fn(LOG_INFO,"tls error. breaking.");
|
||||
return -1; /* XXX deal with close better */
|
||||
case TOR_TLS_WANTWRITE:
|
||||
connection_start_writing(conn);
|
||||
return 0;
|
||||
case TOR_TLS_WANTREAD: /* we're already reading */
|
||||
case TOR_TLS_DONE: /* no data read, so nothing to process */
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
result = read_to_buf(conn->s, at_most, conn->inbuf,
|
||||
&conn->inbuf_reached_eof);
|
||||
|
||||
// log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result);
|
||||
|
||||
if(result < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
global_read_bucket -= result; assert(global_read_bucket >= 0);
|
||||
if(global_read_bucket == 0) {
|
||||
log_fn(LOG_DEBUG,"global bucket exhausted. Pausing.");
|
||||
conn->wants_to_read = 1;
|
||||
connection_stop_reading(conn);
|
||||
return 0;
|
||||
}
|
||||
if(connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
|
||||
conn->receiver_bucket -= result; assert(conn->receiver_bucket >= 0);
|
||||
if(conn->receiver_bucket == 0) {
|
||||
log_fn(LOG_DEBUG,"receiver bucket exhausted. Pausing.");
|
||||
conn->wants_to_read = 1;
|
||||
connection_stop_reading(conn);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_fetch_from_buf(char *string, int len, connection_t *conn) {
|
||||
return fetch_from_buf(string, len, conn->inbuf);
|
||||
}
|
||||
|
||||
int connection_find_on_inbuf(char *string, int len, connection_t *conn) {
|
||||
return find_on_inbuf(string, len, conn->inbuf);
|
||||
}
|
||||
|
||||
int connection_wants_to_flush(connection_t *conn) {
|
||||
return conn->outbuf_flushlen;
|
||||
}
|
||||
|
||||
int connection_outbuf_too_full(connection_t *conn) {
|
||||
return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
/* return -1 if you want to break the conn, else return 0 */
|
||||
int connection_handle_write(connection_t *conn) {
|
||||
|
||||
if(connection_is_listener(conn)) {
|
||||
log_fn(LOG_WARN,"Got a listener socket. Can't happen!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->timestamp_lastwritten = time(NULL);
|
||||
|
||||
if(connection_speaks_cells(conn) && conn->state != OR_CONN_STATE_CONNECTING) {
|
||||
if(conn->state == OR_CONN_STATE_HANDSHAKING) {
|
||||
connection_stop_writing(conn);
|
||||
return connection_tls_continue_handshake(conn);
|
||||
}
|
||||
|
||||
/* else open, or closing */
|
||||
switch(flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen)) {
|
||||
case TOR_TLS_ERROR:
|
||||
case TOR_TLS_CLOSE:
|
||||
log_fn(LOG_INFO,"tls error. breaking.");
|
||||
return -1; /* XXX deal with close better */
|
||||
case TOR_TLS_WANTWRITE:
|
||||
log_fn(LOG_DEBUG,"wanted write.");
|
||||
/* we're already writing */
|
||||
return 0;
|
||||
case TOR_TLS_WANTREAD:
|
||||
/* Make sure to avoid a loop if the receive buckets are empty. */
|
||||
log_fn(LOG_DEBUG,"wanted read.");
|
||||
if(!connection_is_reading(conn)) {
|
||||
connection_stop_writing(conn);
|
||||
conn->wants_to_write = 1;
|
||||
/* we'll start reading again when the next second arrives,
|
||||
* and then also start writing again.
|
||||
*/
|
||||
}
|
||||
/* else no problem, we're already reading */
|
||||
return 0;
|
||||
/* case TOR_TLS_DONE:
|
||||
* for TOR_TLS_DONE, fall through to check if the flushlen
|
||||
* is empty, so we can stop writing.
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
if(flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen) < 0)
|
||||
return -1;
|
||||
/* conns in CONNECTING state will fall through... */
|
||||
}
|
||||
|
||||
if(!connection_wants_to_flush(conn)) /* it's done flushing */
|
||||
if(connection_finished_flushing(conn) < 0) /* ...and get handled here. */
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void connection_write_to_buf(const char *string, int len, connection_t *conn) {
|
||||
|
||||
if(!len || conn->marked_for_close)
|
||||
return;
|
||||
|
||||
if(write_to_buf(string, len, conn->outbuf) < 0) {
|
||||
log_fn(LOG_WARN,"write_to_buf failed. Closing connection (fd %d).", conn->s);
|
||||
conn->marked_for_close = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX if linkpadding, this only applies to conns that aren't open OR connections */
|
||||
connection_start_writing(conn);
|
||||
conn->outbuf_flushlen += len;
|
||||
}
|
||||
|
||||
connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port) {
|
||||
int i, n;
|
||||
connection_t *conn;
|
||||
connection_t **carray;
|
||||
|
||||
get_connection_array(&carray,&n);
|
||||
for(i=0;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) &&
|
||||
!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->marked_for_close)
|
||||
return 0;
|
||||
|
||||
if((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
|
||||
(conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
|
||||
(conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_send_destroy(uint16_t circ_id, connection_t *conn) {
|
||||
cell_t cell;
|
||||
|
||||
assert(conn);
|
||||
|
||||
if(!connection_speaks_cells(conn)) {
|
||||
log_fn(LOG_INFO,"CircID %d: At an edge. Marking connection for close.",
|
||||
circ_id);
|
||||
if(connection_edge_end(conn, END_STREAM_REASON_DESTROY, conn->cpath_layer) < 0)
|
||||
log_fn(LOG_WARN,"1: I called connection_edge_end redundantly.");
|
||||
/* if they already sent a destroy, they know. XXX can just close? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&cell, 0, sizeof(cell_t));
|
||||
cell.circ_id = circ_id;
|
||||
cell.command = CELL_DESTROY;
|
||||
log_fn(LOG_INFO,"Sending destroy (circID %d).", circ_id);
|
||||
connection_or_write_cell_to_buf(&cell, conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_process_inbuf(connection_t *conn) {
|
||||
|
||||
assert(conn);
|
||||
|
||||
switch(conn->type) {
|
||||
case CONN_TYPE_OR:
|
||||
return connection_or_process_inbuf(conn);
|
||||
case CONN_TYPE_EXIT:
|
||||
case CONN_TYPE_AP:
|
||||
return connection_edge_process_inbuf(conn);
|
||||
case CONN_TYPE_DIR:
|
||||
return connection_dir_process_inbuf(conn);
|
||||
case CONN_TYPE_DNSWORKER:
|
||||
return connection_dns_process_inbuf(conn);
|
||||
case CONN_TYPE_CPUWORKER:
|
||||
return connection_cpu_process_inbuf(conn);
|
||||
default:
|
||||
log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int connection_finished_flushing(connection_t *conn) {
|
||||
|
||||
assert(conn);
|
||||
|
||||
// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s);
|
||||
|
||||
switch(conn->type) {
|
||||
case CONN_TYPE_OR:
|
||||
return connection_or_finished_flushing(conn);
|
||||
case CONN_TYPE_AP:
|
||||
case CONN_TYPE_EXIT:
|
||||
return connection_edge_finished_flushing(conn);
|
||||
case CONN_TYPE_DIR:
|
||||
return connection_dir_finished_flushing(conn);
|
||||
case CONN_TYPE_DNSWORKER:
|
||||
return connection_dns_finished_flushing(conn);
|
||||
case CONN_TYPE_CPUWORKER:
|
||||
return connection_cpu_finished_flushing(conn);
|
||||
default:
|
||||
log_fn(LOG_WARN,"got unexpected conn->type %d.", conn->type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_connection_ok(connection_t *conn, time_t now)
|
||||
{
|
||||
return;
|
||||
assert(conn);
|
||||
assert(conn->type >= _CONN_TYPE_MIN);
|
||||
assert(conn->type <= _CONN_TYPE_MAX);
|
||||
|
||||
/* XXX check: wants_to_read, wants_to_write, s, poll_index,
|
||||
* marked_for_close. */
|
||||
|
||||
/* buffers */
|
||||
if (!connection_is_listener(conn)) {
|
||||
assert(conn->inbuf);
|
||||
assert(conn->outbuf);
|
||||
}
|
||||
|
||||
assert(!now || conn->timestamp_lastread <= now);
|
||||
assert(!now || conn->timestamp_lastwritten <= now);
|
||||
assert(conn->timestamp_created <= conn->timestamp_lastread);
|
||||
assert(conn->timestamp_created <= conn->timestamp_lastwritten);
|
||||
|
||||
/* XXX Fix this; no longer so.*/
|
||||
#if 0
|
||||
if(conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR)
|
||||
assert(!conn->pkey);
|
||||
/* pkey is set if we're a dir client, or if we're an OR in state OPEN
|
||||
* connected to another OR.
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (conn->type != CONN_TYPE_OR) {
|
||||
assert(!conn->tls);
|
||||
} else {
|
||||
if(conn->state == OR_CONN_STATE_OPEN) {
|
||||
assert(conn->bandwidth > 0);
|
||||
assert(conn->receiver_bucket >= 0);
|
||||
assert(conn->receiver_bucket <= 10*conn->bandwidth);
|
||||
}
|
||||
assert(conn->addr && conn->port);
|
||||
assert(conn->address);
|
||||
if (conn->state != OR_CONN_STATE_CONNECTING)
|
||||
assert(conn->tls);
|
||||
}
|
||||
|
||||
if (conn->type != CONN_TYPE_EXIT && conn->type != CONN_TYPE_AP) {
|
||||
assert(!conn->stream_id);
|
||||
assert(!conn->next_stream);
|
||||
assert(!conn->cpath_layer);
|
||||
assert(!conn->package_window);
|
||||
assert(!conn->deliver_window);
|
||||
assert(!conn->done_sending);
|
||||
assert(!conn->done_receiving);
|
||||
} else {
|
||||
assert(!conn->next_stream ||
|
||||
conn->next_stream->type == CONN_TYPE_EXIT ||
|
||||
conn->next_stream->type == CONN_TYPE_AP);
|
||||
if(conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN)
|
||||
assert(conn->cpath_layer);
|
||||
if(conn->cpath_layer)
|
||||
assert_cpath_layer_ok(conn->cpath_layer);
|
||||
/* XXX unchecked, package window, deliver window. */
|
||||
}
|
||||
if (conn->type != CONN_TYPE_AP) {
|
||||
assert(!conn->socks_request);
|
||||
}
|
||||
|
||||
switch(conn->type)
|
||||
{
|
||||
case CONN_TYPE_OR_LISTENER:
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
case CONN_TYPE_DIR_LISTENER:
|
||||
assert(conn->state == LISTENER_STATE_READY);
|
||||
break;
|
||||
case CONN_TYPE_OR:
|
||||
assert(conn->state >= _OR_CONN_STATE_MIN &&
|
||||
conn->state <= _OR_CONN_STATE_MAX);
|
||||
break;
|
||||
case CONN_TYPE_EXIT:
|
||||
assert(conn->state >= _EXIT_CONN_STATE_MIN &&
|
||||
conn->state <= _EXIT_CONN_STATE_MAX);
|
||||
break;
|
||||
case CONN_TYPE_AP:
|
||||
assert(conn->state >= _AP_CONN_STATE_MIN &&
|
||||
conn->state <= _AP_CONN_STATE_MAX);
|
||||
assert(conn->socks_request);
|
||||
break;
|
||||
case CONN_TYPE_DIR:
|
||||
assert(conn->state >= _DIR_CONN_STATE_MIN &&
|
||||
conn->state <= _DIR_CONN_STATE_MAX);
|
||||
break;
|
||||
case CONN_TYPE_DNSWORKER:
|
||||
assert(conn->state == DNSWORKER_STATE_IDLE ||
|
||||
conn->state == DNSWORKER_STATE_BUSY);
|
||||
case CONN_TYPE_CPUWORKER:
|
||||
assert(conn->state >= _CPUWORKER_STATE_MIN &&
|
||||
conn->state <= _CPUWORKER_STATE_MAX);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
static int connection_tls_finish_handshake(connection_t *conn);
|
||||
static int connection_or_process_cell_from_inbuf(connection_t *conn);
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
static void cell_pack(char *dest, const cell_t *src) {
|
||||
*(uint16_t*)dest = htons(src->circ_id);
|
||||
*(uint8_t*)(dest+2) = src->command;
|
||||
memcpy(dest+3, src->payload, CELL_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
static void cell_unpack(cell_t *dest, const char *src) {
|
||||
dest->circ_id = ntohs(*(uint16_t*)(src));
|
||||
dest->command = *(uint8_t*)(src+2);
|
||||
memcpy(dest->payload, src+3, CELL_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
int connection_or_process_inbuf(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_OR);
|
||||
|
||||
if(conn->inbuf_reached_eof) {
|
||||
log_fn(LOG_INFO,"conn reached eof. Closing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(conn->state != OR_CONN_STATE_OPEN)
|
||||
return 0; /* don't do anything */
|
||||
return connection_or_process_cell_from_inbuf(conn);
|
||||
}
|
||||
|
||||
int connection_or_finished_flushing(connection_t *conn) {
|
||||
int e, len=sizeof(e);
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_OR);
|
||||
|
||||
switch(conn->state) {
|
||||
case OR_CONN_STATE_CONNECTING:
|
||||
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */
|
||||
if(!ERRNO_CONN_EINPROGRESS(errno)){
|
||||
log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
|
||||
return -1;
|
||||
} else {
|
||||
return 0; /* no change, see if next time is better */
|
||||
}
|
||||
}
|
||||
/* the connect has finished. */
|
||||
|
||||
log_fn(LOG_INFO,"OR connect() to router %s:%u finished.",
|
||||
conn->address,conn->port);
|
||||
|
||||
if(connection_tls_start_handshake(conn, 0) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
case OR_CONN_STATE_OPEN:
|
||||
connection_stop_writing(conn);
|
||||
return 0;
|
||||
default:
|
||||
log_fn(LOG_WARN,"BUG: called in unexpected state.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************/
|
||||
|
||||
void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router) {
|
||||
conn->addr = router->addr;
|
||||
conn->port = router->or_port;
|
||||
conn->receiver_bucket = conn->bandwidth = router->bandwidth;
|
||||
conn->onion_pkey = crypto_pk_dup_key(router->onion_pkey);
|
||||
conn->link_pkey = crypto_pk_dup_key(router->link_pkey);
|
||||
conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
|
||||
conn->nickname = tor_strdup(router->nickname);
|
||||
tor_free(conn->address);
|
||||
conn->address = tor_strdup(router->address);
|
||||
}
|
||||
|
||||
connection_t *connection_or_connect(routerinfo_t *router) {
|
||||
connection_t *conn;
|
||||
|
||||
assert(router);
|
||||
|
||||
if(options.Nickname && !strcmp(router->nickname,options.Nickname)) {
|
||||
log_fn(LOG_WARN,"You asked me to connect to myself! Failing.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* this function should never be called if we're already connected to router, but */
|
||||
/* check first to be sure */
|
||||
conn = connection_exact_get_by_addr_port(router->addr,router->or_port);
|
||||
if(conn)
|
||||
return conn;
|
||||
|
||||
conn = connection_new(CONN_TYPE_OR);
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
connection_or_init_conn_from_router(conn, router);
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(connection_connect(conn, router->address, router->addr, router->or_port)) {
|
||||
case -1:
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
return NULL;
|
||||
case 0:
|
||||
connection_set_poll_socket(conn);
|
||||
connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
|
||||
/* writable indicates finish, readable indicates broken link,
|
||||
error indicates broken link on windows */
|
||||
conn->state = OR_CONN_STATE_CONNECTING;
|
||||
return conn;
|
||||
/* case 1: fall through */
|
||||
}
|
||||
|
||||
connection_set_poll_socket(conn);
|
||||
|
||||
if(connection_tls_start_handshake(conn, 0) >= 0)
|
||||
return conn;
|
||||
|
||||
/* failure */
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ********************************** */
|
||||
|
||||
int connection_tls_start_handshake(connection_t *conn, int receiving) {
|
||||
conn->state = OR_CONN_STATE_HANDSHAKING;
|
||||
conn->tls = tor_tls_new(conn->s, receiving);
|
||||
if(!conn->tls) {
|
||||
log_fn(LOG_WARN,"tor_tls_new failed. Closing.");
|
||||
return -1;
|
||||
}
|
||||
connection_start_reading(conn);
|
||||
log_fn(LOG_DEBUG,"starting the handshake");
|
||||
if(connection_tls_continue_handshake(conn) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_tls_continue_handshake(connection_t *conn) {
|
||||
switch(tor_tls_handshake(conn->tls)) {
|
||||
case TOR_TLS_ERROR:
|
||||
case TOR_TLS_CLOSE:
|
||||
log_fn(LOG_INFO,"tls error. breaking.");
|
||||
return -1;
|
||||
case TOR_TLS_DONE:
|
||||
return connection_tls_finish_handshake(conn);
|
||||
case TOR_TLS_WANTWRITE:
|
||||
connection_start_writing(conn);
|
||||
log_fn(LOG_DEBUG,"wanted write");
|
||||
return 0;
|
||||
case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */
|
||||
log_fn(LOG_DEBUG,"wanted read");
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connection_tls_finish_handshake(connection_t *conn) {
|
||||
crypto_pk_env_t *pk;
|
||||
routerinfo_t *router;
|
||||
char nickname[MAX_NICKNAME_LEN+1];
|
||||
|
||||
conn->state = OR_CONN_STATE_OPEN;
|
||||
directory_set_dirty();
|
||||
connection_watch_events(conn, POLLIN);
|
||||
log_fn(LOG_DEBUG,"tls handshake done. verifying.");
|
||||
if (! tor_tls_peer_has_cert(conn->tls)) { /* It's an OP. */
|
||||
if (options.ORPort) { /* I'm an OR; good. */
|
||||
conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP;
|
||||
return 0;
|
||||
} else { /* Neither side sent a certificate: ouch. */
|
||||
log_fn(LOG_WARN,"Neither peer sent a cert! Closing.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Okay; the other side is an OR. */
|
||||
if (tor_tls_get_peer_cert_nickname(conn->tls, nickname, MAX_NICKNAME_LEN)) {
|
||||
log_fn(LOG_WARN,"Other side (%s:%d) has a cert without a valid nickname. Closing.",
|
||||
conn->address, conn->port);
|
||||
return -1;
|
||||
}
|
||||
log_fn(LOG_DEBUG, "Other side claims to be '%s'", nickname);
|
||||
pk = tor_tls_verify(conn->tls);
|
||||
if(!pk) {
|
||||
log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.",
|
||||
nickname, conn->address, conn->port);
|
||||
return -1;
|
||||
}
|
||||
router = router_get_by_link_pk(pk);
|
||||
if (!router) {
|
||||
log_fn(LOG_WARN,"Unrecognized public key from peer '%s' (%s:%d). Closing.",
|
||||
nickname, conn->address, conn->port);
|
||||
crypto_free_pk_env(pk);
|
||||
return -1;
|
||||
}
|
||||
if(conn->link_pkey) { /* I initiated this connection. */
|
||||
if(crypto_pk_cmp_keys(conn->link_pkey, pk)) {
|
||||
log_fn(LOG_WARN,"We connected to '%s' (%s:%d) but he gave us a different key. Closing.",
|
||||
nickname, conn->address, conn->port);
|
||||
crypto_free_pk_env(pk);
|
||||
return -1;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good.");
|
||||
} else {
|
||||
if(connection_exact_get_by_addr_port(router->addr,router->or_port)) {
|
||||
log_fn(LOG_INFO,"Router %s is already connected. Dropping.", router->nickname);
|
||||
crypto_free_pk_env(pk);
|
||||
return -1;
|
||||
}
|
||||
connection_or_init_conn_from_router(conn, router);
|
||||
}
|
||||
crypto_free_pk_env(pk);
|
||||
if (strcmp(conn->nickname, nickname)) {
|
||||
log_fn(LOG_WARN,"Other side claims to be '%s', but we wanted '%s'",
|
||||
nickname, conn->nickname);
|
||||
return -1;
|
||||
}
|
||||
if (!options.ORPort) { /* If I'm an OP... */
|
||||
conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP;
|
||||
circuit_n_conn_open(conn); /* send the pending creates, if any. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ********************************** */
|
||||
|
||||
void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn) {
|
||||
char networkcell[CELL_NETWORK_SIZE];
|
||||
char *n = networkcell;
|
||||
|
||||
assert(cell && conn);
|
||||
assert(connection_speaks_cells(conn));
|
||||
|
||||
cell_pack(n, cell);
|
||||
|
||||
connection_write_to_buf(n, CELL_NETWORK_SIZE, conn);
|
||||
}
|
||||
|
||||
/* if there's a whole cell there, pull it off and process it. */
|
||||
static int connection_or_process_cell_from_inbuf(connection_t *conn) {
|
||||
char buf[CELL_NETWORK_SIZE];
|
||||
cell_t cell;
|
||||
|
||||
loop:
|
||||
log_fn(LOG_DEBUG,"%d: starting, inbuf_datalen %d (%d pending in tls object).",
|
||||
conn->s,(int)buf_datalen(conn->inbuf),tor_tls_get_pending_bytes(conn->tls));
|
||||
if(buf_datalen(conn->inbuf) < CELL_NETWORK_SIZE) /* entire response available? */
|
||||
return 0; /* not yet */
|
||||
|
||||
connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, conn);
|
||||
|
||||
/* retrieve cell info from buf (create the host-order struct from the
|
||||
* network-order string) */
|
||||
cell_unpack(&cell, buf);
|
||||
|
||||
command_process_cell(&cell, conn);
|
||||
|
||||
goto loop; /* process the remainder of the buffer */
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,288 @@
|
||||
/* Copyright 2003 Roger Dingledine. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
#define MAX_CPUWORKERS 16
|
||||
#define MIN_CPUWORKERS 1
|
||||
|
||||
#define TAG_LEN 8
|
||||
#define LEN_ONION_QUESTION (1+TAG_LEN+ONIONSKIN_CHALLENGE_LEN)
|
||||
#define LEN_ONION_RESPONSE (1+TAG_LEN+ONIONSKIN_REPLY_LEN+40+32)
|
||||
|
||||
int num_cpuworkers=0;
|
||||
int num_cpuworkers_busy=0;
|
||||
|
||||
int cpuworker_main(void *data);
|
||||
static int spawn_cpuworker(void);
|
||||
static void spawn_enough_cpuworkers(void);
|
||||
static void process_pending_task(connection_t *cpuworker);
|
||||
|
||||
void cpu_init(void) {
|
||||
spawn_enough_cpuworkers();
|
||||
}
|
||||
|
||||
int connection_cpu_finished_flushing(connection_t *conn) {
|
||||
assert(conn && conn->type == CONN_TYPE_CPUWORKER);
|
||||
connection_stop_writing(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tag_pack(char *tag, uint32_t addr, uint16_t port, uint16_t circ_id) {
|
||||
*(uint32_t *)tag = addr;
|
||||
*(uint16_t *)(tag+4) = port;
|
||||
*(uint16_t *)(tag+6) = circ_id;
|
||||
}
|
||||
|
||||
static void tag_unpack(char *tag, uint32_t *addr, uint16_t *port, uint16_t *circ_id) {
|
||||
struct in_addr in;
|
||||
|
||||
*addr = *(uint32_t *)tag;
|
||||
*port = *(uint16_t *)(tag+4);
|
||||
*circ_id = *(uint16_t *)(tag+6);
|
||||
|
||||
in.s_addr = htonl(*addr);
|
||||
log_fn(LOG_DEBUG,"onion was from %s:%d, circ_id %d.", inet_ntoa(in), *port, *circ_id);
|
||||
}
|
||||
|
||||
int connection_cpu_process_inbuf(connection_t *conn) {
|
||||
unsigned char buf[LEN_ONION_RESPONSE];
|
||||
uint32_t addr;
|
||||
uint16_t port;
|
||||
uint16_t circ_id;
|
||||
connection_t *p_conn;
|
||||
circuit_t *circ;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_CPUWORKER);
|
||||
|
||||
if(conn->inbuf_reached_eof) {
|
||||
log_fn(LOG_WARN,"Read eof. Worker has died.");
|
||||
if(conn->state != CPUWORKER_STATE_IDLE) {
|
||||
/* the circ associated with this cpuworker will have to wait until
|
||||
* it gets culled in run_connection_housekeeping(), since we have
|
||||
* no way to find out which circ it was. */
|
||||
log_fn(LOG_WARN,"...and leaving a circuit waiting. Oh well.");
|
||||
num_cpuworkers_busy--;
|
||||
}
|
||||
num_cpuworkers--;
|
||||
spawn_enough_cpuworkers(); /* try to regrow. hope we don't end up spinning. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(conn->state == CPUWORKER_STATE_BUSY_ONION) {
|
||||
if(buf_datalen(conn->inbuf) < LEN_ONION_RESPONSE) /* entire answer available? */
|
||||
return 0; /* not yet */
|
||||
assert(buf_datalen(conn->inbuf) == LEN_ONION_RESPONSE);
|
||||
|
||||
connection_fetch_from_buf(buf,LEN_ONION_RESPONSE,conn);
|
||||
|
||||
/* parse out the circ it was talking about */
|
||||
tag_unpack(buf+1, &addr, &port, &circ_id);
|
||||
circ = NULL;
|
||||
p_conn = connection_exact_get_by_addr_port(addr,port);
|
||||
if(p_conn)
|
||||
circ = circuit_get_by_circ_id_conn(circ_id, p_conn);
|
||||
|
||||
if(!circ) {
|
||||
log_fn(LOG_INFO,"processed onion for a circ that's gone. Dropping.");
|
||||
goto done_processing;
|
||||
}
|
||||
assert(circ->p_conn);
|
||||
if(*buf == 0) {
|
||||
log_fn(LOG_WARN,"decoding onionskin failed. Closing.");
|
||||
circuit_close(circ);
|
||||
goto done_processing;
|
||||
}
|
||||
if(onionskin_answer(circ, buf+1+TAG_LEN, buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
|
||||
log_fn(LOG_WARN,"onionskin_answer failed. Closing.");
|
||||
circuit_close(circ);
|
||||
goto done_processing;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"onionskin_answer succeeded. Yay.");
|
||||
} else {
|
||||
assert(0); /* don't ask me to do handshakes yet */
|
||||
}
|
||||
|
||||
done_processing:
|
||||
conn->state = CPUWORKER_STATE_IDLE;
|
||||
num_cpuworkers_busy--;
|
||||
process_pending_task(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpuworker_main(void *data) {
|
||||
unsigned char question[ONIONSKIN_CHALLENGE_LEN];
|
||||
unsigned char question_type;
|
||||
int *fdarray = data;
|
||||
int fd;
|
||||
|
||||
/* variables for onion processing */
|
||||
unsigned char keys[40+32];
|
||||
unsigned char reply_to_proxy[ONIONSKIN_REPLY_LEN];
|
||||
unsigned char buf[LEN_ONION_RESPONSE];
|
||||
char tag[TAG_LEN];
|
||||
|
||||
close(fdarray[0]); /* this is the side of the socketpair the parent uses */
|
||||
fd = fdarray[1]; /* this side is ours */
|
||||
connection_free_all(); /* so the child doesn't hold the parent's fd's open */
|
||||
|
||||
for(;;) {
|
||||
|
||||
if(read(fd, &question_type, 1) != 1) {
|
||||
log_fn(LOG_ERR,"read type failed. Exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
assert(question_type == CPUWORKER_TASK_ONION);
|
||||
|
||||
if(read_all(fd, tag, TAG_LEN) != TAG_LEN) {
|
||||
log_fn(LOG_ERR,"read tag failed. Exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
|
||||
if(read_all(fd, question, ONIONSKIN_CHALLENGE_LEN) != ONIONSKIN_CHALLENGE_LEN) {
|
||||
log_fn(LOG_ERR,"read question failed. Exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
|
||||
if(question_type == CPUWORKER_TASK_ONION) {
|
||||
if(onion_skin_server_handshake(question, get_onion_key(),
|
||||
reply_to_proxy, keys, 40+32) < 0) {
|
||||
/* failure */
|
||||
log_fn(LOG_WARN,"onion_skin_server_handshake failed.");
|
||||
memset(buf,0,LEN_ONION_RESPONSE); /* send all zeros for failure */
|
||||
} else {
|
||||
/* success */
|
||||
log_fn(LOG_INFO,"onion_skin_server_handshake succeeded.");
|
||||
buf[0] = 1; /* 1 means success */
|
||||
memcpy(buf+1,tag,TAG_LEN);
|
||||
memcpy(buf+1+TAG_LEN,reply_to_proxy,ONIONSKIN_REPLY_LEN);
|
||||
memcpy(buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN,keys,40+32);
|
||||
}
|
||||
if(write_all(fd, buf, LEN_ONION_RESPONSE) != LEN_ONION_RESPONSE) {
|
||||
log_fn(LOG_ERR,"writing response buf failed. Exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
log_fn(LOG_DEBUG,"finished writing response.");
|
||||
}
|
||||
}
|
||||
return 0; /* windows wants this function to return an int */
|
||||
}
|
||||
|
||||
static int spawn_cpuworker(void) {
|
||||
int fd[2];
|
||||
connection_t *conn;
|
||||
|
||||
if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
|
||||
log(LOG_ERR, "Couldn't construct socketpair: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
spawn_func(cpuworker_main, (void*)fd);
|
||||
log_fn(LOG_DEBUG,"just spawned a worker.");
|
||||
close(fd[1]); /* we don't need the worker's side of the pipe */
|
||||
|
||||
conn = connection_new(CONN_TYPE_CPUWORKER);
|
||||
|
||||
set_socket_nonblocking(fd[0]);
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
conn->s = fd[0];
|
||||
conn->address = tor_strdup("localhost");
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
log_fn(LOG_WARN,"connection_add failed. Giving up.");
|
||||
connection_free(conn); /* this closes fd[0] */
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->state = CPUWORKER_STATE_IDLE;
|
||||
connection_start_reading(conn);
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static void spawn_enough_cpuworkers(void) {
|
||||
int num_cpuworkers_needed = options.NumCpus;
|
||||
|
||||
if(num_cpuworkers_needed < MIN_CPUWORKERS)
|
||||
num_cpuworkers_needed = MIN_CPUWORKERS;
|
||||
if(num_cpuworkers_needed > MAX_CPUWORKERS)
|
||||
num_cpuworkers_needed = MAX_CPUWORKERS;
|
||||
|
||||
while(num_cpuworkers < num_cpuworkers_needed) {
|
||||
if(spawn_cpuworker() < 0) {
|
||||
log_fn(LOG_WARN,"spawn failed!");
|
||||
return;
|
||||
}
|
||||
num_cpuworkers++;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_pending_task(connection_t *cpuworker) {
|
||||
circuit_t *circ;
|
||||
|
||||
assert(cpuworker);
|
||||
|
||||
/* for now only process onion tasks */
|
||||
|
||||
circ = onion_next_task();
|
||||
if(!circ)
|
||||
return;
|
||||
if(assign_to_cpuworker(cpuworker, CPUWORKER_TASK_ONION, circ) < 0)
|
||||
log_fn(LOG_WARN,"assign_to_cpuworker failed. Ignoring.");
|
||||
}
|
||||
|
||||
/* if cpuworker is defined, assert that he's idle, and use him. else,
|
||||
* look for an idle cpuworker and use him. if none idle, queue task onto
|
||||
* the pending onion list and return.
|
||||
* If question_type is CPUWORKER_TASK_ONION then task is a circ.
|
||||
* No other question_types are allowed.
|
||||
*/
|
||||
int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type,
|
||||
void *task) {
|
||||
circuit_t *circ;
|
||||
char tag[TAG_LEN];
|
||||
|
||||
assert(question_type == CPUWORKER_TASK_ONION);
|
||||
|
||||
if(question_type == CPUWORKER_TASK_ONION) {
|
||||
circ = task;
|
||||
|
||||
if(num_cpuworkers_busy == num_cpuworkers) {
|
||||
log_fn(LOG_DEBUG,"No idle cpuworkers. Queuing.");
|
||||
if(onion_pending_add(circ) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!cpuworker)
|
||||
cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, CPUWORKER_STATE_IDLE);
|
||||
|
||||
assert(cpuworker);
|
||||
|
||||
if(!circ->p_conn) {
|
||||
log_fn(LOG_INFO,"circ->p_conn gone. Failing circ.");
|
||||
return -1;
|
||||
}
|
||||
tag_pack(tag, circ->p_conn->addr, circ->p_conn->port, circ->p_circ_id);
|
||||
|
||||
cpuworker->state = CPUWORKER_STATE_BUSY_ONION;
|
||||
num_cpuworkers_busy++;
|
||||
|
||||
connection_write_to_buf(&question_type, 1, cpuworker);
|
||||
connection_write_to_buf(tag, sizeof(tag), cpuworker);
|
||||
connection_write_to_buf(circ->onionskin, ONIONSKIN_CHALLENGE_LEN, cpuworker);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
static int directory_send_command(connection_t *conn, int command);
|
||||
static int directory_handle_command(connection_t *conn);
|
||||
|
||||
/********* START VARIABLES **********/
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
static char fetchstring[] = "GET / HTTP/1.0\r\n\r\n";
|
||||
static char answerstring[] = "HTTP/1.0 200 OK\r\n\r\n";
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
|
||||
void directory_initiate_command(routerinfo_t *router, int command) {
|
||||
connection_t *conn;
|
||||
|
||||
if (command == DIR_CONN_STATE_CONNECTING_FETCH)
|
||||
log_fn(LOG_DEBUG,"initiating directory fetch");
|
||||
else
|
||||
log_fn(LOG_DEBUG,"initiating directory upload");
|
||||
|
||||
if (!router) { /* i guess they didn't have one in mind for me to use */
|
||||
log_fn(LOG_WARN,"No running dirservers known. Not trying.");
|
||||
return;
|
||||
}
|
||||
|
||||
conn = connection_new(CONN_TYPE_DIR);
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
conn->addr = router->addr;
|
||||
conn->port = router->dir_port;
|
||||
conn->address = tor_strdup(router->address);
|
||||
conn->nickname = tor_strdup(router->nickname);
|
||||
assert(router->identity_pkey);
|
||||
conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(connection_connect(conn, router->address, router->addr, router->dir_port)) {
|
||||
case -1:
|
||||
router_mark_as_down(conn->nickname); /* don't try him again */
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
return;
|
||||
case 0:
|
||||
connection_set_poll_socket(conn);
|
||||
connection_watch_events(conn, POLLIN | POLLOUT | POLLERR);
|
||||
/* writable indicates finish, readable indicates broken link,
|
||||
error indicates broken link in windowsland. */
|
||||
conn->state = command;
|
||||
return;
|
||||
/* case 1: fall through */
|
||||
}
|
||||
|
||||
connection_set_poll_socket(conn);
|
||||
if(directory_send_command(conn, command) < 0) {
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
}
|
||||
}
|
||||
|
||||
static int directory_send_command(connection_t *conn, int command) {
|
||||
const char *s;
|
||||
char tmp[8192];
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
switch(command) {
|
||||
case DIR_CONN_STATE_CONNECTING_FETCH:
|
||||
connection_write_to_buf(fetchstring, strlen(fetchstring), conn);
|
||||
conn->state = DIR_CONN_STATE_CLIENT_SENDING_FETCH;
|
||||
break;
|
||||
case DIR_CONN_STATE_CONNECTING_UPLOAD:
|
||||
s = router_get_my_descriptor();
|
||||
if(!s) {
|
||||
log_fn(LOG_WARN,"Failed to get my descriptor.");
|
||||
return -1;
|
||||
}
|
||||
snprintf(tmp, sizeof(tmp), "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n%s",
|
||||
(int)strlen(s), s);
|
||||
connection_write_to_buf(tmp, strlen(tmp), conn);
|
||||
conn->state = DIR_CONN_STATE_CLIENT_SENDING_UPLOAD;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_dir_process_inbuf(connection_t *conn) {
|
||||
char *directory;
|
||||
int directorylen=0;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
if(conn->inbuf_reached_eof) {
|
||||
switch(conn->state) {
|
||||
case DIR_CONN_STATE_CLIENT_READING_FETCH:
|
||||
/* kill it, but first fetch/process the directory to learn about new routers. */
|
||||
switch(fetch_from_buf_http(conn->inbuf,
|
||||
NULL, 0, &directory, MAX_DIR_SIZE)) {
|
||||
case -1: /* overflow */
|
||||
log_fn(LOG_WARN,"'fetch' response too large. Failing.");
|
||||
return -1;
|
||||
case 0:
|
||||
log_fn(LOG_INFO,"'fetch' response not all here, but we're at eof. Closing.");
|
||||
return -1;
|
||||
/* case 1, fall through */
|
||||
}
|
||||
/* XXX check headers, at least make sure returned 2xx */
|
||||
directorylen = strlen(directory);
|
||||
log_fn(LOG_INFO,"Received directory (size %d):\n%s", directorylen, directory);
|
||||
if(directorylen == 0) {
|
||||
log_fn(LOG_INFO,"Empty directory. Ignoring.");
|
||||
free(directory);
|
||||
return -1;
|
||||
}
|
||||
if(router_set_routerlist_from_directory(directory, conn->identity_pkey) < 0){
|
||||
log_fn(LOG_INFO,"...but parsing failed. Ignoring.");
|
||||
} else {
|
||||
log_fn(LOG_INFO,"updated routers.");
|
||||
}
|
||||
if(options.ORPort) { /* connect to them all */
|
||||
router_retry_connections();
|
||||
}
|
||||
free(directory);
|
||||
return -1;
|
||||
case DIR_CONN_STATE_CLIENT_READING_UPLOAD:
|
||||
/* XXX make sure there's a 200 OK on the buffer */
|
||||
log_fn(LOG_INFO,"eof while reading upload response. Finished.");
|
||||
return -1;
|
||||
default:
|
||||
log_fn(LOG_INFO,"conn reached eof, not reading. Closing.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT)
|
||||
return directory_handle_command(conn);
|
||||
|
||||
/* XXX for READ states, might want to make sure inbuf isn't too big */
|
||||
|
||||
log_fn(LOG_DEBUG,"Got data, not eof. Leaving on inbuf.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int directory_handle_command_get(connection_t *conn,
|
||||
char *headers, char *body) {
|
||||
size_t dlen;
|
||||
const char *cp;
|
||||
|
||||
/* XXX should check url and http version */
|
||||
log_fn(LOG_DEBUG,"Received GET command.");
|
||||
|
||||
dlen = dirserv_get_directory(&cp);
|
||||
|
||||
if(dlen == 0) {
|
||||
log_fn(LOG_WARN,"My directory is empty. Closing.");
|
||||
return -1; /* XXX send some helpful http error code */
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG,"Dumping directory to client.");
|
||||
connection_write_to_buf(answerstring, strlen(answerstring), conn);
|
||||
connection_write_to_buf(cp, dlen, conn);
|
||||
conn->state = DIR_CONN_STATE_SERVER_WRITING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int directory_handle_command_post(connection_t *conn,
|
||||
char *headers, char *body) {
|
||||
const char *cp;
|
||||
|
||||
/* XXX should check url and http version */
|
||||
log_fn(LOG_DEBUG,"Received POST command.");
|
||||
cp = body;
|
||||
if(dirserv_add_descriptor(&cp) < 0) {
|
||||
log_fn(LOG_WARN,"dirserv_add_descriptor() failed. Dropping.");
|
||||
return -1; /* XXX should write an http failed code */
|
||||
}
|
||||
dirserv_get_directory(&cp); /* rebuild and write to disk */
|
||||
connection_write_to_buf(answerstring, strlen(answerstring), conn);
|
||||
conn->state = DIR_CONN_STATE_SERVER_WRITING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int directory_handle_command(connection_t *conn) {
|
||||
char *headers=NULL, *body=NULL;
|
||||
int r;
|
||||
|
||||
#define MAX_HEADERS_SIZE 2048
|
||||
#define MAX_BODY_SIZE 500000
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
switch(fetch_from_buf_http(conn->inbuf,
|
||||
&headers, MAX_HEADERS_SIZE, &body, MAX_BODY_SIZE)) {
|
||||
case -1: /* overflow */
|
||||
log_fn(LOG_WARN,"input too large. Failing.");
|
||||
return -1;
|
||||
case 0:
|
||||
log_fn(LOG_DEBUG,"command not all here yet.");
|
||||
return 0;
|
||||
/* case 1, fall through */
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG,"headers '%s', body '%s'.", headers, body);
|
||||
|
||||
if(!strncasecmp(headers,"GET",3))
|
||||
r = directory_handle_command_get(conn, headers, body);
|
||||
else if (!strncasecmp(headers,"POST",4))
|
||||
r = directory_handle_command_post(conn, headers, body);
|
||||
else {
|
||||
log_fn(LOG_WARN,"Got headers '%s' with unknown command. Closing.", headers);
|
||||
r = -1;
|
||||
}
|
||||
|
||||
tor_free(headers); tor_free(body);
|
||||
return r;
|
||||
}
|
||||
|
||||
int connection_dir_finished_flushing(connection_t *conn) {
|
||||
int e, len=sizeof(e);
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DIR);
|
||||
|
||||
switch(conn->state) {
|
||||
case DIR_CONN_STATE_CONNECTING_FETCH:
|
||||
case DIR_CONN_STATE_CONNECTING_UPLOAD:
|
||||
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { /* not yet */
|
||||
if(!ERRNO_CONN_EINPROGRESS(errno)) {
|
||||
log_fn(LOG_DEBUG,"in-progress connect failed. Removing.");
|
||||
router_mark_as_down(conn->nickname); /* don't try him again */
|
||||
return -1;
|
||||
} else {
|
||||
return 0; /* no change, see if next time is better */
|
||||
}
|
||||
}
|
||||
/* the connect has finished. */
|
||||
|
||||
log_fn(LOG_INFO,"Dir connection to router %s:%u established.",
|
||||
conn->address,conn->port);
|
||||
|
||||
return directory_send_command(conn, conn->state);
|
||||
case DIR_CONN_STATE_CLIENT_SENDING_FETCH:
|
||||
log_fn(LOG_DEBUG,"client finished sending fetch command.");
|
||||
conn->state = DIR_CONN_STATE_CLIENT_READING_FETCH;
|
||||
connection_watch_events(conn, POLLIN);
|
||||
return 0;
|
||||
case DIR_CONN_STATE_CLIENT_SENDING_UPLOAD:
|
||||
log_fn(LOG_DEBUG,"client finished sending upload command.");
|
||||
conn->state = DIR_CONN_STATE_CLIENT_READING_UPLOAD;
|
||||
connection_watch_events(conn, POLLIN);
|
||||
return 0;
|
||||
case DIR_CONN_STATE_SERVER_WRITING:
|
||||
log_fn(LOG_INFO,"Finished writing server response. Closing.");
|
||||
return -1; /* kill it */
|
||||
default:
|
||||
log_fn(LOG_WARN,"BUG: called in unexpected state.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,466 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/* How far in the future do we allow a router to get? (seconds) */
|
||||
#define ROUTER_ALLOW_SKEW (30*60)
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
static int the_directory_is_dirty = 1;
|
||||
|
||||
static int list_running_servers(char **nicknames_out);
|
||||
|
||||
/************** Fingerprint handling code ************/
|
||||
|
||||
typedef struct fingerprint_entry_t {
|
||||
char *nickname;
|
||||
char *fingerprint;
|
||||
} fingerprint_entry_t;
|
||||
|
||||
static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR];
|
||||
static int n_fingerprints = 0;
|
||||
|
||||
static void
|
||||
add_fingerprint_to_dir(const char *nickname, const char *fp)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n_fingerprints; ++i) {
|
||||
if (!strcasecmp(fingerprint_list[i].nickname,nickname)) {
|
||||
free(fingerprint_list[i].fingerprint);
|
||||
fingerprint_list[i].fingerprint = tor_strdup(fp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fingerprint_list[n_fingerprints].nickname = tor_strdup(nickname);
|
||||
fingerprint_list[n_fingerprints].fingerprint = tor_strdup(fp);
|
||||
++n_fingerprints;
|
||||
}
|
||||
|
||||
int
|
||||
dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
|
||||
{
|
||||
char fp[FINGERPRINT_LEN+1];
|
||||
if (crypto_pk_get_fingerprint(pk, fp)<0) {
|
||||
log_fn(LOG_ERR, "Error computing fingerprint");
|
||||
return -1;
|
||||
}
|
||||
add_fingerprint_to_dir(nickname, fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return 0 on success, -1 on failure */
|
||||
int
|
||||
dirserv_parse_fingerprint_file(const char *fname)
|
||||
{
|
||||
FILE *file;
|
||||
char line[FINGERPRINT_LEN+MAX_NICKNAME_LEN+20+1];
|
||||
char *nickname, *fingerprint;
|
||||
fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR];
|
||||
int n_fingerprints_tmp = 0;
|
||||
int i, result;
|
||||
|
||||
if(!(file = fopen(fname, "r"))) {
|
||||
log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname);
|
||||
return -1;
|
||||
}
|
||||
while( (result=parse_line_from_file(line, sizeof(line),file,&nickname,&fingerprint)) > 0) {
|
||||
if (strlen(nickname) > MAX_NICKNAME_LEN) {
|
||||
log(LOG_WARN, "Nickname %s too long in fingerprint file. Skipping.", nickname);
|
||||
continue;
|
||||
}
|
||||
if(strlen(fingerprint) != FINGERPRINT_LEN ||
|
||||
!crypto_pk_check_fingerprint_syntax(fingerprint)) {
|
||||
log_fn(LOG_WARN, "Invalid fingerprint (nickname %s, fingerprint %s). Skipping.",
|
||||
nickname, fingerprint);
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < n_fingerprints_tmp; ++i) {
|
||||
if (0==strcasecmp(fingerprint_list_tmp[i].nickname, nickname)) {
|
||||
log(LOG_WARN, "Duplicate nickname %s. Skipping.",nickname);
|
||||
break; /* out of the for. the 'if' below means skip to the next line. */
|
||||
}
|
||||
}
|
||||
if(i == n_fingerprints_tmp) { /* not a duplicate */
|
||||
fingerprint_list_tmp[n_fingerprints_tmp].nickname = tor_strdup(nickname);
|
||||
fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = tor_strdup(fingerprint);
|
||||
++n_fingerprints_tmp;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
if(result == 0) { /* eof; replace the global fingerprints list. */
|
||||
dirserv_free_fingerprint_list();
|
||||
memcpy(fingerprint_list, fingerprint_list_tmp,
|
||||
sizeof(fingerprint_entry_t)*n_fingerprints_tmp);
|
||||
n_fingerprints = n_fingerprints_tmp;
|
||||
return 0;
|
||||
}
|
||||
/* error */
|
||||
log_fn(LOG_WARN, "Error reading from fingerprint file");
|
||||
for (i = 0; i < n_fingerprints_tmp; ++i) {
|
||||
free(fingerprint_list_tmp[i].nickname);
|
||||
free(fingerprint_list_tmp[i].fingerprint);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return 1 if router's identity and nickname match,
|
||||
* -1 if they don't match, 0 if the nickname is not known. */
|
||||
int
|
||||
dirserv_router_fingerprint_is_known(const routerinfo_t *router)
|
||||
{
|
||||
int i;
|
||||
fingerprint_entry_t *ent =NULL;
|
||||
char fp[FINGERPRINT_LEN+1];
|
||||
|
||||
log_fn(LOG_DEBUG, "%d fingerprints known.", n_fingerprints);
|
||||
for (i=0;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_INFO,"no fingerprint found for %s",router->nickname);
|
||||
return 0;
|
||||
}
|
||||
if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
|
||||
log_fn(LOG_WARN,"error computing fingerprint");
|
||||
return -1;
|
||||
}
|
||||
if (0==strcasecmp(ent->fingerprint, fp)) {
|
||||
log_fn(LOG_DEBUG,"good fingerprint for %s",router->nickname);
|
||||
return 1; /* Right fingerprint. */
|
||||
} else {
|
||||
log_fn(LOG_WARN,"mismatched fingerprint for %s",router->nickname);
|
||||
return -1; /* Wrong fingerprint. */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dirserv_free_fingerprint_list()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n_fingerprints; ++i) {
|
||||
free(fingerprint_list[i].nickname);
|
||||
free(fingerprint_list[i].fingerprint);
|
||||
}
|
||||
n_fingerprints = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptor list
|
||||
*/
|
||||
typedef struct descriptor_entry_t {
|
||||
char *nickname;
|
||||
time_t published;
|
||||
size_t desc_len;
|
||||
char *descriptor;
|
||||
} descriptor_entry_t;
|
||||
|
||||
static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR];
|
||||
static int n_descriptors = 0;
|
||||
|
||||
static void free_descriptor_entry(descriptor_entry_t *desc)
|
||||
{
|
||||
tor_free(desc->descriptor);
|
||||
tor_free(desc->nickname);
|
||||
free(desc);
|
||||
}
|
||||
|
||||
void
|
||||
dirserv_free_descriptors()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n_descriptors; ++i) {
|
||||
free_descriptor_entry(descriptor_list[i]);
|
||||
}
|
||||
n_descriptors = 0;
|
||||
}
|
||||
|
||||
/* Return 0 if descriptor is well-formed; -1 if descriptor is not
|
||||
* well-formed. Update *desc to point after the descriptor if the
|
||||
* descriptor is well-formed.
|
||||
*/
|
||||
/* XXX down the road perhaps we should return 1 for accepted, 0 for
|
||||
* well-formed but rejected, -1 for not-well-formed. So remote servers
|
||||
* can know if their submission was accepted and not just whether it
|
||||
* was well-formed. ...Or maybe we shouldn't give them that info?
|
||||
*/
|
||||
int
|
||||
dirserv_add_descriptor(const char **desc)
|
||||
{
|
||||
descriptor_entry_t **desc_ent_ptr;
|
||||
routerinfo_t *ri = NULL;
|
||||
int i, r;
|
||||
char *start, *end;
|
||||
char *desc_tmp = NULL;
|
||||
const char *cp;
|
||||
size_t desc_len;
|
||||
|
||||
start = strstr(*desc, "router ");
|
||||
if (!start) {
|
||||
log(LOG_WARN, "no descriptor found.");
|
||||
goto err;
|
||||
}
|
||||
if ((end = strstr(start+6, "\nrouter "))) {
|
||||
++end; /* Include NL. */
|
||||
} else if ((end = strstr(start+6, "\ndirectory-signature"))) {
|
||||
++end;
|
||||
} else {
|
||||
end = start+strlen(start);
|
||||
}
|
||||
desc_len = end-start;
|
||||
cp = desc_tmp = tor_strndup(start, desc_len);
|
||||
|
||||
/* Check: is the descriptor syntactically valid? */
|
||||
ri = router_get_entry_from_string(&cp);
|
||||
if (!ri) {
|
||||
log(LOG_WARN, "Couldn't parse descriptor");
|
||||
goto err;
|
||||
}
|
||||
tor_free(desc_tmp);
|
||||
/* Okay. Now check whether the fingerprint is recognized. */
|
||||
r = dirserv_router_fingerprint_is_known(ri);
|
||||
if(r<1) {
|
||||
if(r==0) {
|
||||
log_fn(LOG_WARN, "Unknown nickname %s. Not adding.", ri->nickname);
|
||||
} else {
|
||||
log_fn(LOG_WARN, "Known nickname %s, wrong fingerprint. Not adding.", ri->nickname);
|
||||
}
|
||||
routerinfo_free(ri);
|
||||
*desc = end;
|
||||
return 0;
|
||||
}
|
||||
/* Is there too much clock skew? */
|
||||
if (ri->published_on > time(NULL)+ROUTER_ALLOW_SKEW) {
|
||||
log_fn(LOG_WARN, "Publication time for nickname %s is too far in the future; possible clock skew. Not adding", ri->nickname);
|
||||
routerinfo_free(ri);
|
||||
*desc = end;
|
||||
return 0;
|
||||
}
|
||||
/* Do we already have an entry for this router? */
|
||||
desc_ent_ptr = NULL;
|
||||
for (i = 0; i < n_descriptors; ++i) {
|
||||
if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) {
|
||||
desc_ent_ptr = &descriptor_list[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (desc_ent_ptr) {
|
||||
/* if so, decide whether to update it. */
|
||||
if ((*desc_ent_ptr)->published > ri->published_on) {
|
||||
/* We already have a newer descriptor */
|
||||
log_fn(LOG_INFO,"We already have a newer desc for nickname %s. Not adding.",ri->nickname);
|
||||
/* This isn't really an error; return. */
|
||||
routerinfo_free(ri);
|
||||
*desc = end;
|
||||
return 0;
|
||||
}
|
||||
/* We don't have a newer one; we'll update this one. */
|
||||
free_descriptor_entry(*desc_ent_ptr);
|
||||
} else {
|
||||
/* Add this at the end. */
|
||||
desc_ent_ptr = &descriptor_list[n_descriptors++];
|
||||
/* XXX check if n_descriptors is too big */
|
||||
}
|
||||
|
||||
(*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t));
|
||||
(*desc_ent_ptr)->nickname = tor_strdup(ri->nickname);
|
||||
(*desc_ent_ptr)->published = ri->published_on;
|
||||
(*desc_ent_ptr)->desc_len = desc_len;
|
||||
(*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1);
|
||||
strncpy((*desc_ent_ptr)->descriptor, start, desc_len);
|
||||
(*desc_ent_ptr)->descriptor[desc_len] = '\0';
|
||||
*desc = end;
|
||||
directory_set_dirty();
|
||||
|
||||
routerinfo_free(ri);
|
||||
return 0;
|
||||
err:
|
||||
tor_free(desc_tmp);
|
||||
if (ri)
|
||||
routerinfo_free(ri);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
directory_set_dirty()
|
||||
{
|
||||
the_directory_is_dirty = 1;
|
||||
}
|
||||
|
||||
int
|
||||
dirserv_init_from_directory_string(const char *dir)
|
||||
{
|
||||
const char *cp = dir;
|
||||
while(1) {
|
||||
cp = strstr(cp, "\nrouter ");
|
||||
if (!cp) break;
|
||||
++cp;
|
||||
if (dirserv_add_descriptor(&cp)) {
|
||||
return -1;
|
||||
}
|
||||
--cp; /*Back up to newline.*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
list_running_servers(char **nicknames_out)
|
||||
{
|
||||
char *nickname_lst[MAX_ROUTERS_IN_DIR];
|
||||
connection_t **connection_array;
|
||||
int n_conns;
|
||||
connection_t *conn;
|
||||
char *cp;
|
||||
int n = 0, i;
|
||||
int length;
|
||||
*nicknames_out = NULL;
|
||||
nickname_lst[n++] = options.Nickname;
|
||||
|
||||
get_connection_array(&connection_array, &n_conns);
|
||||
for (i = 0; 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_zero(length);
|
||||
cp = *nicknames_out;
|
||||
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 %s\n"
|
||||
"running-routers %s\n\n", published, options.RecommendedVersions, 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);
|
||||
/* XXX Nick: do strncat and friends null-terminate? man page is ambiguous. */
|
||||
cp += descriptor_list[i]->desc_len;
|
||||
assert(!*cp);
|
||||
}
|
||||
/* These multiple strlen calls are inefficient, but dwarfed by the RSA
|
||||
signature.
|
||||
*/
|
||||
i = strlen(s);
|
||||
strncat(s, "directory-signature\n", maxlen-i);
|
||||
i = strlen(s);
|
||||
cp = s + i;
|
||||
|
||||
if (router_get_dir_hash(s,digest)) {
|
||||
log_fn(LOG_WARN,"couldn't compute digest");
|
||||
return -1;
|
||||
}
|
||||
if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
|
||||
log_fn(LOG_WARN,"couldn't sign digest");
|
||||
return -1;
|
||||
}
|
||||
log(LOG_DEBUG,"generated directory digest begins with %02x:%02x:%02x:%02x",
|
||||
((int)digest[0])&0xff,((int)digest[1])&0xff,
|
||||
((int)digest[2])&0xff,((int)digest[3])&0xff);
|
||||
|
||||
strncpy(cp, "-----BEGIN SIGNATURE-----\n", maxlen-i);
|
||||
cp[maxlen-i-1] = 0;
|
||||
|
||||
i = strlen(s);
|
||||
cp = s+i;
|
||||
if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
|
||||
log_fn(LOG_WARN,"couldn't base64-encode signature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = strlen(s);
|
||||
cp = s+i;
|
||||
strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
|
||||
i = strlen(s);
|
||||
if (i == maxlen) {
|
||||
log_fn(LOG_WARN,"tried to exceed string length.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *the_directory = NULL;
|
||||
static int the_directory_len = -1;
|
||||
|
||||
size_t dirserv_get_directory(const char **directory)
|
||||
{
|
||||
char *new_directory;
|
||||
char filename[512];
|
||||
if (the_directory_is_dirty) {
|
||||
new_directory = tor_malloc(MAX_DIR_SIZE);
|
||||
if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE,
|
||||
get_identity_key())) {
|
||||
log(LOG_WARN, "Error creating directory.");
|
||||
free(new_directory);
|
||||
return 0;
|
||||
}
|
||||
tor_free(the_directory);
|
||||
the_directory = new_directory;
|
||||
the_directory_len = strlen(the_directory);
|
||||
log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len,
|
||||
the_directory);
|
||||
the_directory_is_dirty = 0;
|
||||
/* Now read the directory we just made in order to update our own
|
||||
* router lists. This does more signature checking than is strictly
|
||||
* necessary, but safe is better than sorry. */
|
||||
new_directory = tor_strdup(the_directory);
|
||||
/* use a new copy of the dir, since get_dir_from_string scribbles on it */
|
||||
if (router_set_routerlist_from_directory(new_directory, get_identity_key())) {
|
||||
log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
|
||||
exit(0);
|
||||
}
|
||||
free(new_directory);
|
||||
sprintf(filename,"%s/cached-directory", options.DataDirectory);
|
||||
if(write_str_to_file(filename,the_directory) < 0) {
|
||||
log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring.");
|
||||
}
|
||||
} else {
|
||||
log(LOG_INFO,"Directory still clean, reusing.");
|
||||
}
|
||||
*directory = the_directory;
|
||||
return the_directory_len;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,527 @@
|
||||
/* Copyright 2003 Roger Dingledine. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
/* See http://elvin.dstc.com/ListArchive/elvin-dev/archive/2001/09/msg00027.html
|
||||
* for some approaches to asynchronous dns. We will want to switch once one of
|
||||
* them becomes more commonly available.
|
||||
*/
|
||||
|
||||
#include "or.h"
|
||||
#include "tree.h"
|
||||
|
||||
#define MAX_ADDRESSLEN 256
|
||||
|
||||
#define MAX_DNSWORKERS 50
|
||||
#define MIN_DNSWORKERS 3
|
||||
#define MAX_IDLE_DNSWORKERS 10
|
||||
|
||||
int num_dnsworkers=0;
|
||||
int num_dnsworkers_busy=0;
|
||||
|
||||
static void purge_expired_resolves(uint32_t now);
|
||||
static int assign_to_dnsworker(connection_t *exitconn);
|
||||
static void dns_found_answer(char *address, uint32_t addr);
|
||||
int dnsworker_main(void *data);
|
||||
static int spawn_dnsworker(void);
|
||||
static void spawn_enough_dnsworkers(void);
|
||||
|
||||
struct pending_connection_t {
|
||||
struct connection_t *conn;
|
||||
struct pending_connection_t *next;
|
||||
};
|
||||
|
||||
struct cached_resolve {
|
||||
SPLAY_ENTRY(cached_resolve) node;
|
||||
char address[MAX_ADDRESSLEN]; /* the hostname to be resolved */
|
||||
uint32_t addr; /* in host order. I know I'm horrible for assuming ipv4 */
|
||||
char state; /* 0 is pending; 1 means answer is valid; 2 means resolve failed */
|
||||
#define CACHE_STATE_PENDING 0
|
||||
#define CACHE_STATE_VALID 1
|
||||
#define CACHE_STATE_FAILED 2
|
||||
uint32_t expire; /* remove untouched items from cache after some time? */
|
||||
struct pending_connection_t *pending_connections;
|
||||
struct cached_resolve *next;
|
||||
};
|
||||
|
||||
static SPLAY_HEAD(cache_tree, cached_resolve) cache_root;
|
||||
|
||||
static int compare_cached_resolves(struct cached_resolve *a,
|
||||
struct cached_resolve *b) {
|
||||
/* make this smarter one day? */
|
||||
return strncasecmp(a->address, b->address, MAX_ADDRESSLEN);
|
||||
}
|
||||
|
||||
SPLAY_PROTOTYPE(cache_tree, cached_resolve, node, compare_cached_resolves);
|
||||
SPLAY_GENERATE(cache_tree, cached_resolve, node, compare_cached_resolves);
|
||||
|
||||
static void init_cache_tree(void) {
|
||||
SPLAY_INIT(&cache_root);
|
||||
}
|
||||
|
||||
void dns_init(void) {
|
||||
init_cache_tree();
|
||||
spawn_enough_dnsworkers();
|
||||
}
|
||||
|
||||
static struct cached_resolve *oldest_cached_resolve = NULL; /* linked list, */
|
||||
static struct cached_resolve *newest_cached_resolve = NULL; /* oldest to newest */
|
||||
|
||||
static void purge_expired_resolves(uint32_t now) {
|
||||
struct cached_resolve *resolve;
|
||||
|
||||
/* this is fast because the linked list
|
||||
* oldest_cached_resolve is ordered by when they came in.
|
||||
*/
|
||||
while(oldest_cached_resolve && (oldest_cached_resolve->expire < now)) {
|
||||
resolve = oldest_cached_resolve;
|
||||
log(LOG_DEBUG,"Forgetting old cached resolve (expires %lu)", (unsigned long)resolve->expire);
|
||||
oldest_cached_resolve = resolve->next;
|
||||
if(!oldest_cached_resolve) /* if there are no more, */
|
||||
newest_cached_resolve = NULL; /* then make sure the list's tail knows that too */
|
||||
SPLAY_REMOVE(cache_tree, &cache_root, resolve);
|
||||
free(resolve);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
uint32_t dns_lookup(const char *address) {
|
||||
struct in_addr in;
|
||||
uint32_t now = time(NULL);
|
||||
|
||||
/* first take this opportunity to see if there are any expired
|
||||
resolves in the tree.*/
|
||||
purge_expired_resolves(now);
|
||||
|
||||
if (inet_aton(address, &in)) {
|
||||
log_fn(LOG_DEBUG, "Using static address %s (%08X)", address,
|
||||
ntohl(in.s_addr));
|
||||
return ntohl(in.s_addr);
|
||||
}
|
||||
|
||||
strncpy(search.address, address, MAX_ADDRESSLEN);
|
||||
search.address[MAX_ADDRESSLEN-1] = 0;
|
||||
resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
|
||||
if(resolve) { /* it's there */
|
||||
if(resolve->state == CACHE_STATE_VALID) {
|
||||
in.s_addr = htonl(resolve->addr);
|
||||
log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address,
|
||||
inet_ntoa(in));
|
||||
return resolve->addr;
|
||||
}
|
||||
log_fn(LOG_DEBUG, "Entry found for address %s but it's not valid. Returning 0.",
|
||||
address);
|
||||
return 0;
|
||||
}
|
||||
/* it's not there */
|
||||
log_fn(LOG_DEBUG, "No entry found for address %s", address);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See if we have a cache entry for 'exitconn->address'. if so,
|
||||
* if resolve valid, put it into exitconn->addr and return 1.
|
||||
* If resolve failed, return -1.
|
||||
*
|
||||
* Else, if seen before and pending, add conn to the pending list,
|
||||
* and return 0.
|
||||
*
|
||||
* Else, if not seen before, add conn to pending list, hand to
|
||||
* dns farm, and return 0.
|
||||
*/
|
||||
int dns_resolve(connection_t *exitconn) {
|
||||
struct cached_resolve *resolve;
|
||||
struct cached_resolve search;
|
||||
struct pending_connection_t *pending_connection;
|
||||
uint32_t now = time(NULL);
|
||||
|
||||
/* first take this opportunity to see if there are any expired
|
||||
resolves in the tree.*/
|
||||
purge_expired_resolves(now);
|
||||
|
||||
/* now check the tree to see if 'address' is already there. */
|
||||
strncpy(search.address, exitconn->address, MAX_ADDRESSLEN);
|
||||
search.address[MAX_ADDRESSLEN-1] = 0;
|
||||
resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
|
||||
if(resolve) { /* already there */
|
||||
switch(resolve->state) {
|
||||
case CACHE_STATE_PENDING:
|
||||
/* add us to the pending list */
|
||||
pending_connection = tor_malloc(sizeof(struct pending_connection_t));
|
||||
pending_connection->conn = exitconn;
|
||||
pending_connection->next = resolve->pending_connections;
|
||||
resolve->pending_connections = pending_connection;
|
||||
log_fn(LOG_DEBUG,"Connection (fd %d) waiting for pending DNS resolve of '%s'",
|
||||
exitconn->s, exitconn->address);
|
||||
return 0;
|
||||
case CACHE_STATE_VALID:
|
||||
exitconn->addr = resolve->addr;
|
||||
log_fn(LOG_DEBUG,"Connection (fd %d) found cached answer for '%s'",
|
||||
exitconn->s, exitconn->address);
|
||||
return 1;
|
||||
case CACHE_STATE_FAILED:
|
||||
return -1;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
/* not there, need to add it */
|
||||
resolve = tor_malloc_zero(sizeof(struct cached_resolve));
|
||||
resolve->state = CACHE_STATE_PENDING;
|
||||
resolve->expire = now + MAX_DNS_ENTRY_AGE;
|
||||
strncpy(resolve->address, exitconn->address, MAX_ADDRESSLEN);
|
||||
resolve->address[MAX_ADDRESSLEN-1] = 0;
|
||||
|
||||
/* add us to the pending list */
|
||||
pending_connection = tor_malloc(sizeof(struct pending_connection_t));
|
||||
pending_connection->conn = exitconn;
|
||||
pending_connection->next = resolve->pending_connections;
|
||||
resolve->pending_connections = pending_connection;
|
||||
|
||||
/* add us to the linked list of resolves */
|
||||
if (!oldest_cached_resolve) {
|
||||
oldest_cached_resolve = resolve;
|
||||
} else {
|
||||
newest_cached_resolve->next = resolve;
|
||||
}
|
||||
newest_cached_resolve = resolve;
|
||||
|
||||
SPLAY_INSERT(cache_tree, &cache_root, resolve);
|
||||
return assign_to_dnsworker(exitconn);
|
||||
}
|
||||
|
||||
static int assign_to_dnsworker(connection_t *exitconn) {
|
||||
connection_t *dnsconn;
|
||||
unsigned char len;
|
||||
|
||||
spawn_enough_dnsworkers(); /* respawn here, to be sure there are enough */
|
||||
|
||||
dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE);
|
||||
|
||||
if(!dnsconn) {
|
||||
log_fn(LOG_WARN,"no idle dns workers. Failing.");
|
||||
dns_cancel_pending_resolve(exitconn->address, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG, "Connection (fd %d) needs to resolve '%s'; assigning to DNSWorker (fd %d)",
|
||||
exitconn->s, exitconn->address, dnsconn->s);
|
||||
|
||||
free(dnsconn->address);
|
||||
dnsconn->address = tor_strdup(exitconn->address);
|
||||
dnsconn->state = DNSWORKER_STATE_BUSY;
|
||||
num_dnsworkers_busy++;
|
||||
|
||||
len = strlen(dnsconn->address);
|
||||
connection_write_to_buf(&len, 1, dnsconn);
|
||||
connection_write_to_buf(dnsconn->address, len, dnsconn);
|
||||
|
||||
// log_fn(LOG_DEBUG,"submitted '%s'", exitconn->address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if onlyconn is NULL, cancel the whole thing. if onlyconn is defined,
|
||||
* then remove onlyconn from the pending list, and if the pending list
|
||||
* is now empty, cancel the whole thing.
|
||||
*/
|
||||
void dns_cancel_pending_resolve(char *address, connection_t *onlyconn) {
|
||||
struct pending_connection_t *pend, *victim;
|
||||
struct cached_resolve search;
|
||||
struct cached_resolve *resolve, *tmp;
|
||||
|
||||
strncpy(search.address, address, MAX_ADDRESSLEN);
|
||||
search.address[MAX_ADDRESSLEN-1] = 0;
|
||||
|
||||
resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
|
||||
if(!resolve) {
|
||||
log_fn(LOG_WARN,"Address '%s' is not pending. Dropping.", address);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(resolve->state == CACHE_STATE_PENDING);
|
||||
assert(resolve->pending_connections);
|
||||
|
||||
if(onlyconn) {
|
||||
pend = resolve->pending_connections;
|
||||
if(pend->conn == onlyconn) {
|
||||
resolve->pending_connections = pend->next;
|
||||
free(pend);
|
||||
if(resolve->pending_connections) {/* more pending, don't cancel it */
|
||||
log_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'",
|
||||
onlyconn->s, address);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for( ; pend->next; pend = pend->next) {
|
||||
if(pend->next->conn == onlyconn) {
|
||||
victim = pend->next;
|
||||
pend->next = victim->next;
|
||||
free(victim);
|
||||
log_fn(LOG_DEBUG, "Connection (fd %d) no longer waiting for resolve of '%s'",
|
||||
onlyconn->s, address);
|
||||
return; /* more are pending */
|
||||
}
|
||||
}
|
||||
assert(0); /* not reachable unless onlyconn not in pending list */
|
||||
}
|
||||
} else {
|
||||
/* mark all pending connections to fail */
|
||||
log_fn(LOG_DEBUG, "Failing all connections waiting on DNS resolve of '%s'",
|
||||
address);
|
||||
while(resolve->pending_connections) {
|
||||
pend = resolve->pending_connections;
|
||||
if(connection_edge_end(pend->conn, END_STREAM_REASON_MISC, NULL) < 0)
|
||||
log_fn(LOG_WARN,"1: I called connection_edge_end redundantly.");
|
||||
resolve->pending_connections = pend->next;
|
||||
free(pend);
|
||||
}
|
||||
}
|
||||
|
||||
/* remove resolve from the linked list */
|
||||
if(resolve == oldest_cached_resolve) {
|
||||
oldest_cached_resolve = resolve->next;
|
||||
if(oldest_cached_resolve == NULL)
|
||||
newest_cached_resolve = NULL;
|
||||
} else {
|
||||
/* FFFF make it a doubly linked list if this becomes too slow */
|
||||
for(tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next) ;
|
||||
assert(tmp); /* it's got to be in the list, or we screwed up somewhere else */
|
||||
tmp->next = resolve->next; /* unlink it */
|
||||
|
||||
if(newest_cached_resolve == resolve)
|
||||
newest_cached_resolve = tmp;
|
||||
}
|
||||
|
||||
/* remove resolve from the tree */
|
||||
SPLAY_REMOVE(cache_tree, &cache_root, resolve);
|
||||
|
||||
free(resolve);
|
||||
}
|
||||
|
||||
static void dns_found_answer(char *address, uint32_t addr) {
|
||||
struct pending_connection_t *pend;
|
||||
struct cached_resolve search;
|
||||
struct cached_resolve *resolve;
|
||||
|
||||
strncpy(search.address, address, MAX_ADDRESSLEN);
|
||||
search.address[MAX_ADDRESSLEN-1] = 0;
|
||||
|
||||
resolve = SPLAY_FIND(cache_tree, &cache_root, &search);
|
||||
if(!resolve) {
|
||||
log_fn(LOG_INFO,"Resolved unasked address '%s'? Dropping.", address);
|
||||
/* XXX Why drop? Just because we don't care now doesn't mean we shouldn't
|
||||
* XXX cache the result for later. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolve->state != CACHE_STATE_PENDING) {
|
||||
log_fn(LOG_WARN, "Resolved '%s' which was already resolved; ignoring",
|
||||
address);
|
||||
return;
|
||||
}
|
||||
/* Removed this assertion: in fact, we'll sometimes get a double answer
|
||||
* to the same question. This can happen when we ask one worker to resolve
|
||||
* X.Y.Z., then we cancel the request, and then we ask another worker to
|
||||
* resolve X.Y.Z. */
|
||||
/* assert(resolve->state == CACHE_STATE_PENDING); */
|
||||
|
||||
resolve->addr = ntohl(addr);
|
||||
if(resolve->addr)
|
||||
resolve->state = CACHE_STATE_VALID;
|
||||
else
|
||||
resolve->state = CACHE_STATE_FAILED;
|
||||
|
||||
while(resolve->pending_connections) {
|
||||
pend = resolve->pending_connections;
|
||||
pend->conn->addr = resolve->addr;
|
||||
if(resolve->state == CACHE_STATE_FAILED) {
|
||||
if(connection_edge_end(pend->conn, END_STREAM_REASON_RESOLVEFAILED, NULL) < 0)
|
||||
log_fn(LOG_WARN,"1: I called connection_edge_end redundantly.");
|
||||
} else
|
||||
connection_exit_connect(pend->conn);
|
||||
resolve->pending_connections = pend->next;
|
||||
free(pend);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
||||
int connection_dns_finished_flushing(connection_t *conn) {
|
||||
assert(conn && conn->type == CONN_TYPE_DNSWORKER);
|
||||
connection_stop_writing(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_dns_process_inbuf(connection_t *conn) {
|
||||
uint32_t addr;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_DNSWORKER);
|
||||
|
||||
if(conn->inbuf_reached_eof) {
|
||||
log_fn(LOG_WARN,"Read eof. Worker dying.");
|
||||
if(conn->state == DNSWORKER_STATE_BUSY) {
|
||||
dns_cancel_pending_resolve(conn->address, NULL);
|
||||
num_dnsworkers_busy--;
|
||||
}
|
||||
num_dnsworkers--;
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(conn->state == DNSWORKER_STATE_BUSY);
|
||||
if(buf_datalen(conn->inbuf) < 4) /* entire answer available? */
|
||||
return 0; /* not yet */
|
||||
assert(buf_datalen(conn->inbuf) == 4);
|
||||
|
||||
connection_fetch_from_buf((char*)&addr,sizeof(addr),conn);
|
||||
|
||||
log_fn(LOG_DEBUG, "DNSWorker (fd %d) returned answer for '%s'",
|
||||
conn->s, conn->address);
|
||||
|
||||
dns_found_answer(conn->address, addr);
|
||||
|
||||
free(conn->address);
|
||||
conn->address = tor_strdup("<idle>");
|
||||
conn->state = DNSWORKER_STATE_IDLE;
|
||||
num_dnsworkers_busy--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dnsworker_main(void *data) {
|
||||
char address[MAX_ADDRESSLEN];
|
||||
unsigned char address_len;
|
||||
struct hostent *rent;
|
||||
int *fdarray = data;
|
||||
int fd;
|
||||
|
||||
close(fdarray[0]); /* this is the side of the socketpair the parent uses */
|
||||
fd = fdarray[1]; /* this side is ours */
|
||||
connection_free_all(); /* so the child doesn't hold the parent's fd's open */
|
||||
|
||||
for(;;) {
|
||||
|
||||
if(read(fd, &address_len, 1) != 1) {
|
||||
log_fn(LOG_INFO,"read length failed. Child exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
assert(address_len > 0);
|
||||
|
||||
if(read_all(fd, address, address_len) != address_len) {
|
||||
log_fn(LOG_ERR,"read hostname failed. Child exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
address[address_len] = 0; /* null terminate it */
|
||||
|
||||
rent = gethostbyname(address);
|
||||
if (!rent) {
|
||||
log_fn(LOG_INFO,"Could not resolve dest addr %s. Returning nulls.",address);
|
||||
if(write_all(fd, "\0\0\0\0", 4) != 4) {
|
||||
log_fn(LOG_ERR,"writing nulls failed. Child exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
} else {
|
||||
assert(rent->h_length == 4); /* break to remind us if we move away from ipv4 */
|
||||
if(write_all(fd, rent->h_addr, 4) != 4) {
|
||||
log_fn(LOG_INFO,"writing answer failed. Child exiting.");
|
||||
spawn_exit();
|
||||
}
|
||||
log_fn(LOG_INFO,"Resolved address '%s'.",address);
|
||||
}
|
||||
}
|
||||
return 0; /* windows wants this function to return an int */
|
||||
}
|
||||
|
||||
static int spawn_dnsworker(void) {
|
||||
int fd[2];
|
||||
connection_t *conn;
|
||||
|
||||
if(tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
|
||||
log(LOG_ERR, "Couldn't construct socketpair: %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
spawn_func(dnsworker_main, (void*)fd);
|
||||
log_fn(LOG_DEBUG,"just spawned a worker.");
|
||||
close(fd[1]); /* we don't need the worker's side of the pipe */
|
||||
|
||||
conn = connection_new(CONN_TYPE_DNSWORKER);
|
||||
|
||||
set_socket_nonblocking(fd[0]);
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
conn->s = fd[0];
|
||||
conn->address = tor_strdup("<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;
|
||||
|
||||
/* XXX This may not be the best strategy. Maybe we should queue pending
|
||||
* requests until the old ones finish or time out: otherwise, if
|
||||
* the connection requests come fast enough, we never get any DNS done. -NM
|
||||
* XXX But if we queue them, then the adversary can pile even more
|
||||
* queries onto us, blocking legitimate requests for even longer.
|
||||
* Maybe we should compromise and only kill if it's been at it for
|
||||
* more than, e.g., 2 seconds. -RD
|
||||
*/
|
||||
if(num_dnsworkers_busy == MAX_DNSWORKERS) {
|
||||
/* We always want at least one worker idle.
|
||||
* So find the oldest busy worker and kill it.
|
||||
*/
|
||||
dnsconn = connection_get_by_type_state_lastwritten(CONN_TYPE_DNSWORKER,
|
||||
DNSWORKER_STATE_BUSY);
|
||||
assert(dnsconn);
|
||||
|
||||
log_fn(LOG_WARN, "%d DNS workers are spawned; all are busy. Killing one.",
|
||||
MAX_DNSWORKERS);
|
||||
/* tell the exit connection that it's failed */
|
||||
dns_cancel_pending_resolve(dnsconn->address, NULL);
|
||||
|
||||
dnsconn->marked_for_close = 1;
|
||||
num_dnsworkers_busy--;
|
||||
num_dnsworkers--;
|
||||
}
|
||||
|
||||
if(num_dnsworkers_busy >= MIN_DNSWORKERS)
|
||||
num_dnsworkers_needed = num_dnsworkers_busy+1;
|
||||
else
|
||||
num_dnsworkers_needed = MIN_DNSWORKERS;
|
||||
|
||||
while(num_dnsworkers < num_dnsworkers_needed) {
|
||||
if(spawn_dnsworker() < 0) {
|
||||
log(LOG_WARN,"spawn_enough_dnsworkers(): spawn failed!");
|
||||
return;
|
||||
}
|
||||
num_dnsworkers++;
|
||||
}
|
||||
|
||||
while(num_dnsworkers > num_dnsworkers_busy+MAX_IDLE_DNSWORKERS) { /* too many idle? */
|
||||
/* cull excess workers */
|
||||
log_fn(LOG_WARN,"%d of %d dnsworkers are idle. Killing one.",
|
||||
num_dnsworkers-num_dnsworkers_needed, num_dnsworkers);
|
||||
dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER, DNSWORKER_STATE_IDLE);
|
||||
assert(dnsconn);
|
||||
dnsconn->marked_for_close = 1;
|
||||
num_dnsworkers--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,671 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/********* START PROTOTYPES **********/
|
||||
|
||||
static void dumpstats(int severity); /* log stats */
|
||||
static int init_from_config(int argc, char **argv);
|
||||
|
||||
/********* START VARIABLES **********/
|
||||
|
||||
extern char *conn_type_to_string[];
|
||||
extern char *conn_state_to_string[][_CONN_TYPE_MAX+1];
|
||||
|
||||
or_options_t options; /* command-line and config-file options */
|
||||
int global_read_bucket; /* max number of bytes I can read this second */
|
||||
|
||||
static int stats_prev_global_read_bucket;
|
||||
static uint64_t stats_n_bytes_read = 0;
|
||||
static long stats_n_seconds_reading = 0;
|
||||
|
||||
static connection_t *connection_array[MAXCONNECTIONS] =
|
||||
{ NULL };
|
||||
|
||||
static struct pollfd poll_array[MAXCONNECTIONS];
|
||||
|
||||
static int nfds=0; /* number of connections currently active */
|
||||
|
||||
#ifndef MS_WINDOWS /* do signal stuff only on unix */
|
||||
static int please_dumpstats=0; /* whether we should dump stats during the loop */
|
||||
static int please_reset=0; /* whether we just got a sighup */
|
||||
static int please_reap_children=0; /* whether we should waitpid for exited children */
|
||||
#endif /* signal stuff */
|
||||
|
||||
/********* END VARIABLES ************/
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* This section contains accessors and other methods on the connection_array
|
||||
* and poll_array variables (which are global within this file and unavailable
|
||||
* outside it).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int connection_add(connection_t *conn) {
|
||||
|
||||
if(nfds >= options.MaxConn-1) {
|
||||
log(LOG_WARN,"connection_add(): failing because nfds is too high.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->poll_index = nfds;
|
||||
connection_set_poll_socket(conn);
|
||||
connection_array[nfds] = conn;
|
||||
|
||||
/* zero these out here, because otherwise we'll inherit values from the previously freed one */
|
||||
poll_array[nfds].events = 0;
|
||||
poll_array[nfds].revents = 0;
|
||||
|
||||
nfds++;
|
||||
|
||||
log(LOG_INFO,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void connection_set_poll_socket(connection_t *conn) {
|
||||
poll_array[conn->poll_index].fd = conn->s;
|
||||
}
|
||||
|
||||
/* Remove the connection from the global list, and remove the
|
||||
* corresponding poll entry. Calling this function will shift the last
|
||||
* connection (if any) into the position occupied by conn.
|
||||
*/
|
||||
int connection_remove(connection_t *conn) {
|
||||
int current_index;
|
||||
|
||||
assert(conn);
|
||||
assert(nfds>0);
|
||||
|
||||
log(LOG_INFO,"connection_remove(): removing socket %d, nfds now %d",conn->s, nfds-1);
|
||||
/* if it's an edge conn, remove it from the list
|
||||
* of conn's on this circuit. If it's not on an edge,
|
||||
* flush and send destroys for all circuits on this conn
|
||||
*/
|
||||
circuit_about_to_close_connection(conn);
|
||||
|
||||
current_index = conn->poll_index;
|
||||
if(current_index == nfds-1) { /* this is the end */
|
||||
nfds--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we replace this one with the one at the end, then free it */
|
||||
nfds--;
|
||||
poll_array[current_index].fd = poll_array[nfds].fd;
|
||||
poll_array[current_index].events = poll_array[nfds].events;
|
||||
poll_array[current_index].revents = poll_array[nfds].revents;
|
||||
connection_array[current_index] = connection_array[nfds];
|
||||
connection_array[current_index]->poll_index = current_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void get_connection_array(connection_t ***array, int *n) {
|
||||
*array = connection_array;
|
||||
*n = nfds;
|
||||
}
|
||||
|
||||
void connection_watch_events(connection_t *conn, short events) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
|
||||
poll_array[conn->poll_index].events = events;
|
||||
}
|
||||
|
||||
int connection_is_reading(connection_t *conn) {
|
||||
return poll_array[conn->poll_index].events & POLLIN;
|
||||
}
|
||||
|
||||
void connection_stop_reading(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
|
||||
log(LOG_DEBUG,"connection_stop_reading() called.");
|
||||
if(poll_array[conn->poll_index].events & POLLIN)
|
||||
poll_array[conn->poll_index].events -= POLLIN;
|
||||
}
|
||||
|
||||
void connection_start_reading(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
|
||||
poll_array[conn->poll_index].events |= POLLIN;
|
||||
}
|
||||
|
||||
void connection_stop_writing(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
|
||||
if(poll_array[conn->poll_index].events & POLLOUT)
|
||||
poll_array[conn->poll_index].events -= POLLOUT;
|
||||
}
|
||||
|
||||
void connection_start_writing(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
|
||||
poll_array[conn->poll_index].events |= POLLOUT;
|
||||
}
|
||||
|
||||
static void conn_read(int i) {
|
||||
connection_t *conn = connection_array[i];
|
||||
|
||||
/* see http://www.greenend.org.uk/rjk/2001/06/poll.html for
|
||||
* discussion of POLLIN vs POLLHUP */
|
||||
if(!(poll_array[i].revents & (POLLIN|POLLHUP|POLLERR)))
|
||||
if(!connection_is_reading(conn) ||
|
||||
!connection_has_pending_tls_data(conn))
|
||||
return; /* this conn should not read */
|
||||
|
||||
log_fn(LOG_DEBUG,"socket %d wants to read.",conn->s);
|
||||
|
||||
assert_connection_ok(conn, time(NULL));
|
||||
|
||||
if(
|
||||
/* XXX does POLLHUP also mean it's definitely broken? */
|
||||
#ifdef MS_WINDOWS
|
||||
(poll_array[i].revents & POLLERR) ||
|
||||
#endif
|
||||
connection_handle_read(conn) < 0)
|
||||
{
|
||||
/* this connection is broken. remove it */
|
||||
log_fn(LOG_INFO,"%s connection broken, removing.",
|
||||
conn_type_to_string[conn->type]);
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
if(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) && buf_datalen(conn->outbuf)) {
|
||||
log_fn(LOG_WARN,"Conn (socket %d) still wants to flush. Losing %d bytes!",
|
||||
conn->s, (int)buf_datalen(conn->outbuf));
|
||||
}
|
||||
}
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
if(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.ORPort && !circuit_get_by_conn(conn)) ||
|
||||
(!connection_state_is_open(conn))) {
|
||||
/* we're an onion proxy, with no circuits; or our handshake has expired. kill it. */
|
||||
log_fn(LOG_INFO,"Expiring connection to %d (%s:%d).",
|
||||
i,conn->address, conn->port);
|
||||
conn->marked_for_close = 1;
|
||||
} else {
|
||||
/* either a full router, or we've got a circuit. send a padding cell. */
|
||||
log_fn(LOG_DEBUG,"Sending keepalive to (%s:%d)",
|
||||
conn->address, conn->port);
|
||||
memset(&cell,0,sizeof(cell_t));
|
||||
cell.command = CELL_PADDING;
|
||||
connection_or_write_cell_to_buf(&cell, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform regular maintenance tasks. This function gets run once per
|
||||
* second by prepare_for_poll.
|
||||
*/
|
||||
static void run_scheduled_events(time_t now) {
|
||||
static long time_to_fetch_directory = 0;
|
||||
static long time_to_new_circuit = 0;
|
||||
circuit_t *circ;
|
||||
int i;
|
||||
|
||||
/* 1. Every DirFetchPostPeriod seconds, we get a new directory and upload
|
||||
* our descriptor (if any). */
|
||||
if(time_to_fetch_directory < now) {
|
||||
/* it's time to fetch a new directory and/or post our descriptor */
|
||||
if(options.ORPort) {
|
||||
router_rebuild_descriptor();
|
||||
router_upload_desc_to_dirservers();
|
||||
}
|
||||
if(!options.DirPort) {
|
||||
/* NOTE directory servers do not currently fetch directories.
|
||||
* Hope this doesn't bite us later. */
|
||||
directory_initiate_command(router_pick_directory_server(),
|
||||
DIR_CONN_STATE_CONNECTING_FETCH);
|
||||
}
|
||||
time_to_fetch_directory = now + options.DirFetchPostPeriod;
|
||||
}
|
||||
|
||||
/* 2. Every second, we examine pending circuits and prune the
|
||||
* ones which have been pending for more than 3 seconds.
|
||||
* We do this before step 3, so it can try building more if
|
||||
* it's not comfortable with the number of available circuits.
|
||||
*/
|
||||
circuit_expire_building();
|
||||
|
||||
/* 3. Every second, we try a new circuit if there are no valid
|
||||
* circuits. Every NewCircuitPeriod seconds, we expire circuits
|
||||
* that became dirty more than NewCircuitPeriod seconds ago,
|
||||
* and we make a new circ if there are no clean circuits.
|
||||
*/
|
||||
if(options.SocksPort) {
|
||||
|
||||
/* launch a new circ for any pending streams that need one */
|
||||
connection_ap_attach_pending();
|
||||
|
||||
circ = circuit_get_newest(NULL, 1);
|
||||
if(time_to_new_circuit < now) {
|
||||
client_dns_clean();
|
||||
circuit_expire_unused_circuits();
|
||||
circuit_reset_failure_count();
|
||||
if(circ && circ->timestamp_dirty) {
|
||||
log_fn(LOG_INFO,"Youngest circuit dirty; launching replacement.");
|
||||
circuit_launch_new(); /* make a new circuit */
|
||||
}
|
||||
time_to_new_circuit = now + options.NewCircuitPeriod;
|
||||
}
|
||||
#define CIRCUIT_MIN_BUILDING 2
|
||||
if(!circ && circuit_count_building() < CIRCUIT_MIN_BUILDING) {
|
||||
/* if there's no open circ, and less than 2 are on the way,
|
||||
* go ahead and try another.
|
||||
*/
|
||||
circuit_launch_new();
|
||||
}
|
||||
}
|
||||
|
||||
/* 4. Every second, we check how much bandwidth we've consumed and
|
||||
* increment global_read_bucket.
|
||||
*/
|
||||
stats_n_bytes_read += stats_prev_global_read_bucket-global_read_bucket;
|
||||
if(global_read_bucket < 9*options.TotalBandwidth) {
|
||||
global_read_bucket += options.TotalBandwidth;
|
||||
log_fn(LOG_DEBUG,"global_read_bucket now %d.", global_read_bucket);
|
||||
}
|
||||
stats_prev_global_read_bucket = global_read_bucket;
|
||||
|
||||
/* 5. We do housekeeping for each connection... */
|
||||
for(i=0;i<nfds;i++) {
|
||||
run_connection_housekeeping(i, now);
|
||||
}
|
||||
|
||||
/* 6. 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.
|
||||
* XXX we can remove this step once we audit circuit-building to make sure
|
||||
* it doesn't pick a marked-for-close conn. -RD
|
||||
*/
|
||||
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 int init_from_config(int argc, char **argv) {
|
||||
if(getconfig(argc,argv,&options)) {
|
||||
log_fn(LOG_ERR,"Reading config failed. For usage, try -h.");
|
||||
return -1;
|
||||
}
|
||||
close_logs(); /* we'll close, then open with correct loglevel if necessary */
|
||||
if(!options.LogFile && !options.RunAsDaemon)
|
||||
add_stream_log(options.loglevel, "<stdout>", stdout);
|
||||
if(options.LogFile) {
|
||||
if (add_file_log(options.loglevel, options.LogFile) != 0) {
|
||||
/* opening the log file failed! Use stderr and log a warning */
|
||||
add_stream_log(options.loglevel, "<stderr>", stderr);
|
||||
log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", options.LogFile, strerror(errno));
|
||||
}
|
||||
log_fn(LOG_WARN, "Successfully opened LogFile '%s', redirecting output.",
|
||||
options.LogFile);
|
||||
}
|
||||
if(options.DebugLogFile) {
|
||||
if (add_file_log(LOG_DEBUG, options.DebugLogFile) != 0)
|
||||
log_fn(LOG_WARN, "Cannot write to DebugLogFile '%s': %s.", options.DebugLogFile, strerror(errno));
|
||||
log_fn(LOG_DEBUG, "Successfully opened DebugLogFile '%s'.", options.DebugLogFile);
|
||||
}
|
||||
|
||||
global_read_bucket = options.TotalBandwidth; /* start it at 1 second of traffic */
|
||||
stats_prev_global_read_bucket = global_read_bucket;
|
||||
|
||||
if(options.User || options.Group) {
|
||||
if(switch_id(options.User, options.Group) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(options.RunAsDaemon) {
|
||||
/* XXXX Can we delay this any more? */
|
||||
finish_daemon();
|
||||
}
|
||||
|
||||
/* write our pid to the pid file, if we do not have write permissions we will log a warning */
|
||||
if(options.PidFile)
|
||||
write_pidfile(options.PidFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_main_loop(void) {
|
||||
int i;
|
||||
int timeout;
|
||||
int poll_result;
|
||||
|
||||
/* load the routers file */
|
||||
if(router_set_routerlist_from_file(options.RouterFile) < 0) {
|
||||
log_fn(LOG_ERR,"Error loading router list.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* load the private keys, if we're supposed to have them, and set up the
|
||||
* TLS context. */
|
||||
if (init_keys() < 0) {
|
||||
log_fn(LOG_ERR,"Error initializing keys; exiting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(options.ORPort) {
|
||||
cpu_init(); /* launch cpuworkers. Need to do this *after* we've read the onion key. */
|
||||
router_upload_desc_to_dirservers(); /* upload our descriptor to all dirservers */
|
||||
}
|
||||
|
||||
/* start up the necessary connections based on which ports are
|
||||
* non-zero. This is where we try to connect to all the other ORs,
|
||||
* and start the listeners.
|
||||
*/
|
||||
if(retry_all_connections() < 0) {
|
||||
log_fn(LOG_ERR,"Failed to bind one of the listener ports.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
#ifndef MS_WINDOWS /* do signal stuff only on unix */
|
||||
if(please_dumpstats) {
|
||||
/* prefer to log it at INFO, but make sure we always see it */
|
||||
dumpstats(options.loglevel>LOG_INFO ? options.loglevel : LOG_INFO);
|
||||
please_dumpstats = 0;
|
||||
}
|
||||
if(please_reset) {
|
||||
log_fn(LOG_WARN,"Received sighup. Reloading config.");
|
||||
/* first, reload config variables, in case they've changed */
|
||||
if (init_from_config(0, NULL) < 0) {
|
||||
/* no need to provide argc/v, they've been cached inside init_from_config */
|
||||
exit(1);
|
||||
}
|
||||
if(retry_all_connections() < 0) {
|
||||
log_fn(LOG_ERR,"Failed to bind one of the listener ports.");
|
||||
return -1;
|
||||
}
|
||||
if(options.DirPort) {
|
||||
/* reload the fingerprint file */
|
||||
char keydir[512];
|
||||
sprintf(keydir,"%s/approved-routers", options.DataDirectory);
|
||||
log_fn(LOG_INFO,"Reloading approved fingerprints from %s...",keydir);
|
||||
if(dirserv_parse_fingerprint_file(keydir) < 0) {
|
||||
log_fn(LOG_WARN, "Error reloading fingerprints. Continuing with old list.");
|
||||
}
|
||||
} else {
|
||||
/* fetch a new directory */
|
||||
directory_initiate_command(router_pick_directory_server(), DIR_CONN_STATE_CONNECTING_FETCH);
|
||||
}
|
||||
|
||||
please_reset = 0;
|
||||
}
|
||||
if(please_reap_children) {
|
||||
while(waitpid(-1,NULL,WNOHANG)) ; /* keep reaping until no more zombies */
|
||||
please_reap_children = 0;
|
||||
}
|
||||
#endif /* signal stuff */
|
||||
|
||||
timeout = prepare_for_poll();
|
||||
|
||||
/* poll until we have an event, or the second ends */
|
||||
poll_result = poll(poll_array, nfds, timeout);
|
||||
|
||||
/* let catch() handle things like ^c, and otherwise don't worry about it */
|
||||
if(poll_result < 0) {
|
||||
if(errno != EINTR) { /* let the program survive things like ^z */
|
||||
log_fn(LOG_ERR,"poll failed.");
|
||||
return -1;
|
||||
} else {
|
||||
log_fn(LOG_DEBUG,"poll interrupted.");
|
||||
}
|
||||
}
|
||||
|
||||
/* do all the reads and errors first, so we can detect closed sockets */
|
||||
for(i=0;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_WINDOWS /* 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 */
|
||||
if(options.PidFile)
|
||||
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*RELAY_PAYLOAD_SIZE)) );
|
||||
if (stats_n_data_cells_received)
|
||||
log(severity,"Average incoming cell fullness: %2.3f%%",
|
||||
100*(((double)stats_n_data_bytes_received) /
|
||||
(stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
|
||||
|
||||
if (stats_n_seconds_reading)
|
||||
log(severity,"Average bandwidth used: %d bytes/sec",
|
||||
(int) (stats_n_bytes_read/stats_n_seconds_reading));
|
||||
}
|
||||
|
||||
int tor_main(int argc, char *argv[]) {
|
||||
|
||||
/* give it somewhere to log to initially */
|
||||
add_stream_log(LOG_INFO, "<stdout>", stdout);
|
||||
log_fn(LOG_WARN,"Tor v%s. This is experimental software. Do not use it if you need anonymity.",VERSION);
|
||||
|
||||
#ifndef MS_WINDOWS
|
||||
if(geteuid()==0)
|
||||
log_fn(LOG_WARN,"You are running Tor as root. You don't need to, and you probably shouldn't.");
|
||||
#endif
|
||||
|
||||
if (init_from_config(argc,argv) < 0)
|
||||
return -1;
|
||||
|
||||
if (options.RunAsDaemon) {
|
||||
start_daemon();
|
||||
}
|
||||
|
||||
if(options.ORPort) { /* only spawn dns handlers if we're a router */
|
||||
dns_init(); /* initialize the dns resolve tree, and spawn workers */
|
||||
}
|
||||
if(options.SocksPort) {
|
||||
client_dns_init(); /* init the client dns cache */
|
||||
}
|
||||
|
||||
#ifndef MS_WINDOWS /* do signal stuff only on unix */
|
||||
signal (SIGINT, catch); /* catch kills so we can exit cleanly */
|
||||
signal (SIGTERM, catch);
|
||||
signal (SIGUSR1, catch); /* to dump stats */
|
||||
signal (SIGHUP, catch); /* to reload directory */
|
||||
signal (SIGCHLD, catch); /* for exiting dns/cpu workers */
|
||||
#endif /* signal stuff */
|
||||
|
||||
crypto_global_init();
|
||||
crypto_seed_rng();
|
||||
do_main_loop();
|
||||
crypto_global_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,802 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len);
|
||||
|
||||
int decide_circ_id_type(char *local_nick, char *remote_nick) {
|
||||
int result;
|
||||
|
||||
assert(remote_nick);
|
||||
if(!local_nick)
|
||||
return CIRC_ID_TYPE_LOWER;
|
||||
result = strcmp(local_nick, remote_nick);
|
||||
assert(result);
|
||||
if(result < 0)
|
||||
return CIRC_ID_TYPE_LOWER;
|
||||
return CIRC_ID_TYPE_HIGHER;
|
||||
}
|
||||
|
||||
struct onion_queue_t {
|
||||
circuit_t *circ;
|
||||
struct onion_queue_t *next;
|
||||
};
|
||||
|
||||
/* global (within this file) variables used by the next few functions */
|
||||
static struct onion_queue_t *ol_list=NULL;
|
||||
static struct onion_queue_t *ol_tail=NULL;
|
||||
static int ol_length=0;
|
||||
|
||||
int onion_pending_add(circuit_t *circ) {
|
||||
struct onion_queue_t *tmp;
|
||||
|
||||
tmp = tor_malloc(sizeof(struct onion_queue_t));
|
||||
tmp->circ = circ;
|
||||
tmp->next = NULL;
|
||||
|
||||
if(!ol_tail) {
|
||||
assert(!ol_list);
|
||||
assert(!ol_length);
|
||||
ol_list = tmp;
|
||||
ol_tail = tmp;
|
||||
ol_length++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(ol_list);
|
||||
assert(!ol_tail->next);
|
||||
|
||||
if(ol_length >= options.MaxOnionsPending) {
|
||||
log_fn(LOG_WARN,"Already have %d onions queued. Closing.", ol_length);
|
||||
free(tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ol_length++;
|
||||
ol_tail->next = tmp;
|
||||
ol_tail = tmp;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
circuit_t *onion_next_task(void) {
|
||||
circuit_t *circ;
|
||||
|
||||
if(!ol_list)
|
||||
return NULL; /* no onions pending, we're done */
|
||||
|
||||
assert(ol_list->circ);
|
||||
assert(ol_list->circ->p_conn); /* make sure it's still valid */
|
||||
assert(ol_length > 0);
|
||||
circ = ol_list->circ;
|
||||
onion_pending_remove(ol_list->circ);
|
||||
return circ;
|
||||
}
|
||||
|
||||
/* go through ol_list, find the onion_queue_t element which points to
|
||||
* circ, remove and free that element. leave circ itself alone.
|
||||
*/
|
||||
void onion_pending_remove(circuit_t *circ) {
|
||||
struct onion_queue_t *tmpo, *victim;
|
||||
|
||||
if(!ol_list)
|
||||
return; /* nothing here. */
|
||||
|
||||
/* first check to see if it's the first entry */
|
||||
tmpo = ol_list;
|
||||
if(tmpo->circ == circ) {
|
||||
/* it's the first one. remove it from the list. */
|
||||
ol_list = tmpo->next;
|
||||
if(!ol_list)
|
||||
ol_tail = NULL;
|
||||
ol_length--;
|
||||
victim = tmpo;
|
||||
} else { /* we need to hunt through the rest of the list */
|
||||
for( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ;
|
||||
if(!tmpo->next) {
|
||||
log_fn(LOG_DEBUG,"circ (p_circ_id %d) not in list, probably at cpuworker.",circ->p_circ_id);
|
||||
return;
|
||||
}
|
||||
/* now we know tmpo->next->circ == circ */
|
||||
victim = tmpo->next;
|
||||
tmpo->next = victim->next;
|
||||
if(ol_tail == victim)
|
||||
ol_tail = tmpo;
|
||||
ol_length--;
|
||||
}
|
||||
|
||||
/* now victim points to the element that needs to be removed */
|
||||
|
||||
free(victim);
|
||||
}
|
||||
|
||||
/* given a response payload and keys, initialize, then send a created cell back */
|
||||
int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) {
|
||||
unsigned char iv[16];
|
||||
cell_t cell;
|
||||
|
||||
memset(iv, 0, 16);
|
||||
|
||||
memset(&cell, 0, sizeof(cell_t));
|
||||
cell.command = CELL_CREATED;
|
||||
cell.circ_id = circ->p_circ_id;
|
||||
|
||||
circ->state = CIRCUIT_STATE_OPEN;
|
||||
|
||||
log_fn(LOG_DEBUG,"Entering.");
|
||||
|
||||
memcpy(cell.payload, payload, ONIONSKIN_REPLY_LEN);
|
||||
|
||||
log_fn(LOG_INFO,"init digest forward %d, backward %d.",
|
||||
*(uint32_t*)(keys), *(uint32_t*)(keys+20));
|
||||
circ->n_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
|
||||
crypto_digest_add_bytes(circ->n_digest, keys, 20);
|
||||
circ->p_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
|
||||
crypto_digest_add_bytes(circ->p_digest, keys+20, 20);
|
||||
|
||||
log_fn(LOG_DEBUG,"init cipher forward %d, backward %d.",
|
||||
*(uint32_t*)(keys+40), *(uint32_t*)(keys+40+16));
|
||||
if (!(circ->n_crypto =
|
||||
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40,iv,0))) {
|
||||
log_fn(LOG_WARN,"Cipher initialization failed (n).");
|
||||
return -1;
|
||||
}
|
||||
if (!(circ->p_crypto =
|
||||
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40+16,iv,1))) {
|
||||
log_fn(LOG_WARN,"Cipher initialization failed (p).");
|
||||
return -1;
|
||||
}
|
||||
|
||||
connection_or_write_cell_to_buf(&cell, circ->p_conn);
|
||||
log_fn(LOG_DEBUG,"Finished sending 'created' cell.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) {
|
||||
char *start,*end;
|
||||
char nick[MAX_NICKNAME_LEN];
|
||||
routerinfo_t *router;
|
||||
|
||||
while(isspace(*list) || *list==',') list++;
|
||||
|
||||
start = list;
|
||||
while(*start) {
|
||||
end=start; while(*end && !isspace(*end) && *end != ',') end++;
|
||||
memcpy(nick,start,end-start);
|
||||
nick[end-start] = 0; /* null terminate it */
|
||||
router = router_get_by_nickname(nick);
|
||||
if(router && router->is_running)
|
||||
smartlist_add(sl,router);
|
||||
else
|
||||
log_fn(LOG_WARN,"Nickname list includes '%s' which isn't a known router.",nick);
|
||||
while(isspace(*end) || *end==',') end++;
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
static int new_route_len(double cw, routerinfo_t **rarray, int rarray_len) {
|
||||
int num_acceptable_routers;
|
||||
int routelen;
|
||||
|
||||
assert((cw >= 0) && (cw < 1) && rarray); /* valid parameters */
|
||||
|
||||
for(routelen=3; ; routelen++) { /* 3, increment until coinflip says we're done */
|
||||
if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */
|
||||
break;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, rarray_len);
|
||||
|
||||
num_acceptable_routers = count_acceptable_routers(rarray, rarray_len);
|
||||
|
||||
if(num_acceptable_routers < 2) {
|
||||
log_fn(LOG_INFO,"Not enough acceptable routers. Failing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(num_acceptable_routers < routelen) {
|
||||
log_fn(LOG_INFO,"Not enough routers: cutting routelen from %d to %d.",
|
||||
routelen, num_acceptable_routers);
|
||||
routelen = num_acceptable_routers;
|
||||
}
|
||||
|
||||
return routelen;
|
||||
}
|
||||
|
||||
static routerinfo_t *choose_good_exit_server(routerlist_t *dir)
|
||||
{
|
||||
int *n_supported;
|
||||
int *n_maybe_supported;
|
||||
int i, j;
|
||||
int n_pending_connections = 0;
|
||||
connection_t **carray;
|
||||
int n_connections;
|
||||
int best_support = -1;
|
||||
int best_maybe_support = -1;
|
||||
int best_support_idx = -1;
|
||||
int best_maybe_support_idx = -1;
|
||||
int n_best_support=0, n_best_maybe_support=0;
|
||||
smartlist_t *sl, *preferredexits, *excludedexits;
|
||||
routerinfo_t *router;
|
||||
|
||||
get_connection_array(&carray, &n_connections);
|
||||
|
||||
/* Count how many connections are waiting for a circuit to be built.
|
||||
* We use this for log messages now, but in the future we may depend on it.
|
||||
*/
|
||||
for (i = 0; i < n_connections; ++i) {
|
||||
if (carray[i]->type == CONN_TYPE_AP &&
|
||||
carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT &&
|
||||
!carray[i]->marked_for_close &&
|
||||
!circuit_stream_is_being_handled(carray[i]))
|
||||
++n_pending_connections;
|
||||
}
|
||||
log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending",
|
||||
n_pending_connections);
|
||||
/* Now we count, for each of the routers in the directory: how many
|
||||
* of the pending connections could _definitely_ exit from that
|
||||
* router (n_supported[i]) and how many could _possibly_ exit from
|
||||
* that router (n_maybe_supported[i]). (We can't be sure about
|
||||
* cases where we don't know the IP address of the pending
|
||||
* connection.)
|
||||
*/
|
||||
n_supported = tor_malloc(sizeof(int)*dir->n_routers);
|
||||
n_maybe_supported = tor_malloc(sizeof(int)*dir->n_routers);
|
||||
for (i = 0; i < dir->n_routers; ++i) { /* iterate over routers */
|
||||
if(!dir->routers[i]->is_running) {
|
||||
n_supported[i] = n_maybe_supported[i] = -1;
|
||||
log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.",
|
||||
dir->routers[i]->nickname, i);
|
||||
continue; /* skip routers that are known to be down */
|
||||
}
|
||||
if(router_exit_policy_rejects_all(dir->routers[i])) {
|
||||
n_supported[i] = n_maybe_supported[i] = -1;
|
||||
log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.",
|
||||
dir->routers[i]->nickname, i);
|
||||
continue; /* skip routers that reject all */
|
||||
}
|
||||
n_supported[i] = n_maybe_supported[i] = 0;
|
||||
for (j = 0; j < n_connections; ++j) { /* iterate over connections */
|
||||
if (carray[j]->type != CONN_TYPE_AP ||
|
||||
carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT ||
|
||||
carray[j]->marked_for_close ||
|
||||
circuit_stream_is_being_handled(carray[j]))
|
||||
continue; /* Skip everything but APs in CIRCUIT_WAIT */
|
||||
switch (connection_ap_can_use_exit(carray[j], dir->routers[i]))
|
||||
{
|
||||
case -1:
|
||||
log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.",
|
||||
dir->routers[i]->nickname, i);
|
||||
break; /* would be rejected; try next connection */
|
||||
case 0:
|
||||
++n_supported[i];
|
||||
log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
|
||||
dir->routers[i]->nickname, i, n_supported[i]);
|
||||
; /* Fall through: If it is supported, it is also maybe supported. */
|
||||
case 1:
|
||||
++n_maybe_supported[i];
|
||||
log_fn(LOG_DEBUG,"%s is maybe supported. n_maybe_supported[%d] now %d.",
|
||||
dir->routers[i]->nickname, i, n_maybe_supported[i]);
|
||||
}
|
||||
} /* End looping over connections. */
|
||||
if (n_supported[i] > best_support) {
|
||||
/* If this router is better than previous ones, remember its index
|
||||
* and goodness, and start counting how many routers are this good. */
|
||||
best_support = n_supported[i]; best_support_idx = i; n_best_support=1;
|
||||
log_fn(LOG_DEBUG,"%s is new best supported option so far.",
|
||||
dir->routers[i]->nickname);
|
||||
} else if (n_supported[i] == best_support) {
|
||||
/* If this router is _as good_ as the best one, just increment the
|
||||
* count of equally good routers.*/
|
||||
++n_best_support;
|
||||
}
|
||||
/* As above, but for 'maybe-supported' connections */
|
||||
if (n_maybe_supported[i] > best_maybe_support) {
|
||||
best_maybe_support = n_maybe_supported[i]; best_maybe_support_idx = i;
|
||||
n_best_maybe_support = 1;
|
||||
log_fn(LOG_DEBUG,"%s is new best maybe-supported option so far.",
|
||||
dir->routers[i]->nickname);
|
||||
} else if (n_maybe_supported[i] == best_maybe_support) {
|
||||
++n_best_maybe_support;
|
||||
}
|
||||
}
|
||||
log_fn(LOG_INFO, "Found %d servers that will definitely support %d/%d "
|
||||
"pending connections, and %d that might support %d/%d.",
|
||||
n_best_support, best_support, n_pending_connections,
|
||||
n_best_maybe_support, best_maybe_support, n_pending_connections);
|
||||
|
||||
preferredexits = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
add_nickname_list_to_smartlist(preferredexits,options.ExitNodes);
|
||||
|
||||
excludedexits = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
add_nickname_list_to_smartlist(excludedexits,options.ExcludedNodes);
|
||||
|
||||
sl = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
|
||||
/* If any routers definitely support any pending connections, choose one
|
||||
* at random. */
|
||||
if (best_support > 0) {
|
||||
for (i = best_support_idx; i < dir->n_routers; i++)
|
||||
if (n_supported[i] == best_support)
|
||||
smartlist_add(sl, dir->routers[i]);
|
||||
|
||||
smartlist_subtract(sl,excludedexits);
|
||||
if (smartlist_overlap(sl,preferredexits))
|
||||
smartlist_intersect(sl,preferredexits);
|
||||
router = smartlist_choose(sl);
|
||||
} else if (best_maybe_support > 0) {
|
||||
/* If any routers _maybe_ support pending connections, choose one at
|
||||
* random, as above. */
|
||||
for(i = best_maybe_support_idx; i < dir->n_routers; i++)
|
||||
if(n_maybe_supported[i] == best_maybe_support)
|
||||
smartlist_add(sl, dir->routers[i]);
|
||||
|
||||
smartlist_subtract(sl,excludedexits);
|
||||
if (smartlist_overlap(sl,preferredexits))
|
||||
smartlist_intersect(sl,preferredexits);
|
||||
router = smartlist_choose(sl);
|
||||
} else {
|
||||
/* Either there are no pending connections, or no routers even seem to
|
||||
* possibly support any of them. Choose a router at random. */
|
||||
for(i = best_maybe_support_idx; i < dir->n_routers; i++)
|
||||
if(n_supported[i] != -1)
|
||||
smartlist_add(sl, dir->routers[i]);
|
||||
|
||||
smartlist_subtract(sl,excludedexits);
|
||||
if (smartlist_overlap(sl,preferredexits))
|
||||
smartlist_intersect(sl,preferredexits);
|
||||
router = smartlist_choose(sl);
|
||||
}
|
||||
|
||||
smartlist_free(preferredexits);
|
||||
smartlist_free(excludedexits);
|
||||
smartlist_free(sl);
|
||||
tor_free(n_supported); tor_free(n_maybe_supported);
|
||||
if(router) {
|
||||
log_fn(LOG_DEBUG, "Chose exit server '%s'", router->nickname);
|
||||
return router;
|
||||
}
|
||||
log_fn(LOG_WARN, "No exit routers seem to be running; can't choose an exit.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpath_build_state_t *onion_new_cpath_build_state(void) {
|
||||
routerlist_t *rl;
|
||||
int r;
|
||||
cpath_build_state_t *info;
|
||||
routerinfo_t *exit;
|
||||
|
||||
router_get_routerlist(&rl);
|
||||
r = new_route_len(options.PathlenCoinWeight, rl->routers, rl->n_routers);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
exit = choose_good_exit_server(rl);
|
||||
if(!exit)
|
||||
return NULL;
|
||||
info = tor_malloc(sizeof(cpath_build_state_t));
|
||||
info->desired_path_len = r;
|
||||
info->chosen_exit = tor_strdup(exit->nickname);
|
||||
return info;
|
||||
}
|
||||
|
||||
static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) {
|
||||
int i, j;
|
||||
int num=0;
|
||||
connection_t *conn;
|
||||
|
||||
for(i=0;i<rarray_len;i++) {
|
||||
log_fn(LOG_DEBUG,"Contemplating whether router %d is a new option...",i);
|
||||
if(rarray[i]->is_running == 0) {
|
||||
log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i);
|
||||
goto next_i_loop;
|
||||
}
|
||||
if(options.ORPort) {
|
||||
conn = connection_exact_get_by_addr_port(rarray[i]->addr, rarray[i]->or_port);
|
||||
if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) {
|
||||
log_fn(LOG_DEBUG,"Nope, %d is not connected.",i);
|
||||
goto next_i_loop;
|
||||
}
|
||||
}
|
||||
for(j=0;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;
|
||||
}
|
||||
|
||||
/* prototypes for smartlist operations from routerlist.h
|
||||
* they're here to prevent precedence issues with the .h files
|
||||
*/
|
||||
void router_add_running_routers_to_smartlist(smartlist_t *sl);
|
||||
|
||||
static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) {
|
||||
int i;
|
||||
routerinfo_t *r;
|
||||
|
||||
if(twin == NULL)
|
||||
return;
|
||||
|
||||
/* XXX abstraction violation: this function reaches inside smartlist :( */
|
||||
for(i=0; i < sl->num_used; i++) {
|
||||
r = sl->list[i];
|
||||
if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) {
|
||||
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
|
||||
i--; /* so we process the new i'th element */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, routerinfo_t **router_out)
|
||||
{
|
||||
int cur_len;
|
||||
crypt_path_t *cpath, *hop;
|
||||
routerinfo_t *r;
|
||||
routerinfo_t *choice;
|
||||
int i;
|
||||
smartlist_t *sl, *excludednodes;
|
||||
|
||||
assert(head_ptr);
|
||||
assert(router_out);
|
||||
|
||||
if (!*head_ptr) {
|
||||
cur_len = 0;
|
||||
} else {
|
||||
cur_len = 1;
|
||||
for (cpath = *head_ptr; cpath->next != *head_ptr; cpath = cpath->next) {
|
||||
++cur_len;
|
||||
}
|
||||
}
|
||||
if (cur_len >= state->desired_path_len) {
|
||||
log_fn(LOG_DEBUG, "Path is complete: %d steps long",
|
||||
state->desired_path_len);
|
||||
return 1;
|
||||
}
|
||||
log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len,
|
||||
state->desired_path_len);
|
||||
|
||||
excludednodes = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
add_nickname_list_to_smartlist(excludednodes,options.ExcludedNodes);
|
||||
|
||||
if(cur_len == state->desired_path_len - 1) { /* Picking last node */
|
||||
log_fn(LOG_DEBUG, "Contemplating last hop: choice already made.");
|
||||
choice = router_get_by_nickname(state->chosen_exit);
|
||||
if(!choice) {
|
||||
log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Failing.",
|
||||
state->chosen_exit);
|
||||
return -1;
|
||||
}
|
||||
} else if(cur_len == 0) { /* picking first node */
|
||||
/* try the nodes in EntryNodes first */
|
||||
sl = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
add_nickname_list_to_smartlist(sl,options.EntryNodes);
|
||||
/* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */
|
||||
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
||||
smartlist_subtract(sl,excludednodes);
|
||||
choice = smartlist_choose(sl);
|
||||
smartlist_free(sl);
|
||||
if(!choice) {
|
||||
sl = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
router_add_running_routers_to_smartlist(sl);
|
||||
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
||||
smartlist_subtract(sl,excludednodes);
|
||||
choice = smartlist_choose(sl);
|
||||
smartlist_free(sl);
|
||||
}
|
||||
smartlist_free(excludednodes);
|
||||
if(!choice) {
|
||||
log_fn(LOG_WARN,"No acceptable routers while picking entry node. Failing.");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
|
||||
sl = smartlist_create(MAX_ROUTERS_IN_DIR);
|
||||
router_add_running_routers_to_smartlist(sl);
|
||||
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
||||
for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) {
|
||||
r = router_get_by_addr_port(cpath->addr, cpath->port);
|
||||
assert(r);
|
||||
remove_twins_from_smartlist(sl,r);
|
||||
}
|
||||
smartlist_subtract(sl,excludednodes);
|
||||
choice = smartlist_choose(sl);
|
||||
smartlist_free(sl);
|
||||
smartlist_free(excludednodes);
|
||||
if(!choice) {
|
||||
log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Failing.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
|
||||
choice->nickname, cur_len, state->chosen_exit);
|
||||
|
||||
hop = (crypt_path_t *)tor_malloc_zero(sizeof(crypt_path_t));
|
||||
|
||||
/* link hop into the cpath, at the end. */
|
||||
if (*head_ptr) {
|
||||
hop->next = (*head_ptr);
|
||||
hop->prev = (*head_ptr)->prev;
|
||||
(*head_ptr)->prev->next = hop;
|
||||
(*head_ptr)->prev = hop;
|
||||
} else {
|
||||
*head_ptr = hop;
|
||||
hop->prev = hop->next = hop;
|
||||
}
|
||||
|
||||
hop->state = CPATH_STATE_CLOSED;
|
||||
|
||||
hop->port = choice->or_port;
|
||||
hop->addr = choice->addr;
|
||||
|
||||
hop->package_window = CIRCWINDOW_START;
|
||||
hop->deliver_window = CIRCWINDOW_START;
|
||||
|
||||
log_fn(LOG_DEBUG, "Extended circuit path with %s for hop %d",
|
||||
choice->nickname, cur_len);
|
||||
|
||||
*router_out = choice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* Given a router's 128 byte public key,
|
||||
stores the following in onion_skin_out:
|
||||
[16 bytes] Symmetric key for encrypting blob past RSA
|
||||
[112 bytes] g^x part 1 (inside the RSA)
|
||||
[16 bytes] g^x part 2 (symmetrically encrypted)
|
||||
[ 6 bytes] Meeting point (IP/port)
|
||||
[ 8 bytes] Meeting cookie
|
||||
[16 bytes] End-to-end authentication [optional]
|
||||
|
||||
* Stores the DH private key into handshake_state_out for later completion
|
||||
* of the handshake.
|
||||
*
|
||||
* The meeting point/cookies and auth are zeroed out for now.
|
||||
*/
|
||||
int
|
||||
onion_skin_create(crypto_pk_env_t *dest_router_key,
|
||||
crypto_dh_env_t **handshake_state_out,
|
||||
char *onion_skin_out) /* Must be ONIONSKIN_CHALLENGE_LEN bytes */
|
||||
{
|
||||
char iv[16];
|
||||
char *challenge = NULL;
|
||||
crypto_dh_env_t *dh = NULL;
|
||||
crypto_cipher_env_t *cipher = NULL;
|
||||
int dhbytes, pkbytes;
|
||||
|
||||
*handshake_state_out = NULL;
|
||||
memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
|
||||
memset(iv, 0, 16);
|
||||
|
||||
if (!(dh = crypto_dh_new()))
|
||||
goto err;
|
||||
|
||||
dhbytes = crypto_dh_get_bytes(dh);
|
||||
pkbytes = crypto_pk_keysize(dest_router_key);
|
||||
assert(dhbytes == 128);
|
||||
assert(pkbytes == 128);
|
||||
challenge = (char *)tor_malloc_zero(ONIONSKIN_CHALLENGE_LEN);
|
||||
|
||||
if (crypto_rand(16, challenge))
|
||||
goto err;
|
||||
|
||||
/* You can't just run around RSA-encrypting any bitstream: if it's
|
||||
* greater than the RSA key, then OpenSSL will happily encrypt,
|
||||
* and later decrypt to the wrong value. So we set the first bit
|
||||
* of 'challenge' to 0. This means that our symmetric key is really
|
||||
* only 127 bits.
|
||||
*/
|
||||
challenge[0] &= 0x7f;
|
||||
|
||||
if (crypto_dh_get_public(dh, challenge+16, dhbytes))
|
||||
goto err;
|
||||
|
||||
#ifdef DEBUG_ONION_SKINS
|
||||
#define PA(a,n) \
|
||||
{ int _i; for (_i = 0; _i<n; ++_i) printf("%02x ",((int)(a)[_i])&0xFF); }
|
||||
|
||||
printf("Client: client g^x:");
|
||||
PA(challenge+16,3);
|
||||
printf("...");
|
||||
PA(challenge+141,3);
|
||||
puts("");
|
||||
|
||||
printf("Client: client symkey:");
|
||||
PA(challenge+0,16);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
/* set meeting point, meeting cookie, etc here. Leave zero for now. */
|
||||
|
||||
cipher = crypto_create_init_cipher(ONION_CIPHER, challenge, iv, 1);
|
||||
|
||||
if (!cipher)
|
||||
goto err;
|
||||
|
||||
if (crypto_pk_public_encrypt(dest_router_key, challenge, pkbytes,
|
||||
onion_skin_out, RSA_NO_PADDING)==-1)
|
||||
goto err;
|
||||
|
||||
if (crypto_cipher_encrypt(cipher, challenge+pkbytes, ONIONSKIN_CHALLENGE_LEN-pkbytes,
|
||||
onion_skin_out+pkbytes))
|
||||
goto err;
|
||||
|
||||
tor_free(challenge);
|
||||
crypto_free_cipher_env(cipher);
|
||||
*handshake_state_out = dh;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
tor_free(challenge);
|
||||
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 reply (128-byte
|
||||
* DH plus the first 20 bytes of shared key material), and store the
|
||||
* next key_out_len bytes of key material in key_out.
|
||||
*/
|
||||
int
|
||||
onion_skin_server_handshake(char *onion_skin, /* ONIONSKIN_CHALLENGE_LEN bytes */
|
||||
crypto_pk_env_t *private_key,
|
||||
char *handshake_reply_out, /* ONIONSKIN_REPLY_LEN bytes */
|
||||
char *key_out,
|
||||
int key_out_len)
|
||||
{
|
||||
char challenge[ONIONSKIN_CHALLENGE_LEN];
|
||||
char iv[16];
|
||||
crypto_dh_env_t *dh = NULL;
|
||||
crypto_cipher_env_t *cipher = NULL;
|
||||
int pkbytes;
|
||||
int len;
|
||||
char *key_material=NULL;
|
||||
|
||||
memset(iv, 0, 16);
|
||||
pkbytes = crypto_pk_keysize(private_key);
|
||||
|
||||
if (crypto_pk_private_decrypt(private_key,
|
||||
onion_skin, pkbytes,
|
||||
challenge, 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, challenge, iv, 0);
|
||||
|
||||
if (crypto_cipher_decrypt(cipher, onion_skin+pkbytes, ONIONSKIN_CHALLENGE_LEN-pkbytes,
|
||||
challenge+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
|
||||
|
||||
key_material = tor_malloc(20+key_out_len);
|
||||
len = crypto_dh_compute_secret(dh, challenge+16, DH_KEY_LEN,
|
||||
key_material, 20+key_out_len);
|
||||
if (len < 0)
|
||||
goto err;
|
||||
|
||||
/* send back H(K|0) as proof that we learned K. */
|
||||
memcpy(handshake_reply_out+DH_KEY_LEN, key_material, 20);
|
||||
|
||||
/* use the rest of the key material for our shared keys, digests, etc */
|
||||
memcpy(key_out, key_material+20, key_out_len);
|
||||
|
||||
#ifdef DEBUG_ONION_SKINS
|
||||
printf("Server: key material:");
|
||||
PA(buf, DH_KEY_LEN);
|
||||
puts("");
|
||||
printf("Server: keys out:");
|
||||
PA(key_out, key_out_len);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
tor_free(key_material);
|
||||
crypto_free_cipher_env(cipher);
|
||||
crypto_dh_free(dh);
|
||||
return 0;
|
||||
err:
|
||||
tor_free(key_material);
|
||||
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 + 20 byte hash as generated by
|
||||
* onion_skin_server_handshake and the handshake state generated by
|
||||
* onion_skin_create, verify H(K) with the first 20 bytes of shared
|
||||
* key material, then generate key_out_len more bytes of shared key
|
||||
* material and store them in key_out.
|
||||
*
|
||||
* After the invocation, call crypto_dh_free on handshake_state.
|
||||
*/
|
||||
int
|
||||
onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
|
||||
char *handshake_reply, /* Must be ONIONSKIN_REPLY_LEN bytes */
|
||||
char *key_out,
|
||||
int key_out_len)
|
||||
{
|
||||
int len;
|
||||
char *key_material=NULL;
|
||||
assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
|
||||
|
||||
#ifdef DEBUG_ONION_SKINS
|
||||
printf("Client: server g^y:");
|
||||
PA(handshake_reply+0,3);
|
||||
printf("...");
|
||||
PA(handshake_reply+125,3);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
key_material = tor_malloc(20+key_out_len);
|
||||
len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN,
|
||||
key_material, 20+key_out_len);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
if(memcmp(key_material, handshake_reply+DH_KEY_LEN, 20)) {
|
||||
/* H(K) does *not* match. Something fishy. */
|
||||
tor_free(key_material);
|
||||
log_fn(LOG_WARN,"Digest DOES NOT MATCH on onion handshake. Bug or attack.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* use the rest of the key material for our shared keys, digests, etc */
|
||||
memcpy(key_out, key_material+20, key_out_len);
|
||||
|
||||
#ifdef DEBUG_ONION_SKINS
|
||||
printf("Client: keys out:");
|
||||
PA(key_out, key_out_len);
|
||||
puts("");
|
||||
#endif
|
||||
|
||||
tor_free(key_material);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,832 @@
|
||||
/* 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_PWD_H
|
||||
#include <pwd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GRP_H
|
||||
#include <grp.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 MAXCONNECTIONS 1000 /* upper bound on max connections.
|
||||
can be lowered by config file */
|
||||
|
||||
#define DEFAULT_BANDWIDTH_OP (1024 * 1000)
|
||||
#define MAX_NICKNAME_LEN 32
|
||||
#define MAX_DIR_SIZE 500000
|
||||
|
||||
#define MAX_DNS_ENTRY_AGE (15*60)
|
||||
|
||||
#define CIRC_ID_TYPE_LOWER 0
|
||||
#define CIRC_ID_TYPE_HIGHER 1
|
||||
|
||||
#define _CONN_TYPE_MIN 3
|
||||
#define CONN_TYPE_OR_LISTENER 3
|
||||
#define CONN_TYPE_OR 4
|
||||
#define CONN_TYPE_EXIT 5
|
||||
#define CONN_TYPE_AP_LISTENER 6
|
||||
#define CONN_TYPE_AP 7
|
||||
#define CONN_TYPE_DIR_LISTENER 8
|
||||
#define CONN_TYPE_DIR 9
|
||||
#define CONN_TYPE_DNSWORKER 10
|
||||
#define CONN_TYPE_CPUWORKER 11
|
||||
#define _CONN_TYPE_MAX 11
|
||||
|
||||
#define LISTENER_STATE_READY 0
|
||||
|
||||
#define _DNSWORKER_STATE_MIN 1
|
||||
#define DNSWORKER_STATE_IDLE 1
|
||||
#define DNSWORKER_STATE_BUSY 2
|
||||
#define _DNSWORKER_STATE_MAX 2
|
||||
|
||||
#define _CPUWORKER_STATE_MIN 1
|
||||
#define CPUWORKER_STATE_IDLE 1
|
||||
#define CPUWORKER_STATE_BUSY_ONION 2
|
||||
#define CPUWORKER_STATE_BUSY_HANDSHAKE 3
|
||||
#define _CPUWORKER_STATE_MAX 3
|
||||
|
||||
#define CPUWORKER_TASK_ONION CPUWORKER_STATE_BUSY_ONION
|
||||
|
||||
#define _OR_CONN_STATE_MIN 1
|
||||
#define OR_CONN_STATE_CONNECTING 1 /* waiting for connect() to finish */
|
||||
#define OR_CONN_STATE_HANDSHAKING 2 /* SSL is handshaking, not done yet */
|
||||
#define OR_CONN_STATE_OPEN 3 /* ready to send/receive cells. */
|
||||
#define _OR_CONN_STATE_MAX 3
|
||||
|
||||
#define _EXIT_CONN_STATE_MIN 1
|
||||
#define EXIT_CONN_STATE_RESOLVING 1 /* waiting for response from dns farm */
|
||||
#define EXIT_CONN_STATE_CONNECTING 2 /* waiting for connect() to finish */
|
||||
#define EXIT_CONN_STATE_OPEN 3
|
||||
#define _EXIT_CONN_STATE_MAX 3
|
||||
#if 0
|
||||
#define EXIT_CONN_STATE_CLOSE 3 /* flushing the buffer, then will close */
|
||||
#define EXIT_CONN_STATE_CLOSE_WAIT 4 /* have sent a destroy, awaiting a confirmation */
|
||||
#endif
|
||||
|
||||
/* the AP state values must be disjoint from the EXIT state values */
|
||||
#define _AP_CONN_STATE_MIN 4
|
||||
#define AP_CONN_STATE_SOCKS_WAIT 4
|
||||
#define AP_CONN_STATE_CIRCUIT_WAIT 5
|
||||
#define AP_CONN_STATE_OPEN 6
|
||||
#define _AP_CONN_STATE_MAX 6
|
||||
|
||||
#define _DIR_CONN_STATE_MIN 1
|
||||
#define DIR_CONN_STATE_CONNECTING_FETCH 1
|
||||
#define DIR_CONN_STATE_CONNECTING_UPLOAD 2
|
||||
#define DIR_CONN_STATE_CLIENT_SENDING_FETCH 3
|
||||
#define DIR_CONN_STATE_CLIENT_SENDING_UPLOAD 4
|
||||
#define DIR_CONN_STATE_CLIENT_READING_FETCH 5
|
||||
#define DIR_CONN_STATE_CLIENT_READING_UPLOAD 6
|
||||
#define DIR_CONN_STATE_SERVER_COMMAND_WAIT 7
|
||||
#define DIR_CONN_STATE_SERVER_WRITING 8
|
||||
#define _DIR_CONN_STATE_MAX 8
|
||||
|
||||
#define CIRCUIT_STATE_BUILDING 0 /* I'm the OP, still haven't done all my handshakes */
|
||||
#define CIRCUIT_STATE_ONIONSKIN_PENDING 1 /* waiting to process the onionskin */
|
||||
#define CIRCUIT_STATE_OR_WAIT 2 /* I'm the OP, my firsthop is still connecting */
|
||||
#define CIRCUIT_STATE_OPEN 3 /* onionskin(s) processed, ready to send/receive cells */
|
||||
|
||||
#define RELAY_COMMAND_BEGIN 1
|
||||
#define RELAY_COMMAND_DATA 2
|
||||
#define RELAY_COMMAND_END 3
|
||||
#define RELAY_COMMAND_CONNECTED 4
|
||||
#define RELAY_COMMAND_SENDME 5
|
||||
#define RELAY_COMMAND_EXTEND 6
|
||||
#define RELAY_COMMAND_EXTENDED 7
|
||||
#define RELAY_COMMAND_TRUNCATE 8
|
||||
#define RELAY_COMMAND_TRUNCATED 9
|
||||
#define RELAY_COMMAND_DROP 10
|
||||
|
||||
#define END_STREAM_REASON_MISC 1
|
||||
#define END_STREAM_REASON_RESOLVEFAILED 2
|
||||
#define END_STREAM_REASON_CONNECTFAILED 3
|
||||
#define END_STREAM_REASON_EXITPOLICY 4
|
||||
#define END_STREAM_REASON_DESTROY 5
|
||||
#define END_STREAM_REASON_DONE 6
|
||||
|
||||
/* default cipher function */
|
||||
#define DEFAULT_CIPHER CRYPTO_CIPHER_AES_CTR
|
||||
/* Used to en/decrypt onion skins */
|
||||
#define ONION_CIPHER DEFAULT_CIPHER
|
||||
/* Used to en/decrypt RELAY cells */
|
||||
#define CIRCUIT_CIPHER DEFAULT_CIPHER
|
||||
|
||||
#define CELL_DIRECTION_IN 1
|
||||
#define CELL_DIRECTION_OUT 2
|
||||
#define EDGE_EXIT CONN_TYPE_EXIT
|
||||
#define EDGE_AP CONN_TYPE_AP
|
||||
#define CELL_DIRECTION(x) ((x) == EDGE_EXIT ? CELL_DIRECTION_IN : CELL_DIRECTION_OUT)
|
||||
|
||||
#define CIRCWINDOW_START 1000
|
||||
#define CIRCWINDOW_INCREMENT 100
|
||||
|
||||
#define STREAMWINDOW_START 500
|
||||
#define STREAMWINDOW_INCREMENT 50
|
||||
|
||||
/* cell commands */
|
||||
#define CELL_PADDING 0
|
||||
#define CELL_CREATE 1
|
||||
#define CELL_CREATED 2
|
||||
#define CELL_RELAY 3
|
||||
#define CELL_DESTROY 4
|
||||
|
||||
/* legal characters in a filename */
|
||||
#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/"
|
||||
/* legal characters in a nickname */
|
||||
#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
#define SOCKS4_NETWORK_LEN 8
|
||||
|
||||
/*
|
||||
* Relay payload:
|
||||
* Relay command [1 byte]
|
||||
* Recognized [2 bytes]
|
||||
* Stream ID [2 bytes]
|
||||
* Partial SHA-1 [4 bytes]
|
||||
* Length [2 bytes]
|
||||
* Relay payload [498 bytes]
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define CELL_RELAY_COMMAND(c) (*(uint8_t*)((c).payload))
|
||||
#define SET_CELL_RELAY_COMMAND(c,cmd) (*(uint8_t*)((c).payload) = (cmd))
|
||||
|
||||
#define CELL_RELAY_RECOGNIZED(c) (ntohs(*(uint16_t*)((c).payload+1)))
|
||||
#define SET_CELL_RELAY_RECOGNIZED(c,r) (*(uint16_t*)((c).payload+1) = htons(r))
|
||||
|
||||
#define STREAM_ID_SIZE 2
|
||||
//#define SET_CELL_STREAM_ID(c,id) memcpy((c).payload+1,(id),STREAM_ID_SIZE)
|
||||
#define CELL_RELAY_STREAM_ID(c) (ntohs(*(uint16_t*)((c).payload+3)))
|
||||
#define SET_CELL_RELAY_STREAM_ID(c,id) (*(uint16_t*)((c).payload+3) = htons(id))
|
||||
#define ZERO_STREAM 0
|
||||
|
||||
/* integrity is the first 32 bits (in network order) of a sha-1 of all
|
||||
* cell payloads that are relay cells that have been sent / delivered
|
||||
* to the hop on the * circuit (the integrity is zeroed while doing
|
||||
* each calculation)
|
||||
*/
|
||||
#define CELL_RELAY_INTEGRITY(c) (ntohl(*(uint32_t*)((c).payload+5)))
|
||||
#define SET_CELL_RELAY_INTEGRITY(c,i) (*(uint32_t*)((c).payload+5) = htonl(i))
|
||||
|
||||
/* relay length is how many bytes are used in the cell payload past relay_header_size */
|
||||
#define CELL_RELAY_LENGTH(c) (ntohs(*(uint16_t*)((c).payload+9)))
|
||||
#define SET_CELL_RELAY_LENGTH(c,len) (*(uint16_t*)((c).payload+9) = htons(len))
|
||||
#endif
|
||||
|
||||
#define CELL_PAYLOAD_SIZE 509
|
||||
#define CELL_NETWORK_SIZE 512
|
||||
|
||||
#define RELAY_HEADER_SIZE (1+2+2+4+2)
|
||||
#define RELAY_PAYLOAD_SIZE (CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE)
|
||||
|
||||
/* cell definition */
|
||||
typedef struct {
|
||||
uint16_t circ_id;
|
||||
unsigned char command;
|
||||
unsigned char payload[CELL_PAYLOAD_SIZE];
|
||||
} cell_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t command;
|
||||
uint16_t recognized;
|
||||
uint16_t stream_id;
|
||||
char integrity[4];
|
||||
uint16_t length;
|
||||
} relay_header_t;
|
||||
|
||||
typedef struct buf_t buf_t;
|
||||
typedef struct socks_request_t socks_request_t;
|
||||
|
||||
struct connection_t {
|
||||
|
||||
uint8_t type;
|
||||
uint8_t state;
|
||||
uint8_t wants_to_read; /* should we start reading again once
|
||||
* the bandwidth throttler allows it?
|
||||
*/
|
||||
uint8_t wants_to_write; /* should we start writing again once
|
||||
* the bandwidth throttler allows reads?
|
||||
*/
|
||||
int s; /* our socket */
|
||||
int poll_index; /* index of this conn into the poll_array */
|
||||
int marked_for_close; /* should we close this conn on the next
|
||||
* iteration of the main loop?
|
||||
*/
|
||||
|
||||
buf_t *inbuf;
|
||||
int inbuf_reached_eof; /* did read() return 0 on this conn? */
|
||||
time_t timestamp_lastread; /* when was the last time poll() said we could read? */
|
||||
|
||||
buf_t *outbuf;
|
||||
int outbuf_flushlen; /* how much data should we try to flush from the outbuf? */
|
||||
time_t timestamp_lastwritten; /* when was the last time poll() said we could write? */
|
||||
|
||||
time_t timestamp_created; /* when was this connection_t created? */
|
||||
|
||||
uint32_t addr; /* these two uniquely identify a router. Both in host order. */
|
||||
uint16_t port; /* if non-zero, they identify the guy on the other end
|
||||
* of the connection. */
|
||||
char *address; /* FQDN (or IP) of the guy on the other end.
|
||||
* strdup into this, because free_connection frees it
|
||||
*/
|
||||
crypto_pk_env_t *onion_pkey; /* public RSA key for the other side's onions */
|
||||
crypto_pk_env_t *link_pkey; /* public RSA key for the other side's TLS */
|
||||
crypto_pk_env_t *identity_pkey; /* public RSA key for the other side's signing */
|
||||
char *nickname;
|
||||
|
||||
/* Used only by OR connections: */
|
||||
tor_tls *tls;
|
||||
uint16_t next_circ_id; /* Which circ_id do we try to use next on this connection?
|
||||
* This is always in the range 0..1<<15-1.*/
|
||||
|
||||
/* bandwidth and receiver_bucket only used by ORs in OPEN state: */
|
||||
uint32_t bandwidth; /* connection bandwidth. */
|
||||
int receiver_bucket; /* when this hits 0, stop receiving. Every second we
|
||||
* add 'bandwidth' to this, capping it at 10*bandwidth.
|
||||
*/
|
||||
|
||||
/* Used only by edge connections: */
|
||||
uint16_t stream_id;
|
||||
struct connection_t *next_stream; /* points to the next stream at this edge, if any */
|
||||
struct crypt_path_t *cpath_layer; /* a pointer to which node in the circ this conn exits at */
|
||||
int package_window; /* how many more relay cells can i send into the circuit? */
|
||||
int deliver_window; /* how many more relay cells can end at me? */
|
||||
|
||||
int done_sending; /* for half-open connections; not used currently */
|
||||
int done_receiving;
|
||||
char has_sent_end; /* for debugging: set once we've set the stream end,
|
||||
and check in circuit_about_to_close_connection() */
|
||||
|
||||
/* Used only by AP connections */
|
||||
socks_request_t *socks_request;
|
||||
};
|
||||
|
||||
typedef struct connection_t connection_t;
|
||||
|
||||
#define EXIT_POLICY_ACCEPT 1
|
||||
#define EXIT_POLICY_REJECT 2
|
||||
|
||||
struct exit_policy_t {
|
||||
char policy_type;
|
||||
char *string;
|
||||
uint32_t addr;
|
||||
uint32_t msk;
|
||||
uint16_t prt_min;
|
||||
uint16_t prt_max;
|
||||
|
||||
struct exit_policy_t *next;
|
||||
};
|
||||
|
||||
/* config stuff we know about the other ORs in the network */
|
||||
typedef struct {
|
||||
char *address;
|
||||
char *nickname;
|
||||
|
||||
uint32_t addr; /* all host order */
|
||||
uint16_t or_port;
|
||||
uint16_t socks_port;
|
||||
uint16_t dir_port;
|
||||
|
||||
time_t published_on;
|
||||
|
||||
crypto_pk_env_t *onion_pkey; /* public RSA key for onions */
|
||||
crypto_pk_env_t *link_pkey; /* public RSA key for TLS */
|
||||
crypto_pk_env_t *identity_pkey; /* public RSA key for signing */
|
||||
|
||||
int is_running;
|
||||
|
||||
/* link info */
|
||||
uint32_t bandwidth;
|
||||
struct exit_policy_t *exit_policy;
|
||||
} routerinfo_t;
|
||||
|
||||
#define MAX_ROUTERS_IN_DIR 1024
|
||||
typedef struct {
|
||||
routerinfo_t **routers;
|
||||
int n_routers;
|
||||
char *software_versions;
|
||||
time_t published_on;
|
||||
} routerlist_t;
|
||||
|
||||
struct crypt_path_t {
|
||||
|
||||
/* crypto environments */
|
||||
crypto_cipher_env_t *f_crypto;
|
||||
crypto_cipher_env_t *b_crypto;
|
||||
|
||||
crypto_digest_env_t *f_digest; /* for integrity checking */
|
||||
crypto_digest_env_t *b_digest;
|
||||
|
||||
crypto_dh_env_t *handshake_state;
|
||||
|
||||
uint32_t addr;
|
||||
uint16_t port;
|
||||
|
||||
uint8_t state;
|
||||
#define CPATH_STATE_CLOSED 0
|
||||
#define CPATH_STATE_AWAITING_KEYS 1
|
||||
#define CPATH_STATE_OPEN 2
|
||||
struct crypt_path_t *next;
|
||||
struct crypt_path_t *prev; /* doubly linked list */
|
||||
|
||||
int package_window;
|
||||
int deliver_window;
|
||||
};
|
||||
|
||||
#define DH_KEY_LEN CRYPTO_DH_SIZE
|
||||
#define ONIONSKIN_CHALLENGE_LEN (16+DH_KEY_LEN)
|
||||
#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+20)
|
||||
|
||||
typedef struct crypt_path_t crypt_path_t;
|
||||
|
||||
typedef struct {
|
||||
int desired_path_len;
|
||||
char *chosen_exit; /* nickname of planned exit node */
|
||||
} cpath_build_state_t;
|
||||
|
||||
/* struct for a path (circuit) through the network */
|
||||
struct circuit_t {
|
||||
uint32_t n_addr;
|
||||
uint16_t n_port;
|
||||
connection_t *p_conn;
|
||||
connection_t *n_conn; /* for the OR conn, if there is one */
|
||||
connection_t *p_streams;
|
||||
connection_t *n_streams;
|
||||
uint16_t next_stream_id;
|
||||
int package_window;
|
||||
int deliver_window;
|
||||
|
||||
uint16_t p_circ_id; /* circuit identifiers */
|
||||
uint16_t n_circ_id;
|
||||
|
||||
crypto_cipher_env_t *p_crypto; /* used only for intermediate hops */
|
||||
crypto_cipher_env_t *n_crypto;
|
||||
|
||||
crypto_digest_env_t *p_digest; /* for integrity checking, */
|
||||
crypto_digest_env_t *n_digest; /* intermediate hops only */
|
||||
|
||||
cpath_build_state_t *build_state;
|
||||
crypt_path_t *cpath;
|
||||
|
||||
char onionskin[ONIONSKIN_CHALLENGE_LEN]; /* for storage while onionskin pending */
|
||||
time_t timestamp_created;
|
||||
time_t timestamp_dirty; /* when the circuit was first used, or 0 if clean */
|
||||
|
||||
uint8_t state;
|
||||
|
||||
void *next;
|
||||
};
|
||||
|
||||
typedef struct circuit_t circuit_t;
|
||||
|
||||
typedef struct {
|
||||
char *LogLevel;
|
||||
char *LogFile;
|
||||
char *DebugLogFile;
|
||||
char *DataDirectory;
|
||||
char *RouterFile;
|
||||
char *Nickname;
|
||||
char *Address;
|
||||
char *PidFile;
|
||||
char *ExitNodes;
|
||||
char *EntryNodes;
|
||||
char *ExcludedNodes;
|
||||
char *ExitPolicy;
|
||||
char *SocksBindAddress;
|
||||
char *ORBindAddress;
|
||||
char *DirBindAddress;
|
||||
char *RecommendedVersions;
|
||||
char *User;
|
||||
char *Group;
|
||||
double PathlenCoinWeight;
|
||||
int ORPort;
|
||||
int SocksPort;
|
||||
int DirPort;
|
||||
int MaxConn;
|
||||
int TrafficShaping;
|
||||
int LinkPadding;
|
||||
int IgnoreVersion;
|
||||
int RunAsDaemon;
|
||||
int DirRebuildPeriod;
|
||||
int DirFetchPostPeriod;
|
||||
int KeepalivePeriod;
|
||||
int MaxOnionsPending;
|
||||
int NewCircuitPeriod;
|
||||
int TotalBandwidth;
|
||||
int NumCpus;
|
||||
int loglevel;
|
||||
} or_options_t;
|
||||
|
||||
/* XXX are these good enough defaults? */
|
||||
#define MAX_SOCKS_REPLY_LEN 256
|
||||
/* Not 256; addresses must fit in a begin cell. */
|
||||
#define MAX_SOCKS_ADDR_LEN 200
|
||||
struct socks_request_t {
|
||||
char socks_version;
|
||||
int replylen;
|
||||
char reply[MAX_SOCKS_REPLY_LEN];
|
||||
char address[MAX_SOCKS_ADDR_LEN];
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
/* all the function prototypes go here */
|
||||
|
||||
/********************************* buffers.c ***************************/
|
||||
|
||||
int find_on_inbuf(char *string, int string_len, buf_t *buf);
|
||||
|
||||
buf_t *buf_new();
|
||||
buf_t *buf_new_with_capacity(size_t size);
|
||||
void buf_free(buf_t *buf);
|
||||
|
||||
size_t buf_datalen(const buf_t *buf);
|
||||
size_t buf_capacity(const buf_t *buf);
|
||||
const char *_buf_peek_raw_buffer(const buf_t *buf);
|
||||
|
||||
int read_to_buf(int s, int at_most, buf_t *buf, int *reached_eof);
|
||||
int read_to_buf_tls(tor_tls *tls, int at_most, buf_t *buf);
|
||||
|
||||
int flush_buf(int s, buf_t *buf, int *buf_flushlen);
|
||||
int flush_buf_tls(tor_tls *tls, buf_t *buf, int *buf_flushlen);
|
||||
|
||||
int write_to_buf(const char *string, int string_len, buf_t *buf);
|
||||
int fetch_from_buf(char *string, int string_len, buf_t *buf);
|
||||
int fetch_from_buf_http(buf_t *buf,
|
||||
char **headers_out, int max_headerlen,
|
||||
char **body_out, int max_bodylen);
|
||||
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req);
|
||||
|
||||
/********************************* circuit.c ***************************/
|
||||
|
||||
void circuit_add(circuit_t *circ);
|
||||
void circuit_remove(circuit_t *circ);
|
||||
circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn);
|
||||
void circuit_free(circuit_t *circ);
|
||||
void circuit_free_cpath(crypt_path_t *cpath);
|
||||
|
||||
circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn);
|
||||
circuit_t *circuit_get_by_conn(connection_t *conn);
|
||||
circuit_t *circuit_get_newest(connection_t *conn, int must_be_open);
|
||||
|
||||
void circuit_expire_building(void);
|
||||
int circuit_count_building(void);
|
||||
int circuit_stream_is_being_handled(connection_t *conn);
|
||||
|
||||
int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
|
||||
int cell_direction);
|
||||
int circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
|
||||
int cell_direction, crypt_path_t *layer_hint);
|
||||
|
||||
void circuit_resume_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint);
|
||||
int circuit_consider_stop_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint);
|
||||
void circuit_consider_sending_sendme(circuit_t *circ, int edge_type, crypt_path_t *layer_hint);
|
||||
|
||||
void circuit_close(circuit_t *circ);
|
||||
void circuit_about_to_close_connection(connection_t *conn);
|
||||
|
||||
void circuit_dump_by_conn(connection_t *conn, int severity);
|
||||
|
||||
void circuit_expire_unused_circuits(void);
|
||||
int circuit_launch_new(void);
|
||||
void circuit_increment_failure_count(void);
|
||||
void circuit_reset_failure_count(void);
|
||||
int circuit_establish_circuit(void);
|
||||
void circuit_n_conn_open(connection_t *or_conn);
|
||||
int circuit_send_next_onion_skin(circuit_t *circ);
|
||||
int circuit_extend(cell_t *cell, circuit_t *circ);
|
||||
int circuit_finish_handshake(circuit_t *circ, char *reply);
|
||||
int circuit_truncated(circuit_t *circ, crypt_path_t *layer);
|
||||
|
||||
void assert_cpath_ok(const crypt_path_t *c);
|
||||
void assert_cpath_layer_ok(const crypt_path_t *c);
|
||||
void assert_circuit_ok(const circuit_t *c);
|
||||
|
||||
extern unsigned long stats_n_relay_cells_relayed;
|
||||
extern unsigned long stats_n_relay_cells_delivered;
|
||||
|
||||
/********************************* command.c ***************************/
|
||||
|
||||
void command_process_cell(cell_t *cell, connection_t *conn);
|
||||
|
||||
extern unsigned long stats_n_padding_cells_processed;
|
||||
extern unsigned long stats_n_create_cells_processed;
|
||||
extern unsigned long stats_n_created_cells_processed;
|
||||
extern unsigned long stats_n_relay_cells_processed;
|
||||
extern unsigned long stats_n_destroy_cells_processed;
|
||||
|
||||
/********************************* config.c ***************************/
|
||||
|
||||
int getconfig(int argc, char **argv, or_options_t *options);
|
||||
|
||||
/********************************* connection.c ***************************/
|
||||
|
||||
connection_t *connection_new(int type);
|
||||
void connection_free(connection_t *conn);
|
||||
void connection_free_all(void);
|
||||
|
||||
int connection_create_listener(char *bindaddress, uint16_t bindport, int type);
|
||||
|
||||
int connection_connect(connection_t *conn, char *address, uint32_t addr, uint16_t port);
|
||||
int retry_all_connections(void);
|
||||
|
||||
int connection_handle_read(connection_t *conn);
|
||||
int connection_read_to_buf(connection_t *conn);
|
||||
|
||||
int connection_fetch_from_buf(char *string, int len, connection_t *conn);
|
||||
int connection_find_on_inbuf(char *string, int len, connection_t *conn);
|
||||
|
||||
int connection_wants_to_flush(connection_t *conn);
|
||||
int connection_outbuf_too_full(connection_t *conn);
|
||||
int connection_handle_write(connection_t *conn);
|
||||
void connection_write_to_buf(const char *string, int len, connection_t *conn);
|
||||
|
||||
connection_t *connection_twin_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
|
||||
connection_t *connection_get_by_type(int type);
|
||||
connection_t *connection_get_by_type_state(int type, int state);
|
||||
connection_t *connection_get_by_type_state_lastwritten(int type, int state);
|
||||
|
||||
int connection_receiver_bucket_should_increase(connection_t *conn);
|
||||
|
||||
#define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR)
|
||||
#define connection_has_pending_tls_data(conn) \
|
||||
((conn)->type == CONN_TYPE_OR && \
|
||||
(conn)->state == OR_CONN_STATE_OPEN && \
|
||||
tor_tls_get_pending_bytes(conn->tls))
|
||||
int connection_is_listener(connection_t *conn);
|
||||
int connection_state_is_open(connection_t *conn);
|
||||
|
||||
int connection_send_destroy(uint16_t circ_id, connection_t *conn);
|
||||
|
||||
int connection_process_inbuf(connection_t *conn);
|
||||
int connection_finished_flushing(connection_t *conn);
|
||||
|
||||
void assert_connection_ok(connection_t *conn, time_t now);
|
||||
|
||||
/********************************* connection_edge.c ***************************/
|
||||
|
||||
void relay_header_pack(char *dest, const relay_header_t *src);
|
||||
void relay_header_unpack(relay_header_t *dest, const char *src);
|
||||
int connection_edge_process_inbuf(connection_t *conn);
|
||||
int connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer);
|
||||
|
||||
int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, int relay_command,
|
||||
void *payload, int payload_len, crypt_path_t *cpath_layer);
|
||||
|
||||
int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_t *conn,
|
||||
int edge_type, crypt_path_t *layer_hint);
|
||||
int connection_edge_finished_flushing(connection_t *conn);
|
||||
|
||||
int connection_edge_package_raw_inbuf(connection_t *conn);
|
||||
|
||||
void connection_exit_connect(connection_t *conn);
|
||||
int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit);
|
||||
void connection_ap_attach_pending(void);
|
||||
|
||||
extern uint64_t stats_n_data_cells_packaged;
|
||||
extern uint64_t stats_n_data_bytes_packaged;
|
||||
extern uint64_t stats_n_data_cells_received;
|
||||
extern uint64_t stats_n_data_bytes_received;
|
||||
|
||||
void client_dns_init(void);
|
||||
void client_dns_clean(void);
|
||||
|
||||
/********************************* connection_or.c ***************************/
|
||||
|
||||
int connection_or_process_inbuf(connection_t *conn);
|
||||
int connection_or_finished_flushing(connection_t *conn);
|
||||
|
||||
void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router);
|
||||
connection_t *connection_or_connect(routerinfo_t *router);
|
||||
|
||||
int connection_tls_start_handshake(connection_t *conn, int receiving);
|
||||
int connection_tls_continue_handshake(connection_t *conn);
|
||||
|
||||
void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn);
|
||||
|
||||
/********************************* cpuworker.c *****************************/
|
||||
|
||||
void cpu_init(void);
|
||||
int connection_cpu_finished_flushing(connection_t *conn);
|
||||
int connection_cpu_process_inbuf(connection_t *conn);
|
||||
int cpuworker_main(void *data);
|
||||
int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type,
|
||||
void *task);
|
||||
|
||||
/********************************* directory.c ***************************/
|
||||
|
||||
void directory_initiate_command(routerinfo_t *router, int command);
|
||||
int connection_dir_process_inbuf(connection_t *conn);
|
||||
int connection_dir_finished_flushing(connection_t *conn);
|
||||
|
||||
/********************************* dns.c ***************************/
|
||||
|
||||
void dns_init(void);
|
||||
int connection_dns_finished_flushing(connection_t *conn);
|
||||
int connection_dns_process_inbuf(connection_t *conn);
|
||||
void dns_cancel_pending_resolve(char *question, connection_t *onlyconn);
|
||||
int dns_resolve(connection_t *exitconn);
|
||||
|
||||
/********************************* main.c ***************************/
|
||||
|
||||
int connection_add(connection_t *conn);
|
||||
int connection_remove(connection_t *conn);
|
||||
void connection_set_poll_socket(connection_t *conn);
|
||||
|
||||
void get_connection_array(connection_t ***array, int *n);
|
||||
|
||||
void connection_watch_events(connection_t *conn, short events);
|
||||
int connection_is_reading(connection_t *conn);
|
||||
void connection_stop_reading(connection_t *conn);
|
||||
void connection_start_reading(connection_t *conn);
|
||||
void connection_stop_writing(connection_t *conn);
|
||||
void connection_start_writing(connection_t *conn);
|
||||
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
/********************************* onion.c ***************************/
|
||||
|
||||
int decide_circ_id_type(char *local_nick, char *remote_nick);
|
||||
|
||||
int onion_pending_add(circuit_t *circ);
|
||||
circuit_t *onion_next_task(void);
|
||||
void onion_pending_remove(circuit_t *circ);
|
||||
|
||||
int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys);
|
||||
|
||||
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state,
|
||||
routerinfo_t **router_out);
|
||||
|
||||
int onion_skin_create(crypto_pk_env_t *router_key,
|
||||
crypto_dh_env_t **handshake_state_out,
|
||||
char *onion_skin_out);
|
||||
|
||||
int onion_skin_server_handshake(char *onion_skin,
|
||||
crypto_pk_env_t *private_key,
|
||||
char *handshake_reply_out,
|
||||
char *key_out,
|
||||
int key_out_len);
|
||||
|
||||
int onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
|
||||
char *handshake_reply,
|
||||
char *key_out,
|
||||
int key_out_len);
|
||||
|
||||
cpath_build_state_t *onion_new_cpath_build_state(void);
|
||||
|
||||
/********************************* router.c ***************************/
|
||||
|
||||
void set_onion_key(crypto_pk_env_t *k);
|
||||
crypto_pk_env_t *get_onion_key(void);
|
||||
void set_identity_key(crypto_pk_env_t *k);
|
||||
crypto_pk_env_t *get_identity_key(void);
|
||||
crypto_pk_env_t *get_link_key(void);
|
||||
int init_keys(void);
|
||||
|
||||
void router_retry_connections(void);
|
||||
void router_upload_desc_to_dirservers(void);
|
||||
int router_compare_to_my_exit_policy(connection_t *conn);
|
||||
const char *router_get_my_descriptor(void);
|
||||
int router_rebuild_descriptor(void);
|
||||
int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
|
||||
crypto_pk_env_t *ident_key);
|
||||
|
||||
/********************************* routerlist.c ***************************/
|
||||
|
||||
routerinfo_t *router_pick_directory_server(void);
|
||||
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk);
|
||||
routerinfo_t *router_get_by_nickname(char *nickname);
|
||||
void router_get_routerlist(routerlist_t **prouterlist);
|
||||
void routerinfo_free(routerinfo_t *router);
|
||||
void router_mark_as_down(char *nickname);
|
||||
int router_set_routerlist_from_file(char *routerfile);
|
||||
int router_get_dir_hash(const char *s, char *digest);
|
||||
int router_get_router_hash(const char *s, char *digest);
|
||||
int router_set_routerlist_from_directory(const char *s, crypto_pk_env_t *pkey);
|
||||
routerinfo_t *router_get_entry_from_string(const char **s);
|
||||
int router_add_exit_policy_from_string(routerinfo_t *router, const char *s);
|
||||
int router_supports_exit_address(uint32_t addr, uint16_t port,
|
||||
routerinfo_t *router);
|
||||
int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port,
|
||||
struct exit_policy_t *policy);
|
||||
int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port);
|
||||
int router_exit_policy_rejects_all(routerinfo_t *router);
|
||||
|
||||
/********************************* dirserv.c ***************************/
|
||||
int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk);
|
||||
int dirserv_parse_fingerprint_file(const char *fname);
|
||||
int dirserv_router_fingerprint_is_known(const routerinfo_t *router);
|
||||
void dirserv_free_fingerprint_list();
|
||||
int dirserv_add_descriptor(const char **desc);
|
||||
int dirserv_init_from_directory_string(const char *dir);
|
||||
void dirserv_free_descriptors();
|
||||
int dirserv_dump_directory_to_string(char *s, int maxlen,
|
||||
crypto_pk_env_t *private_key);
|
||||
void directory_set_dirty();
|
||||
size_t dirserv_get_directory(const char **cp);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -0,0 +1,524 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
||||
/* See LICENSE for licensing information */
|
||||
/* $Id$ */
|
||||
|
||||
#include "or.h"
|
||||
|
||||
extern or_options_t options; /* command-line and config-file options */
|
||||
|
||||
/************************************************************/
|
||||
|
||||
/* private keys */
|
||||
static crypto_pk_env_t *onionkey=NULL;
|
||||
static crypto_pk_env_t *linkkey=NULL;
|
||||
static crypto_pk_env_t *identitykey=NULL;
|
||||
|
||||
void set_onion_key(crypto_pk_env_t *k) {
|
||||
onionkey = k;
|
||||
}
|
||||
|
||||
crypto_pk_env_t *get_onion_key(void) {
|
||||
assert(onionkey);
|
||||
return onionkey;
|
||||
}
|
||||
|
||||
void set_link_key(crypto_pk_env_t *k)
|
||||
{
|
||||
linkkey = k;
|
||||
}
|
||||
|
||||
crypto_pk_env_t *get_link_key(void)
|
||||
{
|
||||
assert(linkkey);
|
||||
return linkkey;
|
||||
}
|
||||
|
||||
void set_identity_key(crypto_pk_env_t *k) {
|
||||
identitykey = k;
|
||||
}
|
||||
|
||||
crypto_pk_env_t *get_identity_key(void) {
|
||||
assert(identitykey);
|
||||
return identitykey;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
|
||||
static crypto_pk_env_t *init_key_from_file(const char *fname)
|
||||
{
|
||||
crypto_pk_env_t *prkey = NULL;
|
||||
int fd = -1;
|
||||
FILE *file = NULL;
|
||||
|
||||
if (!(prkey = crypto_new_pk_env(CRYPTO_PK_RSA))) {
|
||||
log(LOG_ERR, "Error creating crypto environment.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch(file_status(fname)) {
|
||||
case FN_DIR:
|
||||
case FN_ERROR:
|
||||
log(LOG_ERR, "Can't read key from %s", fname);
|
||||
goto error;
|
||||
case FN_NOENT:
|
||||
log(LOG_INFO, "No key found in %s; generating fresh key.", fname);
|
||||
if (crypto_pk_generate_key(prkey)) {
|
||||
log(LOG_ERR, "Error generating key: %s", crypto_perror());
|
||||
goto error;
|
||||
}
|
||||
if (crypto_pk_check_key(prkey) <= 0) {
|
||||
log(LOG_ERR, "Generated key seems invalid");
|
||||
goto error;
|
||||
}
|
||||
log(LOG_INFO, "Generated key seems valid");
|
||||
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
|
||||
log(LOG_ERR, "Couldn't write generated key to %s.", fname);
|
||||
goto error;
|
||||
}
|
||||
return prkey;
|
||||
case FN_FILE:
|
||||
if (crypto_pk_read_private_key_from_filename(prkey, fname)) {
|
||||
log(LOG_ERR, "Error loading private key.");
|
||||
goto error;
|
||||
}
|
||||
return prkey;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
error:
|
||||
if (prkey)
|
||||
crypto_free_pk_env(prkey);
|
||||
if (fd >= 0 && !file)
|
||||
close(fd);
|
||||
if (file)
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int init_keys(void) {
|
||||
char keydir[512];
|
||||
char fingerprint[FINGERPRINT_LEN+MAX_NICKNAME_LEN+3];
|
||||
char *cp;
|
||||
const char *tmp, *mydesc;
|
||||
crypto_pk_env_t *prkey;
|
||||
|
||||
/* OP's don't need keys. Just initialize the TLS context.*/
|
||||
if (!options.ORPort) {
|
||||
assert(!options.DirPort);
|
||||
if (tor_tls_context_new(NULL, 0, NULL)<0) {
|
||||
log_fn(LOG_ERR, "Error creating TLS context for OP.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
assert(options.DataDirectory);
|
||||
if (strlen(options.DataDirectory) > (512-128)) {
|
||||
log_fn(LOG_ERR, "DataDirectory is too long.");
|
||||
return -1;
|
||||
}
|
||||
if (check_private_dir(options.DataDirectory, 1)) {
|
||||
return -1;
|
||||
}
|
||||
sprintf(keydir,"%s/keys",options.DataDirectory);
|
||||
if (check_private_dir(keydir, 1)) {
|
||||
return -1;
|
||||
}
|
||||
cp = keydir + strlen(keydir); /* End of string. */
|
||||
|
||||
/* 1. Read identity key. Make it if none is found. */
|
||||
strcpy(cp, "/identity.key");
|
||||
log_fn(LOG_INFO,"Reading/making identity key %s...",keydir);
|
||||
prkey = init_key_from_file(keydir);
|
||||
if (!prkey) return -1;
|
||||
set_identity_key(prkey);
|
||||
/* 2. Read onion key. Make it if none is found. */
|
||||
strcpy(cp, "/onion.key");
|
||||
log_fn(LOG_INFO,"Reading/making onion key %s...",keydir);
|
||||
prkey = init_key_from_file(keydir);
|
||||
if (!prkey) return -1;
|
||||
set_onion_key(prkey);
|
||||
|
||||
/* 3. Initialize link key and TLS context. */
|
||||
strcpy(cp, "/link.key");
|
||||
log_fn(LOG_INFO,"Reading/making link key %s...",keydir);
|
||||
prkey = init_key_from_file(keydir);
|
||||
if (!prkey) return -1;
|
||||
set_link_key(prkey);
|
||||
if (tor_tls_context_new(prkey, 1, options.Nickname) < 0) {
|
||||
log_fn(LOG_ERR, "Error initializing TLS context");
|
||||
return -1;
|
||||
}
|
||||
/* 4. Dump router descriptor to 'router.desc' */
|
||||
/* Must be called after keys are initialized. */
|
||||
if (!(router_get_my_descriptor())) {
|
||||
log_fn(LOG_ERR, "Error initializing descriptor.");
|
||||
return -1;
|
||||
}
|
||||
/* We need to add our own fingerprint so it gets recognized. */
|
||||
if (dirserv_add_own_fingerprint(options.Nickname, get_identity_key())) {
|
||||
log_fn(LOG_ERR, "Error adding own fingerprint to approved set");
|
||||
return -1;
|
||||
}
|
||||
tmp = mydesc = router_get_my_descriptor();
|
||||
if (dirserv_add_descriptor(&tmp)) {
|
||||
log(LOG_ERR, "Unable to add own descriptor to directory.");
|
||||
return -1;
|
||||
}
|
||||
sprintf(keydir,"%s/router.desc", options.DataDirectory);
|
||||
log_fn(LOG_INFO,"Dumping descriptor to %s...",keydir);
|
||||
if (write_str_to_file(keydir, mydesc)) {
|
||||
return -1;
|
||||
}
|
||||
/* 5. Dump fingerprint to 'fingerprint' */
|
||||
sprintf(keydir,"%s/fingerprint", options.DataDirectory);
|
||||
log_fn(LOG_INFO,"Dumping fingerprint to %s...",keydir);
|
||||
assert(strlen(options.Nickname) <= MAX_NICKNAME_LEN);
|
||||
strcpy(fingerprint, options.Nickname);
|
||||
strcat(fingerprint, " ");
|
||||
if (crypto_pk_get_fingerprint(get_identity_key(),
|
||||
fingerprint+strlen(fingerprint))<0) {
|
||||
log_fn(LOG_ERR, "Error computing fingerprint");
|
||||
return -1;
|
||||
}
|
||||
strcat(fingerprint, "\n");
|
||||
if (write_str_to_file(keydir, fingerprint))
|
||||
return -1;
|
||||
if(!options.DirPort)
|
||||
return 0;
|
||||
/* 6. [dirserver only] load approved-routers file */
|
||||
sprintf(keydir,"%s/approved-routers", options.DataDirectory);
|
||||
log_fn(LOG_INFO,"Loading approved fingerprints from %s...",keydir);
|
||||
if(dirserv_parse_fingerprint_file(keydir) < 0) {
|
||||
log_fn(LOG_ERR, "Error loading fingerprints");
|
||||
return -1;
|
||||
}
|
||||
/* 7. [dirserver only] load old directory, if it's there */
|
||||
sprintf(keydir,"%s/cached-directory", options.DataDirectory);
|
||||
log_fn(LOG_INFO,"Loading cached directory from %s...",keydir);
|
||||
cp = read_file_to_str(keydir);
|
||||
if(!cp) {
|
||||
log_fn(LOG_INFO,"Cached directory %s not present. Ok.",keydir);
|
||||
} else {
|
||||
if(dirserv_init_from_directory_string(cp) < 0) {
|
||||
log_fn(LOG_ERR, "Cached directory %s is corrupt", keydir);
|
||||
free(cp);
|
||||
return -1;
|
||||
}
|
||||
free(cp);
|
||||
}
|
||||
/* success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
|
||||
static routerinfo_t *desc_routerinfo = NULL; /* my descriptor */
|
||||
static char descriptor[8192]; /* string representation of my descriptor */
|
||||
|
||||
void router_retry_connections(void) {
|
||||
int i;
|
||||
routerinfo_t *router;
|
||||
routerlist_t *rl;
|
||||
|
||||
router_get_routerlist(&rl);
|
||||
for (i=0;i<rl->n_routers;i++) {
|
||||
router = rl->routers[i];
|
||||
if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) {
|
||||
/* not in the list */
|
||||
log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port);
|
||||
connection_or_connect(router);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void router_upload_desc_to_dirservers(void) {
|
||||
int i;
|
||||
routerinfo_t *router;
|
||||
routerlist_t *rl;
|
||||
|
||||
router_get_routerlist(&rl);
|
||||
if(!rl)
|
||||
return;
|
||||
|
||||
if (!router_get_my_descriptor()) {
|
||||
log_fn(LOG_WARN, "No descriptor; skipping upload");
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0;i<rl->n_routers;i++) {
|
||||
router = rl->routers[i];
|
||||
if(router->dir_port > 0)
|
||||
directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD);
|
||||
}
|
||||
}
|
||||
|
||||
static void router_add_exit_policy_from_config(routerinfo_t *router) {
|
||||
char *s = options.ExitPolicy, *e;
|
||||
int last=0;
|
||||
char line[1024];
|
||||
|
||||
if(!s) {
|
||||
log_fn(LOG_INFO,"No exit policy configured. Ok.");
|
||||
return; /* nothing to see here */
|
||||
}
|
||||
if(!*s) {
|
||||
log_fn(LOG_INFO,"Exit policy is empty. Ok.");
|
||||
return; /* nothing to see here */
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
e = strchr(s,',');
|
||||
if(!e) {
|
||||
last = 1;
|
||||
strncpy(line,s,1023);
|
||||
} else {
|
||||
memcpy(line,s, ((e-s)<1023)?(e-s):1023);
|
||||
line[e-s] = 0;
|
||||
}
|
||||
line[1023]=0;
|
||||
log_fn(LOG_DEBUG,"Adding new entry '%s'",line);
|
||||
if(router_add_exit_policy_from_string(router,line) < 0)
|
||||
log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line);
|
||||
if(last)
|
||||
return;
|
||||
s = e+1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return 0 if my exit policy says to allow connection to conn.
|
||||
* Else return -1.
|
||||
*/
|
||||
int router_compare_to_my_exit_policy(connection_t *conn) {
|
||||
assert(desc_routerinfo);
|
||||
assert(conn->addr); /* make sure it's resolved to something. this
|
||||
way we can't get a 'maybe' below. */
|
||||
|
||||
if (router_compare_addr_to_exit_policy(conn->addr, conn->port,
|
||||
desc_routerinfo->exit_policy) == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *router_get_my_descriptor(void) {
|
||||
if (!desc_routerinfo) {
|
||||
if (router_rebuild_descriptor())
|
||||
return NULL;
|
||||
}
|
||||
log_fn(LOG_DEBUG,"my desc is '%s'",descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
int router_rebuild_descriptor(void) {
|
||||
routerinfo_t *ri;
|
||||
char localhostname[256];
|
||||
char *address = options.Address;
|
||||
|
||||
if(!address) { /* if not specified in config, we find a default */
|
||||
if(gethostname(localhostname,sizeof(localhostname)) < 0) {
|
||||
log_fn(LOG_WARN,"Error obtaining local hostname");
|
||||
return -1;
|
||||
}
|
||||
address = localhostname;
|
||||
if(!strchr(address,'.')) {
|
||||
log_fn(LOG_WARN,"fqdn '%s' has only one element. Misconfigured machine?",address);
|
||||
log_fn(LOG_WARN,"Try setting the Address line in your config file.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ri = tor_malloc(sizeof(routerinfo_t));
|
||||
ri->address = tor_strdup(address);
|
||||
ri->nickname = tor_strdup(options.Nickname);
|
||||
/* No need to set addr. */
|
||||
ri->or_port = options.ORPort;
|
||||
ri->socks_port = options.SocksPort;
|
||||
ri->dir_port = options.DirPort;
|
||||
ri->published_on = time(NULL);
|
||||
ri->onion_pkey = crypto_pk_dup_key(get_onion_key());
|
||||
ri->link_pkey = crypto_pk_dup_key(get_link_key());
|
||||
ri->identity_pkey = crypto_pk_dup_key(get_identity_key());
|
||||
ri->bandwidth = options.TotalBandwidth;
|
||||
ri->exit_policy = NULL; /* zero it out first */
|
||||
router_add_exit_policy_from_config(ri);
|
||||
if (desc_routerinfo)
|
||||
routerinfo_free(desc_routerinfo);
|
||||
desc_routerinfo = ri;
|
||||
if (router_dump_router_to_string(descriptor, 8192, ri, get_identity_key())<0) {
|
||||
log_fn(LOG_WARN, "Couldn't dump router to string.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_platform_str(char *platform, int len)
|
||||
{
|
||||
snprintf(platform, len-1, "Tor %s on %s", VERSION, get_uname());
|
||||
platform[len-1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX need to audit this thing and count fenceposts. maybe
|
||||
* refactor so we don't have to keep asking if we're
|
||||
* near the end of maxlen?
|
||||
*/
|
||||
#define DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
|
||||
int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
|
||||
crypto_pk_env_t *ident_key) {
|
||||
char *onion_pkey;
|
||||
char *link_pkey;
|
||||
char *identity_pkey;
|
||||
struct in_addr in;
|
||||
char platform[256];
|
||||
char digest[20];
|
||||
char signature[128];
|
||||
char published[32];
|
||||
int onion_pkeylen, link_pkeylen, identity_pkeylen;
|
||||
int written;
|
||||
int result=0;
|
||||
struct exit_policy_t *tmpe;
|
||||
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
|
||||
char *s_tmp, *s_dup;
|
||||
const char *cp;
|
||||
routerinfo_t *ri_tmp;
|
||||
#endif
|
||||
|
||||
get_platform_str(platform, sizeof(platform));
|
||||
|
||||
if (crypto_pk_cmp_keys(ident_key, router->identity_pkey)) {
|
||||
log_fn(LOG_WARN,"Tried to sign a router with a private key that didn't match router's public key!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(crypto_pk_write_public_key_to_string(router->onion_pkey,
|
||||
&onion_pkey,&onion_pkeylen)<0) {
|
||||
log_fn(LOG_WARN,"write onion_pkey to string failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(crypto_pk_write_public_key_to_string(router->identity_pkey,
|
||||
&identity_pkey,&identity_pkeylen)<0) {
|
||||
log_fn(LOG_WARN,"write identity_pkey to string failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(crypto_pk_write_public_key_to_string(router->link_pkey,
|
||||
&link_pkey,&link_pkeylen)<0) {
|
||||
log_fn(LOG_WARN,"write link_pkey to string failed!");
|
||||
return -1;
|
||||
}
|
||||
strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on));
|
||||
|
||||
result = snprintf(s, maxlen,
|
||||
"router %s %s %d %d %d %d\n"
|
||||
"platform %s\n"
|
||||
"published %s\n"
|
||||
"onion-key\n%s"
|
||||
"link-key\n%s"
|
||||
"signing-key\n%s",
|
||||
router->nickname,
|
||||
router->address,
|
||||
router->or_port,
|
||||
router->socks_port,
|
||||
router->dir_port,
|
||||
(int) router->bandwidth,
|
||||
platform,
|
||||
published,
|
||||
onion_pkey, link_pkey, identity_pkey);
|
||||
|
||||
free(onion_pkey);
|
||||
free(link_pkey);
|
||||
free(identity_pkey);
|
||||
|
||||
if(result < 0 || result >= maxlen) {
|
||||
/* apparently different glibcs do different things on snprintf error.. so check both */
|
||||
return -1;
|
||||
}
|
||||
written = result;
|
||||
|
||||
for(tmpe=router->exit_policy; tmpe; tmpe=tmpe->next) {
|
||||
in.s_addr = htonl(tmpe->addr);
|
||||
result = snprintf(s+written, maxlen-written, "%s %s",
|
||||
tmpe->policy_type == EXIT_POLICY_ACCEPT ? "accept" : "reject",
|
||||
tmpe->msk == 0 ? "*" : inet_ntoa(in));
|
||||
if(result < 0 || result+written > maxlen) {
|
||||
/* apparently different glibcs do different things on snprintf error.. so check both */
|
||||
return -1;
|
||||
}
|
||||
written += result;
|
||||
if (tmpe->msk != 0xFFFFFFFFu && tmpe->msk != 0) {
|
||||
in.s_addr = htonl(tmpe->msk);
|
||||
result = snprintf(s+written, maxlen-written, "/%s", inet_ntoa(in));
|
||||
if (result<0 || result+written > maxlen)
|
||||
return -1;
|
||||
written += result;
|
||||
}
|
||||
if (tmpe->prt_min == 1 && tmpe->prt_max == 65535) {
|
||||
if (written > maxlen-4)
|
||||
return -1;
|
||||
strcat(s+written, ":*\n");
|
||||
written += 3;
|
||||
} else if (tmpe->prt_min == tmpe->prt_max) {
|
||||
result = snprintf(s+written, maxlen-written, ":%d\n", tmpe->prt_min);
|
||||
if (result<0 || result+written > maxlen)
|
||||
return -1;
|
||||
written += result;
|
||||
} else {
|
||||
result = snprintf(s+written, maxlen-written, ":%d-%d\n", tmpe->prt_min,
|
||||
tmpe->prt_max);
|
||||
if (result<0 || result+written > maxlen)
|
||||
return -1;
|
||||
written += result;
|
||||
}
|
||||
} /* end for */
|
||||
if (written > maxlen-256) /* Not enough room for signature. */
|
||||
return -1;
|
||||
|
||||
strcat(s+written, "router-signature\n");
|
||||
written += strlen(s+written);
|
||||
s[written] = '\0';
|
||||
if (router_get_router_hash(s, digest) < 0)
|
||||
return -1;
|
||||
|
||||
if (crypto_pk_private_sign(ident_key, digest, 20, signature) < 0) {
|
||||
log_fn(LOG_WARN, "Error signing digest");
|
||||
return -1;
|
||||
}
|
||||
strcat(s+written, "-----BEGIN SIGNATURE-----\n");
|
||||
written += strlen(s+written);
|
||||
if (base64_encode(s+written, maxlen-written, signature, 128) < 0) {
|
||||
log_fn(LOG_WARN, "Couldn't base64-encode signature");
|
||||
return -1;
|
||||
}
|
||||
written += strlen(s+written);
|
||||
strcat(s+written, "-----END SIGNATURE-----\n");
|
||||
written += strlen(s+written);
|
||||
|
||||
if (written > maxlen-2)
|
||||
return -1;
|
||||
/* include a last '\n' */
|
||||
s[written] = '\n';
|
||||
s[written+1] = 0;
|
||||
|
||||
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
|
||||
cp = s_tmp = s_dup = tor_strdup(s);
|
||||
ri_tmp = router_get_entry_from_string(&cp);
|
||||
if (!ri_tmp) {
|
||||
log_fn(LOG_ERR, "We just generated a router descriptor we can't parse: <<%s>>",
|
||||
s);
|
||||
return -1;
|
||||
}
|
||||
free(s_dup);
|
||||
routerinfo_free(ri_tmp);
|
||||
#endif
|
||||
|
||||
return written+1;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
||||
/* Copyright 2001,2002,2003 Roger Dingledine. */
|
||||
/* 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 1024*1024
|
||||
char str[256];
|
||||
char str2[256];
|
||||
|
||||
buf_t *buf;
|
||||
buf_t *buf2;
|
||||
|
||||
int s, i, j, eof;
|
||||
|
||||
/****
|
||||
* buf_new
|
||||
****/
|
||||
if (!(buf = buf_new()))
|
||||
test_fail();
|
||||
|
||||
test_eq(buf_capacity(buf), 512*1024);
|
||||
test_eq(buf_datalen(buf), 0);
|
||||
|
||||
/****
|
||||
* read_to_buf
|
||||
****/
|
||||
s = open("/tmp/tor_test/data", O_WRONLY|O_CREAT|O_TRUNC, 0600);
|
||||
for (j=0;j<256;++j) {
|
||||
str[j] = (char)j;
|
||||
}
|
||||
write(s, str, 256);
|
||||
close(s);
|
||||
|
||||
s = open("/tmp/tor_test/data", O_RDONLY, 0);
|
||||
eof = 0;
|
||||
i = read_to_buf(s, 10, buf, &eof);
|
||||
test_eq(buf_capacity(buf), 512*1024);
|
||||
test_eq(buf_datalen(buf), 10);
|
||||
test_eq(eof, 0);
|
||||
test_eq(i, 10);
|
||||
test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10);
|
||||
|
||||
/* Test reading 0 bytes. */
|
||||
i = read_to_buf(s, 0, buf, &eof);
|
||||
test_eq(buf_capacity(buf), 512*1024);
|
||||
test_eq(buf_datalen(buf), 10);
|
||||
test_eq(eof, 0);
|
||||
test_eq(i, 0);
|
||||
|
||||
/* Now test when buffer is filled exactly. */
|
||||
buf2 = buf_new_with_capacity(6);
|
||||
i = read_to_buf(s, 6, buf2, &eof);
|
||||
test_eq(buf_capacity(buf2), 6);
|
||||
test_eq(buf_datalen(buf2), 6);
|
||||
test_eq(eof, 0);
|
||||
test_eq(i, 6);
|
||||
test_memeq(str+10, (char*)_buf_peek_raw_buffer(buf2), 6);
|
||||
buf_free(buf2);
|
||||
|
||||
/* Now test when buffer is filled with more data to read. */
|
||||
buf2 = buf_new_with_capacity(32);
|
||||
i = read_to_buf(s, 128, buf2, &eof);
|
||||
test_eq(buf_capacity(buf2), 128);
|
||||
test_eq(buf_datalen(buf2), 32);
|
||||
test_eq(eof, 0);
|
||||
test_eq(i, 32);
|
||||
buf_free(buf2);
|
||||
|
||||
/* Now read to eof. */
|
||||
test_assert(buf_capacity(buf) > 256);
|
||||
i = read_to_buf(s, 1024, buf, &eof);
|
||||
test_eq(i, (256-32-10-6));
|
||||
test_eq(buf_capacity(buf), MAX_BUF_SIZE);
|
||||
test_eq(buf_datalen(buf), 256-6-32);
|
||||
test_memeq(str, (char*)_buf_peek_raw_buffer(buf), 10); /* XXX Check rest. */
|
||||
test_eq(eof, 0);
|
||||
|
||||
i = read_to_buf(s, 1024, buf, &eof);
|
||||
test_eq(i, 0);
|
||||
test_eq(buf_capacity(buf), MAX_BUF_SIZE);
|
||||
test_eq(buf_datalen(buf), 256-6-32);
|
||||
test_eq(eof, 1);
|
||||
|
||||
close(s);
|
||||
|
||||
/****
|
||||
* find_on_inbuf
|
||||
****/
|
||||
buf_free(buf);
|
||||
buf = buf_new();
|
||||
s = open("/tmp/tor_test/data", O_RDONLY, 0);
|
||||
eof = 0;
|
||||
i = read_to_buf(s, 1024, buf, &eof);
|
||||
test_eq(256, i);
|
||||
close(s);
|
||||
|
||||
test_eq(((int)'d') + 1, find_on_inbuf("abcd", 4, buf));
|
||||
test_eq(-1, find_on_inbuf("xyzzy", 5, buf));
|
||||
/* Make sure we don't look off the end of the buffef */
|
||||
((char*)_buf_peek_raw_buffer(buf))[256] = 'A';
|
||||
((char*)_buf_peek_raw_buffer(buf))[257] = 'X';
|
||||
test_eq(-1, find_on_inbuf("\xff" "A", 2, buf));
|
||||
test_eq(-1, find_on_inbuf("AX", 2, buf));
|
||||
/* Make sure we use the string length */
|
||||
test_eq(((int)'d')+1, find_on_inbuf("abcdX", 4, buf));
|
||||
|
||||
/****
|
||||
* fetch_from_buf
|
||||
****/
|
||||
memset(str2, 255, 256);
|
||||
test_eq(246, fetch_from_buf(str2, 10, buf));
|
||||
test_memeq(str2, str, 10);
|
||||
test_memeq(str+10,(char*)_buf_peek_raw_buffer(buf),246);
|
||||
test_eq(buf_datalen(buf),246);
|
||||
|
||||
test_eq(0, fetch_from_buf(str2, 246, buf));
|
||||
test_memeq(str2, str+10, 246);
|
||||
test_eq(buf_capacity(buf),MAX_BUF_SIZE);
|
||||
test_eq(buf_datalen(buf),0);
|
||||
|
||||
/****
|
||||
* write_to_buf
|
||||
****/
|
||||
memset((char *)_buf_peek_raw_buffer(buf), (int)'-', 256);
|
||||
i = write_to_buf("Hello world", 11, buf);
|
||||
test_eq(i, 11);
|
||||
test_eq(buf_datalen(buf), 11);
|
||||
test_memeq((char*)_buf_peek_raw_buffer(buf), "Hello world", 11);
|
||||
i = write_to_buf("XYZZY", 5, buf);
|
||||
test_eq(i, 16);
|
||||
test_eq(buf_datalen(buf), 16);
|
||||
test_memeq((char*)_buf_peek_raw_buffer(buf), "Hello worldXYZZY", 16);
|
||||
/* Test when buffer is overfull. */
|
||||
#if 0
|
||||
buflen = 18;
|
||||
test_eq(-1, write_to_buf("This string will not fit.", 25,
|
||||
&buf, &buflen, &buf_datalen));
|
||||
test_eq(buf_datalen, 16);
|
||||
test_memeq(buf, "Hello worldXYZZY--", 18);
|
||||
buflen = MAX_BUF_SIZE;
|
||||
#endif
|
||||
|
||||
/****
|
||||
* flush_buf
|
||||
****/
|
||||
/* XXXX Needs tests. */
|
||||
|
||||
buf_free(buf);
|
||||
}
|
||||
|
||||
void
|
||||
test_crypto_dh()
|
||||
{
|
||||
crypto_dh_env_t *dh1, *dh2;
|
||||
char p1[CRYPTO_DH_SIZE];
|
||||
char p2[CRYPTO_DH_SIZE];
|
||||
char s1[CRYPTO_DH_SIZE];
|
||||
char s2[CRYPTO_DH_SIZE];
|
||||
int s1len, s2len;
|
||||
|
||||
dh1 = crypto_dh_new();
|
||||
dh2 = crypto_dh_new();
|
||||
test_eq(crypto_dh_get_bytes(dh1), CRYPTO_DH_SIZE);
|
||||
test_eq(crypto_dh_get_bytes(dh2), CRYPTO_DH_SIZE);
|
||||
|
||||
memset(p1, 0, CRYPTO_DH_SIZE);
|
||||
memset(p2, 0, CRYPTO_DH_SIZE);
|
||||
test_memeq(p1, p2, CRYPTO_DH_SIZE);
|
||||
test_assert(! crypto_dh_get_public(dh1, p1, CRYPTO_DH_SIZE));
|
||||
test_memneq(p1, p2, CRYPTO_DH_SIZE);
|
||||
test_assert(! crypto_dh_get_public(dh2, p2, CRYPTO_DH_SIZE));
|
||||
test_memneq(p1, p2, CRYPTO_DH_SIZE);
|
||||
|
||||
memset(s1, 0, CRYPTO_DH_SIZE);
|
||||
memset(s2, 0xFF, CRYPTO_DH_SIZE);
|
||||
s1len = crypto_dh_compute_secret(dh1, p2, CRYPTO_DH_SIZE, s1, 50);
|
||||
s2len = crypto_dh_compute_secret(dh2, p1, CRYPTO_DH_SIZE, s2, 50);
|
||||
test_assert(s1len > 0);
|
||||
test_eq(s1len, s2len);
|
||||
test_memeq(s1, s2, s1len);
|
||||
|
||||
crypto_dh_free(dh1);
|
||||
crypto_dh_free(dh2);
|
||||
}
|
||||
|
||||
void
|
||||
test_crypto()
|
||||
{
|
||||
crypto_cipher_env_t *env1, *env2;
|
||||
crypto_pk_env_t *pk1, *pk2;
|
||||
char *data1, *data2, *data3, *cp;
|
||||
FILE *f;
|
||||
int i, j;
|
||||
int str_ciphers[] = { CRYPTO_CIPHER_IDENTITY,
|
||||
CRYPTO_CIPHER_DES,
|
||||
CRYPTO_CIPHER_RC4,
|
||||
CRYPTO_CIPHER_3DES,
|
||||
CRYPTO_CIPHER_AES_CTR,
|
||||
-1 };
|
||||
|
||||
data1 = tor_malloc(1024);
|
||||
data2 = tor_malloc(1024);
|
||||
data3 = tor_malloc(1024);
|
||||
test_assert(data1 && data2 && data3);
|
||||
|
||||
/* Try out RNG. */
|
||||
test_assert(! crypto_seed_rng());
|
||||
crypto_rand(100, data1);
|
||||
crypto_rand(100, data2);
|
||||
test_memneq(data1,data2,100);
|
||||
|
||||
/* Try out identity ciphers. */
|
||||
env1 = crypto_new_cipher_env(CRYPTO_CIPHER_IDENTITY);
|
||||
test_neq(env1, 0);
|
||||
test_eq(crypto_cipher_generate_key(env1), 0);
|
||||
test_eq(crypto_cipher_set_iv(env1, ""), 0);
|
||||
test_eq(crypto_cipher_encrypt_init_cipher(env1), 0);
|
||||
for(i = 0; i < 1024; ++i) {
|
||||
data1[i] = (char) i*73;
|
||||
}
|
||||
crypto_cipher_encrypt(env1, data1, 1024, data2);
|
||||
test_memeq(data1, data2, 1024);
|
||||
crypto_free_cipher_env(env1);
|
||||
|
||||
/* Now, test encryption and decryption with stream ciphers. */
|
||||
data1[0]='\0';
|
||||
for(i = 1023; i>0; i -= 35)
|
||||
strncat(data1, "Now is the time for all good onions", i);
|
||||
for(i=0; str_ciphers[i] >= 0; ++i) {
|
||||
/* For each cipher... */
|
||||
memset(data2, 0, 1024);
|
||||
memset(data3, 0, 1024);
|
||||
env1 = crypto_new_cipher_env(str_ciphers[i]);
|
||||
test_neq(env1, 0);
|
||||
env2 = crypto_new_cipher_env(str_ciphers[i]);
|
||||
test_neq(env2, 0);
|
||||
j = crypto_cipher_generate_key(env1);
|
||||
if (str_ciphers[i] != CRYPTO_CIPHER_IDENTITY) {
|
||||
crypto_cipher_set_key(env2, crypto_cipher_get_key(env1));
|
||||
}
|
||||
crypto_cipher_set_iv(env1, "12345678901234567890");
|
||||
crypto_cipher_set_iv(env2, "12345678901234567890");
|
||||
crypto_cipher_encrypt_init_cipher(env1);
|
||||
crypto_cipher_decrypt_init_cipher(env2);
|
||||
|
||||
/* Try encrypting 512 chars. */
|
||||
crypto_cipher_encrypt(env1, data1, 512, data2);
|
||||
crypto_cipher_decrypt(env2, data2, 512, data3);
|
||||
test_memeq(data1, data3, 512);
|
||||
if (str_ciphers[i] == CRYPTO_CIPHER_IDENTITY) {
|
||||
test_memeq(data1, data2, 512);
|
||||
} else {
|
||||
test_memneq(data1, data2, 512);
|
||||
}
|
||||
/* Now encrypt 1 at a time, and get 1 at a time. */
|
||||
for (j = 512; j < 560; ++j) {
|
||||
crypto_cipher_encrypt(env1, data1+j, 1, data2+j);
|
||||
}
|
||||
for (j = 512; j < 560; ++j) {
|
||||
crypto_cipher_decrypt(env2, data2+j, 1, data3+j);
|
||||
}
|
||||
test_memeq(data1, data3, 560);
|
||||
/* Now encrypt 3 at a time, and get 5 at a time. */
|
||||
for (j = 560; j < 1024-5; j += 3) {
|
||||
crypto_cipher_encrypt(env1, data1+j, 3, data2+j);
|
||||
}
|
||||
for (j = 560; j < 1024-5; j += 5) {
|
||||
crypto_cipher_decrypt(env2, data2+j, 5, data3+j);
|
||||
}
|
||||
test_memeq(data1, data3, 1024-5);
|
||||
/* Now make sure that when we encrypt with different chunk sizes, we get
|
||||
the same results. */
|
||||
crypto_free_cipher_env(env2);
|
||||
|
||||
memset(data3, 0, 1024);
|
||||
env2 = crypto_new_cipher_env(str_ciphers[i]);
|
||||
test_neq(env2, 0);
|
||||
if (str_ciphers[i] != CRYPTO_CIPHER_IDENTITY) {
|
||||
crypto_cipher_set_key(env2, crypto_cipher_get_key(env1));
|
||||
}
|
||||
crypto_cipher_set_iv(env2, "12345678901234567890");
|
||||
crypto_cipher_encrypt_init_cipher(env2);
|
||||
for (j = 0; j < 1024-16; j += 17) {
|
||||
crypto_cipher_encrypt(env2, data1+j, 17, data3+j);
|
||||
}
|
||||
for (j= 0; j < 1024-16; ++j) {
|
||||
if (data2[j] != data3[j]) {
|
||||
printf("%d: %d\t%d\n", j, (int) data2[j], (int) data3[j]);
|
||||
}
|
||||
}
|
||||
test_memeq(data2, data3, 1024-16);
|
||||
crypto_free_cipher_env(env1);
|
||||
crypto_free_cipher_env(env2);
|
||||
}
|
||||
|
||||
/* Test vectors for stream ciphers. */
|
||||
/* XXXX Look up some test vectors for the ciphers and make sure we match. */
|
||||
|
||||
/* Test SHA-1 with a test vector from the specification. */
|
||||
i = crypto_SHA_digest("abc", 3, data1);
|
||||
test_memeq(data1,
|
||||
"\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78"
|
||||
"\x50\xC2\x6C\x9C\xD0\xD8\x9D", 20);
|
||||
|
||||
/* Public-key ciphers */
|
||||
pk1 = crypto_new_pk_env(CRYPTO_PK_RSA);
|
||||
pk2 = crypto_new_pk_env(CRYPTO_PK_RSA);
|
||||
test_assert(pk1 && pk2);
|
||||
test_assert(! crypto_pk_generate_key(pk1));
|
||||
test_assert(! crypto_pk_write_public_key_to_string(pk1, &cp, &i));
|
||||
test_assert(! crypto_pk_read_public_key_from_string(pk2, cp, i));
|
||||
test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
|
||||
|
||||
test_eq(128, crypto_pk_keysize(pk1));
|
||||
test_eq(128, crypto_pk_keysize(pk2));
|
||||
|
||||
test_eq(128, crypto_pk_public_encrypt(pk2, "Hello whirled.", 15, data1,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
test_eq(128, crypto_pk_public_encrypt(pk1, "Hello whirled.", 15, data2,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
/* oaep padding should make encryption not match */
|
||||
test_memneq(data1, data2, 128);
|
||||
test_eq(15, crypto_pk_private_decrypt(pk1, data1, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
test_streq(data3, "Hello whirled.");
|
||||
memset(data3, 0, 1024);
|
||||
test_eq(15, crypto_pk_private_decrypt(pk1, data2, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
test_streq(data3, "Hello whirled.");
|
||||
/* Can't decrypt with public key. */
|
||||
test_eq(-1, crypto_pk_private_decrypt(pk2, data2, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
/* Try again with bad padding */
|
||||
memcpy(data2+1, "XYZZY", 5); /* This has fails ~ once-in-2^40 */
|
||||
test_eq(-1, crypto_pk_private_decrypt(pk1, data2, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
|
||||
/* File operations: save and load private key */
|
||||
f = fopen("/tmp/tor_test/pkey1", "wb");
|
||||
test_assert(! crypto_pk_write_private_key_to_file(pk1, f));
|
||||
fclose(f);
|
||||
f = fopen("/tmp/tor_test/pkey1", "rb");
|
||||
test_assert(! crypto_pk_read_private_key_from_file(pk2, f));
|
||||
fclose(f);
|
||||
test_eq(15, crypto_pk_private_decrypt(pk2, data1, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
test_assert(! crypto_pk_read_private_key_from_filename(pk2,
|
||||
"/tmp/tor_test/pkey1"));
|
||||
test_eq(15, crypto_pk_private_decrypt(pk2, data1, 128, data3,
|
||||
RSA_PKCS1_OAEP_PADDING));
|
||||
|
||||
/* Now try signing. */
|
||||
strcpy(data1, "Ossifrage");
|
||||
test_eq(128, crypto_pk_private_sign(pk1, data1, 10, data2));
|
||||
test_eq(10, crypto_pk_public_checksig(pk1, data2, 128, data3));
|
||||
test_streq(data3, "Ossifrage");
|
||||
/*XXXX test failed signing*/
|
||||
|
||||
crypto_free_pk_env(pk1);
|
||||
crypto_free_pk_env(pk2);
|
||||
|
||||
/* Base64 tests */
|
||||
strcpy(data1, "Test string that contains 35 chars.");
|
||||
strcat(data1, " 2nd string that contains 35 chars.");
|
||||
|
||||
i = base64_encode(data2, 1024, data1, 71);
|
||||
j = base64_decode(data3, 1024, data2, i);
|
||||
test_streq(data3, data1);
|
||||
test_eq(j, 71);
|
||||
test_assert(data2[i] == '\0');
|
||||
|
||||
|
||||
free(data1);
|
||||
free(data2);
|
||||
free(data3);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
test_util() {
|
||||
struct timeval start, end;
|
||||
struct tm a_time;
|
||||
|
||||
start.tv_sec = 5;
|
||||
start.tv_usec = 5000;
|
||||
|
||||
end.tv_sec = 5;
|
||||
end.tv_usec = 5000;
|
||||
|
||||
test_eq(0L, tv_udiff(&start, &end));
|
||||
|
||||
end.tv_usec = 7000;
|
||||
|
||||
test_eq(2000L, tv_udiff(&start, &end));
|
||||
|
||||
end.tv_sec = 6;
|
||||
|
||||
test_eq(1002000L, tv_udiff(&start, &end));
|
||||
|
||||
end.tv_usec = 0;
|
||||
|
||||
test_eq(995000L, tv_udiff(&start, &end));
|
||||
|
||||
end.tv_sec = 4;
|
||||
|
||||
test_eq(0L, tv_udiff(&start, &end));
|
||||
|
||||
/* The test values here are confirmed to be correct on a platform
|
||||
* with a working timegm. */
|
||||
a_time.tm_year = 2003-1900;
|
||||
a_time.tm_mon = 7;
|
||||
a_time.tm_mday = 30;
|
||||
a_time.tm_hour = 6;
|
||||
a_time.tm_min = 14;
|
||||
a_time.tm_sec = 55;
|
||||
test_eq((time_t) 1062224095UL, tor_timegm(&a_time));
|
||||
a_time.tm_year = 2004-1900; /* Try a leap year, after feb. */
|
||||
test_eq((time_t) 1093846495UL, tor_timegm(&a_time));
|
||||
a_time.tm_mon = 1; /* Try a leap year, in feb. */
|
||||
a_time.tm_mday = 10;
|
||||
test_eq((time_t) 1076393695UL, tor_timegm(&a_time));
|
||||
}
|
||||
|
||||
void test_onion() {
|
||||
#if 0
|
||||
char **names;
|
||||
int i,num;
|
||||
|
||||
names = parse_nickname_list(" foo bar baz quux ", &num);
|
||||
test_eq(num,4);
|
||||
test_streq(names[0],"foo");
|
||||
test_streq(names[1],"bar");
|
||||
test_streq(names[2],"baz");
|
||||
test_streq(names[3],"quux");
|
||||
for(i=0;i<num;i++)
|
||||
tor_free(names[i]);
|
||||
tor_free(names);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
test_onion_handshake() {
|
||||
/* client-side */
|
||||
crypto_dh_env_t *c_dh = NULL;
|
||||
char c_buf[ONIONSKIN_CHALLENGE_LEN];
|
||||
char c_keys[40];
|
||||
|
||||
/* server-side */
|
||||
char s_buf[ONIONSKIN_REPLY_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, ONIONSKIN_CHALLENGE_LEN);
|
||||
test_assert(! onion_skin_create(pk, &c_dh, c_buf));
|
||||
|
||||
/* server handshake */
|
||||
memset(s_buf, 0, ONIONSKIN_REPLY_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;
|
||||
routerlist_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.socks_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.addr = 0;
|
||||
ex1.msk = 0;
|
||||
ex1.prt_min = ex1.prt_max = 80;
|
||||
ex1.next = &ex2;
|
||||
ex2.policy_type = EXIT_POLICY_REJECT;
|
||||
ex2.addr = 18 << 24;
|
||||
ex2.msk = 0xFF000000u;
|
||||
ex2.prt_min = ex1.prt_max = 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.socks_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);
|
||||
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((const char**)&cp);
|
||||
test_assert(rp1);
|
||||
test_streq(rp1->address, r1.address);
|
||||
test_eq(rp1->or_port, r1.or_port);
|
||||
test_eq(rp1->socks_port, r1.socks_port);
|
||||
test_eq(rp1->dir_port, r1.dir_port);
|
||||
test_eq(rp1->bandwidth, r1.bandwidth);
|
||||
test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
|
||||
test_assert(crypto_pk_cmp_keys(rp1->link_pkey, pk3) == 0);
|
||||
test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
|
||||
test_assert(rp1->exit_policy == NULL);
|
||||
|
||||
#if 0
|
||||
/* XXX Once we have exit policies, test this again. XXX */
|
||||
strcpy(buf2, "router tor.tor.tor 9005 0 0 3000\n");
|
||||
strcat(buf2, pk2_str);
|
||||
strcat(buf2, "signing-key\n");
|
||||
strcat(buf2, pk1_str);
|
||||
strcat(buf2, "accept *:80\nreject 18.*:24\n\n");
|
||||
test_assert(router_dump_router_to_string(buf, 2048, &r2, pk2)>0);
|
||||
test_streq(buf, buf2);
|
||||
|
||||
cp = buf;
|
||||
rp2 = router_get_entry_from_string(&cp);
|
||||
test_assert(rp2);
|
||||
test_streq(rp2->address, r2.address);
|
||||
test_eq(rp2->or_port, r2.or_port);
|
||||
test_eq(rp2->socks_port, r2.socks_port);
|
||||
test_eq(rp2->dir_port, r2.dir_port);
|
||||
test_eq(rp2->bandwidth, r2.bandwidth);
|
||||
test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
|
||||
test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
|
||||
test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT);
|
||||
test_streq(rp2->exit_policy->string, "accept *:80");
|
||||
test_streq(rp2->exit_policy->address, "*");
|
||||
test_streq(rp2->exit_policy->port, "80");
|
||||
test_eq(rp2->exit_policy->next->policy_type, EXIT_POLICY_REJECT);
|
||||
test_streq(rp2->exit_policy->next->string, "reject 18.*:24");
|
||||
test_streq(rp2->exit_policy->next->address, "18.*");
|
||||
test_streq(rp2->exit_policy->next->port, "24");
|
||||
test_assert(rp2->exit_policy->next->next == NULL);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* XXX To re-enable this test, we need to separate directory generation
|
||||
* XXX from the directory backend again. Do this the next time we have
|
||||
* XXX directory trouble. */
|
||||
/* Okay, now for the directories. */
|
||||
dir1 = (directory_t*) tor_malloc(sizeof(directory_t));
|
||||
dir1->n_routers = 2;
|
||||
dir1->routers = (routerinfo_t**) tor_malloc(sizeof(routerinfo_t*)*2);
|
||||
dir1->routers[0] = &r1;
|
||||
dir1->routers[1] = &r2;
|
||||
test_assert(! dump_signed_directory_to_string_impl(buf, 4096, dir1, pk1));
|
||||
/* puts(buf); */
|
||||
|
||||
test_assert(! router_get_dir_from_string_impl(buf, &dir2, pk1));
|
||||
test_eq(2, dir2->n_routers);
|
||||
#endif
|
||||
|
||||
tor_free(pk1_str);
|
||||
tor_free(pk2_str);
|
||||
if (pk1) crypto_free_pk_env(pk1);
|
||||
if (pk2) crypto_free_pk_env(pk2);
|
||||
if (rp1) routerinfo_free(rp1);
|
||||
if (rp2) routerinfo_free(rp2);
|
||||
tor_free(dir1); /* And more !*/
|
||||
tor_free(dir2); /* And more !*/
|
||||
|
||||
/* make sure compare_recommended_versions() works */
|
||||
test_eq(0, compare_recommended_versions("abc", "abc"));
|
||||
test_eq(0, compare_recommended_versions("abc", "ab,abd,abde,abc,abcde"));
|
||||
test_eq(0, compare_recommended_versions("abc", "ab,abd,abde,abcde,abc"));
|
||||
test_eq(0, compare_recommended_versions("abc", "abc,abd,abde,abc,abcde"));
|
||||
test_eq(0, compare_recommended_versions("a", "a,ab,abd,abde,abc,abcde"));
|
||||
test_eq(-1, compare_recommended_versions("a", "ab,abd,abde,abc,abcde"));
|
||||
test_eq(-1, compare_recommended_versions("abb", "ab,abd,abde,abc,abcde"));
|
||||
test_eq(-1, compare_recommended_versions("a", ""));
|
||||
}
|
||||
|
||||
int
|
||||
main(int c, char**v){
|
||||
#if 0
|
||||
or_options_t options; /* command-line and config-file options */
|
||||
|
||||
if(getconfig(c,v,&options))
|
||||
exit(1);
|
||||
#endif
|
||||
|
||||
crypto_seed_rng();
|
||||
|
||||
setup_directory();
|
||||
puts("========================== Buffers =========================");
|
||||
test_buffers();
|
||||
puts("\n========================== Crypto ==========================");
|
||||
test_crypto();
|
||||
test_crypto_dh();
|
||||
puts("\n========================= Util ============================");
|
||||
test_util();
|
||||
puts("\n========================= Onion Skins =====================");
|
||||
test_onion();
|
||||
test_onion_handshake();
|
||||
puts("\n========================= Directory Formats ===============");
|
||||
test_dir_format();
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
mode:c
|
||||
indent-tabs-mode:nil
|
||||
c-basic-offset:2
|
||||
End:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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_ */
|
||||
@@ -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"
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
|
||||
TORBIN=@BINDIR@/tor
|
||||
TORPID=@LOCALSTATEDIR@/run/tor.pid
|
||||
TORLOG=@LOCALSTATEDIR@/log/tor/tor.log
|
||||
TORCONF=@CONFDIR@/torrc
|
||||
TORARGS="--pidfile $TORPID --logfile $TORLOG --runasdaemon 1"
|
||||
RETVAL=0
|
||||
|
||||
case "$1" in
|
||||
|
||||
start)
|
||||
if [ -f $TORPID ]; then
|
||||
echo "tor appears to be already running (pid file exists)"
|
||||
echo "Maybe you should run: $0 restart ?"
|
||||
RETVAL=1
|
||||
else
|
||||
echo -n "Starting tor..."
|
||||
$TORBIN -f $TORCONF $TORARGS
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
echo " ok"
|
||||
else
|
||||
echo " ERROR!"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
stop)
|
||||
if [ -f $TORPID ]; then
|
||||
echo -n "Killing tor..."
|
||||
kill `cat $TORPID`
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
echo " ok"
|
||||
else
|
||||
echo " ERROR!"
|
||||
fi
|
||||
else
|
||||
echo "Unable to kill tor: $TORPID does not exist"
|
||||
RETVAL=1
|
||||
fi
|
||||
;;
|
||||
|
||||
restart)
|
||||
$0 stop
|
||||
if [ -f $TORPID ]; then
|
||||
rm -f $TORPID
|
||||
fi
|
||||
$0 start
|
||||
;;
|
||||
|
||||
status)
|
||||
PID=`cat $TORPID 2>/dev/null`
|
||||
if [ "$PID" != "" ]; then
|
||||
torstat=`ps -p $PID | grep -c "^$PID"`
|
||||
if [ $torstat ]; then
|
||||
echo "tor is running ($PID)"
|
||||
else
|
||||
echo "tor is not running (looks like it crashed, look for core? $PID)"
|
||||
fi
|
||||
else
|
||||
echo "tor is not running (exited gracefully)"
|
||||
fi
|
||||
;;
|
||||
|
||||
log)
|
||||
cat $TORLOG
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 (start|stop|restart|status|log)"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $RETVAL
|
||||
Reference in New Issue
Block a user