]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Wed, 22 Feb 2012 13:23:59 +0000 (14:23 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Wed, 22 Feb 2012 13:23:59 +0000 (14:23 +0100)
Conflicts:
NEWS
README
configure.in
doc/tincd.8.in
src/Makefile.am
src/bsd/device.c
src/connection.c
src/connection.h
src/cygwin/device.c
src/device.h
src/dropin.h
src/linux/device.c
src/mingw/device.c
src/net.c
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/process.c
src/protocol.c
src/protocol_key.c
src/raw_socket_device.c
src/route.c
src/solaris/device.c
src/tincd.c
src/uml_device.c

154 files changed:
Makefile.am
NEWS
README
README.git
THANKS
configure.in
doc/Makefile.am
doc/tinc-gui.8.in [new file with mode: 0644]
doc/tinc.conf.5.in
doc/tinc.texi
doc/tincctl.8.in [new file with mode: 0644]
doc/tincd.8.in
gui/Makefile.am [new file with mode: 0644]
gui/README.gui [new file with mode: 0644]
gui/tinc-gui [new file with mode: 0755]
have.h
lib/Makefile.am [deleted file]
lib/alloca.c [deleted file]
lib/avl_tree.c [deleted file]
lib/avl_tree.h [deleted file]
lib/dropin.c [deleted file]
lib/dropin.h [deleted file]
lib/ethernet.h [deleted file]
lib/fake-gai-errnos.h [deleted file]
lib/fake-getaddrinfo.c [deleted file]
lib/fake-getaddrinfo.h [deleted file]
lib/fake-getnameinfo.c [deleted file]
lib/fake-getnameinfo.h [deleted file]
lib/getopt.c [deleted file]
lib/getopt.h [deleted file]
lib/getopt1.c [deleted file]
lib/ipv4.h [deleted file]
lib/ipv6.h [deleted file]
lib/list.c [deleted file]
lib/list.h [deleted file]
lib/malloc.c [deleted file]
lib/memcmp.c [deleted file]
lib/pidfile.c [deleted file]
lib/pidfile.h [deleted file]
lib/realloc.c [deleted file]
lib/splay_tree.c [deleted file]
lib/splay_tree.h [deleted file]
lib/utils.c [deleted file]
lib/utils.h [deleted file]
lib/xalloc.h [deleted file]
lib/xmalloc.c [deleted file]
m4/curses.m4 [new file with mode: 0644]
m4/libevent.m4 [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
po/nl.po [new file with mode: 0644]
src/Makefile.am
src/bsd/device.c
src/buffer.c [new file with mode: 0644]
src/buffer.h [new file with mode: 0644]
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/control.c [new file with mode: 0644]
src/control.h [new file with mode: 0644]
src/control_common.h [new file with mode: 0644]
src/cygwin/device.c
src/device.h
src/dropin.c [new file with mode: 0644]
src/dropin.h [new file with mode: 0644]
src/edge.c
src/edge.h
src/ethernet.h [new file with mode: 0644]
src/event.c [deleted file]
src/event.h [deleted file]
src/fake-gai-errnos.h [new file with mode: 0644]
src/fake-getaddrinfo.c [new file with mode: 0644]
src/fake-getaddrinfo.h [new file with mode: 0644]
src/fake-getnameinfo.c [new file with mode: 0644]
src/fake-getnameinfo.h [new file with mode: 0644]
src/gcrypt/cipher.c [new file with mode: 0644]
src/gcrypt/cipher.h [new file with mode: 0644]
src/gcrypt/crypto.c [new file with mode: 0644]
src/gcrypt/crypto.h [new file with mode: 0644]
src/gcrypt/digest.c [new file with mode: 0644]
src/gcrypt/digest.h [new file with mode: 0644]
src/gcrypt/rsa.c [new file with mode: 0644]
src/gcrypt/rsa.h [new file with mode: 0644]
src/gcrypt/rsagen.c [new file with mode: 0644]
src/gcrypt/rsagen.h [new file with mode: 0644]
src/getopt.c [new file with mode: 0644]
src/getopt.h [new file with mode: 0644]
src/getopt1.c [new file with mode: 0644]
src/graph.c
src/ipv4.h [new file with mode: 0644]
src/ipv6.h [new file with mode: 0644]
src/linux/device.c
src/list.c [new file with mode: 0644]
src/list.h [new file with mode: 0644]
src/logger.c
src/meta.c
src/meta.h
src/mingw/device.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/netutl.c
src/node.c
src/node.h
src/openssl/cipher.c [new file with mode: 0644]
src/openssl/cipher.h [new file with mode: 0644]
src/openssl/crypto.c [new file with mode: 0644]
src/openssl/crypto.h [new file with mode: 0644]
src/openssl/digest.c [new file with mode: 0644]
src/openssl/digest.h [new file with mode: 0644]
src/openssl/ecdh.c [new file with mode: 0644]
src/openssl/ecdh.h [new file with mode: 0644]
src/openssl/ecdsa.c [new file with mode: 0644]
src/openssl/ecdsa.h [new file with mode: 0644]
src/openssl/ecdsagen.c [new file with mode: 0644]
src/openssl/ecdsagen.h [new file with mode: 0644]
src/openssl/prf.c [new file with mode: 0644]
src/openssl/prf.h [new file with mode: 0644]
src/openssl/rsa.c [new file with mode: 0644]
src/openssl/rsa.h [new file with mode: 0644]
src/openssl/rsagen.c [new file with mode: 0644]
src/openssl/rsagen.h [new file with mode: 0644]
src/process.c
src/process.h
src/protocol.c
src/protocol.h
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/protocol_misc.c
src/protocol_subnet.c
src/raw_socket_device.c
src/route.c
src/route.h
src/solaris/device.c
src/splay_tree.c [new file with mode: 0644]
src/splay_tree.h [new file with mode: 0644]
src/sptps.c [new file with mode: 0644]
src/sptps.h [new file with mode: 0644]
src/sptps_test.c [new file with mode: 0644]
src/subnet.c
src/subnet.h
src/tincctl.c [new file with mode: 0644]
src/tincctl.h [new file with mode: 0644]
src/tincd.c
src/top.c [new file with mode: 0644]
src/top.h [new file with mode: 0644]
src/uml_device.c
src/utils.c [new file with mode: 0644]
src/utils.h [new file with mode: 0644]
src/xalloc.h [new file with mode: 0644]
system.h

index b13689aa35566e99a3c9fe75e84ddd600e2d3132..e37864d05f092cce1d5ad72760be88baa82adbc0 100644 (file)
@@ -2,7 +2,7 @@
 
 AUTOMAKE_OPTIONS = gnu
 
-SUBDIRS =  m4 lib src doc
+SUBDIRS =  m4 src doc gui
 
 ACLOCAL_AMFLAGS = -I m4 
 
diff --git a/NEWS b/NEWS
index ab36c2378fdd3242e3450d9597bb7fb826a7f5f3..36f50606cd4bddad0c8cf063143dadd8b734560d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,33 @@
+Version 1.1pre2              Juli 17 2011
+
+ * .cookie files are renamed to .pid files, which are compatible with 1.0.x.
+
+ * Experimental protocol enhancements that can be enabled with the option
+   ExperimentalProtocol = yes:
+
+   * Ephemeral ECDH key exchange will be used for both the meta protocol and
+     UDP session keys.
+   * Key exchanges are signed with ECDSA.
+   * ECDSA public keys are automatically exchanged after RSA authentication if
+     nodes do not know each other's ECDSA public key yet.
+
+Version 1.1pre1              June 25 2011
+
+ * Control interface allows control of a running tinc daemon. Used by:
+   * tincctl, a commandline utility
+   * tinc-gui, a preliminary GUI implemented in Python/wxWidgets
+
+ * Code cleanups and reorganization. 
+
+ * Repleacable cryptography backend, currently supports OpenSSL and libgcrypt.
+
+ * Use libevent to handle I/O events and timeouts.
+
+ * Use splay trees instead of AVL trees to manage internal datastructures.
+
+ Thanks to Scott Lamb and Sven-Haegar Koch for their contributions to this
+ version of tinc.
+
 Version 1.0.16               July 23 2011
 
  * Fixed a performance issue with TCP communication under Windows.
@@ -13,6 +43,8 @@ Version 1.0.15               June 24 2011
 
  * Fixed ProcessPriority option under Windows.
 
+  Thanks to Loïc Grenié for his contribution to this version of tinc.
+
 Version 1.0.14               May  8 2011
 
  * Fixed reading configuration files that do not end with a newline. Again.
diff --git a/README b/README
index 338f23c9b264531b2701b79f2dfd92cd7087ad60..09f6e6e9e60cbaee09d7ed5b7cefb58f226e6a74 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-This is the README file for tinc version 1.0.16. Installation
+This is the README file for tinc version 1.1pre2. Installation
 instructions may be found in the INSTALL file.
 
 tinc is Copyright (C) 1998-2011 by:
@@ -15,68 +15,51 @@ the Free Software Foundation; either version 2 of the License, or (at
 your option) any later version. See the file COPYING for more details.
 
 
-Security statement
-------------------
+This is a pre-release
+---------------------
 
-In August 2000, we discovered the existence of a security hole in all versions
-of tinc up to and including 1.0pre2. This had to do with the way we exchanged
-keys. Since then, we have been working on a new authentication scheme to make
-tinc as secure as possible. The current version uses the OpenSSL library and
-uses strong authentication with RSA keys.
+Please note that this is NOT a stable release. Until version 1.1.0 is released,
+please use one of the 1.0.x versions if you need a stable version of tinc.
 
-On the 29th of December 2001, Jerome Etienne posted a security analysis of tinc
-1.0pre4. Due to a lack of sequence numbers and a message authentication code
-for each packet, an attacker could possibly disrupt certain network services or
-launch a denial of service attack by replaying intercepted packets. The current
-version adds sequence numbers and message authentication codes to prevent such
-attacks.
+Although tinc 1.1 will be protocol compatible with tinc 1.0.x, the
+functionality of the tincctl program may still change, and the control socket
+protocol is not fixed yet.
 
-On September the 15th of 2003, Peter Gutmann contacted us and showed us a
-writeup describing various security issues in several VPN daemons. He showed
-that tinc lacks perfect forward security, the connection authentication could
-be done more properly, that the sequence number we use as an IV is not the best
-practice and that the default length of the HMAC for packets is too short in
-his opinion. We do not know of a way to exploit these weaknesses, but we will
-address these issues in tinc 2.0.
-
-Cryptography is a hard thing to get right. We cannot make any
-guarantees. Time, review and feedback are the only things that can
-prove the security of any cryptographic product. If you wish to review
-tinc or give us feedback, you are stronly encouraged to do so.
 
+Security statement
+------------------
 
-Changes to configuration file format since 1.0pre5
---------------------------------------------------
+This version uses an experimental and unfinished cryptographic protocol. Use
+it at your own risk.
 
-Some configuration variables have different names now. Most notably "TapDevice"
-should be changed into "Device", and "Device" should be changed into
-"BindToDevice".
 
 Compatibility
 -------------
 
-Version 1.0.16 is compatible with 1.0pre8, 1.0 and later, but not with older
+Version 1.1pre2 is compatible with 1.0pre8, 1.0 and later, but not with older
 versions of tinc.
 
+When the ExperimentalProtocol option is used, tinc is still compatible with
+1.0.X and 1.1pre2 itself, but not with any other 1.1preX version.
+
 
 Requirements
 ------------
 
-Since 1.0pre3, we use OpenSSL for all cryptographic functions.  So you
-need to install this library first; grab it from
-http://www.openssl.org/.  You will need version 0.9.7 or later.  If
-this library is not installed on you system, configure will fail.  The
-manual in doc/tinc.texi contains more detailed information on how to
-install this library.
+Either OpenSSL (http://www.openssl.org/) or libgcrypt
+(http://www.gnupg.org/download/#libgcrypt).
+
+The zlib library is used for optional compression. You can find it at
+http://www.gzip.org/zlib/.
 
-Since 1.0pre6, the zlib library is used for optional compression. You can
-find it at http://www.gzip.org/zlib/. Because of a possible exploit in
-earlier versions we recommend that you download version 1.1.4 or later.
+The lzo library is also used for optional compression. You can find it at
+http://www.oberhumer.com/opensource/lzo/.
 
-Since 1.0, the lzo library is also used for optional compression. You can
-find it at http://www.oberhumer.com/opensource/lzo/.
+Since 1.1, the libevent library is used for the main event loop. You can find
+it at http://monkey.org/~provos/libevent/.
 
-In order to compile tinc, you will need a GNU C compiler environment.
+In order to compile tinc, you will need a GNU C compiler environment. Please
+ensure you have the latest stable versions of all the required libraries.
 
 
 Features
index 92aa11375d67597ec5aa850b4822cea62b7298f0..77595d6c6c701a98caeff0ef6b05a15a0ee8782e 100644 (file)
@@ -8,6 +8,7 @@ to install the very latest versions of the following packages:
 - automake
 - autoconf
 - gettext
+- libevent
 
 Then you have to let the autotools create all the autogenerated files, using
 this command:
diff --git a/THANKS b/THANKS
index f26f268c46df5fba2130115b8c1392b466ce3eab..521f1a20990c26d8bf5e496085a220258ac227cd 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -9,6 +9,7 @@ We would like to thank the following people for their contributions to tinc:
 * Delf Eldkraft
 * dnk
 * Enrique Zanardi
+* Erik Tews
 * Flynn Marquardt
 * Grzegorz Dymarek
 * Hans Bayle
index 4d6a9493ee621efe60329fed48fdeb0389438fa2..4d2bcbd6e0130f93d40a72b0264608b12316091b 100644 (file)
@@ -3,7 +3,8 @@ dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.61)
 AC_INIT
 AC_CONFIG_SRCDIR([src/tincd.c])
-AM_INIT_AUTOMAKE(tinc, 1.0.16)
+AC_GNU_SOURCE
+AM_INIT_AUTOMAKE(tinc, 1.1pre2)
 AC_CONFIG_HEADERS([config.h])
 AM_MAINTAINER_MODE
 
@@ -112,14 +113,12 @@ if test -d /sw/lib ; then
   LIBS="$LIBS -L/sw/lib"
 fi
 
-dnl Checks for libraries.
-
 dnl Checks for header files.
 dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies.
 
 AC_HEADER_STDC
-AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h dirent.h])
-AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h netpacket/packet.h],
+AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/uio.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h])
+AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h netpacket/packet.h],
   [], [], [#include "have.h"]
 )
 AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h],
@@ -144,14 +143,10 @@ AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp,
 )
 
 dnl Checks for library functions.
-AC_FUNC_MEMCMP
-AC_FUNC_ALLOCA
 AC_TYPE_SIGNAL
-AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random select strdup strerror strsignal strtol system unsetenv usleep vsyslog writev],
+AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev],
   [], [], [#include "have.h"]
 )
-AC_FUNC_MALLOC
-AC_FUNC_REALLOC
 
 dnl Support for SunOS
 
@@ -170,9 +165,21 @@ AC_CACHE_SAVE
 
 dnl These are defined in files in m4/
 
+AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], [])
+
+tinc_CURSES
+tinc_LIBEVENT
 tinc_ZLIB
 tinc_LZO
-tinc_OPENSSL
+
+if test "$with_libgcrypt" = yes; then
+       AM_PATH_LIBGCRYPT([1.4.0], [], [])
+       ln -sf gcrypt/cipher.c gcrypt/cipher.h gcrypt/crypto.c gcrypt/crypto.h gcrypt/digest.c gcrypt/digest.h gcrypt/ecdh.c gcrypt/ecdh.h gcrypt/ecdsa.c gcrypt/ecdsa.h gcrypt/ecdsagen.c gcrypt/ecdsagen.h gcrypt/prf.c gcrypt/prf.h gcrypt/rsa.c gcrypt/rsa.h gcrypt/rsagen.c gcrypt/rsagen.h src/
+else
+       tinc_OPENSSL
+       ln -sf openssl/cipher.c openssl/cipher.h openssl/crypto.c openssl/crypto.h openssl/digest.c openssl/digest.h openssl/ecdh.c openssl/ecdh.h openssl/ecdsa.c openssl/ecdsa.h openssl/ecdsagen.c openssl/ecdsagen.h openssl/prf.c openssl/prf.h openssl/rsa.c openssl/rsa.h openssl/rsagen.c openssl/rsagen.h src/
+fi
+       
 
 dnl Check if support for jumbograms is requested 
 AC_ARG_ENABLE(jumbograms,
@@ -182,6 +189,6 @@ AC_ARG_ENABLE(jumbograms,
 
 AC_SUBST(INCLUDES)
 
-AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile lib/Makefile m4/Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile m4/Makefile gui/Makefile])
 
 AC_OUTPUT
index 7c49d844b6f8e90f7da66c39ea777a020d816cf6..8f0305e4c2901d9e2f1dc90b98eed61bacb94c06 100644 (file)
@@ -2,11 +2,11 @@
 
 info_TEXINFOS = tinc.texi
 
-man_MANS = tincd.8 tinc.conf.5
+man_MANS = tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8
 
-EXTRA_DIST = tincinclude.texi.in tincd.8.in tinc.conf.5.in sample-config.tar.gz
+EXTRA_DIST = tincinclude.texi.in tincd.8.in tincctl.8.in tinc.conf.5.in tinc-gui.8.in sample-config.tar.gz
 
-CLEANFILES = *.html tinc.info tincd.8 tinc.conf.5 tincinclude.texi
+CLEANFILES = *.html tinc.info tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8 tincinclude.texi
 
 # Use `ginstall' in the definition of man_MANS to avoid
 # confusion with the `install' target.  The install rule transforms `ginstall'
@@ -25,6 +25,12 @@ texi2html: tinc.texi
 tincd.8.html: tincd.8
        w3mman2html $< > $@
 
+tincctl.8.html: tincctl.8
+       w3mman2html $< > $@
+
+tinc-gui.8.html: tinc-gui.8
+       w3mman2html $< > $@
+
 tinc.conf.5.html: tinc.conf.5
        w3mman2html $< > $@
 
@@ -35,12 +41,18 @@ substitute = sed \
        -e s,'@localstatedir\@',"$(localstatedir)",g
 
 tincd.8: tincd.8.in
-       $(substitute) tincd.8.in > tincd.8
+       $(substitute) $< > $@
+
+tincctl.8: tincctl.8.in
+       $(substitute) $< > $@
+
+tinc-gui.8: tinc-gui.8.in
+       $(substitute) $< > $@
 
 tinc.conf.5: tinc.conf.5.in
-       $(substitute) tinc.conf.5.in > tinc.conf.5
+       $(substitute) $< > $@
 
 tincinclude.texi: tincinclude.texi.in
-       $(substitute) tincinclude.texi.in > tincinclude.texi
+       $(substitute) $< > $@
        
 tinc.texi: tincinclude.texi
diff --git a/doc/tinc-gui.8.in b/doc/tinc-gui.8.in
new file mode 100644 (file)
index 0000000..f5ebadb
--- /dev/null
@@ -0,0 +1,57 @@
+.Dd 2011-06-26
+.Dt TINC-GUI 8
+.\" Manual page created by:
+.\" Guus Sliepen <guus@tinc-vpn.org>
+.Sh NAME
+.Nm tinc-gui
+.Nd tinc GUI
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl -net Ns = Ns Ar NETNAME
+.Op Fl -pidfile Ns = Ns Ar FILENAME
+.Op Fl -help
+.Sh DESCRIPTION
+This is a Python/wxWidgets based graphical user interface for tinc, a secure virtual private network (VPN) project.
+.Nm
+communicates with
+.Xr tincd 8
+to alter and inspect the running VPN's state.
+It can show the current settings, the list of connections, nodes, subnets, and edges.
+For now, the debug level can be changed from the GUI, and by right-clicking on a node in the list of connections,
+a pop-up menu will appear that allows one to disconnect that node.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl n, -net Ns = Ns Ar NETNAME
+Communicate with tincd(8) connected with
+.Ar NETNAME .
+.It Fl -pidfile Ns = Ns Ar FILENAME
+Use the cookie from
+.Ar FILENAME
+to authenticate with a running tinc daemon.
+If unspecified, the default is
+.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
+.It Fl -help
+Display short list of options.
+.El
+.Sh BUGS
+The GUI is not finished yet, the final version will have much more functionality.
+If you find any bugs, report them to tinc@tinc-vpn.org.
+.Sh SEE ALSO
+.Xr tincd 8 ,
+.Pa http://www.tinc-vpn.org/ .
+.Pp
+The full documentation for tinc is maintained as a Texinfo manual.
+If the info and tinc programs are properly installed at your site,
+the command
+.Ic info tinc
+should give you access to the complete manual.
+.Pp
+tinc comes with ABSOLUTELY NO WARRANTY.
+This is free software, and you are welcome to redistribute it under certain conditions;
+see the file COPYING for details.
+.Sh AUTHORS
+.An "Ivo Timmermans"
+.An "Guus Sliepen" Aq guus@tinc-vpn.org
+.Pp
+And thanks to many others for their contributions to tinc!
index 8a2aa348cd29c43b0b925abcd4a5499709af469b..47cf82e5c7c0d06817f8fffe9ba09f671bd60ef8 100644 (file)
@@ -261,6 +261,21 @@ but which would have to be forwarded by an intermediate node, are dropped instea
 When combined with the IndirectData option,
 packets for nodes for which we do not have a meta connection with are also dropped.
 
+.It Va ECDSAPrivateKeyFile Li = Ar filename Po Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa /ecdsa_key.priv Pc
+The file in which the private ECDSA key of this tinc daemon resides.
+This is only used if
+.Va ExperimentalProtocol
+is enabled.
+
+.It Va ExperimentalProtocol Li = yes | no Po no Pc Bq experimental
+When this option is enabled, experimental protocol enhancements will be used.
+Ephemeral ECDH will be used for key exchanges,
+and ECDSA will be used instead of RSA for authentication.
+When enabled, an ECDSA key must have been generated before with
+.Nm tincctl generate-ecdsa-keys .
+The experimental protocol may change at any time,
+and there is no guarantee that tinc will run stable when it is used.
+
 .It Va Forwarding Li = off | internal | kernel Po internal Pc Bq experimental
 This option selects the way indirect packets are forwarded.
 .Bl -tag -width indent
index b6d0c21626ee080049b9d3132379a261e1a0c254..4d9f233ac7a1c4e6ad6073ea3b9342dd2ce3f2a6 100644 (file)
@@ -65,6 +65,7 @@ permission notice identical to this one.
 * Installation::
 * Configuration::
 * Running tinc::
+* Controlling tinc::
 * Technical information::
 * Platform specific information::
 * About us::
@@ -337,6 +338,7 @@ having them installed, configure will give you an error message, and stop.
 * OpenSSL::
 * zlib::
 * lzo::
+* libevent::
 @end menu
 
 
@@ -449,6 +451,27 @@ make sure you build development and runtime libraries (which is the
 default).
 
 
+@c ==================================================================
+@node       libevent
+@subsection libevent
+
+@cindex libevent
+For the main event loop, tinc uses the libevent library.
+
+If this library is not installed, you wil get an error when configuring
+tinc for build.
+
+You can use your operating system's package manager to install this if
+available.  Make sure you install the development AND runtime versions
+of this package.
+
+If you have to install libevent manually, you can get the source code
+from @url{http://monkey.org/~provos/libevent/}.  Instructions on how to configure,
+build and install this package are included within the package.  Please
+make sure you build development and runtime libraries (which is the
+default).
+
+
 @c
 @c
 @c
@@ -876,6 +899,21 @@ but which would have to be forwarded by an intermediate node, are dropped instea
 When combined with the IndirectData option,
 packets for nodes for which we do not have a meta connection with are also dropped.
 
+@cindex ECDSAPrivateKeyFile
+@item ECDSAPrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/ecdsa_key.priv})
+The file in which the private ECDSA key of this tinc daemon resides.
+This is only used if ExperimentalProtocol is enabled.
+
+@cindex ExperimentalProtocol
+@item ExperimentalProtocol = <yes|no> (no) [experimental]
+When this option is enabled, experimental protocol enhancements will be used.
+Ephemeral ECDH will be used for key exchanges,
+and ECDSA will be used instead of RSA for authentication.
+When enabled, an ECDSA key must have been generated before with
+@samp{tincctl generate-ecdsa-keys}.
+The experimental protocol may change at any time,
+and there is no guarantee that tinc will run stable when it is used.
+
 @cindex Forwarding
 @item Forwarding = <off|internal|kernel> (internal) [experimental]
 This option selects the way indirect packets are forwarded.
@@ -994,7 +1032,7 @@ accidental eavesdropping if you are editting the configuration file.
 @cindex PrivateKeyFile
 @item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv})
 This is the full path name of the RSA private key file that was
-generated by @samp{tincd --generate-keys}.  It must be a full path, not a
+generated by @samp{tincctl generate-keys}.  It must be a full path, not a
 relative directory.
 
 Note that there must be exactly one of PrivateKey
@@ -1116,7 +1154,7 @@ This is the RSA public key for this host.
 @cindex PublicKeyFile
 @item PublicKeyFile = <@var{path}> [obsolete]
 This is the full path name of the RSA public key file that was generated
-by @samp{tincd --generate-keys}.  It must be a full path, not a relative
+by @samp{tincctl generate-keys}.  It must be a full path, not a relative
 directory.
 
 @cindex PEM format
@@ -1152,7 +1190,6 @@ example: netmask 255.255.255.0 would become /24, 255.255.252.0 becomes
 /22. This conforms to standard CIDR notation as described in
 @uref{ftp://ftp.isi.edu/in-notes/rfc1519.txt, RFC1519}
 
-@cindex Subnet weight
 A Subnet can be given a weight to indicate its priority over identical Subnets
 owned by different nodes. The default weight is 10. Lower values indicate
 higher priority. Packets will be sent to the node with the highest priority,
@@ -1160,15 +1197,12 @@ unless that node is not reachable, in which case the node with the next highest
 priority will be tried, and so on.
 
 @cindex TCPonly
-@item TCPonly = <yes|no> (no) [deprecated]
+@item TCPonly = <yes|no> (no)
 If this variable is set to yes, then the packets are tunnelled over a
 TCP connection instead of a UDP connection.  This is especially useful
 for those who want to run a tinc daemon from behind a masquerading
 firewall, or if UDP packet routing is disabled somehow.
 Setting this options also implicitly sets IndirectData.
-
-Since version 1.0.10, tinc will automatically detect whether communication via
-UDP is possible or not.
 @end table
 
 
@@ -1257,10 +1291,6 @@ this is set to the port number it uses for communication with other tinc daemons
 @item SUBNET
 When a subnet becomes (un)reachable, this is set to the subnet.
 
-@cindex WEIGHT
-@item WEIGHT
-When a subnet becomes (un)reachable, this is set to the subnet weight.
-
 @end table
 
 
@@ -1307,7 +1337,7 @@ Now that you have already created the main configuration file and your host conf
 you can easily create a public/private keypair by entering the following command:
 
 @example
-tincd -n @var{netname} -K
+tincctl -n @var{netname} generate-keys
 @end example
 
 Tinc will generate a public and a private key and ask you where to put them.
@@ -1536,7 +1566,7 @@ Address = 4.5.6.7
 A, B, C and D all have generated a public/private keypair with the following command:
 
 @example
-tincd -n company -K
+tincctl -n company generate-keys
 @end example
 
 The private key is stored in @file{@value{sysconfdir}/tinc/company/rsa_key.priv},
@@ -1602,12 +1632,6 @@ This will also disable the automatic restart mechanism for fatal errors.
 Set debug level to @var{level}.  The higher the debug level, the more gets
 logged.  Everything goes via syslog.
 
-@item -k, --kill[=@var{signal}]
-Attempt to kill a running tincd (optionally with the specified @var{signal} instead of SIGTERM) and exit.
-Use it in conjunction with the -n option to make sure you kill the right tinc daemon.
-Under native Windows the optional argument is ignored,
-the service will always be stopped and removed.
-
 @item -n, --net=@var{netname}
 Use configuration for net @var{netname}.
 This will let tinc read all configuration files from
@@ -1615,11 +1639,10 @@ This will let tinc read all configuration files from
 Specifying . for @var{netname} is the same as not specifying any @var{netname}.
 @xref{Multiple networks}.
 
-@item -K, --generate-keys[=@var{bits}]
-Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
-2048 is the default. tinc will ask where you want to store the files,
-but will default to the configuration directory (you can use the -c or -n option
-in combination with -K). After that, tinc will quit.
+@item --pidfile=@var{filename}
+Store a cookie in @var{filename} which allows tincctl to authenticate.
+If unspecified, the default is
+@file{@value{localstatedir}/run/tinc.@var{netname}.pid}.
 
 @item -o, --option=[@var{HOST}.]@var{KEY}=@var{VALUE}
 Without specifying a @var{HOST}, this will set server configuration variable @var{KEY} to @var{VALUE}.
@@ -1635,9 +1658,6 @@ This will prevent sensitive data like shared private keys to be written to the s
 Write log entries to a file instead of to the system logging facility.
 If @var{file} is omitted, the default is @file{@value{localstatedir}/log/tinc.@var{netname}.log}.
 
-@item --pidfile=@var{file}
-Write PID to @var{file} instead of @file{@value{localstatedir}/run/tinc.@var{netname}.pid}.
-
 @item --bypass-security
 Disables encryption and authentication.
 Only useful for debugging.
@@ -1691,19 +1711,6 @@ New outgoing connections specified in @file{tinc.conf} will be made.
 If the --logfile option is used, this will also close and reopen the log file,
 useful when log rotation is used.
 
-@item INT
-Temporarily increases debug level to 5.
-Send this signal again to revert to the original level.
-
-@item USR1
-Dumps the connection list to syslog.
-
-@item USR2
-Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
-
-@item WINCH
-Purges all information remembered about unreachable nodes.
-
 @end table
 
 @c ==================================================================
@@ -1767,7 +1774,7 @@ Do you have a firewall or a NAT device (a masquerading firewall or perhaps an AD
 If so, check that it allows TCP and UDP traffic on port 655.
 If it masquerades and the host running tinc is behind it, make sure that it forwards TCP and UDP traffic to port 655 to the host running tinc.
 You can add @samp{TCPOnly = yes} to your host config file to force tinc to only use a single TCP connection,
-this works through most firewalls and NATs. Since version 1.0.10, tinc will automatically fall back to TCP if direct communication via UDP is not possible.
+this works through most firewalls and NATs.
 
 @end itemize
 
@@ -1866,6 +1873,8 @@ or if that is not the case, try changing the prefix length into /32.
 
 @itemize
 @item If you see this only sporadically, it is harmless and caused by a node sending packets using an old key.
+@item If you see this often and another node is not reachable anymore, then a NAT (masquerading firewall) is changing the source address of UDP packets.
+You can add @samp{TCPOnly = yes} to host configuration files to force all VPN traffic to go over a TCP connection.
 @end itemize
 
 @item Got bad/bogus/unauthorized REQUEST from foo (1.2.3.4 port 12345)
@@ -1896,6 +1905,198 @@ Be sure to include the following information in your bugreport:
 @item The output of any command that fails to work as it should (like ping or traceroute).
 @end itemize
 
+@c ==================================================================
+@node    Controlling tinc
+@chapter Controlling tinc
+
+You can control and inspect a running tincd through the tincctl
+command. A quick example:
+
+@example
+tincctl -n @var{netname} reload
+@end example
+
+@menu
+* tincctl runtime options::
+* tincctl commands::
+* tincctl examples::
+* tincctl top::
+@end menu
+
+
+@c ==================================================================
+@node    tincctl runtime options
+@section tincctl runtime options
+
+@c from the manpage
+@table @option
+@item -c, --config=@var{path}
+Read configuration options from the directory @var{path}.  The default is
+@file{@value{sysconfdir}/tinc/@var{netname}/}.
+
+@item -n, --net=@var{netname}
+Use configuration for net @var{netname}. @xref{Multiple networks}.
+
+@item --pidfile=@var{filename}
+Use the cookie from @var{filename} to authenticate with a running tinc daemon.
+If unspecified, the default is
+@file{@value{localstatedir}/run/tinc.@var{netname}.pid}.
+
+@item --help
+Display a short reminder of runtime options and commands, then terminate.
+
+@item --version
+Output version information and exit.
+
+@end table
+
+
+@c ==================================================================
+@node    tincctl commands
+@section tincctl commands
+
+@c from the manpage
+@table @code
+
+@item start
+Start @samp{tincd}.
+
+@item stop
+Stop @samp{tincd}.
+
+@item restart
+Restart @samp{tincd}.
+
+@item reload
+Partially rereads configuration files. Connections to hosts whose host
+config files are removed are closed. New outgoing connections specified
+in @file{tinc.conf} will be made.
+
+@item pid
+Shows the PID of the currently running @samp{tincd}.
+
+@item generate-keys [@var{bits}]
+Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
+1024 is the default. tinc will ask where you want to store the files,
+but will default to the configuration directory (you can use the -c or -n
+option).
+
+@item dump nodes
+Dump a list of all known nodes in the VPN.
+
+@item dump edges
+Dump a list of all known connections in the VPN.
+
+@item dump subnets
+Dump a list of all known subnets in the VPN.
+
+@item dump connections
+Dump a list of all meta connections with ourself.
+
+@item dump graph
+Dump a graph of the VPN in dotty format.
+
+@item purge
+Purges all information remembered about unreachable nodes.
+
+@item debug @var{level}
+Sets debug level to @var{level}.
+
+@item retry
+Forces tinc to try to connect to all uplinks immediately.
+Usually tinc attempts to do this itself,
+but increases the time it waits between the attempts each time it failed,
+and if tinc didn't succeed to connect to an uplink the first time after it started,
+it defaults to the maximum time of 15 minutes.
+
+@item disconnect @var{node}
+Closes the meta connection with the given @var{node}.
+
+@item top
+If tincctl is compiled with libcurses support, this will display live traffic statistics for all the known nodes,
+similar to the UNIX top command.
+See below for more information.
+
+@item pcap
+Dump VPN traffic going through the local tinc node in pcap-savefile format to standard output,
+from where it can be redirected to a file or piped through a program that can parse it directly,
+such as tcpdump.
+
+@end table
+
+@c ==================================================================
+@node    tincctl examples
+@section tincctl examples
+
+Examples of some commands:
+
+@example
+tincctl -n vpn dump graph | circo -Txlib
+tincctl -n vpn pcap | tcpdump -r -
+tincctl -n vpn top
+@end example
+
+@c ==================================================================
+@node    tincctl top
+@section tincctl top
+
+The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.
+It displays a list of all the known nodes in the left-most column,
+and the amount of bytes and packets read from and sent to each node in the other columns.
+By default, the information is updated every second.
+The behaviour of the top command can be changed using the following keys:
+
+@table @key
+
+@item s
+Change the interval between updates.
+After pressing the @key{s} key, enter the desired interval in seconds, followed by enter.
+Fractional seconds are honored.
+Intervals lower than 0.1 seconds are not allowed.
+
+@item c
+Toggle between displaying current traffic rates (in packets and bytes per second)
+and cummulative traffic (total packets and bytes since the tinc daemon started).
+
+@item n
+Sort the list of nodes by name.
+
+@item i
+Sort the list of nodes by incoming amount of bytes.
+
+@item I
+Sort the list of nodes by incoming amount of packets.
+
+@item o
+Sort the list of nodes by outgoing amount of bytes.
+
+@item O
+Sort the list of nodes by outgoing amount of packets.
+
+@item t
+Sort the list of nodes by sum of incoming and outgoing amount of bytes.
+
+@item T
+Sort the list of nodes by sum of incoming and outgoing amount of packets.
+
+@item b
+Show amount of traffic in bytes.
+
+@item k
+Show amount of traffic in kilobytes.
+
+@item M
+Show amount of traffic in megabytes.
+
+@item G
+Show amount of traffic in gigabytes.
+
+@item q
+Quit.
+
+@end table
+
+
 @c ==================================================================
 @node    Technical information
 @chapter Technical information
diff --git a/doc/tincctl.8.in b/doc/tincctl.8.in
new file mode 100644 (file)
index 0000000..bbc8dba
--- /dev/null
@@ -0,0 +1,194 @@
+.Dd 2011-06-25
+.Dt TINCCTL 8
+.\" Manual page created by:
+.\" Scott Lamb
+.Sh NAME
+.Nm tincctl
+.Nd tinc VPN control
+.Sh SYNOPSIS
+.Nm
+.Op Fl cn
+.Op Fl -config Ns = Ns Ar DIR
+.Op Fl -net Ns = Ns Ar NETNAME
+.Op Fl -pidfile Ns = Ns Ar FILENAME
+.Op Fl -help
+.Op Fl -version
+.Ar COMMAND
+.Sh DESCRIPTION
+This is the control program of tinc, a secure virtual private network (VPN)
+project.
+.Nm
+communicates with
+.Xr tincd 8
+to alter and inspect the running VPN's state.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl n, -net Ns = Ns Ar NETNAME
+Communicate with tincd(8) connected with
+.Ar NETNAME .
+.It Fl -pidfile Ns = Ns Ar FILENAME
+Use the cookie from
+.Ar FILENAME
+to authenticate with a running tinc daemon.
+If unspecified, the default is
+.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
+.It Fl -help
+Display short list of options.
+.It Fl -version
+Output version information and exit.
+.El
+.Sh COMMANDS
+.zZ
+.Bl -tag -width indent
+.It start
+Start
+.Xr tincd 8 .
+.It stop
+Stop
+.Xr tincd 8 .
+.It restart
+Restart
+.Xr tincd 8 .
+.It reload
+Partially rereads configuration files. Connections to hosts whose host
+config files are removed are closed. New outgoing connections specified
+in
+.Xr tinc.conf 5
+will be made.
+.It pid
+Shows the PID of the currently running
+.Xr tincd 8 .
+.It generate-keys Op bits
+Generate both RSA and ECDSA keypairs (see below) and exit.
+.It generate-ecdsa-keys
+Generate public/private ECDSA keypair and exit.
+.It generate-rsa-keys Op bits
+Generate public/private RSA keypair and exit.
+If
+.Ar bits
+is omitted, the default length will be 2048 bits.
+When saving keys to existing files, tinc will not delete the old keys;
+you have to remove them manually.
+.It dump nodes
+Dump a list of all known nodes in the VPN.
+.It dump edges
+Dump a list of all known connections in the VPN.
+.It dump subnets
+Dump a list of all known subnets in the VPN.
+.It dump connections
+Dump a list of all meta connections with ourself.
+.It dump graph
+Dump a graph of the VPN in
+.Xr dotty 1
+format.
+.It purge
+Purges all information remembered about unreachable nodes.
+.It debug Ar N
+Sets debug level to
+.Ar N .
+.It retry
+Forces
+.Xr tincd 8
+to try to connect to all uplinks immediately.
+Usually
+.Xr tincd 8
+attempts to do this itself,
+but increases the time it waits between the attempts each time it failed,
+and if
+.Xr tincd 8
+didn't succeed to connect to an uplink the first time after it started,
+it defaults to the maximum time of 15 minutes.
+.It disconnect Ar NODE
+Closes the meta connection with the given
+.Ar NODE .
+.It top
+If
+.Nm
+is compiled with libcurses support, this will display live traffic statistics
+for all the known nodes, similar to the UNIX
+.Xr top 1
+command.
+See below for more information.
+.It pcap
+Dump VPN traffic going through the local tinc node in
+.Xr pcap-savefile 5
+format to standard output,
+from where it can be redirected to a file or piped through a program that can parse it directly,
+such as
+.Xr tcpdump 8 .
+.El
+.Sh EXAMPLES
+Examples of some commands:
+.Bd -literal -offset indent
+tincctl -n vpn dump graph | circo -Txlib
+tincctl -n vpn pcap | tcpdump -r -
+tincctl -n vpn top
+.Ed
+.Sh TOP
+The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.
+It displays a list of all the known nodes in the left-most column,
+and the amount of bytes and packets read from and sent to each node in the other columns.
+By default, the information is updated every second.
+The behaviour of the top command can be changed using the following keys:
+.Bl -tag
+.It Ic s
+Change the interval between updates.
+After pressing the
+.Ic s
+key, enter the desired interval in seconds, followed by enter.
+Fractional seconds are honored.
+Intervals lower than 0.1 seconds are not allowed.
+.It Ic c
+Toggle between displaying current traffic rates (in packets and bytes per second)
+and cummulative traffic (total packets and bytes since the tinc daemon started).
+.It Ic n
+Sort the list of nodes by name.
+.It Ic i
+Sort the list of nodes by incoming amount of bytes.
+.It Ic I
+Sort the list of nodes by incoming amount of packets.
+.It Ic o
+Sort the list of nodes by outgoing amount of bytes.
+.It Ic O
+Sort the list of nodes by outgoing amount of packets.
+.It Ic t
+Sort the list of nodes by sum of incoming and outgoing amount of bytes.
+.It Ic T
+Sort the list of nodes by sum of incoming and outgoing amount of packets.
+.It Ic b
+Show amount of traffic in bytes.
+.It Ic k
+Show amount of traffic in kilobytes.
+.It Ic M
+Show amount of traffic in megabytes.
+.It Ic G
+Show amount of traffic in gigabytes.
+.It Ic q
+Quit.
+.El
+.Sh BUGS
+If you find any bugs, report them to tinc@tinc-vpn.org.
+.Sh SEE ALSO
+.Xr tincd 8 ,
+.Xr tinc.conf 5 ,
+.Xr dotty 1 ,
+.Xr pcap-savefile 7 ,
+.Xr tcpdump 8 ,
+.Xr top 1 ,
+.Pa http://www.tinc-vpn.org/ ,
+.Pa http://www.cabal.org/ .
+.Pp
+The full documentation for tinc is maintained as a Texinfo manual.
+If the info and tinc programs are properly installed at your site,
+the command
+.Ic info tinc
+should give you access to the complete manual.
+.Pp
+tinc comes with ABSOLUTELY NO WARRANTY.
+This is free software, and you are welcome to redistribute it under certain conditions;
+see the file COPYING for details.
+.Sh AUTHORS
+.An "Ivo Timmermans"
+.An "Guus Sliepen" Aq guus@tinc-vpn.org
+.Pp
+And thanks to many others for their contributions to tinc!
index b117e8a922925e10e72146dc1abe959a7bb73d8c..24b4da462cdd2b6a9b95e4ed765d082c46c0d97c 100644 (file)
@@ -1,4 +1,4 @@
-.Dd 2011-01-02
+.Dd 2011-06-25
 .Dt TINCD 8
 .\" Manual page created by:
 .\" Ivo Timmermans
@@ -8,17 +8,14 @@
 .Nd tinc VPN daemon
 .Sh SYNOPSIS
 .Nm
-.Op Fl cdDkKnoLRU
+.Op Fl cdDKnoLRU
 .Op Fl -config Ns = Ns Ar DIR
 .Op Fl -no-detach
 .Op Fl -debug Ns Op = Ns Ar LEVEL
-.Op Fl -kill Ns Op = Ns Ar SIGNAL
 .Op Fl -net Ns = Ns Ar NETNAME
-.Op Fl -generate-keys Ns Op = Ns Ar BITS
 .Op Fl -option Ns = Ns Ar [HOST.]KEY=VALUE
 .Op Fl -mlock
 .Op Fl -logfile Ns Op = Ns Ar FILE
-.Op Fl -pidfile Ns = Ns Ar FILE
 .Op Fl -bypass-security
 .Op Fl -chroot
 .Op Fl -user Ns = Ns Ar USER
@@ -54,14 +51,6 @@ If not mentioned otherwise, this will show log messages on the standard error ou
 Increase debug level or set it to
 .Ar LEVEL
 (see below).
-.It Fl k, -kill Ns Op = Ns Ar SIGNAL
-Attempt to kill a running
-.Nm
-(optionally with the specified
-.Ar SIGNAL
-instead of SIGTERM) and exit.
-Under Windows (not Cygwin) the optional argument is ignored,
-the service will always be stopped and removed.
 .It Fl n, -net Ns = Ns Ar NETNAME
 Connect to net
 .Ar NETNAME .
@@ -73,13 +62,6 @@ for
 .Ar NETNAME
 is the same as not specifying any
 .Ar NETNAME .
-.It Fl K, -generate-keys Ns Op = Ns Ar BITS
-Generate public/private RSA keypair and exit.
-If
-.Ar BITS
-is omitted, the default length will be 2048 bits.
-When saving keys to existing files, tinc will not delete the old keys,
-you have to remove them manually.
 .It Fl o, -option Ns = Ns Ar [HOST.]KEY=VALUE
 Without specifying a
 .Ar HOST ,
@@ -105,12 +87,16 @@ If
 .Ar FILE
 is omitted, the default is
 .Pa @localstatedir@/log/tinc. Ns Ar NETNAME Ns Pa .log.
-.It Fl -pidfile Ns = Ns Ar FILE
-Write PID to
+.It Fl -pidfile Ns = Ns Ar FILENAME
+Store a cookie in
+.Ar FILENAME
+which allows
+.Xr tincctl 8
+to authenticate.
+If
 .Ar FILE
-instead of
+is omitted, the default is
 .Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
-Under Windows this option will be ignored.
 .It Fl -bypass-security
 Disables encryption and authentication of the meta protocol.
 Only useful for debugging.
@@ -151,15 +137,6 @@ If the
 .Fl -logfile
 option is used, this will also close and reopen the log file,
 useful when log rotation is used.
-.It INT
-Temporarily increases debug level to 5.
-Send this signal again to revert to the original level.
-.It USR1
-Dumps the connection list to syslog.
-.It USR2
-Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
-.It WINCH
-Purges all information remembered about unreachable nodes.
 .El
 .Sh DEBUG LEVELS
 The tinc daemon can send a lot of messages to the syslog.
@@ -206,6 +183,7 @@ If you find any bugs, report them to tinc@tinc-vpn.org.
 .Sh TODO
 A lot, especially security auditing.
 .Sh SEE ALSO
+.Xr tincctl 8 ,
 .Xr tinc.conf 5 ,
 .Pa http://www.tinc-vpn.org/ ,
 .Pa http://www.cabal.org/ .
diff --git a/gui/Makefile.am b/gui/Makefile.am
new file mode 100644 (file)
index 0000000..4f2c7fe
--- /dev/null
@@ -0,0 +1,3 @@
+dist_bin_SCRIPTS = tinc-gui
+
+extra_DIST = README.gui
diff --git a/gui/README.gui b/gui/README.gui
new file mode 100644 (file)
index 0000000..da09dfb
--- /dev/null
@@ -0,0 +1,24 @@
+This experimental GUI is written in Python with wxPython widgets. You need to
+have both installed for it to work. After starting tinc with either tincd or
+tincctl, you can start the gui:
+
+tincd -n vpn
+tinc-gui -n vpn
+
+If the GUI cannot find the pid file (for example if it is not in
+/var/run), you can specify its location manually:
+
+tinc-gui --pidfile /usr/local/var/run/tinc.vpn.pid
+
+The following things sort of work:
+
+- Changing the debug level from the settings page
+- Viewing the list of connections, nodes, edges and subnets. These lists will
+  be refreshed once per second.
+- Right-clicking on a connection brings up a popup menu, which allows you to
+  close a connection.
+
+Python was chosen to enable rapid application development, wxWidgets for its
+cross-platform compatibility and platform-native widgets. Once the GUI is
+matured, it will probably rewritten in C++ to allow static linking and easy
+distribution, without needing to install both Python and wxWidgets.
diff --git a/gui/tinc-gui b/gui/tinc-gui
new file mode 100755 (executable)
index 0000000..c0f1cb1
--- /dev/null
@@ -0,0 +1,537 @@
+#!/usr/bin/python
+
+import string
+import socket
+import wx
+import sys
+from wx.lib.mixins.listctrl import ColumnSorterMixin
+from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
+
+# Classes to interface with a running tinc daemon
+
+REQ_STOP = 0
+REQ_RELOAD = 1
+REQ_RESTART = 2
+REQ_DUMP_NODES = 3
+REQ_DUMP_EDGES = 4
+REQ_DUMP_SUBNETS = 5
+REQ_DUMP_CONNECTIONS = 6
+REQ_DUMP_GRAPH = 7
+REQ_PURGE = 8
+REQ_SET_DEBUG = 9
+REQ_RETRY = 10
+REQ_CONNECT = 11
+REQ_DISCONNECT = 12
+
+ID = 0
+ACK = 4
+CONTROL = 18
+
+class Node:
+       def parse(self, args):
+               self.name = args[0]
+               self.address = args[2]
+               if args[3] != 'port':
+                       args.insert(3, 'port')
+                       args.insert(4, '')
+               self.port = args[4]
+               self.cipher = int(args[6])
+               self.digest = int(args[8])
+               self.maclength = int(args[10])
+               self.compression = int(args[12])
+               self.options = int(args[14], 0x10)
+               self.status = int(args[16], 0x10)
+               self.nexthop = args[18]
+               self.via = args[20]
+               self.distance = int(args[22])
+               self.pmtu = int(args[24])
+               self.minmtu = int(args[26])
+               self.maxmtu = int(args[28][:-1])
+
+               self.subnets = {}
+
+class Edge:
+       def parse(self, args):
+               self.fr = args[0]
+               self.to = args[2]
+               self.address = args[4]
+               self.port = args[6]
+               self.options = int(args[8], 16)
+               self.weight = int(args[10])
+
+class Subnet:
+       def parse(self, args):
+               if args[0].find('#') >= 0:
+                       (address, self.weight) = args[0].split('#', 1)
+               else:
+                       self.weight = 10
+                       address = args[0]
+
+               if address.find('/') >= 0:
+                       (self.address, self.prefixlen) = address.split('/', 1)
+               else:
+                       self.address = address
+                       self.prefixlen = '48'
+
+               self.owner = args[2]    
+
+class Connection:
+       def parse(self, args):
+               self.name = args[0]
+               self.address = args[2]
+               if args[3] != 'port':
+                       args.insert(3, 'port')
+                       args.insert(4, '')
+               self.port = args[4]
+               self.options = int(args[6], 0x10)
+               self.socket = int(args[8])
+               self.status = int(args[10], 0x10)
+               self.weight = 123
+
+class VPN:
+       confdir = '/etc/tinc'
+       piddir = '/var/run/'
+
+       def connect(self):
+               f = open(self.pidfile)
+               info = string.split(f.readline())
+               f.close()
+               s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+               s.connect((info[2], int(info[4])))
+               self.sf = s.makefile()
+               s.close()
+               hello = string.split(self.sf.readline())
+               self.name = hello[1]
+               self.sf.write('0 ^' + info[1] + ' 17\r\n')
+               self.sf.flush()
+               resp = string.split(self.sf.readline())
+               self.port = info[4]
+               self.nodes = {}
+               self.edges = {}
+               self.subnets = {}
+               self.connections = {}
+               self.refresh()
+
+       def refresh(self):
+               self.sf.write('18 3\r\n18 4\r\n18 5\r\n18 6\r\n')
+               self.sf.flush()
+
+               for node in self.nodes.values():
+                       node.visited = False
+               for edge in self.edges.values():
+                       edge.visited = False
+               for subnet in self.subnets.values():
+                       subnet.visited = False
+               for connections in self.connections.values():
+                       connections.visited = False
+
+               while True:
+                       resp = string.split(self.sf.readline())
+                       if len(resp) < 2:
+                               break
+                       if resp[0] != '18':
+                               break
+                       if resp[1] == '3':
+                               if len(resp) < 3:
+                                       continue
+                               node = self.nodes.get(resp[2]) or Node()
+                               node.parse(resp[2:])
+                               node.visited = True
+                               self.nodes[resp[2]] = node
+                       elif resp[1] == '4':
+                               if len(resp) < 5:
+                                       continue
+                               edge = self.nodes.get((resp[2], resp[4])) or Edge()
+                               edge.parse(resp[2:])
+                               edge.visited = True
+                               self.edges[(resp[2], resp[4])] = edge
+                       elif resp[1] == '5':
+                               if len(resp) < 5:
+                                       continue
+                               subnet = self.subnets.get((resp[2], resp[4])) or Subnet()
+                               subnet.parse(resp[2:])
+                               subnet.visited = True
+                               self.subnets[(resp[2], resp[4])] = subnet
+                               self.nodes[subnet.owner].subnets[resp[2]] = subnet
+                       elif resp[1] == '6':
+                               if len(resp) < 5:
+                                       break
+                               connection = self.connections.get((resp[2], resp[4])) or Connection()
+                               connection.parse(resp[2:])
+                               connection.visited = True
+                               self.connections[(resp[2], resp[4])] = connection
+                       else:
+                               break
+
+               for key, subnet in self.subnets.items():
+                       if not subnet.visited:
+                               del self.subnets[key]
+
+               for key, edge in self.edges.items():
+                       if not edge.visited:
+                               del self.edges[key]
+
+               for key, node in self.nodes.items():
+                       if not node.visited:
+                               del self.nodes[key]
+                       else:
+                               for key, subnet in node.subnets.items():
+                                       if not subnet.visited:
+                                               del node.subnets[key]
+
+               for key, connection in self.connections.items():
+                       if not connection.visited:
+                               del self.connections[key]
+
+       def close(self):
+               self.sf.close()
+
+       def disconnect(self, name):
+               self.sf.write('18 12 ' + name + '\r\n')
+               self.sf.flush()
+               resp = string.split(self.sf.readline())
+
+       def debug(self, level = -1):
+               self.sf.write('18 9 ' + str(level) + '\r\n')
+               self.sf.flush()
+               resp = string.split(self.sf.readline())
+               return int(resp[2])
+
+       def __init__(self, netname = None, pidfile = None):
+               self.tincconf = VPN.confdir + '/'
+
+               if netname:
+                       self.netname = netname
+                       self.tincconf += netname + '/'
+
+               self.tincconf += 'tinc.conf'
+
+               if pidfile is not None:
+                       self.pidfile = pidfile
+               else:
+                       self.pidfile = VPN.piddir + 'tinc.'
+                       if netname:
+                               self.pidfile += netname + '.'
+                       self.pidfile += 'pid'
+
+# GUI starts here
+
+argv0 = sys.argv[0]
+del sys.argv[0]
+net = None
+pidfile = None
+
+def usage(exitcode = 0):
+       print('Usage: ' + argv0 + ' [options]')
+       print('\nValid options are:')
+       print('  -n, --net=NETNAME       Connect to net NETNAME.')
+       print('      --pidfile=FILENAME  Read control cookie from FILENAME.')
+       print('      --help              Display this help and exit.')
+       print('\nReport bugs to tinc@tinc-vpn.org.')
+       sys.exit(exitcode)
+
+while len(sys.argv):
+       if sys.argv[0] in ('-n', '--net'):
+               del sys.argv[0]
+               net = sys.argv[0]
+       elif sys.argv[0] in ('--pidfile'):
+               del sys.argv[0]
+               pidfile = sys.argv[0]
+       elif sys.argv[0] in ('--help'):
+               usage(0)
+       else:
+               print(argv0 + ': unrecognized option \'' + sys.argv[0] + '\'')
+               usage(1)
+
+       del sys.argv[0]
+
+vpn = VPN(net, pidfile)
+vpn.connect()
+
+class SuperListCtrl(wx.ListCtrl, ColumnSorterMixin, ListCtrlAutoWidthMixin):
+    def __init__(self, parent, style):
+        wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
+       ListCtrlAutoWidthMixin.__init__(self)
+        ColumnSorterMixin.__init__(self, 14)
+
+    def GetListCtrl(self):
+        return self
+
+
+class SettingsPage(wx.Panel):
+       def OnDebugLevel(self, event):
+               vpn.debug(self.debug.GetValue())
+
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+               grid = wx.FlexGridSizer(cols = 2)
+               grid.AddGrowableCol(0, 1)
+
+               namelabel = wx.StaticText(self, -1, 'Name:')
+               self.name = wx.TextCtrl(self, -1, vpn.name)
+               grid.Add(namelabel)
+               grid.Add(self.name)
+
+               portlabel = wx.StaticText(self, -1, 'Port:')
+               self.port = wx.TextCtrl(self, -1, vpn.port)
+               grid.Add(portlabel)
+               grid.Add(self.port)
+
+               debuglabel = wx.StaticText(self, -1, 'Debug level:')
+               self.debug = wx.SpinCtrl(self, min = 0, max = 5, initial = vpn.debug())
+               self.debug.Bind(wx.EVT_SPINCTRL, self.OnDebugLevel)
+               grid.Add(debuglabel)
+               grid.Add(self.debug)
+
+               modelabel = wx.StaticText(self, -1, 'Mode:')
+               self.mode = wx.ComboBox(self, -1, style = wx.CB_READONLY, value = 'Router', choices = ['Router', 'Switch', 'Hub'])
+               grid.Add(modelabel)
+               grid.Add(self.mode)
+
+               self.SetSizer(grid)
+
+class ConnectionsPage(wx.Panel):
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+               self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
+               self.list.InsertColumn(0, 'Name')
+               self.list.InsertColumn(1, 'Address')
+               self.list.InsertColumn(2, 'Port')
+               self.list.InsertColumn(3, 'Options')
+               self.list.InsertColumn(4, 'Weight')
+
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               hbox.Add(self.list, 1, wx.EXPAND)
+               self.SetSizer(hbox)
+               self.refresh()
+
+       class ContextMenu(wx.Menu):
+               def __init__(self, item):
+                       wx.Menu.__init__(self)
+
+                       self.item = item
+
+                       disconnect = wx.MenuItem(self, -1, 'Disconnect')
+                       self.AppendItem(disconnect)
+                       self.Bind(wx.EVT_MENU, self.OnDisconnect, id=disconnect.GetId())
+
+               def OnDisconnect(self, event):
+                       vpn.disconnect(self.item[0])
+
+       def OnContext(self, event):
+               i = event.GetIndex()
+               self.PopupMenu(self.ContextMenu(self.list.itemDataMap[event.GetIndex()]), event.GetPosition())
+
+       def refresh(self):
+               self.list.itemDataMap = {}
+               i = 0
+
+               for key, connection in vpn.connections.items():
+                       if self.list.GetItemCount() <= i:
+                               self.list.InsertStringItem(i, connection.name)
+                       else:
+                               self.list.SetStringItem(i, 0, connection.name)
+                       self.list.SetStringItem(i, 1, connection.address)
+                       self.list.SetStringItem(i, 2, connection.port)
+                       self.list.SetStringItem(i, 3, str(connection.options))
+                       self.list.SetStringItem(i, 4, str(connection.weight))
+                       self.list.itemDataMap[i] = (connection.name, connection.address, connection.port, connection.options, connection.weight)
+                       self.list.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnContext)
+                       i += 1
+
+               while self.list.GetItemCount() > i:
+                       self.list.DeleteItem(self.list.GetItemCount() - 1)
+
+
+class NodesPage(wx.Panel):
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+               self.list = SuperListCtrl(self, id)
+               self.list.InsertColumn( 0, 'Name')
+               self.list.InsertColumn( 1, 'Address')
+               self.list.InsertColumn( 2, 'Port')
+               self.list.InsertColumn( 3, 'Cipher')
+               self.list.InsertColumn( 4, 'Digest')
+               self.list.InsertColumn( 5, 'MACLength')
+               self.list.InsertColumn( 6, 'Compression')
+               self.list.InsertColumn( 7, 'Options')
+               self.list.InsertColumn( 8, 'Status')
+               self.list.InsertColumn( 9, 'Nexthop')
+               self.list.InsertColumn(10, 'Via')
+               self.list.InsertColumn(11, 'Distance')
+               self.list.InsertColumn(12, 'PMTU')
+               self.list.InsertColumn(13, 'Min MTU')
+               self.list.InsertColumn(14, 'Max MTU')
+
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               hbox.Add(self.list, 1, wx.EXPAND)
+               self.SetSizer(hbox)
+               self.refresh()
+
+       def refresh(self):
+               self.list.itemDataMap = {}
+               i = 0
+
+               for key, node in vpn.nodes.items():
+                       if self.list.GetItemCount() <= i:
+                               self.list.InsertStringItem(i, node.name)
+                       else:
+                               self.list.SetStringItem(i,  0, node.name)
+                       self.list.SetStringItem(i,  1, node.address)
+                       self.list.SetStringItem(i,  2, node.port)
+                       self.list.SetStringItem(i,  3, str(node.cipher))
+                       self.list.SetStringItem(i,  4, str(node.digest))
+                       self.list.SetStringItem(i,  5, str(node.maclength))
+                       self.list.SetStringItem(i,  6, str(node.compression))
+                       self.list.SetStringItem(i,  7, str(node.options))
+                       self.list.SetStringItem(i,  8, str(node.status))
+                       self.list.SetStringItem(i,  9, node.nexthop)
+                       self.list.SetStringItem(i, 10, node.via)
+                       self.list.SetStringItem(i, 11, str(node.distance))
+                       self.list.SetStringItem(i, 12, str(node.pmtu))
+                       self.list.SetStringItem(i, 13, str(node.minmtu))
+                       self.list.SetStringItem(i, 14, str(node.maxmtu))
+                       self.list.itemDataMap[i] = (node.name, node.address, node.port, node.cipher, node.digest, node.maclength, node.compression, node.options, node.status, node.nexthop, node.via, node.distance, node.pmtu, node.minmtu, node.maxmtu)
+                       self.list.SetItemData(i, i)
+                       i += 1
+
+               while self.list.GetItemCount() > i:
+                       self.list.DeleteItem(self.list.GetItemCount() - 1)
+
+class EdgesPage(wx.Panel):
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+               self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
+               self.list.InsertColumn(0, 'From')
+               self.list.InsertColumn(1, 'To')
+               self.list.InsertColumn(2, 'Address')
+               self.list.InsertColumn(3, 'Port')
+               self.list.InsertColumn(4, 'Options')
+               self.list.InsertColumn(5, 'Weight')
+
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               hbox.Add(self.list, 1, wx.EXPAND)
+               self.SetSizer(hbox)
+               self.refresh()
+
+       def refresh(self):
+               self.list.itemDataMap = {}
+               i = 0
+
+               for key, edge in vpn.edges.items():
+                       if self.list.GetItemCount() <= i:
+                               self.list.InsertStringItem(i, edge.fr)
+                       else:
+                               self.list.SetStringItem(i, 0, edge.fr)
+                       self.list.SetStringItem(i, 1, edge.to)
+                       self.list.SetStringItem(i, 2, edge.address)
+                       self.list.SetStringItem(i, 3, edge.port)
+                       self.list.SetStringItem(i, 4, str(edge.options))
+                       self.list.SetStringItem(i, 5, str(edge.weight))
+                       self.list.itemDataMap[i] = (edge.fr, edge.to, edge.address, edge.port, edge.options, edge.weight)
+                       i += 1
+
+               while self.list.GetItemCount() > i:
+                       self.list.DeleteItem(self.list.GetItemCount() - 1)
+
+class SubnetsPage(wx.Panel):
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+               self.list = SuperListCtrl(self, id)
+               self.list.InsertColumn(0, 'Subnet', wx.LIST_FORMAT_RIGHT)
+               self.list.InsertColumn(1, 'Weight', wx.LIST_FORMAT_RIGHT)
+               self.list.InsertColumn(2, 'Owner')
+               hbox = wx.BoxSizer(wx.HORIZONTAL)
+               hbox.Add(self.list, 1, wx.EXPAND)
+               self.SetSizer(hbox)
+               self.refresh()
+
+       def refresh(self):
+               self.list.itemDataMap = {}
+               i = 0
+
+               for key, subnet in vpn.subnets.items():
+                       if self.list.GetItemCount() <= i:
+                               self.list.InsertStringItem(i, subnet.address + '/' + subnet.prefixlen)
+                       else:
+                               self.list.SetStringItem(i, 0, subnet.address + '/' + subnet.prefixlen)
+                       self.list.SetStringItem(i, 1, subnet.weight)
+                       self.list.SetStringItem(i, 2, subnet.owner)
+                       self.list.itemDataMap[i] = (subnet.address + '/' + subnet.prefixlen, subnet.weight, subnet.owner)
+                       i = i + 1
+
+               while self.list.GetItemCount() > i:
+                       self.list.DeleteItem(self.list.GetItemCount() - 1)
+
+class StatusPage(wx.Panel):
+       def __init__(self, parent, id):
+               wx.Panel.__init__(self, parent, id)
+
+class GraphPage(wx.Window):
+       def __init__(self, parent, id):
+               wx.Window.__init__(self, parent, id)
+
+class NetPage(wx.Notebook):
+       def __init__(self, parent, id):
+               wx.Notebook.__init__(self, parent)
+               self.settings = SettingsPage(self, id)
+               self.connections = ConnectionsPage(self, id)
+               self.nodes = NodesPage(self, id)
+               self.edges = EdgesPage(self, id)
+               self.subnets = SubnetsPage(self, id)
+               self.graph = GraphPage(self, id)
+               self.status = StatusPage(self, id)
+
+               self.AddPage(self.settings, 'Settings')
+               #self.AddPage(self.status, 'Status')
+               self.AddPage(self.connections, 'Connections')
+               self.AddPage(self.nodes, 'Nodes')
+               self.AddPage(self.edges, 'Edges')
+               self.AddPage(self.subnets, 'Subnets')
+               #self.AddPage(self.graph, 'Graph')
+               
+
+class MainWindow(wx.Frame):
+       def OnQuit(self, event):
+               self.Close(True)
+
+       def OnTimer(self, event):
+               vpn.refresh()
+               self.np.nodes.refresh()
+               self.np.subnets.refresh()
+               self.np.edges.refresh()
+               self.np.connections.refresh()
+
+       def __init__(self, parent, id, title):
+               wx.Frame.__init__(self, parent, id, title)
+
+               menubar = wx.MenuBar()
+               file = wx.Menu()
+               file.Append(1, '&Quit\tCtrl-X', 'Quit tinc GUI')
+               menubar.Append(file, '&File')
+
+               #nb = wx.Notebook(self, -1)
+               #nb.SetPadding((0, 0))
+               self.np = NetPage(self, -1)
+               #nb.AddPage(np, 'VPN')
+               
+               self.timer = wx.Timer(self, -1)
+               self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
+               self.timer.Start(1000)
+               self.Bind(wx.EVT_MENU, self.OnQuit, id=1)
+               self.SetMenuBar(menubar)
+               self.Show()
+
+app = wx.App()
+mw = MainWindow(None, -1, 'Tinc GUI')
+
+#def OnTaskBarIcon(event):
+#      mw.Raise()
+#
+#icon = wx.Icon("tincgui.ico", wx.BITMAP_TYPE_PNG)
+#taskbaricon = wx.TaskBarIcon()
+#taskbaricon.SetIcon(icon, 'Tinc GUI')
+#wx.EVT_TASKBAR_RIGHT_UP(taskbaricon, OnTaskBarIcon)
+
+app.MainLoop()
+vpn.close()
diff --git a/have.h b/have.h
index 72af069884f8e1a0d077cbe8edbfd61618c45ee4..0ab813461c41bcf54bd8d397cfa18d334066feb8 100644 (file)
--- a/have.h
+++ b/have.h
@@ -27,6 +27,7 @@
 #else
 #define WINVER WindowsXP
 #endif
+#define WIN32_LEAN_AND_MEAN
 #endif
 
 #include <stdio.h>
 #include <inttypes.h>
 #endif
 
-#ifdef HAVE_ALLOCA_H
-#include <alloca.h>
-#endif
-
 /* Include system specific headers */
 
 #ifdef HAVE_SYSLOG_H
 #include <sys/uio.h>
 #endif
 
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
 #ifdef HAVE_DIRENT_H
 #include <dirent.h>
 #endif
 #include <netinet/if_ether.h>
 #endif
 
+#ifdef HAVE_EVENT_H
+#include <event.h>
+#endif
+
 #endif /* __TINC_SYSTEM_H__ */
diff --git a/lib/Makefile.am b/lib/Makefile.am
deleted file mode 100644 (file)
index 734bb64..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-## Process this file with automake to produce Makefile.in
-
-noinst_LIBRARIES = libvpn.a
-
-INCLUDES = @INCLUDES@ -I. -I$(top_builddir)
-
-libvpn_a_SOURCES = xmalloc.c pidfile.c utils.c getopt.c getopt1.c list.c avl_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c
-
-libvpn_a_LIBADD = @LIBOBJS@ @ALLOCA@
-libvpn_a_DEPENDENCIES = $(libvpn_a_LIBADD)
-
-noinst_HEADERS = xalloc.h pidfile.h utils.h getopt.h list.h avl_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h
-
-EXTRA_DIST = 
diff --git a/lib/alloca.c b/lib/alloca.c
deleted file mode 100644 (file)
index 10e5d65..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-/* alloca.c -- allocate automatically reclaimed memory
-   (Mostly) portable public-domain implementation -- D A Gwyn
-
-   This implementation of the PWB library alloca function,
-   which is used to allocate space off the run-time stack so
-   that it is automatically reclaimed upon procedure exit,
-   was inspired by discussions with J. Q. Johnson of Cornell.
-   J.Otto Tennant <jot@cray.com> contributed the Cray support.
-
-   There are some preprocessor constants that can
-   be defined when compiling for your specific system, for
-   improved efficiency; however, the defaults should be okay.
-
-   The general concept of this implementation is to keep
-   track of all alloca-allocated blocks, and reclaim any
-   that are found to be deeper in the stack than the current
-   invocation.  This heuristic does not reclaim storage as
-   soon as it becomes invalid, but it will do so eventually.
-
-   As a special case, alloca(0) reclaims storage without
-   allocating any.  It is a good idea to use alloca(0) in
-   your main control loop, etc. to force garbage collection.  */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef emacs
-# include "blockinput.h"
-#endif
-
-/* If compiling with GCC 2, this file's not needed.  */
-#if !defined (__GNUC__) || __GNUC__ < 2
-
-/* If someone has defined alloca as a macro,
-   there must be some other way alloca is supposed to work.  */
-# ifndef alloca
-
-#  ifdef emacs
-#   ifdef static
-/* actually, only want this if static is defined as ""
-   -- this is for usg, in which emacs must undefine static
-   in order to make unexec workable
-   */
-#    ifndef STACK_DIRECTION
-you
-lose
--- must know STACK_DIRECTION at compile-time
-#    endif /* STACK_DIRECTION undefined */
-#   endif /* static */
-#  endif /* emacs */
-
-/* If your stack is a linked list of frames, you have to
-   provide an "address metric" ADDRESS_FUNCTION macro.  */
-
-#  if defined (CRAY) && defined (CRAY_STACKSEG_END)
-long i00afunc ();
-#   define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
-#  else
-#   define ADDRESS_FUNCTION(arg) &(arg)
-#  endif
-
-#  if __STDC__
-typedef void *pointer;
-#  else
-typedef char *pointer;
-#  endif
-
-#  ifndef NULL
-#   define NULL 0
-#  endif
-
-/* Different portions of Emacs need to call different versions of
-   malloc.  The Emacs executable needs alloca to call xmalloc, because
-   ordinary malloc isn't protected from input signals.  On the other
-   hand, the utilities in lib-src need alloca to call malloc; some of
-   them are very simple, and don't have an xmalloc routine.
-
-   Non-Emacs programs expect this to call xmalloc.
-
-   Callers below should use malloc.  */
-
-#  ifndef emacs
-#   define malloc xmalloc
-#  endif
-extern pointer malloc ();
-
-/* Define STACK_DIRECTION if you know the direction of stack
-   growth for your system; otherwise it will be automatically
-   deduced at run-time.
-
-   STACK_DIRECTION > 0 => grows toward higher addresses
-   STACK_DIRECTION < 0 => grows toward lower addresses
-   STACK_DIRECTION = 0 => direction of growth unknown  */
-
-#  ifndef STACK_DIRECTION
-#   define STACK_DIRECTION     0       /* Direction unknown.  */
-#  endif
-
-#  if STACK_DIRECTION != 0
-
-#   define STACK_DIR   STACK_DIRECTION /* Known at compile-time.  */
-
-#  else /* STACK_DIRECTION == 0; need run-time code.  */
-
-static int stack_dir;          /* 1 or -1 once known.  */
-#   define STACK_DIR   stack_dir
-
-static void
-find_stack_direction ()
-{
-  static char *addr = NULL;    /* Address of first `dummy', once known.  */
-  auto char dummy;             /* To get stack address.  */
-
-  if (addr == NULL)
-    {                          /* Initial entry.  */
-      addr = ADDRESS_FUNCTION (dummy);
-
-      find_stack_direction (); /* Recurse once.  */
-    }
-  else
-    {
-      /* Second entry.  */
-      if (ADDRESS_FUNCTION (dummy) > addr)
-       stack_dir = 1;          /* Stack grew upward.  */
-      else
-       stack_dir = -1;         /* Stack grew downward.  */
-    }
-}
-
-#  endif /* STACK_DIRECTION == 0 */
-
-/* An "alloca header" is used to:
-   (a) chain together all alloca'ed blocks;
-   (b) keep track of stack depth.
-
-   It is very important that sizeof(header) agree with malloc
-   alignment chunk size.  The following default should work okay.  */
-
-#  ifndef      ALIGN_SIZE
-#   define ALIGN_SIZE  sizeof(double)
-#  endif
-
-typedef union hdr
-{
-  char align[ALIGN_SIZE];      /* To force sizeof(header).  */
-  struct
-    {
-      union hdr *next;         /* For chaining headers.  */
-      char *deep;              /* For stack depth measure.  */
-    } h;
-} header;
-
-static header *last_alloca_header = NULL;      /* -> last alloca header.  */
-
-/* Return a pointer to at least SIZE bytes of storage,
-   which will be automatically reclaimed upon exit from
-   the procedure that called alloca.  Originally, this space
-   was supposed to be taken from the current stack frame of the
-   caller, but that method cannot be made to work for some
-   implementations of C, for example under Gould's UTX/32.  */
-
-pointer
-alloca (size)
-     unsigned size;
-{
-  auto char probe;             /* Probes stack depth: */
-  register char *depth = ADDRESS_FUNCTION (probe);
-
-#  if STACK_DIRECTION == 0
-  if (STACK_DIR == 0)          /* Unknown growth direction.  */
-    find_stack_direction ();
-#  endif
-
-  /* Reclaim garbage, defined as all alloca'd storage that
-     was allocated from deeper in the stack than currently.  */
-
-  {
-    register header *hp;       /* Traverses linked list.  */
-
-#  ifdef emacs
-    BLOCK_INPUT;
-#  endif
-
-    for (hp = last_alloca_header; hp != NULL;)
-      if ((STACK_DIR > 0 && hp->h.deep > depth)
-         || (STACK_DIR < 0 && hp->h.deep < depth))
-       {
-         register header *np = hp->h.next;
-
-         free ((pointer) hp);  /* Collect garbage.  */
-
-         hp = np;              /* -> next header.  */
-       }
-      else
-       break;                  /* Rest are not deeper.  */
-
-    last_alloca_header = hp;   /* -> last valid storage.  */
-
-#  ifdef emacs
-    UNBLOCK_INPUT;
-#  endif
-  }
-
-  if (size == 0)
-    return NULL;               /* No allocation required.  */
-
-  /* Allocate combined header + user data storage.  */
-
-  {
-    register pointer new = malloc (sizeof (header) + size);
-    /* Address of header.  */
-
-    ((header *) new)->h.next = last_alloca_header;
-    ((header *) new)->h.deep = depth;
-
-    last_alloca_header = (header *) new;
-
-    /* User storage begins just after header.  */
-
-    return (pointer) ((char *) new + sizeof (header));
-  }
-}
-
-#  if defined (CRAY) && defined (CRAY_STACKSEG_END)
-
-#   ifdef DEBUG_I00AFUNC
-#    include <stdio.h>
-#   endif
-
-#   ifndef CRAY_STACK
-#    define CRAY_STACK
-#    ifndef CRAY2
-/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
-struct stack_control_header
-  {
-    long shgrow:32;            /* Number of times stack has grown.  */
-    long shaseg:32;            /* Size of increments to stack.  */
-    long shhwm:32;             /* High water mark of stack.  */
-    long shsize:32;            /* Current size of stack (all segments).  */
-  };
-
-/* The stack segment linkage control information occurs at
-   the high-address end of a stack segment.  (The stack
-   grows from low addresses to high addresses.)  The initial
-   part of the stack segment linkage control information is
-   0200 (octal) words.  This provides for register storage
-   for the routine which overflows the stack.  */
-
-struct stack_segment_linkage
-  {
-    long ss[0200];             /* 0200 overflow words.  */
-    long sssize:32;            /* Number of words in this segment.  */
-    long ssbase:32;            /* Offset to stack base.  */
-    long:32;
-    long sspseg:32;            /* Offset to linkage control of previous
-                                  segment of stack.  */
-    long:32;
-    long sstcpt:32;            /* Pointer to task common address block.  */
-    long sscsnm;               /* Private control structure number for
-                                  microtasking.  */
-    long ssusr1;               /* Reserved for user.  */
-    long ssusr2;               /* Reserved for user.  */
-    long sstpid;               /* Process ID for pid based multi-tasking.  */
-    long ssgvup;               /* Pointer to multitasking thread giveup.  */
-    long sscray[7];            /* Reserved for Cray Research.  */
-    long ssa0;
-    long ssa1;
-    long ssa2;
-    long ssa3;
-    long ssa4;
-    long ssa5;
-    long ssa6;
-    long ssa7;
-    long sss0;
-    long sss1;
-    long sss2;
-    long sss3;
-    long sss4;
-    long sss5;
-    long sss6;
-    long sss7;
-  };
-
-#    else /* CRAY2 */
-/* The following structure defines the vector of words
-   returned by the STKSTAT library routine.  */
-struct stk_stat
-  {
-    long now;                  /* Current total stack size.  */
-    long maxc;                 /* Amount of contiguous space which would
-                                  be required to satisfy the maximum
-                                  stack demand to date.  */
-    long high_water;           /* Stack high-water mark.  */
-    long overflows;            /* Number of stack overflow ($STKOFEN) calls.  */
-    long hits;                 /* Number of internal buffer hits.  */
-    long extends;              /* Number of block extensions.  */
-    long stko_mallocs;         /* Block allocations by $STKOFEN.  */
-    long underflows;           /* Number of stack underflow calls ($STKRETN).  */
-    long stko_free;            /* Number of deallocations by $STKRETN.  */
-    long stkm_free;            /* Number of deallocations by $STKMRET.  */
-    long segments;             /* Current number of stack segments.  */
-    long maxs;                 /* Maximum number of stack segments so far.  */
-    long pad_size;             /* Stack pad size.  */
-    long current_address;      /* Current stack segment address.  */
-    long current_size;         /* Current stack segment size.  This
-                                  number is actually corrupted by STKSTAT to
-                                  include the fifteen word trailer area.  */
-    long initial_address;      /* Address of initial segment.  */
-    long initial_size;         /* Size of initial segment.  */
-  };
-
-/* The following structure describes the data structure which trails
-   any stack segment.  I think that the description in 'asdef' is
-   out of date.  I only describe the parts that I am sure about.  */
-
-struct stk_trailer
-  {
-    long this_address;         /* Address of this block.  */
-    long this_size;            /* Size of this block (does not include
-                                  this trailer).  */
-    long unknown2;
-    long unknown3;
-    long link;                 /* Address of trailer block of previous
-                                  segment.  */
-    long unknown5;
-    long unknown6;
-    long unknown7;
-    long unknown8;
-    long unknown9;
-    long unknown10;
-    long unknown11;
-    long unknown12;
-    long unknown13;
-    long unknown14;
-  };
-
-#    endif /* CRAY2 */
-#   endif /* not CRAY_STACK */
-
-#   ifdef CRAY2
-/* Determine a "stack measure" for an arbitrary ADDRESS.
-   I doubt that "lint" will like this much.  */
-
-static long
-i00afunc (long *address)
-{
-  struct stk_stat status;
-  struct stk_trailer *trailer;
-  long *block, size;
-  long result = 0;
-
-  /* We want to iterate through all of the segments.  The first
-     step is to get the stack status structure.  We could do this
-     more quickly and more directly, perhaps, by referencing the
-     $LM00 common block, but I know that this works.  */
-
-  STKSTAT (&status);
-
-  /* Set up the iteration.  */
-
-  trailer = (struct stk_trailer *) (status.current_address
-                                   + status.current_size
-                                   - 15);
-
-  /* There must be at least one stack segment.  Therefore it is
-     a fatal error if "trailer" is null.  */
-
-  if (trailer == 0)
-    abort ();
-
-  /* Discard segments that do not contain our argument address.  */
-
-  while (trailer != 0)
-    {
-      block = (long *) trailer->this_address;
-      size = trailer->this_size;
-      if (block == 0 || size == 0)
-       abort ();
-      trailer = (struct stk_trailer *) trailer->link;
-      if ((block <= address) && (address < (block + size)))
-       break;
-    }
-
-  /* Set the result to the offset in this segment and add the sizes
-     of all predecessor segments.  */
-
-  result = address - block;
-
-  if (trailer == 0)
-    {
-      return result;
-    }
-
-  do
-    {
-      if (trailer->this_size <= 0)
-       abort ();
-      result += trailer->this_size;
-      trailer = (struct stk_trailer *) trailer->link;
-    }
-  while (trailer != 0);
-
-  /* We are done.  Note that if you present a bogus address (one
-     not in any segment), you will get a different number back, formed
-     from subtracting the address of the first block.  This is probably
-     not what you want.  */
-
-  return (result);
-}
-
-#   else /* not CRAY2 */
-/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
-   Determine the number of the cell within the stack,
-   given the address of the cell.  The purpose of this
-   routine is to linearize, in some sense, stack addresses
-   for alloca.  */
-
-static long
-i00afunc (long address)
-{
-  long stkl = 0;
-
-  long size, pseg, this_segment, stack;
-  long result = 0;
-
-  struct stack_segment_linkage *ssptr;
-
-  /* Register B67 contains the address of the end of the
-     current stack segment.  If you (as a subprogram) store
-     your registers on the stack and find that you are past
-     the contents of B67, you have overflowed the segment.
-
-     B67 also points to the stack segment linkage control
-     area, which is what we are really interested in.  */
-
-  stkl = CRAY_STACKSEG_END ();
-  ssptr = (struct stack_segment_linkage *) stkl;
-
-  /* If one subtracts 'size' from the end of the segment,
-     one has the address of the first word of the segment.
-
-     If this is not the first segment, 'pseg' will be
-     nonzero.  */
-
-  pseg = ssptr->sspseg;
-  size = ssptr->sssize;
-
-  this_segment = stkl - size;
-
-  /* It is possible that calling this routine itself caused
-     a stack overflow.  Discard stack segments which do not
-     contain the target address.  */
-
-  while (!(this_segment <= address && address <= stkl))
-    {
-#    ifdef DEBUG_I00AFUNC
-      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
-#    endif
-      if (pseg == 0)
-       break;
-      stkl = stkl - pseg;
-      ssptr = (struct stack_segment_linkage *) stkl;
-      size = ssptr->sssize;
-      pseg = ssptr->sspseg;
-      this_segment = stkl - size;
-    }
-
-  result = address - this_segment;
-
-  /* If you subtract pseg from the current end of the stack,
-     you get the address of the previous stack segment's end.
-     This seems a little convoluted to me, but I'll bet you save
-     a cycle somewhere.  */
-
-  while (pseg != 0)
-    {
-#    ifdef DEBUG_I00AFUNC
-      fprintf (stderr, "%011o %011o\n", pseg, size);
-#    endif
-      stkl = stkl - pseg;
-      ssptr = (struct stack_segment_linkage *) stkl;
-      size = ssptr->sssize;
-      pseg = ssptr->sspseg;
-      result += size;
-    }
-  return (result);
-}
-
-#   endif /* not CRAY2 */
-#  endif /* CRAY */
-
-# endif /* no alloca */
-#endif /* not GCC version 2 */
diff --git a/lib/avl_tree.c b/lib/avl_tree.c
deleted file mode 100644 (file)
index a8bf5d5..0000000
+++ /dev/null
@@ -1,739 +0,0 @@
-/*
-    avl_tree.c -- avl_ tree and linked list convenience
-    Copyright (C) 1998 Michael H. Buselli
-                  2000-2005 Ivo Timmermans,
-                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
-                  2000-2005 Wessel Dankers <wsl@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-    Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
-
-    Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> to use counts
-    instead of depths, to add the ->next and ->prev and to generally obfuscate
-    the code. Mail me if you found a bug.
-
-    Cleaned up and incorporated some of the ideas from the red-black tree
-    library for inclusion into tinc (http://www.tinc-vpn.org/) by
-    Guus Sliepen <guus@tinc-vpn.org>.
-*/
-
-#include "system.h"
-
-#include "avl_tree.h"
-#include "xalloc.h"
-
-#ifdef AVL_COUNT
-#define AVL_NODE_COUNT(n)  ((n) ? (n)->count : 0)
-#define AVL_L_COUNT(n)     (AVL_NODE_COUNT((n)->left))
-#define AVL_R_COUNT(n)     (AVL_NODE_COUNT((n)->right))
-#define AVL_CALC_COUNT(n)  (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1)
-#endif
-
-#ifdef AVL_DEPTH
-#define AVL_NODE_DEPTH(n)  ((n) ? (n)->depth : 0)
-#define L_AVL_DEPTH(n)     (AVL_NODE_DEPTH((n)->left))
-#define R_AVL_DEPTH(n)     (AVL_NODE_DEPTH((n)->right))
-#define AVL_CALC_DEPTH(n)  ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1)
-#endif
-
-#ifndef AVL_DEPTH
-static int lg(unsigned int u) __attribute__ ((__const__));
-
-static int lg(unsigned int u)
-{
-       int r = 1;
-
-       if(!u)
-               return 0;
-
-       if(u & 0xffff0000) {
-               u >>= 16;
-               r += 16;
-       }
-
-       if(u & 0x0000ff00) {
-               u >>= 8;
-               r += 8;
-       }
-
-       if(u & 0x000000f0) {
-               u >>= 4;
-               r += 4;
-       }
-
-       if(u & 0x0000000c) {
-               u >>= 2;
-               r += 2;
-       }
-
-       if(u & 0x00000002)
-               r++;
-
-       return r;
-}
-#endif
-
-/* Internal helper functions */
-
-static int avl_check_balance(const avl_node_t *node)
-{
-#ifdef AVL_DEPTH
-       int d;
-
-       d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node);
-
-       return d < -1 ? -1 : d > 1 ? 1 : 0;
-#else
-/*      int d;
- *      d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node));
- *      d = d<-1?-1:d>1?1:0;
- */
-       int pl, r;
-
-       pl = lg(AVL_L_COUNT(node));
-       r = AVL_R_COUNT(node);
-
-       if(r >> pl + 1)
-               return 1;
-
-       if(pl < 2 || r >> pl - 2)
-               return 0;
-
-       return -1;
-#endif
-}
-
-static void avl_rebalance(avl_tree_t *tree, avl_node_t *node)
-{
-       avl_node_t *child;
-       avl_node_t *gchild;
-       avl_node_t *parent;
-       avl_node_t **superparent;
-
-       parent = node;
-
-       while(node) {
-               parent = node->parent;
-
-               superparent =
-                       parent ? node ==
-                       parent->left ? &parent->left : &parent->right : &tree->root;
-
-               switch (avl_check_balance(node)) {
-                       case -1:
-                               child = node->left;
-#ifdef AVL_DEPTH
-                               if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) {
-#else
-                               if(AVL_L_COUNT(child) >= AVL_R_COUNT(child)) {
-#endif
-                                       node->left = child->right;
-                                       if(node->left)
-                                               node->left->parent = node;
-
-                                       child->right = node;
-                                       node->parent = child;
-                                       *superparent = child;
-                                       child->parent = parent;
-#ifdef AVL_COUNT
-                                       node->count = AVL_CALC_COUNT(node);
-                                       child->count = AVL_CALC_COUNT(child);
-#endif
-#ifdef AVL_DEPTH
-                                       node->depth = AVL_CALC_DEPTH(node);
-                                       child->depth = AVL_CALC_DEPTH(child);
-#endif
-                               } else {
-                                       gchild = child->right;
-                                       node->left = gchild->right;
-
-                                       if(node->left)
-                                               node->left->parent = node;
-                                       child->right = gchild->left;
-
-                                       if(child->right)
-                                               child->right->parent = child;
-                                       gchild->right = node;
-
-                                       if(gchild->right)
-                                               gchild->right->parent = gchild;
-                                       gchild->left = child;
-
-                                       if(gchild->left)
-                                               gchild->left->parent = gchild;
-                                       *superparent = gchild;
-
-                                       gchild->parent = parent;
-#ifdef AVL_COUNT
-                                       node->count = AVL_CALC_COUNT(node);
-                                       child->count = AVL_CALC_COUNT(child);
-                                       gchild->count = AVL_CALC_COUNT(gchild);
-#endif
-#ifdef AVL_DEPTH
-                                       node->depth = AVL_CALC_DEPTH(node);
-                                       child->depth = AVL_CALC_DEPTH(child);
-                                       gchild->depth = AVL_CALC_DEPTH(gchild);
-#endif
-                               }
-                               break;
-
-                       case 1:
-                               child = node->right;
-#ifdef AVL_DEPTH
-                               if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) {
-#else
-                               if(AVL_R_COUNT(child) >= AVL_L_COUNT(child)) {
-#endif
-                                       node->right = child->left;
-                                       if(node->right)
-                                               node->right->parent = node;
-                                       child->left = node;
-                                       node->parent = child;
-                                       *superparent = child;
-                                       child->parent = parent;
-#ifdef AVL_COUNT
-                                       node->count = AVL_CALC_COUNT(node);
-                                       child->count = AVL_CALC_COUNT(child);
-#endif
-#ifdef AVL_DEPTH
-                                       node->depth = AVL_CALC_DEPTH(node);
-                                       child->depth = AVL_CALC_DEPTH(child);
-#endif
-                               } else {
-                                       gchild = child->left;
-                                       node->right = gchild->left;
-
-                                       if(node->right)
-                                               node->right->parent = node;
-                                       child->left = gchild->right;
-
-                                       if(child->left)
-                                               child->left->parent = child;
-                                       gchild->left = node;
-
-                                       if(gchild->left)
-                                               gchild->left->parent = gchild;
-                                       gchild->right = child;
-
-                                       if(gchild->right)
-                                               gchild->right->parent = gchild;
-
-                                       *superparent = gchild;
-                                       gchild->parent = parent;
-#ifdef AVL_COUNT
-                                       node->count = AVL_CALC_COUNT(node);
-                                       child->count = AVL_CALC_COUNT(child);
-                                       gchild->count = AVL_CALC_COUNT(gchild);
-#endif
-#ifdef AVL_DEPTH
-                                       node->depth = AVL_CALC_DEPTH(node);
-                                       child->depth = AVL_CALC_DEPTH(child);
-                                       gchild->depth = AVL_CALC_DEPTH(gchild);
-#endif
-                               }
-                               break;
-
-                       default:
-#ifdef AVL_COUNT
-                               node->count = AVL_CALC_COUNT(node);
-#endif
-#ifdef AVL_DEPTH
-                               node->depth = AVL_CALC_DEPTH(node);
-#endif
-               }
-               node = parent;
-       }
-}
-
-/* (De)constructors */
-
-avl_tree_t *avl_alloc_tree(avl_compare_t compare, avl_action_t delete)
-{
-       avl_tree_t *tree;
-
-       tree = xmalloc_and_zero(sizeof(avl_tree_t));
-       tree->compare = compare;
-       tree->delete = delete;
-
-       return tree;
-}
-
-void avl_free_tree(avl_tree_t *tree)
-{
-       free(tree);
-}
-
-avl_node_t *avl_alloc_node(void)
-{
-       return xmalloc_and_zero(sizeof(avl_node_t));
-}
-
-void avl_free_node(avl_tree_t *tree, avl_node_t *node)
-{
-       if(node->data && tree->delete)
-               tree->delete(node->data);
-
-       free(node);
-}
-
-/* Searching */
-
-void *avl_search(const avl_tree_t *tree, const void *data)
-{
-       avl_node_t *node;
-
-       node = avl_search_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-void *avl_search_closest(const avl_tree_t *tree, const void *data, int *result)
-{
-       avl_node_t *node;
-
-       node = avl_search_closest_node(tree, data, result);
-
-       return node ? node->data : NULL;
-}
-
-void *avl_search_closest_smaller(const avl_tree_t *tree, const void *data)
-{
-       avl_node_t *node;
-
-       node = avl_search_closest_smaller_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-void *avl_search_closest_greater(const avl_tree_t *tree, const void *data)
-{
-       avl_node_t *node;
-
-       node = avl_search_closest_greater_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-avl_node_t *avl_search_node(const avl_tree_t *tree, const void *data)
-{
-       avl_node_t *node;
-       int result;
-
-       node = avl_search_closest_node(tree, data, &result);
-
-       return result ? NULL : node;
-}
-
-avl_node_t *avl_search_closest_node(const avl_tree_t *tree, const void *data,
-                                                                       int *result)
-{
-       avl_node_t *node;
-       int c;
-
-       node = tree->root;
-
-       if(!node) {
-               if(result)
-                       *result = 0;
-               return NULL;
-       }
-
-       for(;;) {
-               c = tree->compare(data, node->data);
-
-               if(c < 0) {
-                       if(node->left)
-                               node = node->left;
-                       else {
-                               if(result)
-                                       *result = -1;
-                               break;
-                       }
-               } else if(c > 0) {
-                       if(node->right)
-                               node = node->right;
-                       else {
-                               if(result)
-                                       *result = 1;
-                               break;
-                       }
-               } else {
-                       if(result)
-                               *result = 0;
-                       break;
-               }
-       }
-
-       return node;
-}
-
-avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *tree,
-                                                                                       const void *data)
-{
-       avl_node_t *node;
-       int result;
-
-       node = avl_search_closest_node(tree, data, &result);
-
-       if(result < 0)
-               node = node->prev;
-
-       return node;
-}
-
-avl_node_t *avl_search_closest_greater_node(const avl_tree_t *tree,
-                                                                                       const void *data)
-{
-       avl_node_t *node;
-       int result;
-
-       node = avl_search_closest_node(tree, data, &result);
-
-       if(result > 0)
-               node = node->next;
-
-       return node;
-}
-
-/* Insertion and deletion */
-
-avl_node_t *avl_insert(avl_tree_t *tree, void *data)
-{
-       avl_node_t *closest, *new;
-       int result;
-
-       if(!tree->root) {
-               new = avl_alloc_node();
-               new->data = data;
-               avl_insert_top(tree, new);
-       } else {
-               closest = avl_search_closest_node(tree, data, &result);
-
-               switch (result) {
-                       case -1:
-                               new = avl_alloc_node();
-                               new->data = data;
-                               avl_insert_before(tree, closest, new);
-                               break;
-
-                       case 1:
-                               new = avl_alloc_node();
-                               new->data = data;
-                               avl_insert_after(tree, closest, new);
-                               break;
-
-                       default:
-                               return NULL;
-               }
-       }
-
-#ifdef AVL_COUNT
-       new->count = 1;
-#endif
-#ifdef AVL_DEPTH
-       new->depth = 1;
-#endif
-
-       return new;
-}
-
-avl_node_t *avl_insert_node(avl_tree_t *tree, avl_node_t *node)
-{
-       avl_node_t *closest;
-       int result;
-
-       if(!tree->root)
-               avl_insert_top(tree, node);
-       else {
-               closest = avl_search_closest_node(tree, node->data, &result);
-
-               switch (result) {
-                       case -1:
-                               avl_insert_before(tree, closest, node);
-                               break;
-
-                       case 1:
-                               avl_insert_after(tree, closest, node);
-                               break;
-
-                       case 0:
-                               return NULL;
-               }
-       }
-
-#ifdef AVL_COUNT
-       node->count = 1;
-#endif
-#ifdef AVL_DEPTH
-       node->depth = 1;
-#endif
-
-       return node;
-}
-
-void avl_insert_top(avl_tree_t *tree, avl_node_t *node)
-{
-       node->prev = node->next = node->parent = NULL;
-       tree->head = tree->tail = tree->root = node;
-}
-
-void avl_insert_before(avl_tree_t *tree, avl_node_t *before,
-                                          avl_node_t *node)
-{
-       if(!before) {
-               if(tree->tail)
-                       avl_insert_after(tree, tree->tail, node);
-               else
-                       avl_insert_top(tree, node);
-               return;
-       }
-
-       node->next = before;
-       node->parent = before;
-       node->prev = before->prev;
-
-       if(before->left) {
-               avl_insert_after(tree, before->prev, node);
-               return;
-       }
-
-       if(before->prev)
-               before->prev->next = node;
-       else
-               tree->head = node;
-
-       before->prev = node;
-       before->left = node;
-
-       avl_rebalance(tree, before);
-}
-
-void avl_insert_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node)
-{
-       if(!after) {
-               if(tree->head)
-                       avl_insert_before(tree, tree->head, node);
-               else
-                       avl_insert_top(tree, node);
-               return;
-       }
-
-       if(after->right) {
-               avl_insert_before(tree, after->next, node);
-               return;
-       }
-
-       node->prev = after;
-       node->parent = after;
-       node->next = after->next;
-
-       if(after->next)
-               after->next->prev = node;
-       else
-               tree->tail = node;
-
-       after->next = node;
-       after->right = node;
-
-       avl_rebalance(tree, after);
-}
-
-avl_node_t *avl_unlink(avl_tree_t *tree, void *data)
-{
-       avl_node_t *node;
-
-       node = avl_search_node(tree, data);
-
-       if(node)
-               avl_unlink_node(tree, node);
-
-       return node;
-}
-
-void avl_unlink_node(avl_tree_t *tree, avl_node_t *node)
-{
-       avl_node_t *parent;
-       avl_node_t **superparent;
-       avl_node_t *subst, *left, *right;
-       avl_node_t *balnode;
-
-       if(node->prev)
-               node->prev->next = node->next;
-       else
-               tree->head = node->next;
-       if(node->next)
-               node->next->prev = node->prev;
-       else
-               tree->tail = node->prev;
-
-       parent = node->parent;
-
-       superparent =
-               parent ? node ==
-               parent->left ? &parent->left : &parent->right : &tree->root;
-
-       left = node->left;
-       right = node->right;
-       if(!left) {
-               *superparent = right;
-
-               if(right)
-                       right->parent = parent;
-
-               balnode = parent;
-       } else if(!right) {
-               *superparent = left;
-               left->parent = parent;
-               balnode = parent;
-       } else {
-               subst = node->prev;
-
-               if(subst == left) {
-                       balnode = subst;
-               } else {
-                       balnode = subst->parent;
-                       balnode->right = subst->left;
-
-                       if(balnode->right)
-                               balnode->right->parent = balnode;
-
-                       subst->left = left;
-                       left->parent = subst;
-               }
-
-               subst->right = right;
-               subst->parent = parent;
-               right->parent = subst;
-               *superparent = subst;
-       }
-
-       avl_rebalance(tree, balnode);
-
-       node->next = node->prev = node->parent = node->left = node->right = NULL;
-
-#ifdef AVL_COUNT
-       node->count = 0;
-#endif
-#ifdef AVL_DEPTH
-       node->depth = 0;
-#endif
-}
-
-void avl_delete_node(avl_tree_t *tree, avl_node_t *node)
-{
-       avl_unlink_node(tree, node);
-       avl_free_node(tree, node);
-}
-
-void avl_delete(avl_tree_t *tree, void *data)
-{
-       avl_node_t *node;
-
-       node = avl_search_node(tree, data);
-
-       if(node)
-               avl_delete_node(tree, node);
-}
-
-/* Fast tree cleanup */
-
-void avl_delete_tree(avl_tree_t *tree)
-{
-       avl_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               avl_free_node(tree, node);
-       }
-
-       avl_free_tree(tree);
-}
-
-/* Tree walking */
-
-void avl_foreach(const avl_tree_t *tree, avl_action_t action)
-{
-       avl_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               action(node->data);
-       }
-}
-
-void avl_foreach_node(const avl_tree_t *tree, avl_action_t action)
-{
-       avl_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               action(node);
-       }
-}
-
-/* Indexing */
-
-#ifdef AVL_COUNT
-unsigned int avl_count(const avl_tree_t *tree)
-{
-       return AVL_NODE_COUNT(tree->root);
-}
-
-avl_node_t *avl_get_node(const avl_tree_t *tree, unsigned int index)
-{
-       avl_node_t *node;
-       unsigned int c;
-
-       node = tree->root;
-
-       while(node) {
-               c = AVL_L_COUNT(node);
-
-               if(index < c) {
-                       node = node->left;
-               } else if(index > c) {
-                       node = node->right;
-                       index -= c + 1;
-               } else {
-                       return node;
-               }
-       }
-
-       return NULL;
-}
-
-unsigned int avl_index(const avl_node_t *node)
-{
-       avl_node_t *next;
-       unsigned int index;
-
-       index = AVL_L_COUNT(node);
-
-       while((next = node->parent)) {
-               if(node == next->right)
-                       index += AVL_L_COUNT(next) + 1;
-               node = next;
-       }
-
-       return index;
-}
-#endif
-#ifdef AVL_DEPTH
-unsigned int avl_depth(const avl_tree_t *tree)
-{
-       return AVL_NODE_DEPTH(tree->root);
-}
-#endif
diff --git a/lib/avl_tree.h b/lib/avl_tree.h
deleted file mode 100644 (file)
index f442934..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-    avl_tree.h -- header file for avl_tree.c
-    Copyright (C) 1998 Michael H. Buselli
-                  2000-2005 Ivo Timmermans,
-                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
-                  2000-2005 Wessel Dankers <wsl@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-    Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
-
-    Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> to use counts
-    instead of depths, to add the ->next and ->prev and to generally obfuscate
-    the code. Mail me if you found a bug.
-
-    Cleaned up and incorporated some of the ideas from the red-black tree
-    library for inclusion into tinc (http://www.tinc-vpn.org/) by
-    Guus Sliepen <guus@tinc-vpn.org>.
-*/
-
-
-#ifndef __AVL_TREE_H__
-#define __AVL_TREE_H__
-
-#ifndef AVL_DEPTH
-#ifndef AVL_COUNT
-#define AVL_DEPTH
-#endif
-#endif
-
-typedef struct avl_node_t {
-
-       /* Linked list part */
-
-       struct avl_node_t *next;
-       struct avl_node_t *prev;
-
-       /* Tree part */
-
-       struct avl_node_t *parent;
-       struct avl_node_t *left;
-       struct avl_node_t *right;
-
-#ifdef AVL_COUNT
-       unsigned int count;
-#endif
-#ifdef AVL_DEPTH
-       unsigned char depth;
-#endif
-
-       /* Payload */
-
-       void *data;
-
-} avl_node_t;
-
-typedef int (*avl_compare_t)(const void *, const void *);
-typedef void (*avl_action_t)(const void *);
-typedef void (*avl_action_node_t)(const avl_node_t *);
-
-typedef struct avl_tree_t {
-
-       /* Linked list part */
-
-       avl_node_t *head;
-       avl_node_t *tail;
-
-       /* Tree part */
-
-       avl_node_t *root;
-
-       avl_compare_t compare;
-       avl_action_t delete;
-
-} avl_tree_t;
-
-/* (De)constructors */
-
-extern avl_tree_t *avl_alloc_tree(avl_compare_t, avl_action_t);
-extern void avl_free_tree(avl_tree_t *);
-
-extern avl_node_t *avl_alloc_node(void);
-extern void avl_free_node(avl_tree_t *tree, avl_node_t *);
-
-/* Insertion and deletion */
-
-extern avl_node_t *avl_insert(avl_tree_t *, void *);
-extern avl_node_t *avl_insert_node(avl_tree_t *, avl_node_t *);
-
-extern void avl_insert_top(avl_tree_t *, avl_node_t *);
-extern void avl_insert_before(avl_tree_t *, avl_node_t *, avl_node_t *);
-extern void avl_insert_after(avl_tree_t *, avl_node_t *, avl_node_t *);
-
-extern avl_node_t *avl_unlink(avl_tree_t *, void *);
-extern void avl_unlink_node(avl_tree_t *tree, avl_node_t *);
-extern void avl_delete(avl_tree_t *, void *);
-extern void avl_delete_node(avl_tree_t *, avl_node_t *);
-
-/* Fast tree cleanup */
-
-extern void avl_delete_tree(avl_tree_t *);
-
-/* Searching */
-
-extern void *avl_search(const avl_tree_t *, const void *);
-extern void *avl_search_closest(const avl_tree_t *, const void *, int *);
-extern void *avl_search_closest_smaller(const avl_tree_t *, const void *);
-extern void *avl_search_closest_greater(const avl_tree_t *, const void *);
-
-extern avl_node_t *avl_search_node(const avl_tree_t *, const void *);
-extern avl_node_t *avl_search_closest_node(const avl_tree_t *, const void *, int *);
-extern avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *, const void *);
-extern avl_node_t *avl_search_closest_greater_node(const avl_tree_t *, const void *);
-
-/* Tree walking */
-
-extern void avl_foreach(const avl_tree_t *, avl_action_t);
-extern void avl_foreach_node(const avl_tree_t *, avl_action_t);
-
-/* Indexing */
-
-#ifdef AVL_COUNT
-extern unsigned int avl_count(const avl_tree_t *);
-extern avl_node_t *avl_get_node(const avl_tree_t *, unsigned int);
-extern unsigned int avl_index(const avl_node_t *);
-#endif
-#ifdef AVL_DEPTH
-extern unsigned int avl_depth(const avl_tree_t *);
-#endif
-
-#endif                                                 /* __AVL_TREE_H__ */
diff --git a/lib/dropin.c b/lib/dropin.c
deleted file mode 100644 (file)
index eb17aca..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
-    dropin.c -- a set of drop-in replacements for libc functions
-    Copyright (C) 2000-2005 Ivo Timmermans,
-                  2000-2011 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "system.h"
-
-#include "xalloc.h"
-
-#ifndef HAVE_DAEMON
-/*
-  Replacement for the daemon() function.
-  
-  The daemon() function is for programs wishing to detach themselves
-  from the controlling terminal and run in the background as system
-  daemons.
-
-  Unless the argument nochdir is non-zero, daemon() changes the
-  current working directory to the root (``/'').
-
-  Unless the argument noclose is non-zero, daemon() will redirect
-  standard input, standard output and standard error to /dev/null.
-*/
-int daemon(int nochdir, int noclose) {
-#ifdef HAVE_FORK
-       pid_t pid;
-       int fd;
-
-       pid = fork();
-
-       /* Check if forking failed */
-       if(pid < 0) {
-               perror("fork");
-               exit(-1);
-       }
-
-       /* If we are the parent, terminate */
-       if(pid)
-               exit(0);
-
-       /* Detach by becoming the new process group leader */
-       if(setsid() < 0) {
-               perror("setsid");
-               return -1;
-       }
-
-       /* Change working directory to the root (to avoid keeping mount
-          points busy) */
-       if(!nochdir) {
-               chdir("/");
-       }
-
-       /* Redirect stdin/out/err to /dev/null */
-       if(!noclose) {
-               fd = open("/dev/null", O_RDWR);
-
-               if(fd < 0) {
-                       perror("opening /dev/null");
-                       return -1;
-               } else {
-                       dup2(fd, 0);
-                       dup2(fd, 1);
-                       dup2(fd, 2);
-               }
-       }
-
-       return 0;
-#else
-       return -1;
-#endif
-}
-#endif
-
-#ifndef HAVE_GET_CURRENT_DIR_NAME
-/*
-  Replacement for the GNU get_current_dir_name function:
-
-  get_current_dir_name will malloc(3) an array big enough to hold the
-  current directory name.  If the environment variable PWD is set, and
-  its value is correct, then that value will be returned.
-*/
-char *get_current_dir_name(void) {
-       size_t size;
-       char *buf;
-       char *r;
-
-       /* Start with 100 bytes.  If this turns out to be insufficient to
-          contain the working directory, double the size.  */
-       size = 100;
-       buf = xmalloc(size);
-
-       errno = 0;                                      /* Success */
-       r = getcwd(buf, size);
-
-       /* getcwd returns NULL and sets errno to ERANGE if the bufferspace
-          is insufficient to contain the entire working directory.  */
-       while(r == NULL && errno == ERANGE) {
-               free(buf);
-               size <<= 1;                             /* double the size */
-               buf = xmalloc(size);
-               r = getcwd(buf, size);
-       }
-
-       return buf;
-}
-#endif
-
-#ifndef HAVE_ASPRINTF
-int asprintf(char **buf, const char *fmt, ...) {
-       int result;
-       va_list ap;
-       va_start(ap, fmt);
-       result = vasprintf(buf, fmt, ap);
-       va_end(ap);
-       return result;
-}
-
-int vasprintf(char **buf, const char *fmt, va_list ap) {
-       int status;
-       va_list aq;
-       int len;
-
-       len = 4096;
-       *buf = xmalloc(len);
-
-       va_copy(aq, ap);
-       status = vsnprintf(*buf, len, fmt, aq);
-       va_end(aq);
-
-       if(status >= 0)
-               *buf = xrealloc(*buf, status + 1);
-
-       if(status > len - 1) {
-               len = status;
-               va_copy(aq, ap);
-               status = vsnprintf(*buf, len, fmt, aq);
-               va_end(aq);
-       }
-
-       return status;
-}
-#endif
-
-#ifndef HAVE_GETTIMEOFDAY
-int gettimeofday(struct timeval *tv, void *tz) {
-       tv->tv_sec = time(NULL);
-       tv->tv_usec = 0;
-       return 0;
-}
-#endif
-
-#ifndef HAVE_USLEEP
-int usleep(long long usec) {
-       struct timeval tv = {usec / 1000000, (usec / 1000) % 1000};
-       select(0, NULL, NULL, NULL, &tv);
-       return 0;
-}
-#endif
diff --git a/lib/dropin.h b/lib/dropin.h
deleted file mode 100644 (file)
index dc7dbee..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-    dropin.h -- header file for dropin.c
-    Copyright (C) 2000-2005 Ivo Timmermans,
-                  2000-2011 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __DROPIN_H__
-#define __DROPIN_H__
-
-#include "fake-getaddrinfo.h"
-#include "fake-getnameinfo.h"
-
-#ifndef HAVE_DAEMON
-extern int daemon(int, int);
-#endif
-
-#ifndef HAVE_GET_CURRENT_DIR_NAME
-extern char *get_current_dir_name(void);
-#endif
-
-#ifndef HAVE_ASPRINTF
-extern int asprintf(char **, const char *, ...);
-extern int vasprintf(char **, const char *, va_list ap);
-#endif
-
-#ifndef HAVE_GETTIMEOFDAY
-extern int gettimeofday(struct timeval *, void *);
-#endif
-
-#ifndef HAVE_USLEEP
-extern int usleep(long long);
-#endif
-
-#endif                                                 /* __DROPIN_H__ */
diff --git a/lib/ethernet.h b/lib/ethernet.h
deleted file mode 100644 (file)
index eef5f42..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-    ethernet.h -- missing Ethernet related definitions
-    Copyright (C) 2005 Ivo Timmermans
-                  2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_ETHERNET_H__
-#define __TINC_ETHERNET_H__
-
-#ifndef ETH_ALEN
-#define ETH_ALEN 6
-#endif
-
-#ifndef ARPHRD_ETHER
-#define ARPHRD_ETHER 1
-#endif
-
-#ifndef ETH_P_IP
-#define ETH_P_IP 0x0800
-#endif
-
-#ifndef ETH_P_ARP
-#define ETH_P_ARP 0x0806
-#endif
-
-#ifndef ETH_P_IPV6
-#define ETH_P_IPV6 0x86DD
-#endif
-
-#ifndef HAVE_STRUCT_ETHER_HEADER
-struct ether_header {
-       uint8_t ether_dhost[ETH_ALEN];
-       uint8_t ether_shost[ETH_ALEN];
-       uint16_t ether_type;
-} __attribute__ ((__packed__));
-#endif
-
-#ifndef HAVE_STRUCT_ARPHDR
-struct arphdr {
-       uint16_t ar_hrd;
-       uint16_t ar_pro;
-       uint8_t ar_hln;
-       uint8_t ar_pln; 
-       uint16_t ar_op; 
-} __attribute__ ((__packed__));
-
-#define ARPOP_REQUEST 1 
-#define ARPOP_REPLY 2 
-#define ARPOP_RREQUEST 3 
-#define ARPOP_RREPLY 4 
-#define ARPOP_InREQUEST 8 
-#define ARPOP_InREPLY 9 
-#define ARPOP_NAK 10 
-#endif
-
-#ifndef HAVE_STRUCT_ETHER_ARP
-struct  ether_arp {
-       struct  arphdr ea_hdr;
-       uint8_t arp_sha[ETH_ALEN];
-       uint8_t arp_spa[4];
-       uint8_t arp_tha[ETH_ALEN];
-       uint8_t arp_tpa[4];
-} __attribute__ ((__packed__));
-#define arp_hrd ea_hdr.ar_hrd
-#define arp_pro ea_hdr.ar_pro
-#define arp_hln ea_hdr.ar_hln
-#define arp_pln ea_hdr.ar_pln
-#define arp_op ea_hdr.ar_op
-#endif
-
-#endif /* __TINC_ETHERNET_H__ */
diff --git a/lib/fake-gai-errnos.h b/lib/fake-gai-errnos.h
deleted file mode 100644 (file)
index 4ffabb6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * fake library for ssh
- *
- * This file is included in getaddrinfo.c and getnameinfo.c.
- * See getaddrinfo.c and getnameinfo.c.
- */
-
-/* for old netdb.h */
-#ifndef EAI_NODATA
-#define EAI_NODATA     1
-#endif
-
-#ifndef EAI_MEMORY
-#define EAI_MEMORY     2
-#endif
-
-#ifndef EAI_FAMILY
-#define EAI_FAMILY     3
-#endif
diff --git a/lib/fake-getaddrinfo.c b/lib/fake-getaddrinfo.c
deleted file mode 100644 (file)
index 10672b7..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * fake library for ssh
- *
- * This file includes getaddrinfo(), freeaddrinfo() and gai_strerror().
- * These funtions are defined in rfc2133.
- *
- * But these functions are not implemented correctly. The minimum subset
- * is implemented for ssh use only. For exapmle, this routine assumes
- * that ai_family is AF_INET. Don't use it for another purpose.
- */
-
-#include "system.h"
-
-#include "ipv4.h"
-#include "ipv6.h"
-#include "fake-getaddrinfo.h"
-#include "xalloc.h"
-
-#if !HAVE_DECL_GAI_STRERROR
-char *gai_strerror(int ecode)
-{
-       switch (ecode) {
-               case EAI_NODATA:
-                       return "No address associated with hostname";
-               case EAI_MEMORY:
-                       return "Memory allocation failure";
-               case EAI_FAMILY:
-                       return "Address family not supported";
-               default:
-                       return "Unknown error";
-       }
-}    
-#endif /* !HAVE_GAI_STRERROR */
-
-#if !HAVE_DECL_FREEADDRINFO
-void freeaddrinfo(struct addrinfo *ai)
-{
-       struct addrinfo *next;
-
-       while(ai) {
-               next = ai->ai_next;
-               free(ai);
-               ai = next;
-       }
-}
-#endif /* !HAVE_FREEADDRINFO */
-
-#if !HAVE_DECL_GETADDRINFO
-static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr)
-{
-       struct addrinfo *ai;
-
-       ai = xmalloc_and_zero(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
-       
-       ai->ai_addr = (struct sockaddr *)(ai + 1);
-       ai->ai_addrlen = sizeof(struct sockaddr_in);
-       ai->ai_addr->sa_family = ai->ai_family = AF_INET;
-
-       ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
-       ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
-       
-       return ai;
-}
-
-int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res)
-{
-       struct addrinfo *prev = NULL;
-       struct hostent *hp;
-       struct in_addr in = {0};
-       int i;
-       uint16_t port = 0;
-
-       if(hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC)
-               return EAI_FAMILY;
-
-       if (servname)
-               port = htons(atoi(servname));
-
-       if (hints && hints->ai_flags & AI_PASSIVE) {
-               *res = malloc_ai(port, htonl(0x00000000));
-               return 0;
-       }
-               
-       if (!hostname) {
-               *res = malloc_ai(port, htonl(0x7f000001));
-               return 0;
-       }
-       
-       hp = gethostbyname(hostname);
-
-       if(!hp || !hp->h_addr_list || !hp->h_addr_list[0])
-               return EAI_NODATA;
-
-       for (i = 0; hp->h_addr_list[i]; i++) {
-               *res = malloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr);
-
-               if(prev)
-                       prev->ai_next = *res;
-
-               prev = *res;
-       }
-
-       return 0;
-}
-#endif /* !HAVE_GETADDRINFO */
diff --git a/lib/fake-getaddrinfo.h b/lib/fake-getaddrinfo.h
deleted file mode 100644 (file)
index 5af7491..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef _FAKE_GETADDRINFO_H
-#define _FAKE_GETADDRINFO_H
-
-#include "fake-gai-errnos.h"
-
-#ifndef AI_PASSIVE
-# define AI_PASSIVE        1
-# define AI_CANONNAME      2
-#endif
-
-#ifndef NI_NUMERICHOST
-# define NI_NUMERICHOST    2
-# define NI_NAMEREQD       4
-# define NI_NUMERICSERV    8
-#endif
-
-#ifndef AI_NUMERICHOST
-#define AI_NUMERICHOST 4
-#endif
-
-#ifndef HAVE_STRUCT_ADDRINFO
-struct addrinfo {
-       int     ai_flags;       /* AI_PASSIVE, AI_CANONNAME */
-       int     ai_family;      /* PF_xxx */
-       int     ai_socktype;    /* SOCK_xxx */
-       int     ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
-       size_t  ai_addrlen;     /* length of ai_addr */
-       char    *ai_canonname;  /* canonical name for hostname */
-       struct sockaddr *ai_addr;       /* binary address */
-       struct addrinfo *ai_next;       /* next structure in linked list */
-};
-#endif /* !HAVE_STRUCT_ADDRINFO */
-
-#if !HAVE_DECL_GETADDRINFO
-int getaddrinfo(const char *hostname, const char *servname, 
-                const struct addrinfo *hints, struct addrinfo **res);
-#endif /* !HAVE_GETADDRINFO */
-
-#if !HAVE_DECL_GAI_STRERROR
-char *gai_strerror(int ecode);
-#endif /* !HAVE_GAI_STRERROR */
-
-#if !HAVE_DECL_FREEADDRINFO
-void freeaddrinfo(struct addrinfo *ai);
-#endif /* !HAVE_FREEADDRINFO */
-
-#endif /* _FAKE_GETADDRINFO_H */
diff --git a/lib/fake-getnameinfo.c b/lib/fake-getnameinfo.c
deleted file mode 100644 (file)
index 8047173..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * fake library for ssh
- *
- * This file includes getnameinfo().
- * These funtions are defined in rfc2133.
- *
- * But these functions are not implemented correctly. The minimum subset
- * is implemented for ssh use only. For exapmle, this routine assumes
- * that ai_family is AF_INET. Don't use it for another purpose.
- */
-
-#include "system.h"
-
-#include "fake-getnameinfo.h"
-#include "fake-getaddrinfo.h"
-
-#if !HAVE_DECL_GETNAMEINFO
-
-int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
-{
-       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
-       struct hostent *hp;
-       int len;
-
-       if(sa->sa_family != AF_INET)
-               return EAI_FAMILY;
-
-       if(serv && servlen) {
-               len = snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
-               if(len < 0 || len >= servlen)
-                       return EAI_MEMORY;
-       }
-
-       if(!host || !hostlen)
-               return 0;
-
-       if(flags & NI_NUMERICHOST) {
-               len = snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
-               if(len < 0 || len >= hostlen)
-                       return EAI_MEMORY;
-               return 0;
-       }
-
-       hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
-       
-       if(!hp || !hp->h_name || !hp->h_name[0])
-               return EAI_NODATA;
-       
-       len = snprintf(host, hostlen, "%s", hp->h_name);
-       if(len < 0 || len >= hostlen)
-               return EAI_MEMORY;
-
-       return 0;
-}
-#endif /* !HAVE_GETNAMEINFO */
diff --git a/lib/fake-getnameinfo.h b/lib/fake-getnameinfo.h
deleted file mode 100644 (file)
index 4389a8f..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _FAKE_GETNAMEINFO_H
-#define _FAKE_GETNAMEINFO_H
-
-#if !HAVE_DECL_GETNAMEINFO
-int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, 
-                size_t hostlen, char *serv, size_t servlen, int flags);
-#endif /* !HAVE_GETNAMEINFO */
-
-#ifndef NI_MAXSERV
-# define NI_MAXSERV 32
-#endif /* !NI_MAXSERV */
-#ifndef NI_MAXHOST
-# define NI_MAXHOST 1025
-#endif /* !NI_MAXHOST */
-
-#endif /* _FAKE_GETNAMEINFO_H */
diff --git a/lib/getopt.c b/lib/getopt.c
deleted file mode 100644 (file)
index a6782ed..0000000
+++ /dev/null
@@ -1,1048 +0,0 @@
-/* Getopt for GNU.
-   NOTE: getopt is now part of the C library, so if you don't know what
-   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
-   before changing it!
-
-   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
-       Free Software Foundation, Inc.
-
-NOTE: The canonical source of this file is maintained with the GNU C Library.
-Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-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.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-\f
-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
-   Ditto for AIX 3.2 and <stdlib.h>.  */
-#ifndef _NO_PROTO
-#define _NO_PROTO
-#endif
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#if !defined (__STDC__) || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
-   contain conflicting prototypes for getopt.  */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library.  */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#if defined (WIN32) && !defined (__CYGWIN32__)
-/* It's not Unix, really.  See?  Capital letters.  */
-#include <windows.h>
-#define getpid() GetCurrentProcessId()
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
-   but it behaves differently for the user, since it allows the user
-   to intersperse the options with the other arguments.
-
-   As `getopt' works, it permutes the elements of ARGV so that,
-   when it is done, all the options precede everything else.  Thus
-   all application programs are extended to handle flexible argument order.
-
-   Setting the environment variable POSIXLY_CORRECT disables permutation.
-   Then the behavior is completely standard.
-
-   GNU application programs can use a third alternative mode in which
-   they can distinguish the relative order of options and other arguments.  */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
-   the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
-   each non-option ARGV-element is returned here.  */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
-   This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
-
-   On entry to `getopt', zero means this is the first call; initialize.
-
-   When `getopt' returns -1, this is the index of the first of the
-   non-option elements that the caller should itself scan.
-
-   Otherwise, `optind' communicates from one call to the next
-   how much of ARGV has been scanned so far.  */
-
-/* 1003.2 says this must be 1 before any call.  */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
-   causes problems with re-calling getopt as programs generally don't
-   know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
-   in which the last option character we returned was found.
-   This allows us to pick up the scan where we left off.
-
-   If this is zero, or a null string, it means resume the scan
-   by advancing to the next ARGV-element.  */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
-   for unrecognized options.  */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
-   This must be initialized on some systems to avoid linking in the
-   system's own getopt implementation.  */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
-   If the caller did not specify anything,
-   the default is REQUIRE_ORDER if the environment variable
-   POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
-   REQUIRE_ORDER means don't recognize them as options;
-   stop option processing when the first non-option is seen.
-   This is what Unix does.
-   This mode of operation is selected by either setting the environment
-   variable POSIXLY_CORRECT, or using `+' as the first character
-   of the list of option characters.
-
-   PERMUTE is the default.  We permute the contents of ARGV as we scan,
-   so that eventually all the non-options are at the end.  This allows options
-   to be given in any order, even with programs that were not written to
-   expect this.
-
-   RETURN_IN_ORDER is an option available to programs that were written
-   to expect options and other ARGV-elements in any order and that care about
-   the ordering of the two.  We describe each non-option ARGV-element
-   as if it were the argument of an option with character code 1.
-   Using `-' as the first character of the list of option characters
-   selects this mode of operation.
-
-   The special argument `--' forces an end of option-scanning regardless
-   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
-   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
-
-static enum
-{
-  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
-} ordering;
-
-/* Value of POSIXLY_CORRECT environment variable.  */
-static char *posixly_correct;
-\f
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
-   because there are many ways it can cause trouble.
-   On some systems, it contains special magic macros that don't work
-   in GCC.  */
-#include <string.h>
-#define        my_index        strchr
-#else
-
-/* Avoid depending on library functions or files
-   whose names are inconsistent.  */
-
-char *getenv ();
-
-static char *
-my_index (str, chr)
-     const char *str;
-     int chr;
-{
-  while (*str)
-    {
-      if (*str == chr)
-       return (char *) str;
-      str++;
-    }
-  return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
-   If not using GCC, it is ok not to declare it.  */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
-   That was relevant to code that was here before.  */
-#if !defined (__STDC__) || !__STDC__
-/* gcc with -traditional declares the built-in strlen to return int,
-   and has done so at least since version 2.4.5. -- rms.  */
-extern int strlen (const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-\f
-/* Handle permutation of arguments.  */
-
-/* Describe the part of ARGV that contains non-options that have
-   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
-   `last_nonopt' is the index after the last of them.  */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
-   indicating ARGV elements that should not be considered arguments.  */
-
-/* Defined in getopt_init.c  */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-extern pid_t __libc_pid;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
-   is valid for the getopt call we must make sure that the ARGV passed
-   to getopt is that one passed to the process.  */
-static void
-__attribute__ ((__unused__))
-store_args_and_env (int argc, char *const *argv)
-{
-  /* XXX This is no good solution.  We should rather copy the args so
-     that we can compare them later.  But we must not use malloc(3).  */
-  original_argc = argc;
-  original_argv = argv;
-}
-text_set_element (__libc_subinit, store_args_and_env);
-
-# define SWAP_FLAGS(ch1, ch2) \
-  if (nonoption_flags_len > 0)                                               \
-    {                                                                        \
-      char __tmp = __getopt_nonoption_flags[ch1];                            \
-      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
-      __getopt_nonoption_flags[ch2] = __tmp;                                 \
-    }
-#else  /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
-   One subsequence is elements [first_nonopt,last_nonopt)
-   which contains all the non-options that have been skipped so far.
-   The other is elements [last_nonopt,optind), which contains all
-   the options processed since those non-options were skipped.
-
-   `first_nonopt' and `last_nonopt' are relocated so that they describe
-   the new indices of the non-options in ARGV after they are moved.  */
-
-#if defined (__STDC__) && __STDC__
-static void exchange (char **);
-#endif
-
-static void
-exchange (argv)
-     char **argv;
-{
-  int bottom = first_nonopt;
-  int middle = last_nonopt;
-  int top = optind;
-  char *tem;
-
-  /* Exchange the shorter segment with the far end of the longer segment.
-     That puts the shorter segment into the right place.
-     It leaves the longer segment in the right place overall,
-     but it consists of two parts that need to be swapped next.  */
-
-#ifdef _LIBC
-  /* First make sure the handling of the `__getopt_nonoption_flags'
-     string can work normally.  Our top argument must be in the range
-     of the string.  */
-  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
-    {
-      /* We must extend the array.  The user plays games with us and
-        presents new arguments.  */
-      char *new_str = malloc (top + 1);
-      if (new_str == NULL)
-       nonoption_flags_len = nonoption_flags_max_len = 0;
-      else
-       {
-         memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
-         memset (&new_str[nonoption_flags_max_len], '\0',
-                 top + 1 - nonoption_flags_max_len);
-         nonoption_flags_max_len = top + 1;
-         __getopt_nonoption_flags = new_str;
-       }
-    }
-#endif
-
-  while (top > middle && middle > bottom)
-    {
-      if (top - middle > middle - bottom)
-       {
-         /* Bottom segment is the short one.  */
-         int len = middle - bottom;
-         register int i;
-
-         /* Swap it with the top part of the top segment.  */
-         for (i = 0; i < len; i++)
-           {
-             tem = argv[bottom + i];
-             argv[bottom + i] = argv[top - (middle - bottom) + i];
-             argv[top - (middle - bottom) + i] = tem;
-             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
-           }
-         /* Exclude the moved bottom segment from further swapping.  */
-         top -= len;
-       }
-      else
-       {
-         /* Top segment is the short one.  */
-         int len = top - middle;
-         register int i;
-
-         /* Swap it with the bottom part of the bottom segment.  */
-         for (i = 0; i < len; i++)
-           {
-             tem = argv[bottom + i];
-             argv[bottom + i] = argv[middle + i];
-             argv[middle + i] = tem;
-             SWAP_FLAGS (bottom + i, middle + i);
-           }
-         /* Exclude the moved top segment from further swapping.  */
-         bottom += len;
-       }
-    }
-
-  /* Update records for the slots the non-options now occupy.  */
-
-  first_nonopt += (optind - last_nonopt);
-  last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made.  */
-
-#if defined (__STDC__) && __STDC__
-static const char *_getopt_initialize (int, char *const *, const char *);
-#endif
-static const char *
-_getopt_initialize (argc, argv, optstring)
-     int argc;
-     char *const *argv;
-     const char *optstring;
-{
-  /* Start processing options with ARGV-element 1 (since ARGV-element 0
-     is the program name); the sequence of previously skipped
-     non-option ARGV-elements is empty.  */
-
-  first_nonopt = last_nonopt = optind;
-
-  nextchar = NULL;
-
-  posixly_correct = getenv ("POSIXLY_CORRECT");
-
-  /* Determine how to handle the ordering of options and nonoptions.  */
-
-  if (optstring[0] == '-')
-    {
-      ordering = RETURN_IN_ORDER;
-      ++optstring;
-    }
-  else if (optstring[0] == '+')
-    {
-      ordering = REQUIRE_ORDER;
-      ++optstring;
-    }
-  else if (posixly_correct != NULL)
-    ordering = REQUIRE_ORDER;
-  else
-    ordering = PERMUTE;
-
-#ifdef _LIBC
-  if (posixly_correct == NULL
-      && argc == original_argc && argv == original_argv)
-    {
-      if (nonoption_flags_max_len == 0)
-       {
-         if (__getopt_nonoption_flags == NULL
-             || __getopt_nonoption_flags[0] == '\0')
-           nonoption_flags_max_len = -1;
-         else
-           {
-             const char *orig_str = __getopt_nonoption_flags;
-             int len = nonoption_flags_max_len = strlen (orig_str);
-             if (nonoption_flags_max_len < argc)
-               nonoption_flags_max_len = argc;
-             __getopt_nonoption_flags =
-               (char *) malloc (nonoption_flags_max_len);
-             if (__getopt_nonoption_flags == NULL)
-               nonoption_flags_max_len = -1;
-             else
-               {
-                 memcpy (__getopt_nonoption_flags, orig_str, len);
-                 memset (&__getopt_nonoption_flags[len], '\0',
-                         nonoption_flags_max_len - len);
-               }
-           }
-       }
-      nonoption_flags_len = nonoption_flags_max_len;
-    }
-  else
-    nonoption_flags_len = 0;
-#endif
-
-  return optstring;
-}
-\f
-/* Scan elements of ARGV (whose length is ARGC) for option characters
-   given in OPTSTRING.
-
-   If an element of ARGV starts with '-', and is not exactly "-" or "--",
-   then it is an option element.  The characters of this element
-   (aside from the initial '-') are option characters.  If `getopt'
-   is called repeatedly, it returns successively each of the option characters
-   from each of the option elements.
-
-   If `getopt' finds another option character, it returns that character,
-   updating `optind' and `nextchar' so that the next call to `getopt' can
-   resume the scan with the following option character or ARGV-element.
-
-   If there are no more option characters, `getopt' returns -1.
-   Then `optind' is the index in ARGV of the first ARGV-element
-   that is not an option.  (The ARGV-elements have been permuted
-   so that those that are not options now come last.)
-
-   OPTSTRING is a string containing the legitimate option characters.
-   If an option character is seen that is not listed in OPTSTRING,
-   return '?' after printing an error message.  If you set `opterr' to
-   zero, the error message is suppressed but we still return '?'.
-
-   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
-   so the following text in the same ARGV-element, or the text of the following
-   ARGV-element, is returned in `optarg'.  Two colons mean an option that
-   wants an optional arg; if there is text in the current ARGV-element,
-   it is returned in `optarg', otherwise `optarg' is set to zero.
-
-   If OPTSTRING starts with `-' or `+', it requests different methods of
-   handling the non-option ARGV-elements.
-   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
-   Long-named options begin with `--' instead of `-'.
-   Their names may be abbreviated as long as the abbreviation is unique
-   or is an exact match for some defined option.  If they have an
-   argument, it follows the option name in the same ARGV-element, separated
-   from the option name by a `=', or else the in next ARGV-element.
-   When `getopt' finds a long-named option, it returns 0 if that option's
-   `flag' field is nonzero, the value of the option's `val' field
-   if the `flag' field is zero.
-
-   The elements of ARGV aren't really const, because we permute them.
-   But we pretend they're const in the prototype to be compatible
-   with other systems.
-
-   LONGOPTS is a vector of `struct option' terminated by an
-   element containing a name which is zero.
-
-   LONGIND returns the index in LONGOPT of the long-named option found.
-   It is only valid when a long-named option has been found by the most
-   recent call.
-
-   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
-   long-named options.  */
-
-int
-_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
-     int argc;
-     char *const *argv;
-     const char *optstring;
-     const struct option *longopts;
-     int *longind;
-     int long_only;
-{
-  optarg = NULL;
-
-  if (optind == 0 || !__getopt_initialized)
-    {
-      if (optind == 0)
-       optind = 1;     /* Don't scan ARGV[0], the program name.  */
-      optstring = _getopt_initialize (argc, argv, optstring);
-      __getopt_initialized = 1;
-    }
-
-  /* Test whether ARGV[optind] points to a non-option argument.
-     Either it does not have option syntax, or there is an environment flag
-     from the shell indicating it is not an option.  The later information
-     is only used when the used in the GNU libc.  */
-#ifdef _LIBC
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
-                    || (optind < nonoption_flags_len                         \
-                        && __getopt_nonoption_flags[optind] == '1'))
-#else
-#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
-  if (nextchar == NULL || *nextchar == '\0')
-    {
-      /* Advance to the next ARGV-element.  */
-
-      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
-        moved back by the user (who may also have changed the arguments).  */
-      if (last_nonopt > optind)
-       last_nonopt = optind;
-      if (first_nonopt > optind)
-       first_nonopt = optind;
-
-      if (ordering == PERMUTE)
-       {
-         /* If we have just processed some options following some non-options,
-            exchange them so that the options come first.  */
-
-         if (first_nonopt != last_nonopt && last_nonopt != optind)
-           exchange ((char **) argv);
-         else if (last_nonopt != optind)
-           first_nonopt = optind;
-
-         /* Skip any additional non-options
-            and extend the range of non-options previously skipped.  */
-
-         while (optind < argc && NONOPTION_P)
-           optind++;
-         last_nonopt = optind;
-       }
-
-      /* The special ARGV-element `--' means premature end of options.
-        Skip it like a null option,
-        then exchange with previous non-options as if it were an option,
-        then skip everything else like a non-option.  */
-
-      if (optind != argc && !strcmp (argv[optind], "--"))
-       {
-         optind++;
-
-         if (first_nonopt != last_nonopt && last_nonopt != optind)
-           exchange ((char **) argv);
-         else if (first_nonopt == last_nonopt)
-           first_nonopt = optind;
-         last_nonopt = argc;
-
-         optind = argc;
-       }
-
-      /* If we have done all the ARGV-elements, stop the scan
-        and back over any non-options that we skipped and permuted.  */
-
-      if (optind == argc)
-       {
-         /* Set the next-arg-index to point at the non-options
-            that we previously skipped, so the caller will digest them.  */
-         if (first_nonopt != last_nonopt)
-           optind = first_nonopt;
-         return -1;
-       }
-
-      /* If we have come to a non-option and did not permute it,
-        either stop the scan or describe it to the caller and pass it by.  */
-
-      if (NONOPTION_P)
-       {
-         if (ordering == REQUIRE_ORDER)
-           return -1;
-         optarg = argv[optind++];
-         return 1;
-       }
-
-      /* We have found another option-ARGV-element.
-        Skip the initial punctuation.  */
-
-      nextchar = (argv[optind] + 1
-                 + (longopts != NULL && argv[optind][1] == '-'));
-    }
-
-  /* Decode the current option-ARGV-element.  */
-
-  /* Check whether the ARGV-element is a long option.
-
-     If long_only and the ARGV-element has the form "-f", where f is
-     a valid short option, don't consider it an abbreviated form of
-     a long option that starts with f.  Otherwise there would be no
-     way to give the -f short option.
-
-     On the other hand, if there's a long option "fubar" and
-     the ARGV-element is "-fu", do consider that an abbreviation of
-     the long option, just like "--fu", and not "-f" with arg "u".
-
-     This distinction seems to be the most useful approach.  */
-
-  if (longopts != NULL
-      && (argv[optind][1] == '-'
-         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
-    {
-      char *nameend;
-      const struct option *p;
-      const struct option *pfound = NULL;
-      int exact = 0;
-      int ambig = 0;
-      int indfound = -1;
-      int option_index;
-
-      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
-       /* Do nothing.  */ ;
-
-      /* Test all long options for either exact match
-        or abbreviated matches.  */
-      for (p = longopts, option_index = 0; p->name; p++, option_index++)
-       if (!strncmp (p->name, nextchar, nameend - nextchar))
-         {
-           if ((unsigned int) (nameend - nextchar)
-               == (unsigned int) strlen (p->name))
-             {
-               /* Exact match found.  */
-               pfound = p;
-               indfound = option_index;
-               exact = 1;
-               break;
-             }
-           else if (pfound == NULL)
-             {
-               /* First nonexact match found.  */
-               pfound = p;
-               indfound = option_index;
-             }
-           else
-             /* Second or later nonexact match found.  */
-             ambig = 1;
-         }
-
-      if (ambig && !exact)
-       {
-         if (opterr)
-           fprintf (stderr, "%s: option `%s' is ambiguous\n",
-                    argv[0], argv[optind]);
-         nextchar += strlen (nextchar);
-         optind++;
-         optopt = 0;
-         return '?';
-       }
-
-      if (pfound != NULL)
-       {
-         option_index = indfound;
-         optind++;
-         if (*nameend)
-           {
-             /* Don't test has_arg with >, because some C compilers don't
-                allow it to be used on enums.  */
-             if (pfound->has_arg)
-               optarg = nameend + 1;
-             else
-               {
-                 if (opterr)
-                   {
-                    if (argv[optind - 1][1] == '-')
-                     /* --option */
-                     fprintf (stderr,
-                      "%s: option `--%s' doesn't allow an argument\n",
-                      argv[0], pfound->name);
-                    else
-                     /* +option or -option */
-                     fprintf (stderr,
-                      "%s: option `%c%s' doesn't allow an argument\n",
-                      argv[0], argv[optind - 1][0], pfound->name);
-                   }
-
-                 nextchar += strlen (nextchar);
-
-                 optopt = pfound->val;
-                 return '?';
-               }
-           }
-         else if (pfound->has_arg == 1)
-           {
-             if (optind < argc)
-               optarg = argv[optind++];
-             else
-               {
-                 if (opterr)
-                   fprintf (stderr,
-                          "%s: option `%s' requires an argument\n",
-                          argv[0], argv[optind - 1]);
-                 nextchar += strlen (nextchar);
-                 optopt = pfound->val;
-                 return optstring[0] == ':' ? ':' : '?';
-               }
-           }
-         nextchar += strlen (nextchar);
-         if (longind != NULL)
-           *longind = option_index;
-         if (pfound->flag)
-           {
-             *(pfound->flag) = pfound->val;
-             return 0;
-           }
-         return pfound->val;
-       }
-
-      /* Can't find it as a long option.  If this is not getopt_long_only,
-        or the option starts with '--' or is not a valid short
-        option, then it's an error.
-        Otherwise interpret it as a short option.  */
-      if (!long_only || argv[optind][1] == '-'
-         || my_index (optstring, *nextchar) == NULL)
-       {
-         if (opterr)
-           {
-             if (argv[optind][1] == '-')
-               /* --option */
-               fprintf (stderr, "%s: unrecognized option `--%s'\n",
-                        argv[0], nextchar);
-             else
-               /* +option or -option */
-               fprintf (stderr, "%s: unrecognized option `%c%s'\n",
-                        argv[0], argv[optind][0], nextchar);
-           }
-         nextchar = (char *) "";
-         optind++;
-         optopt = 0;
-         return '?';
-       }
-    }
-
-  /* Look at and handle the next short option-character.  */
-
-  {
-    char c = *nextchar++;
-    char *temp = my_index (optstring, c);
-
-    /* Increment `optind' when we start to process its last character.  */
-    if (*nextchar == '\0')
-      ++optind;
-
-    if (temp == NULL || c == ':')
-      {
-       if (opterr)
-         {
-           if (posixly_correct)
-             /* 1003.2 specifies the format of this message.  */
-             fprintf (stderr, "%s: illegal option -- %c\n",
-                      argv[0], c);
-           else
-             fprintf (stderr, "%s: invalid option -- %c\n",
-                      argv[0], c);
-         }
-       optopt = c;
-       return '?';
-      }
-    /* Convenience. Treat POSIX -W foo same as long option --foo */
-    if (temp[0] == 'W' && temp[1] == ';')
-      {
-       char *nameend;
-       const struct option *p;
-       const struct option *pfound = NULL;
-       int exact = 0;
-       int ambig = 0;
-       int indfound = 0;
-       int option_index;
-
-       /* This is an option that requires an argument.  */
-       if (*nextchar != '\0')
-         {
-           optarg = nextchar;
-           /* If we end this ARGV-element by taking the rest as an arg,
-              we must advance to the next element now.  */
-           optind++;
-         }
-       else if (optind == argc)
-         {
-           if (opterr)
-             {
-               /* 1003.2 specifies the format of this message.  */
-               fprintf (stderr, "%s: option requires an argument -- %c\n",
-                        argv[0], c);
-             }
-           optopt = c;
-           if (optstring[0] == ':')
-             c = ':';
-           else
-             c = '?';
-           return c;
-         }
-       else
-         /* We already incremented `optind' once;
-            increment it again when taking next ARGV-elt as argument.  */
-         optarg = argv[optind++];
-
-       /* optarg is now the argument, see if it's in the
-          table of longopts.  */
-
-       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
-         /* Do nothing.  */ ;
-
-       /* Test all long options for either exact match
-          or abbreviated matches.  */
-       for (p = longopts, option_index = 0; p->name; p++, option_index++)
-         if (!strncmp (p->name, nextchar, nameend - nextchar))
-           {
-             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
-               {
-                 /* Exact match found.  */
-                 pfound = p;
-                 indfound = option_index;
-                 exact = 1;
-                 break;
-               }
-             else if (pfound == NULL)
-               {
-                 /* First nonexact match found.  */
-                 pfound = p;
-                 indfound = option_index;
-               }
-             else
-               /* Second or later nonexact match found.  */
-               ambig = 1;
-           }
-       if (ambig && !exact)
-         {
-           if (opterr)
-             fprintf (stderr, "%s: option `-W %s' is ambiguous\n",
-                      argv[0], argv[optind]);
-           nextchar += strlen (nextchar);
-           optind++;
-           return '?';
-         }
-       if (pfound != NULL)
-         {
-           option_index = indfound;
-           if (*nameend)
-             {
-               /* Don't test has_arg with >, because some C compilers don't
-                  allow it to be used on enums.  */
-               if (pfound->has_arg)
-                 optarg = nameend + 1;
-               else
-                 {
-                   if (opterr)
-                     fprintf (stderr,
-                              "%s: option `-W %s' doesn't allow an argument\n",
-                              argv[0], pfound->name);
-
-                   nextchar += strlen (nextchar);
-                   return '?';
-                 }
-             }
-           else if (pfound->has_arg == 1)
-             {
-               if (optind < argc)
-                 optarg = argv[optind++];
-               else
-                 {
-                   if (opterr)
-                     fprintf (stderr,
-                              "%s: option `%s' requires an argument\n",
-                              argv[0], argv[optind - 1]);
-                   nextchar += strlen (nextchar);
-                   return optstring[0] == ':' ? ':' : '?';
-                 }
-             }
-           nextchar += strlen (nextchar);
-           if (longind != NULL)
-             *longind = option_index;
-           if (pfound->flag)
-             {
-               *(pfound->flag) = pfound->val;
-               return 0;
-             }
-           return pfound->val;
-         }
-         nextchar = NULL;
-         return 'W';   /* Let the application handle it.   */
-      }
-    if (temp[1] == ':')
-      {
-       if (temp[2] == ':')
-         {
-           /* This is an option that accepts an argument optionally.  */
-           if (*nextchar != '\0')
-             {
-               optarg = nextchar;
-               optind++;
-             }
-           else
-             optarg = NULL;
-           nextchar = NULL;
-         }
-       else
-         {
-           /* This is an option that requires an argument.  */
-           if (*nextchar != '\0')
-             {
-               optarg = nextchar;
-               /* If we end this ARGV-element by taking the rest as an arg,
-                  we must advance to the next element now.  */
-               optind++;
-             }
-           else if (optind == argc)
-             {
-               if (opterr)
-                 {
-                   /* 1003.2 specifies the format of this message.  */
-                   fprintf (stderr,
-                          "%s: option requires an argument -- %c\n",
-                          argv[0], c);
-                 }
-               optopt = c;
-               if (optstring[0] == ':')
-                 c = ':';
-               else
-                 c = '?';
-             }
-           else
-             /* We already incremented `optind' once;
-                increment it again when taking next ARGV-elt as argument.  */
-             optarg = argv[optind++];
-           nextchar = NULL;
-         }
-      }
-    return c;
-  }
-}
-
-int
-getopt (argc, argv, optstring)
-     int argc;
-     char *const *argv;
-     const char *optstring;
-{
-  return _getopt_internal (argc, argv, optstring,
-                          (const struct option *) 0,
-                          (int *) 0,
-                          0);
-}
-
-#endif /* Not ELIDE_CODE.  */
-\f
-#ifdef TEST
-
-/* Compile with -DTEST to make an executable for use in testing
-   the above definition of `getopt'.  */
-
-int
-main (argc, argv)
-     int argc;
-     char **argv;
-{
-  int c;
-  int digit_optind = 0;
-
-  while (1)
-    {
-      int this_option_optind = optind ? optind : 1;
-
-      c = getopt (argc, argv, "abc:d:0123456789");
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-       case '8':
-       case '9':
-         if (digit_optind != 0 && digit_optind != this_option_optind)
-           printf ("digits occur in two different argv-elements.\n");
-         digit_optind = this_option_optind;
-         printf ("option %c\n", c);
-         break;
-
-       case 'a':
-         printf ("option a\n");
-         break;
-
-       case 'b':
-         printf ("option b\n");
-         break;
-
-       case 'c':
-         printf ("option c with value `%s'\n", optarg);
-         break;
-
-       case '?':
-         break;
-
-       default:
-         printf ("?? getopt returned character code 0%o ??\n", c);
-       }
-    }
-
-  if (optind < argc)
-    {
-      printf ("non-option ARGV-elements: ");
-      while (optind < argc)
-       printf ("%s ", argv[optind++]);
-      printf ("\n");
-    }
-
-  exit (0);
-}
-
-#endif /* TEST */
diff --git a/lib/getopt.h b/lib/getopt.h
deleted file mode 100644 (file)
index ddf6fdd..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Declarations for getopt.
-   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
-
-NOTE: The canonical source of this file is maintained with the GNU C Library.
-Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-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.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
-   the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
-   each non-option ARGV-element is returned here.  */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
-   This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
-
-   On entry to `getopt', zero means this is the first call; initialize.
-
-   When `getopt' returns -1, this is the index of the first of the
-   non-option elements that the caller should itself scan.
-
-   Otherwise, `optind' communicates from one call to the next
-   how much of ARGV has been scanned so far.  */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
-   for unrecognized options.  */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized.  */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
-   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
-   of `struct option' terminated by an element containing a name which is
-   zero.
-
-   The field `has_arg' is:
-   no_argument         (or 0) if the option does not take an argument,
-   required_argument   (or 1) if the option requires an argument,
-   optional_argument   (or 2) if the option takes an optional argument.
-
-   If the field `flag' is not NULL, it points to a variable that is set
-   to the value given in the field `val' when the option is found, but
-   left unchanged if the option is not found.
-
-   To have a long-named option do something other than set an `int' to
-   a compiled-in constant, such as set a value from `optarg', set the
-   option's `flag' field to zero and its `val' field to a nonzero
-   value (the equivalent single-letter option character, if there is
-   one).  For long options that have a zero `flag' field, `getopt'
-   returns the contents of the `val' field.  */
-
-struct option
-{
-#if defined (__STDC__) && __STDC__
-  const char *name;
-#else
-  char *name;
-#endif
-  /* has_arg can't be an enum because some compilers complain about
-     type mismatches in all the code that assumes it is an int.  */
-  int has_arg;
-  int *flag;
-  int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'.  */
-
-#define        no_argument             0
-#define required_argument      1
-#define optional_argument      2
-
-#if defined (__STDC__) && __STDC__
-#ifdef __GNU_LIBRARY__
-/* Many other libraries have conflicting prototypes for getopt, with
-   differences in the consts, in stdlib.h.  To avoid compilation
-   errors, only prototype getopt for the GNU C library.  */
-extern int getopt (int argc, char *const *argv, const char *shortopts);
-#else /* not __GNU_LIBRARY__ */
-extern int getopt ();
-#endif /* __GNU_LIBRARY__ */
-extern int getopt_long (int argc, char *const *argv, const char *shortopts,
-                       const struct option *longopts, int *longind);
-extern int getopt_long_only (int argc, char *const *argv,
-                            const char *shortopts,
-                            const struct option *longopts, int *longind);
-
-/* Internal only.  Users should not call this directly.  */
-extern int _getopt_internal (int argc, char *const *argv,
-                            const char *shortopts,
-                            const struct option *longopts, int *longind,
-                            int long_only);
-#else /* not __STDC__ */
-extern int getopt ();
-extern int getopt_long ();
-extern int getopt_long_only ();
-
-extern int _getopt_internal ();
-#endif /* __STDC__ */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _GETOPT_H */
diff --git a/lib/getopt1.c b/lib/getopt1.c
deleted file mode 100644 (file)
index 86545f2..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/* getopt_long and getopt_long_only entry points for GNU getopt.
-   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
-
-NOTE: The canonical source of this file is maintained with the GNU C Library.
-Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-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.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-\f
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "getopt.h"
-
-#if !defined (__STDC__) || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef        NULL
-#define NULL 0
-#endif
-
-int
-getopt_long (argc, argv, options, long_options, opt_index)
-     int argc;
-     char *const *argv;
-     const char *options;
-     const struct option *long_options;
-     int *opt_index;
-{
-  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
-   If an option that starts with '-' (not '--') doesn't match a long option,
-   but does match a short option, it is parsed as a short option
-   instead.  */
-
-int
-getopt_long_only (argc, argv, options, long_options, opt_index)
-     int argc;
-     char *const *argv;
-     const char *options;
-     const struct option *long_options;
-     int *opt_index;
-{
-  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
-}
-
-
-#endif /* Not ELIDE_CODE.  */
-\f
-#ifdef TEST
-
-#include <stdio.h>
-
-int
-main (argc, argv)
-     int argc;
-     char **argv;
-{
-  int c;
-  int digit_optind = 0;
-
-  while (1)
-    {
-      int this_option_optind = optind ? optind : 1;
-      int option_index = 0;
-      static struct option long_options[] =
-      {
-       {"add", 1, 0, 0},
-       {"append", 0, 0, 0},
-       {"delete", 1, 0, 0},
-       {"verbose", 0, 0, 0},
-       {"create", 0, 0, 0},
-       {"file", 1, 0, 0},
-       {0, 0, 0, 0}
-      };
-
-      c = getopt_long (argc, argv, "abc:d:0123456789",
-                      long_options, &option_index);
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-       case 0:
-         printf ("option %s", long_options[option_index].name);
-         if (optarg)
-           printf (" with arg %s", optarg);
-         printf ("\n");
-         break;
-
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-       case '8':
-       case '9':
-         if (digit_optind != 0 && digit_optind != this_option_optind)
-           printf ("digits occur in two different argv-elements.\n");
-         digit_optind = this_option_optind;
-         printf ("option %c\n", c);
-         break;
-
-       case 'a':
-         printf ("option a\n");
-         break;
-
-       case 'b':
-         printf ("option b\n");
-         break;
-
-       case 'c':
-         printf ("option c with value `%s'\n", optarg);
-         break;
-
-       case 'd':
-         printf ("option d with value `%s'\n", optarg);
-         break;
-
-       case '?':
-         break;
-
-       default:
-         printf ("?? getopt returned character code 0%o ??\n", c);
-       }
-    }
-
-  if (optind < argc)
-    {
-      printf ("non-option ARGV-elements: ");
-      while (optind < argc)
-       printf ("%s ", argv[optind++]);
-      printf ("\n");
-    }
-
-  exit (0);
-}
-
-#endif /* TEST */
diff --git a/lib/ipv4.h b/lib/ipv4.h
deleted file mode 100644 (file)
index 57d236d..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
-    ipv4.h -- missing IPv4 related definitions
-    Copyright (C) 2005 Ivo Timmermans
-                  2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_IPV4_H__
-#define __TINC_IPV4_H__
-
-#ifndef AF_INET
-#define AF_INET 2
-#endif
-
-#ifndef IPPROTO_ICMP
-#define IPPROTO_ICMP 1
-#endif
-
-#ifndef ICMP_DEST_UNREACH
-#define ICMP_DEST_UNREACH 3
-#endif
-
-#ifndef ICMP_FRAG_NEEDED
-#define ICMP_FRAG_NEEDED 4
-#endif
-
-#ifndef ICMP_NET_UNKNOWN
-#define ICMP_NET_UNKNOWN 6
-#endif
-
-#ifndef ICMP_TIME_EXCEEDED
-#define ICMP_TIME_EXCEEDED 11
-#endif
-
-#ifndef ICMP_EXC_TTL
-#define ICMP_EXC_TTL 0
-#endif
-
-#ifndef ICMP_NET_UNREACH
-#define ICMP_NET_UNREACH 0
-#endif
-
-#ifndef ICMP_NET_ANO
-#define ICMP_NET_ANO 9
-#endif
-
-#ifndef IP_MSS
-#define       IP_MSS          576
-#endif
-
-#ifndef HAVE_STRUCT_IP
-struct ip {
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-       unsigned int ip_hl:4;
-       unsigned int ip_v:4;
-#else
-       unsigned int ip_v:4;
-       unsigned int ip_hl:4;
-#endif
-       uint8_t ip_tos;
-       uint16_t ip_len;
-       uint16_t ip_id; 
-       uint16_t ip_off;
-#define IP_RF 0x8000
-#define IP_DF 0x4000
-#define IP_MF 0x2000
-       uint8_t ip_ttl;
-       uint8_t ip_p;
-       uint16_t ip_sum;
-       struct in_addr ip_src, ip_dst;
-} __attribute__ ((__packed__));
-#endif
-
-#ifndef IP_OFFMASK
-#define IP_OFFMASK 0x1fff
-#endif
-
-#ifndef HAVE_STRUCT_ICMP
-struct icmp {
-       uint8_t icmp_type;
-       uint8_t icmp_code;
-       uint16_t icmp_cksum;
-       union {
-               uint8_t ih_pptr;
-               struct in_addr ih_gwaddr;
-               struct ih_idseq {
-                       uint16_t icd_id;
-                       uint16_t icd_seq;
-               } ih_idseq;
-               uint32_t ih_void;
-
-
-               struct ih_pmtu {
-                       uint16_t ipm_void;
-                       uint16_t ipm_nextmtu;
-               } ih_pmtu;
-
-               struct ih_rtradv {
-                       uint8_t irt_num_addrs;
-                       uint8_t irt_wpa;
-                       uint16_t irt_lifetime;
-               } ih_rtradv;
-       } icmp_hun;
-#define icmp_pptr icmp_hun.ih_pptr
-#define icmp_gwaddr icmp_hun.ih_gwaddr
-#define icmp_id icmp_hun.ih_idseq.icd_id
-#define icmp_seq icmp_hun.ih_idseq.icd_seq
-#define icmp_void icmp_hun.ih_void
-#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
-#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
-#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
-#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
-#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
-       union {
-               struct {
-                       uint32_t its_otime;
-                       uint32_t its_rtime;
-                       uint32_t its_ttime;
-               } id_ts;
-               struct {
-                       struct ip idi_ip;
-               } id_ip;
-               uint32_t id_mask;
-               uint8_t id_data[1];
-       } icmp_dun;
-#define icmp_otime icmp_dun.id_ts.its_otime
-#define icmp_rtime icmp_dun.id_ts.its_rtime
-#define icmp_ttime icmp_dun.id_ts.its_ttime
-#define icmp_ip icmp_dun.id_ip.idi_ip
-#define icmp_radv icmp_dun.id_radv
-#define icmp_mask icmp_dun.id_mask
-#define icmp_data icmp_dun.id_data
-} __attribute__ ((__packed__));
-#endif
-
-#endif /* __TINC_IPV4_H__ */
diff --git a/lib/ipv6.h b/lib/ipv6.h
deleted file mode 100644 (file)
index d98001d..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-    ipv6.h -- missing IPv6 related definitions
-    Copyright (C) 2005 Ivo Timmermans
-                  2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_IPV6_H__
-#define __TINC_IPV6_H__
-
-#ifndef AF_INET6
-#define AF_INET6 10
-#endif
-
-#ifndef IPPROTO_ICMPV6
-#define IPPROTO_ICMPV6 58
-#endif
-
-#ifndef HAVE_STRUCT_IN6_ADDR
-struct in6_addr {
-       union {
-               uint8_t u6_addr8[16];
-               uint16_t u6_addr16[8];
-               uint32_t u6_addr32[4];
-       } in6_u;
-} __attribute__ ((__packed__));
-#define s6_addr in6_u.u6_addr8
-#define s6_addr16 in6_u.u6_addr16
-#define s6_addr32 in6_u.u6_addr32
-#endif
-
-#ifndef HAVE_STRUCT_SOCKADDR_IN6
-struct sockaddr_in6 {
-       uint16_t sin6_family;
-       uint16_t sin6_port;
-       uint32_t sin6_flowinfo;
-       struct in6_addr sin6_addr;
-       uint32_t sin6_scope_id;
-} __attribute__ ((__packed__));
-#endif
-
-#ifndef IN6_IS_ADDR_V4MAPPED
-#define IN6_IS_ADDR_V4MAPPED(a) \
-        ((((__const uint32_t *) (a))[0] == 0) \
-        && (((__const uint32_t *) (a))[1] == 0) \
-        && (((__const uint32_t *) (a))[2] == htonl (0xffff)))
-#endif
-
-#ifndef HAVE_STRUCT_IP6_HDR
-struct ip6_hdr {
-       union {
-               struct ip6_hdrctl {
-                       uint32_t ip6_un1_flow;
-                       uint16_t ip6_un1_plen;
-                       uint8_t ip6_un1_nxt;
-                       uint8_t ip6_un1_hlim;
-               } ip6_un1;
-               uint8_t ip6_un2_vfc;
-       } ip6_ctlun;
-       struct in6_addr ip6_src;
-       struct in6_addr ip6_dst;
-} __attribute__ ((__packed__));
-#define ip6_vfc ip6_ctlun.ip6_un2_vfc
-#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
-#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
-#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
-#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
-#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
-#endif
-
-#ifndef HAVE_STRUCT_ICMP6_HDR
-struct icmp6_hdr {
-       uint8_t icmp6_type;
-       uint8_t icmp6_code;
-       uint16_t icmp6_cksum;
-       union {
-               uint32_t icmp6_un_data32[1];
-               uint16_t icmp6_un_data16[2];
-               uint8_t icmp6_un_data8[4];
-       } icmp6_dataun;
-} __attribute__ ((__packed__));
-#define ICMP6_DST_UNREACH_NOROUTE 0
-#define ICMP6_DST_UNREACH 1
-#define ICMP6_PACKET_TOO_BIG 2
-#define ICMP6_DST_UNREACH_ADMIN 1
-#define ICMP6_DST_UNREACH_ADDR 3
-#define ND_NEIGHBOR_SOLICIT 135
-#define ND_NEIGHBOR_ADVERT 136
-#define icmp6_data32 icmp6_dataun.icmp6_un_data32
-#define icmp6_data16 icmp6_dataun.icmp6_un_data16
-#define icmp6_data8 icmp6_dataun.icmp6_un_data8
-#define icmp6_mtu icmp6_data32[0]
-#endif
-
-#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT
-struct nd_neighbor_solicit {
-       struct icmp6_hdr nd_ns_hdr;
-       struct in6_addr nd_ns_target;
-} __attribute__ ((__packed__));
-#define ND_OPT_SOURCE_LINKADDR 1
-#define ND_OPT_TARGET_LINKADDR 2
-#define nd_ns_type nd_ns_hdr.icmp6_type
-#define nd_ns_code nd_ns_hdr.icmp6_code
-#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
-#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
-#endif
-
-#ifndef HAVE_STRUCT_ND_OPT_HDR
-struct nd_opt_hdr {
-       uint8_t nd_opt_type;
-       uint8_t nd_opt_len;
-} __attribute__ ((__packed__));
-#endif
-
-#endif /* __TINC_IPV6_H__ */
diff --git a/lib/list.c b/lib/list.c
deleted file mode 100644 (file)
index a26c58d..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
-    list.c -- functions to deal with double linked lists
-    Copyright (C) 2000-2005 Ivo Timmermans
-                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "system.h"
-
-#include "list.h"
-#include "xalloc.h"
-
-/* (De)constructors */
-
-list_t *list_alloc(list_action_t delete) {
-       list_t *list;
-
-       list = xmalloc_and_zero(sizeof(list_t));
-       list->delete = delete;
-
-       return list;
-}
-
-void list_free(list_t *list) {
-       free(list);
-}
-
-list_node_t *list_alloc_node(void) {
-       return xmalloc_and_zero(sizeof(list_node_t));
-}
-
-void list_free_node(list_t *list, list_node_t *node) {
-       if(node->data && list->delete)
-               list->delete(node->data);
-
-       free(node);
-}
-
-/* Insertion and deletion */
-
-list_node_t *list_insert_head(list_t *list, void *data) {
-       list_node_t *node;
-
-       node = list_alloc_node();
-
-       node->data = data;
-       node->prev = NULL;
-       node->next = list->head;
-       list->head = node;
-
-       if(node->next)
-               node->next->prev = node;
-       else
-               list->tail = node;
-
-       list->count++;
-
-       return node;
-}
-
-list_node_t *list_insert_tail(list_t *list, void *data) {
-       list_node_t *node;
-
-       node = list_alloc_node();
-
-       node->data = data;
-       node->next = NULL;
-       node->prev = list->tail;
-       list->tail = node;
-
-       if(node->prev)
-               node->prev->next = node;
-       else
-               list->head = node;
-
-       list->count++;
-
-       return node;
-}
-
-void list_unlink_node(list_t *list, list_node_t *node) {
-       if(node->prev)
-               node->prev->next = node->next;
-       else
-               list->head = node->next;
-
-       if(node->next)
-               node->next->prev = node->prev;
-       else
-               list->tail = node->prev;
-
-       list->count--;
-}
-
-void list_delete_node(list_t *list, list_node_t *node) {
-       list_unlink_node(list, node);
-       list_free_node(list, node);
-}
-
-void list_delete_head(list_t *list) {
-       list_delete_node(list, list->head);
-}
-
-void list_delete_tail(list_t *list) {
-       list_delete_node(list, list->tail);
-}
-
-/* Head/tail lookup */
-
-void *list_get_head(list_t *list) {
-       if(list->head)
-               return list->head->data;
-       else
-               return NULL;
-}
-
-void *list_get_tail(list_t *list) {
-       if(list->tail)
-               return list->tail->data;
-       else
-               return NULL;
-}
-
-/* Fast list deletion */
-
-void list_delete_list(list_t *list) {
-       list_node_t *node, *next;
-
-       for(node = list->head; node; node = next) {
-               next = node->next;
-               list_free_node(list, node);
-       }
-
-       list_free(list);
-}
-
-/* Traversing */
-
-void list_foreach_node(list_t *list, list_action_node_t action) {
-       list_node_t *node, *next;
-
-       for(node = list->head; node; node = next) {
-               next = node->next;
-               action(node);
-       }
-}
-
-void list_foreach(list_t *list, list_action_t action) {
-       list_node_t *node, *next;
-
-       for(node = list->head; node; node = next) {
-               next = node->next;
-               if(node->data)
-                       action(node->data);
-       }
-}
diff --git a/lib/list.h b/lib/list.h
deleted file mode 100644 (file)
index 197fbb8..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-    list.h -- header file for list.c
-    Copyright (C) 2000-2005 Ivo Timmermans
-                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_LIST_H__
-#define __TINC_LIST_H__
-
-typedef struct list_node_t {
-       struct list_node_t *prev;
-       struct list_node_t *next;
-
-       /* Payload */
-
-       void *data;
-} list_node_t;
-
-typedef void (*list_action_t)(const void *);
-typedef void (*list_action_node_t)(const list_node_t *);
-
-typedef struct list_t {
-       list_node_t *head;
-       list_node_t *tail;
-       int count;
-
-       /* Callbacks */
-
-       list_action_t delete;
-} list_t;
-
-/* (De)constructors */
-
-extern list_t *list_alloc(list_action_t) __attribute__ ((__malloc__));
-extern void list_free(list_t *);
-extern list_node_t *list_alloc_node(void);
-extern void list_free_node(list_t *, list_node_t *);
-
-/* Insertion and deletion */
-
-extern list_node_t *list_insert_head(list_t *, void *);
-extern list_node_t *list_insert_tail(list_t *, void *);
-
-extern void list_unlink_node(list_t *, list_node_t *);
-extern void list_delete_node(list_t *, list_node_t *);
-
-extern void list_delete_head(list_t *);
-extern void list_delete_tail(list_t *);
-
-/* Head/tail lookup */
-
-extern void *list_get_head(list_t *);
-extern void *list_get_tail(list_t *);
-
-/* Fast list deletion */
-
-extern void list_delete_list(list_t *);
-
-/* Traversing */
-
-extern void list_foreach(list_t *, list_action_t);
-extern void list_foreach_node(list_t *, list_action_node_t);
-
-#endif                                                 /* __TINC_LIST_H__ */
diff --git a/lib/malloc.c b/lib/malloc.c
deleted file mode 100644 (file)
index 2a996d4..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Work around bug on some systems where malloc (0) fails.
-   Copyright (C) 1997 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., Foundation,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
-
-/* written by Jim Meyering */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-#undef malloc
-
-#include <sys/types.h>
-
-char *malloc ();
-
-/* Allocate an N-byte block of memory from the heap.
-   If N is zero, allocate a 1-byte block.  */
-
-char *
-rpl_malloc (n)
-     size_t n;
-{
-  if (n == 0)
-    n = 1;
-  return malloc (n);
-}
diff --git a/lib/memcmp.c b/lib/memcmp.c
deleted file mode 100644 (file)
index 9104994..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-/* Copyright (C) 1991, 1993, 1995, 1997, 1998 Free Software Foundation, Inc.
-   Contributed by Torbjorn Granlund (tege@sics.se).
-
-   NOTE: The canonical source of this file is maintained with the GNU C Library.
-   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-   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.,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-
-#undef __ptr_t
-#if defined __cplusplus || (defined __STDC__ && __STDC__)
-# define __ptr_t       void *
-#else /* Not C++ or ANSI C.  */
-# undef        const
-# define const
-# define __ptr_t       char *
-#endif /* C++ or ANSI C.  */
-
-#ifndef __P
-# if defined __GNUC__ || (defined __STDC__ && __STDC__)
-#  define __P(args) args
-# else
-#  define __P(args) ()
-# endif  /* GCC.  */
-#endif  /* Not __P.  */
-
-#if defined HAVE_STRING_H || defined _LIBC
-# include <string.h>
-#endif
-
-#undef memcmp
-
-#ifdef _LIBC
-
-# include <memcopy.h>
-
-#else  /* Not in the GNU C library.  */
-
-# include <sys/types.h>
-
-/* Type to use for aligned memory operations.
-   This should normally be the biggest type supported by a single load
-   and store.  Must be an unsigned type.  */
-# define op_t  unsigned long int
-# define OPSIZ (sizeof(op_t))
-
-/* Threshold value for when to enter the unrolled loops.  */
-# define OP_T_THRES    16
-
-/* Type to use for unaligned operations.  */
-typedef unsigned char byte;
-
-# ifndef WORDS_BIGENDIAN
-#  define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
-# else
-#  define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))
-# endif
-
-#endif /* In the GNU C library.  */
-
-#ifdef WORDS_BIGENDIAN
-# define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1)
-#else
-# define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b))
-#endif
-
-/* BE VERY CAREFUL IF YOU CHANGE THIS CODE!  */
-
-/* The strategy of this memcmp is:
-
-   1. Compare bytes until one of the block pointers is aligned.
-
-   2. Compare using memcmp_common_alignment or
-      memcmp_not_common_alignment, regarding the alignment of the other
-      block after the initial byte operations.  The maximum number of
-      full words (of type op_t) are compared in this way.
-
-   3. Compare the few remaining bytes.  */
-
-#ifndef WORDS_BIGENDIAN
-/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine.
-   A and B are known to be different.
-   This is needed only on little-endian machines.  */
-
-static int memcmp_bytes __P((op_t, op_t));
-
-# ifdef  __GNUC__
-__inline
-# endif
-static int
-memcmp_bytes (a, b)
-     op_t a, b;
-{
-  intptr_t srcp1 = (intptr_t) &a;
-  intptr_t srcp2 = (intptr_t) &b;
-  op_t a0, b0;
-
-  do
-    {
-      a0 = ((byte *) srcp1)[0];
-      b0 = ((byte *) srcp2)[0];
-      srcp1 += 1;
-      srcp2 += 1;
-    }
-  while (a0 == b0);
-  return a0 - b0;
-}
-#endif
-
-static int memcmp_common_alignment __P((intptr_t, intptr_t, size_t));
-
-/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t'
-   objects (not LEN bytes!).  Both SRCP1 and SRCP2 should be aligned for
-   memory operations on `op_t's.  */
-#ifdef __GNUC__
-__inline
-#endif
-static int
-memcmp_common_alignment (srcp1, srcp2, len)
-     intptr_t srcp1;
-     intptr_t srcp2;
-     size_t len;
-{
-  op_t a0, a1;
-  op_t b0, b1;
-
-  switch (len % 4)
-    {
-    default: /* Avoid warning about uninitialized local variables.  */
-    case 2:
-      a0 = ((op_t *) srcp1)[0];
-      b0 = ((op_t *) srcp2)[0];
-      srcp1 -= 2 * OPSIZ;
-      srcp2 -= 2 * OPSIZ;
-      len += 2;
-      goto do1;
-    case 3:
-      a1 = ((op_t *) srcp1)[0];
-      b1 = ((op_t *) srcp2)[0];
-      srcp1 -= OPSIZ;
-      srcp2 -= OPSIZ;
-      len += 1;
-      goto do2;
-    case 0:
-      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
-       return 0;
-      a0 = ((op_t *) srcp1)[0];
-      b0 = ((op_t *) srcp2)[0];
-      goto do3;
-    case 1:
-      a1 = ((op_t *) srcp1)[0];
-      b1 = ((op_t *) srcp2)[0];
-      srcp1 += OPSIZ;
-      srcp2 += OPSIZ;
-      len -= 1;
-      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
-       goto do0;
-      /* Fall through.  */
-    }
-
-  do
-    {
-      a0 = ((op_t *) srcp1)[0];
-      b0 = ((op_t *) srcp2)[0];
-      if (a1 != b1)
-       return CMP_LT_OR_GT (a1, b1);
-
-    do3:
-      a1 = ((op_t *) srcp1)[1];
-      b1 = ((op_t *) srcp2)[1];
-      if (a0 != b0)
-       return CMP_LT_OR_GT (a0, b0);
-
-    do2:
-      a0 = ((op_t *) srcp1)[2];
-      b0 = ((op_t *) srcp2)[2];
-      if (a1 != b1)
-       return CMP_LT_OR_GT (a1, b1);
-
-    do1:
-      a1 = ((op_t *) srcp1)[3];
-      b1 = ((op_t *) srcp2)[3];
-      if (a0 != b0)
-       return CMP_LT_OR_GT (a0, b0);
-
-      srcp1 += 4 * OPSIZ;
-      srcp2 += 4 * OPSIZ;
-      len -= 4;
-    }
-  while (len != 0);
-
-  /* This is the right position for do0.  Please don't move
-     it into the loop.  */
- do0:
-  if (a1 != b1)
-    return CMP_LT_OR_GT (a1, b1);
-  return 0;
-}
-
-static int memcmp_not_common_alignment __P((intptr_t, intptr_t, size_t));
-
-/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN
-   `op_t' objects (not LEN bytes!).  SRCP2 should be aligned for memory
-   operations on `op_t', but SRCP1 *should be unaligned*.  */
-#ifdef __GNUC__
-__inline
-#endif
-static int
-memcmp_not_common_alignment (srcp1, srcp2, len)
-     intptr_t srcp1;
-     intptr_t srcp2;
-     size_t len;
-{
-  op_t a0, a1, a2, a3;
-  op_t b0, b1, b2, b3;
-  op_t x;
-  int shl, shr;
-
-  /* Calculate how to shift a word read at the memory operation
-     aligned srcp1 to make it aligned for comparison.  */
-
-  shl = 8 * (srcp1 % OPSIZ);
-  shr = 8 * OPSIZ - shl;
-
-  /* Make SRCP1 aligned by rounding it down to the beginning of the `op_t'
-     it points in the middle of.  */
-  srcp1 &= -OPSIZ;
-
-  switch (len % 4)
-    {
-    default: /* Avoid warning about uninitialized local variables.  */
-    case 2:
-      a1 = ((op_t *) srcp1)[0];
-      a2 = ((op_t *) srcp1)[1];
-      b2 = ((op_t *) srcp2)[0];
-      srcp1 -= 1 * OPSIZ;
-      srcp2 -= 2 * OPSIZ;
-      len += 2;
-      goto do1;
-    case 3:
-      a0 = ((op_t *) srcp1)[0];
-      a1 = ((op_t *) srcp1)[1];
-      b1 = ((op_t *) srcp2)[0];
-      srcp2 -= 1 * OPSIZ;
-      len += 1;
-      goto do2;
-    case 0:
-      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
-       return 0;
-      a3 = ((op_t *) srcp1)[0];
-      a0 = ((op_t *) srcp1)[1];
-      b0 = ((op_t *) srcp2)[0];
-      srcp1 += 1 * OPSIZ;
-      goto do3;
-    case 1:
-      a2 = ((op_t *) srcp1)[0];
-      a3 = ((op_t *) srcp1)[1];
-      b3 = ((op_t *) srcp2)[0];
-      srcp1 += 2 * OPSIZ;
-      srcp2 += 1 * OPSIZ;
-      len -= 1;
-      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
-       goto do0;
-      /* Fall through.  */
-    }
-
-  do
-    {
-      a0 = ((op_t *) srcp1)[0];
-      b0 = ((op_t *) srcp2)[0];
-      x = MERGE(a2, shl, a3, shr);
-      if (x != b3)
-       return CMP_LT_OR_GT (x, b3);
-
-    do3:
-      a1 = ((op_t *) srcp1)[1];
-      b1 = ((op_t *) srcp2)[1];
-      x = MERGE(a3, shl, a0, shr);
-      if (x != b0)
-       return CMP_LT_OR_GT (x, b0);
-
-    do2:
-      a2 = ((op_t *) srcp1)[2];
-      b2 = ((op_t *) srcp2)[2];
-      x = MERGE(a0, shl, a1, shr);
-      if (x != b1)
-       return CMP_LT_OR_GT (x, b1);
-
-    do1:
-      a3 = ((op_t *) srcp1)[3];
-      b3 = ((op_t *) srcp2)[3];
-      x = MERGE(a1, shl, a2, shr);
-      if (x != b2)
-       return CMP_LT_OR_GT (x, b2);
-
-      srcp1 += 4 * OPSIZ;
-      srcp2 += 4 * OPSIZ;
-      len -= 4;
-    }
-  while (len != 0);
-
-  /* This is the right position for do0.  Please don't move
-     it into the loop.  */
- do0:
-  x = MERGE(a2, shl, a3, shr);
-  if (x != b3)
-    return CMP_LT_OR_GT (x, b3);
-  return 0;
-}
-
-int
-rpl_memcmp (s1, s2, len)
-     const __ptr_t s1;
-     const __ptr_t s2;
-     size_t len;
-{
-  op_t a0;
-  op_t b0;
-  intptr_t srcp1 = (intptr_t) s1;
-  intptr_t srcp2 = (intptr_t) s2;
-  op_t res;
-
-  if (len >= OP_T_THRES)
-    {
-      /* There are at least some bytes to compare.  No need to test
-        for LEN == 0 in this alignment loop.  */
-      while (srcp2 % OPSIZ != 0)
-       {
-         a0 = ((byte *) srcp1)[0];
-         b0 = ((byte *) srcp2)[0];
-         srcp1 += 1;
-         srcp2 += 1;
-         res = a0 - b0;
-         if (res != 0)
-           return res;
-         len -= 1;
-       }
-
-      /* SRCP2 is now aligned for memory operations on `op_t'.
-        SRCP1 alignment determines if we can do a simple,
-        aligned compare or need to shuffle bits.  */
-
-      if (srcp1 % OPSIZ == 0)
-       res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ);
-      else
-       res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ);
-      if (res != 0)
-       return res;
-
-      /* Number of bytes remaining in the interval [0..OPSIZ-1].  */
-      srcp1 += len & -OPSIZ;
-      srcp2 += len & -OPSIZ;
-      len %= OPSIZ;
-    }
-
-  /* There are just a few bytes to compare.  Use byte memory operations.  */
-  while (len != 0)
-    {
-      a0 = ((byte *) srcp1)[0];
-      b0 = ((byte *) srcp2)[0];
-      srcp1 += 1;
-      srcp2 += 1;
-      res = a0 - b0;
-      if (res != 0)
-       return res;
-      len -= 1;
-    }
-
-  return 0;
-}
-
-#ifdef weak_alias
-# undef bcmp
-weak_alias (memcmp, bcmp)
-#endif
diff --git a/lib/pidfile.c b/lib/pidfile.c
deleted file mode 100644 (file)
index 6e24d5f..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-    pidfile.c - interact with pidfiles
-    Copyright (c) 1995  Martin Schulze <Martin.Schulze@Linux.DE>
-
-    This file is part of the sysklogd package, a kernel and system log daemon.
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-/* left unaltered for tinc -- Ivo Timmermans */
-/*
- * Sat Aug 19 13:24:33 MET DST 1995: Martin Schulze
- *     First version (v0.2) released
- */
-
-#include "system.h"
-
-#include "pidfile.h"
-
-#ifndef HAVE_MINGW
-/* read_pid
- *
- * Reads the specified pidfile and returns the read pid.
- * 0 is returned if either there's no pidfile, it's empty
- * or no pid can be read.
- */
-pid_t read_pid (const char *pidfile)
-{
-  FILE *f;
-  long pid;
-
-  if (!(f=fopen(pidfile,"r")))
-    return 0;
-  if(fscanf(f,"%20ld", &pid) != 1)
-    pid = 0;
-  fclose(f);
-  return pid;
-}
-
-/* check_pid
- *
- * Reads the pid using read_pid and looks up the pid in the process
- * table (using /proc) to determine if the process already exists. If
- * so the pid is returned, otherwise 0.
- */
-pid_t check_pid (const char *pidfile)
-{
-  pid_t pid = read_pid(pidfile);
-
-  /* Amazing ! _I_ am already holding the pid file... */
-  if ((!pid) || (pid == getpid ()))
-    return 0;
-
-  /*
-   * The 'standard' method of doing this is to try and do a 'fake' kill
-   * of the process.  If an ESRCH error is returned the process cannot
-   * be found -- GW
-   */
-  /* But... errno is usually changed only on error.. */
-  errno = 0;
-  if (kill(pid, 0) && errno == ESRCH)
-         return 0;
-
-  return pid;
-}
-
-/* write_pid
- *
- * Writes the pid to the specified file. If that fails 0 is
- * returned, otherwise the pid.
- */
-pid_t write_pid (const char *pidfile)
-{
-  FILE *f;
-  int fd;
-  pid_t pid;
-
-  if ((fd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1) {
-      return 0;
-  }
-
-  if ((f = fdopen(fd, "r+")) == NULL) {
-      close(fd);
-      return 0;
-  }
-  
-#ifdef HAVE_FLOCK
-  if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
-      fclose(f);
-      return 0;
-  }
-#endif
-
-  pid = getpid();
-  if (!fprintf(f,"%ld\n", (long)pid)) {
-      fclose(f);
-      return 0;
-  }
-  fflush(f);
-
-#ifdef HAVE_FLOCK
-  if (flock(fd, LOCK_UN) == -1) {
-      fclose(f);
-      return 0;
-  }
-#endif
-  fclose(f);
-
-  return pid;
-}
-
-/* remove_pid
- *
- * Remove the the specified file. The result from unlink(2)
- * is returned
- */
-int remove_pid (const char *pidfile)
-{
-  return unlink (pidfile);
-}
-#endif
diff --git a/lib/pidfile.h b/lib/pidfile.h
deleted file mode 100644 (file)
index 590a7bc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    pidfile.h - interact with pidfiles
-    Copyright (c) 1995  Martin Schulze <Martin.Schulze@Linux.DE>
-
-    This file is part of the sysklogd package, a kernel and system log daemon.
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef HAVE_MINGW
-/* read_pid
- *
- * Reads the specified pidfile and returns the read pid.
- * 0 is returned if either there's no pidfile, it's empty
- * or no pid can be read.
- */
-extern pid_t read_pid (const char *pidfile);
-
-/* check_pid
- *
- * Reads the pid using read_pid and looks up the pid in the process
- * table (using /proc) to determine if the process already exists. If
- * so 1 is returned, otherwise 0.
- */
-extern pid_t check_pid (const char *pidfile);
-
-/* write_pid
- *
- * Writes the pid to the specified file. If that fails 0 is
- * returned, otherwise the pid.
- */
-extern pid_t write_pid (const char *pidfile);
-
-/* remove_pid
- *
- * Remove the the specified file. The result from unlink(2)
- * is returned
- */
-extern int remove_pid (const char *pidfile);
-#endif
diff --git a/lib/realloc.c b/lib/realloc.c
deleted file mode 100644 (file)
index 4971b28..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Work around bug on some systems where realloc (NULL, 0) fails.
-   Copyright (C) 1997 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., Foundation,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
-
-/* written by Jim Meyering */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-#undef realloc
-
-#include <sys/types.h>
-
-char *malloc ();
-char *realloc ();
-
-/* Change the size of an allocated block of memory P to N bytes,
-   with error checking.  If N is zero, change it to 1.  If P is NULL,
-   use malloc.  */
-
-char *
-rpl_realloc (p, n)
-     char *p;
-     size_t n;
-{
-  if (n == 0)
-    n = 1;
-  if (p == 0)
-    return malloc (n);
-  return realloc (p, n);
-}
diff --git a/lib/splay_tree.c b/lib/splay_tree.c
deleted file mode 100644 (file)
index 3a792e8..0000000
+++ /dev/null
@@ -1,561 +0,0 @@
-/*
-    splay_tree.c -- splay tree and linked list convenience
-    Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "system.h"
-
-#include "splay_tree.h"
-#include "xalloc.h"
-
-/* Splay operation */
-
-static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *result) {
-       splay_node_t left = {0}, right = {0};
-       splay_node_t *leftbottom = &left, *rightbottom = &right, *child, *grandchild;
-       splay_node_t *root = tree->root;
-       int c;
-
-       if(!root) {
-               if(result)
-                       *result = 0;
-               return NULL;
-       }
-
-       while((c = tree->compare(data, root->data))) {
-               if(c < 0 && (child = root->left)) {
-                       c = tree->compare(data, child->data);
-
-                       if(c < 0 && (grandchild = child->left)) {
-                               rightbottom->left = child;
-                               child->parent = rightbottom;
-                               rightbottom = child;
-                               
-                               if((root->left = child->right))
-                                       child->right->parent = root;
-                               
-                               child->right = root;
-                               root->parent = child;
-
-                               child->left = NULL;
-                               grandchild->parent = NULL;
-
-                               root = grandchild;
-                       } else if (c > 0 && (grandchild = child->right)) {
-                               leftbottom->right = child;
-                               child->parent = leftbottom;
-                               leftbottom = child;
-
-                               child->right = NULL;
-                               grandchild->parent = NULL;
-
-                               rightbottom->left = root;
-                               root->parent = rightbottom;
-                               rightbottom = root;
-
-                               root->left = NULL;
-
-                               root = grandchild;
-                       } else {
-                               rightbottom->left = root;
-                               root->parent = rightbottom;
-                               rightbottom = root;
-                               
-                               root->left = NULL;
-                               child->parent = NULL;
-
-                               root = child;
-                               break;
-                       }
-               } else if(c > 0 && (child = root->right)) {
-                       c = tree->compare(data, child->data);
-
-                       if(c > 0 && (grandchild = child->right)) {
-                               leftbottom->right = child;
-                               child->parent = leftbottom;
-                               leftbottom = child;
-                               
-                               if((root->right = child->left))
-                                       child->left->parent = root;
-                               
-                               child->left = root;
-                               root->parent = child;
-
-                               child->right = NULL;
-                               grandchild->parent = NULL;
-
-                               root = grandchild;
-                       } else if (c < 0 && (grandchild = child->left)) {
-                               rightbottom->left = child;
-                               child->parent = rightbottom;
-                               rightbottom = child;
-
-                               child->left = NULL;
-                               grandchild->parent = NULL;
-
-                               leftbottom->right = root;
-                               root->parent = leftbottom;
-                               leftbottom = root;
-
-                               root->right = NULL;
-
-                               root = grandchild;
-                       } else {
-                               leftbottom->right = root;
-                               root->parent = leftbottom;
-                               leftbottom = root;
-
-                               root->right = NULL;
-                               child->parent = NULL;
-
-                               root = child;
-                               break;
-                       }
-               } else {
-                       break;
-               }
-       }
-
-       /* Merge trees */
-
-       if(left.right) {
-               if(root->left) {
-                       leftbottom->right = root->left;
-                       root->left->parent = leftbottom;
-               }
-               root->left = left.right;
-               left.right->parent = root;
-       }
-
-       if(right.left) {
-               if(root->right) {
-                       rightbottom->left = root->right;
-                       root->right->parent = rightbottom;
-               }
-               root->right = right.left;
-               right.left->parent = root;
-       }
-
-       /* Return result */
-
-       tree->root = root;
-       if(result)
-               *result = c;
-
-       return tree->root;
-}
-                       
-static void splay_bottom_up(splay_tree_t *tree, splay_node_t *node) {
-       splay_node_t *parent, *grandparent, *greatgrandparent;
-
-       while((parent = node->parent)) {
-               if(!(grandparent = parent->parent)) { /* zig */
-                       if(node == parent->left) {
-                               if((parent->left = node->right))                                
-                                       parent->left->parent = parent;
-                               node->right = parent;
-                       } else {
-                               if((parent->right = node->left))
-                                       parent->right->parent = parent;
-                               node->left = parent;
-                       }
-
-                       parent->parent = node;
-                       node->parent = NULL;
-               } else {
-                       greatgrandparent = grandparent->parent;
-
-                       if(node == parent->left && parent == grandparent->left) { /* left zig-zig */
-                               if((grandparent->left = parent->right))
-                                       grandparent->left->parent = grandparent;
-                               parent->right = grandparent;
-                               grandparent->parent = parent;
-
-                               if((parent->left = node->right))
-                                       parent->left->parent = parent;
-                               node->right = parent;
-                               parent->parent = node;
-                       } else if(node == parent->right && parent == grandparent->right) { /* right zig-zig */
-                               if((grandparent->right = parent->left))
-                                       grandparent->right->parent = grandparent;
-                               parent->left = grandparent;
-                               grandparent->parent = parent;
-
-                               if((parent->right = node->left))
-                                       parent->right->parent = parent;
-                               node->left = parent;
-                               parent->parent = node;
-                       } else if(node == parent->right && parent == grandparent->left) { /* left-right zig-zag */
-                               if((parent->right = node->left))
-                                       parent->right->parent = parent;
-                               node->left = parent;
-                               parent->parent = node;
-
-                               if((grandparent->left = node->right))
-                                       grandparent->left->parent = grandparent;
-                               node->right = grandparent;
-                               grandparent->parent = node;
-                       } else { /* right-left zig-zag */
-                               if((parent->left = node->right))
-                                       parent->left->parent = parent;
-                               node->right = parent;
-                               parent->parent = node;
-
-                               if((grandparent->right = node->left))
-                                       grandparent->right->parent = grandparent;
-                               node->left = grandparent;
-                               grandparent->parent = node;
-                       }
-
-                       if((node->parent = greatgrandparent)) {
-                               if(grandparent == greatgrandparent->left)
-                                       greatgrandparent->left = node;
-                               else
-                                       greatgrandparent->right = node;
-                       }
-               }
-       }
-
-       tree->root = node;
-}
-
-/* (De)constructors */
-
-splay_tree_t *splay_alloc_tree(splay_compare_t compare, splay_action_t delete) {
-       splay_tree_t *tree;
-
-       tree = xmalloc_and_zero(sizeof(splay_tree_t));
-       tree->compare = compare;
-       tree->delete = delete;
-
-       return tree;
-}
-
-void splay_free_tree(splay_tree_t *tree) {
-       free(tree);
-}
-
-splay_node_t *splay_alloc_node(void) {
-       return xmalloc_and_zero(sizeof(splay_node_t));
-}
-
-void splay_free_node(splay_tree_t *tree, splay_node_t *node) {
-       if(node->data && tree->delete)
-               tree->delete(node->data);
-
-       free(node);
-}
-
-/* Searching */
-
-void *splay_search(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-
-       node = splay_search_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-void *splay_search_closest(splay_tree_t *tree, const void *data, int *result) {
-       splay_node_t *node;
-
-       node = splay_search_closest_node(tree, data, result);
-
-       return node ? node->data : NULL;
-}
-
-void *splay_search_closest_smaller(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-
-       node = splay_search_closest_smaller_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-void *splay_search_closest_greater(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-
-       node = splay_search_closest_greater_node(tree, data);
-
-       return node ? node->data : NULL;
-}
-
-splay_node_t *splay_search_node(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-       int result;
-
-       node = splay_search_closest_node(tree, data, &result);
-
-       return result ? NULL : node;
-}
-
-splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *tree, const void *data, int *result) {
-       splay_node_t *node;
-       int c;
-
-       node = tree->root;
-
-       if(!node) {
-               if(result)
-                       *result = 0;
-               return NULL;
-       }
-
-       for(;;) {
-               c = tree->compare(data, node->data);
-
-               if(c < 0) {
-                       if(node->left)
-                               node = node->left;
-                       else
-                               break;
-               } else if(c > 0) {
-                       if(node->right)
-                               node = node->right;
-                       else 
-                               break;
-               } else {
-                       break;
-               }
-       }
-
-       if(result)
-               *result = c;
-       return node;
-}
-
-splay_node_t *splay_search_closest_node(splay_tree_t *tree, const void *data, int *result) {
-       return splay_top_down(tree, data, result);
-}
-
-splay_node_t *splay_search_closest_smaller_node(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-       int result;
-
-       node = splay_search_closest_node(tree, data, &result);
-
-       if(result < 0)
-               node = node->prev;
-
-       return node;
-}
-
-splay_node_t *splay_search_closest_greater_node(splay_tree_t *tree, const void *data) {
-       splay_node_t *node;
-       int result;
-
-       node = splay_search_closest_node(tree, data, &result);
-
-       if(result > 0)
-               node = node->next;
-
-       return node;
-}
-
-/* Insertion and deletion */
-
-splay_node_t *splay_insert(splay_tree_t *tree, void *data) {
-       splay_node_t *closest, *new;
-       int result;
-
-       if(!tree->root) {
-               new = splay_alloc_node();
-               new->data = data;
-               splay_insert_top(tree, new);
-       } else {
-               closest = splay_search_closest_node(tree, data, &result);
-
-               if(!result)
-                       return NULL;
-
-               new = splay_alloc_node();
-               new->data = data;
-               
-               if(result < 0)
-                       splay_insert_before(tree, closest, new);
-               else
-                       splay_insert_after(tree, closest, new);
-       }                       
-
-       return new;
-}
-
-splay_node_t *splay_insert_node(splay_tree_t *tree, splay_node_t *node) {
-       splay_node_t *closest;
-       int result;
-
-       if(!tree->root)
-               splay_insert_top(tree, node);
-       else {
-               closest = splay_search_closest_node(tree, node->data, &result);
-               
-               if(!result)
-                       return NULL;
-
-               if(result < 0)
-                       splay_insert_before(tree, closest, node);
-               else
-                       splay_insert_after(tree, closest, node);
-       }
-
-       return node;
-}
-
-void splay_insert_top(splay_tree_t *tree, splay_node_t *node) {
-       node->prev = node->next = node->left = node->right = node->parent = NULL;
-       tree->head = tree->tail = tree->root = node;
-}
-
-void splay_insert_before(splay_tree_t *tree, splay_node_t *before, splay_node_t *node) {
-       if(!before) {
-               if(tree->tail)
-                       splay_insert_after(tree, tree->tail, node);
-               else
-                       splay_insert_top(tree, node);
-               return;
-       }
-
-       node->next = before;
-       if((node->prev = before->prev))
-               before->prev->next = node;
-       else
-               tree->head = node;
-       before->prev = node;
-
-       splay_bottom_up(tree, before);
-
-       node->right = before;
-       before->parent = node;
-       if((node->left = before->left))
-               before->left->parent = node;
-       before->left = NULL;
-
-       node->parent = NULL;
-       tree->root = node;
-}
-
-void splay_insert_after(splay_tree_t *tree, splay_node_t *after, splay_node_t *node) {
-       if(!after) {
-               if(tree->head)
-                       splay_insert_before(tree, tree->head, node);
-               else
-                       splay_insert_top(tree, node);
-               return;
-       }
-
-       node->prev = after;
-       if((node->next = after->next))
-               after->next->prev = node;
-       else
-               tree->tail = node;
-       after->next = node;
-
-       splay_bottom_up(tree, after);
-
-       node->left = after;
-       after->parent = node;
-       if((node->right = after->right))
-               after->right->parent = node;
-       after->right = NULL;
-
-       node->parent = NULL;
-       tree->root = node;
-}
-
-splay_node_t *splay_unlink(splay_tree_t *tree, void *data) {
-       splay_node_t *node;
-
-       node = splay_search_node(tree, data);
-
-       if(node)
-               splay_unlink_node(tree, node);
-
-       return node;
-}
-
-void splay_unlink_node(splay_tree_t *tree, splay_node_t *node) {
-       if(node->prev)
-               node->prev->next = node->next;
-       else
-               tree->head = node->next;
-
-       if(node->next)
-               node->next->prev = node->prev;
-       else
-               tree->tail = node->prev;
-
-       splay_bottom_up(tree, node);
-
-       if(node->prev) {
-               node->left->parent = NULL;
-               tree->root = node->left;
-               if((node->prev->right = node->right))
-                       node->right->parent = node->prev;
-       } else if(node->next) {
-               tree->root = node->right;
-               node->right->parent = NULL;
-       } else {
-               tree->root = NULL;
-       }
-}
-
-void splay_delete_node(splay_tree_t *tree, splay_node_t *node) {
-       splay_unlink_node(tree, node);
-       splay_free_node(tree, node);
-}
-
-void splay_delete(splay_tree_t *tree, void *data) {
-       splay_node_t *node;
-
-       node = splay_search_node(tree, data);
-
-       if(node)
-               splay_delete_node(tree, node);
-}
-
-/* Fast tree cleanup */
-
-void splay_delete_tree(splay_tree_t *tree) {
-       splay_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               splay_free_node(tree, node);
-       }
-
-       splay_free_tree(tree);
-}
-
-/* Tree walking */
-
-void splay_foreach(const splay_tree_t *tree, splay_action_t action) {
-       splay_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               action(node->data);
-       }
-}
-
-void splay_foreach_node(const splay_tree_t *tree, splay_action_t action) {
-       splay_node_t *node, *next;
-
-       for(node = tree->head; node; node = next) {
-               next = node->next;
-               action(node);
-       }
-}
diff --git a/lib/splay_tree.h b/lib/splay_tree.h
deleted file mode 100644 (file)
index e4af0c4..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-    splay_tree.h -- header file for splay_tree.c
-    Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-
-#ifndef __SPLAY_TREE_H__
-#define __SPLAY_TREE_H__
-
-typedef struct splay_node_t {
-
-       /* Linked list part */
-
-       struct splay_node_t *next;
-       struct splay_node_t *prev;
-
-       /* Tree part */
-
-       struct splay_node_t *parent;
-       struct splay_node_t *left;
-       struct splay_node_t *right;
-
-       /* Payload */
-
-       void *data;
-
-} splay_node_t;
-
-typedef int (*splay_compare_t)(const void *, const void *);
-typedef void (*splay_action_t)(const void *);
-typedef void (*splay_action_node_t)(const splay_node_t *);
-
-typedef struct splay_tree_t {
-
-       /* Linked list part */
-
-       splay_node_t *head;
-       splay_node_t *tail;
-
-       /* Tree part */
-
-       splay_node_t *root;
-
-       splay_compare_t compare;
-       splay_action_t delete;
-
-} splay_tree_t;
-
-/* (De)constructors */
-
-extern splay_tree_t *splay_alloc_tree(splay_compare_t, splay_action_t);
-extern void splay_free_tree(splay_tree_t *);
-
-extern splay_node_t *splay_alloc_node(void);
-extern void splay_free_node(splay_tree_t *tree, splay_node_t *);
-
-/* Insertion and deletion */
-
-extern splay_node_t *splay_insert(splay_tree_t *, void *);
-extern splay_node_t *splay_insert_node(splay_tree_t *, splay_node_t *);
-
-extern void splay_insert_top(splay_tree_t *, splay_node_t *);
-extern void splay_insert_before(splay_tree_t *, splay_node_t *, splay_node_t *);
-extern void splay_insert_after(splay_tree_t *, splay_node_t *, splay_node_t *);
-
-extern splay_node_t *splay_unlink(splay_tree_t *, void *);
-extern void splay_unlink_node(splay_tree_t *tree, splay_node_t *);
-extern void splay_delete(splay_tree_t *, void *);
-extern void splay_delete_node(splay_tree_t *, splay_node_t *);
-
-/* Fast tree cleanup */
-
-extern void splay_delete_tree(splay_tree_t *);
-
-/* Searching */
-
-extern void *splay_search(splay_tree_t *, const void *);
-extern void *splay_search_closest(splay_tree_t *, const void *, int *);
-extern void *splay_search_closest_smaller(splay_tree_t *, const void *);
-extern void *splay_search_closest_greater(splay_tree_t *, const void *);
-
-extern splay_node_t *splay_search_node(splay_tree_t *, const void *);
-extern splay_node_t *splay_search_closest_node(splay_tree_t *, const void *, int *);
-extern splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *, const void *, int *);
-extern splay_node_t *splay_search_closest_smaller_node(splay_tree_t *, const void *);
-extern splay_node_t *splay_search_closest_greater_node(splay_tree_t *, const void *);
-
-/* Tree walking */
-
-extern void splay_foreach(const splay_tree_t *, splay_action_t);
-extern void splay_foreach_node(const splay_tree_t *, splay_action_t);
-
-#endif
diff --git a/lib/utils.c b/lib/utils.c
deleted file mode 100644 (file)
index 6ea904a..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-    utils.c -- gathering of some stupid small functions
-    Copyright (C) 1999-2005 Ivo Timmermans
-                  2000-2009 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "system.h"
-
-#include "../src/logger.h"
-#include "utils.h"
-
-static const char hexadecimals[] = "0123456789ABCDEF";
-
-static int charhex2bin(char c) {
-       if(isdigit(c))
-               return c - '0';
-       else
-               return toupper(c) - 'A' + 10;
-}
-
-
-void hex2bin(char *src, char *dst, int length) {
-       int i;
-       for(i = 0; i < length; i++)
-               dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]);
-}
-
-void bin2hex(char *src, char *dst, int length) {
-       int i;
-       for(i = length - 1; i >= 0; i--) {
-               dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15];
-               dst[i * 2] = hexadecimals[(unsigned char) src[i] >> 4];
-       }
-}
-
-#if defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
-#ifdef HAVE_CYGWIN
-#include <w32api/windows.h>
-#endif
-
-const char *winerror(int err) {
-       static char buf[1024], *newline;
-
-       if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-               NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
-               strncpy(buf, "(unable to format errormessage)", sizeof(buf));
-       };
-
-       if((newline = strchr(buf, '\r')))
-               *newline = '\0';
-
-       return buf;
-}
-#endif
-
-unsigned int bitfield_to_int(const void *bitfield, size_t size) {
-       unsigned int value = 0;
-       if(size > sizeof value)
-               size = sizeof value;
-       memcpy(&value, bitfield, size);
-       return value;
-}
diff --git a/lib/utils.h b/lib/utils.h
deleted file mode 100644 (file)
index f6ff705..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-    utils.h -- header file for utils.c
-    Copyright (C) 1999-2005 Ivo Timmermans
-                  2000-2009 Guus Sliepen <guus@tinc-vpn.org>
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_UTILS_H__
-#define __TINC_UTILS_H__
-
-extern void hex2bin(char *src, char *dst, int length);
-extern void bin2hex(char *src, char *dst, int length);
-
-#ifdef HAVE_MINGW
-extern const char *winerror(int);
-#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
-#define sockerrno WSAGetLastError()
-#define sockstrerror(x) winerror(x)
-#define sockwouldblock(x) ((x) == WSAEWOULDBLOCK || (x) == WSAEINTR)
-#define sockmsgsize(x) ((x) == WSAEMSGSIZE)
-#define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
-#else
-#define sockerrno errno
-#define sockstrerror(x) strerror(x)
-#define sockwouldblock(x) ((x) == EWOULDBLOCK || (x) == EINTR)
-#define sockmsgsize(x) ((x) == EMSGSIZE)
-#define sockinprogress(x) ((x) == EINPROGRESS)
-#endif
-
-extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
-
-#endif                                                 /* __TINC_UTILS_H__ */
diff --git a/lib/xalloc.h b/lib/xalloc.h
deleted file mode 100644 (file)
index 952f921..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <sys/types.h>
-
-#ifndef PARAMS
-# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
-#  define PARAMS(Args) Args
-# else
-#  define PARAMS(Args) ()
-# endif
-#endif
-
-/* Exit value when the requested amount of memory is not available.
-   The caller may set it to some other value.  */
-extern int xalloc_exit_failure;
-
-/* FIXME: describe */
-extern char *const xalloc_msg_memory_exhausted;
-
-/* FIXME: describe */
-extern void (*xalloc_fail_func) (int);
-
-void *xmalloc PARAMS ((size_t n)) __attribute__ ((__malloc__));
-void *xmalloc_and_zero PARAMS ((size_t n)) __attribute__ ((__malloc__));
-void *xcalloc PARAMS ((size_t n, size_t s));
-void *xrealloc PARAMS ((void *p, size_t n)) __attribute__ ((__malloc__));
-
-char *xstrdup PARAMS ((const char *s)) __attribute__ ((__malloc__));
-
-extern int xasprintf(char **strp, const char *fmt, ...);
-extern int xvasprintf(char **strp, const char *fmt, va_list ap);
diff --git a/lib/xmalloc.c b/lib/xmalloc.c
deleted file mode 100644 (file)
index e4079ce..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/* xmalloc.c -- malloc with out of memory checking
-   Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 97 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., Foundation,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#if STDC_HEADERS
-# include <stdlib.h>
-#else
-void *calloc ();
-void *malloc ();
-void *realloc ();
-void free ();
-#endif
-
-#include "dropin.h"
-#include "xalloc.h"
-
-#ifndef EXIT_FAILURE
-# define EXIT_FAILURE 1
-#endif
-
-/* Prototypes for functions defined here.  */
-#if defined (__STDC__) && __STDC__
-void *xmalloc (size_t n);
-void *xcalloc (size_t n, size_t s);
-void *xrealloc (void *p, size_t n);
-#endif
-
-/* Exit value when the requested amount of memory is not available.
-   The caller may set it to some other value.  */
-int xalloc_exit_failure = EXIT_FAILURE;
-
-/* FIXME: describe */
-char *const xalloc_msg_memory_exhausted = "Memory exhausted";
-
-/* FIXME: describe */
-void (*xalloc_fail_func) (int) = NULL;
-
-static void
-xalloc_fail (int size)
-{
-  if (xalloc_fail_func)
-    (*xalloc_fail_func) (size);
-  fprintf(stderr, "%s\n", xalloc_msg_memory_exhausted);
-  exit(xalloc_exit_failure);
-}
-
-/* Allocate N bytes of memory dynamically, with error checking.  */
-
-void *
-xmalloc (size_t n)
-{
-  void *p;
-
-  p = malloc (n);
-  if (p == NULL)
-    xalloc_fail ((int)n);
-  return p;
-}
-
-/* Allocate N bytes of memory dynamically, and set it all to zero. */
-
-void *
-xmalloc_and_zero (size_t n)
-{
-  void *p;
-
-  p = malloc (n);
-  if (p == NULL)
-    xalloc_fail ((int)n);
-  memset (p, '\0', n);
-  return p;
-}
-
-/* Change the size of an allocated block of memory P to N bytes,
-   with error checking.
-   If P is NULL, run xmalloc.  */
-
-void *
-xrealloc (void *p, size_t n)
-{
-  p = realloc (p, n);
-  if (p == NULL)
-    xalloc_fail (n);
-  return p;
-}
-
-/* Duplicate a string */
-
-char *xstrdup(const char *s)
-{
-  char *p;
-  
-  p = strdup(s);
-  if(!p)
-    xalloc_fail ((int)strlen(s));
-  return p;
-}
-
-#ifdef NOT_USED
-
-/* Allocate memory for N elements of S bytes, with error checking.  */
-
-void *
-xcalloc (n, s)
-     size_t n, s;
-{
-  void *p;
-
-  p = calloc (n, s);
-  if (p == NULL)
-    xalloc_fail ();
-  return p;
-}
-
-#endif /* NOT_USED */
-
-int xasprintf(char **strp, const char *fmt, ...) {
-       int result;
-       va_list ap;
-       va_start(ap, fmt);
-       result = xvasprintf(strp, fmt, ap);
-       va_end(ap);
-       return result;
-}
-
-int xvasprintf(char **strp, const char *fmt, va_list ap) {
-       int result = vasprintf(strp, fmt, ap);
-       if(result < 0) {
-               fprintf(stderr, "vasprintf() failed: %s\n", strerror(errno));
-               exit(xalloc_exit_failure);
-       }
-       return result;
-}
diff --git a/m4/curses.m4 b/m4/curses.m4
new file mode 100644 (file)
index 0000000..50a90a5
--- /dev/null
@@ -0,0 +1,41 @@
+dnl Check to find the curses headers/libraries
+
+AC_DEFUN([tinc_CURSES],
+[
+  AC_ARG_ENABLE([curses],
+    AS_HELP_STRING([--disable-curses], [disable curses support]))
+  AS_IF([test "x$enable_curses" != "xno"], [
+  AC_DEFINE(HAVE_CURSES, 1, [have curses support])
+    curses=true
+    AC_ARG_WITH(curses,
+      AS_HELP_STRING([--with-curses=DIR], [curses base directory, or:]),
+      [curses="$withval"
+       CPPFLAGS="$CPPFLAGS -I$withval/include"
+       LDFLAGS="$LDFLAGS -L$withval/lib"]
+    )
+
+    AC_ARG_WITH(curses-include,
+      AS_HELP_STRING([--with-curses-include=DIR], [curses headers directory]),
+      [curses_include="$withval"
+       CPPFLAGS="$CPPFLAGS -I$withval"]
+    )
+
+    AC_ARG_WITH(curses-lib,
+      AS_HELP_STRING([--with-curses-lib=DIR], [curses library directory]),
+      [curses_lib="$withval"
+       LDFLAGS="$LDFLAGS -L$withval"]
+    )
+
+    AC_CHECK_HEADERS(curses.h,
+      [],
+      [AC_MSG_ERROR("curses header files not found."); break]
+    )
+
+    AC_CHECK_LIB(curses, initscr,
+      [CURSES_LIBS="-lcurses"],
+      [AC_MSG_ERROR("curses libraries not found.")]
+    )
+  ])
+
+  AC_SUBST(CURSES_LIBS)
+])
diff --git a/m4/libevent.m4 b/m4/libevent.m4
new file mode 100644 (file)
index 0000000..34bdef7
--- /dev/null
@@ -0,0 +1,33 @@
+dnl Check to find the libevent headers/libraries
+
+AC_DEFUN([tinc_LIBEVENT],
+[
+  AC_ARG_WITH(libevent,
+    AS_HELP_STRING([--with-libevent=DIR], [libevent base directory, or:]),
+    [libevent="$withval"
+     CPPFLAGS="$CPPFLAGS -I$withval/include"
+     LDFLAGS="$LDFLAGS -L$withval/lib"]
+  )
+
+  AC_ARG_WITH(libevent-include,
+    AS_HELP_STRING([--with-libevent-include=DIR], [libevent headers directory]),
+    [libevent_include="$withval"
+     CPPFLAGS="$CPPFLAGS -I$withval"]
+  )
+
+  AC_ARG_WITH(libevent-lib,
+    AS_HELP_STRING([--with-libevent-lib=DIR], [libevent library directory]),
+    [libevent_lib="$withval"
+     LDFLAGS="$LDFLAGS -L$withval"]
+  )
+
+  AC_CHECK_HEADERS(event.h,
+    [],
+    [AC_MSG_ERROR("libevent header files not found."); break]
+  )
+
+  AC_CHECK_LIB(event, event_init,
+    [LIBS="-levent $LIBS"],
+    [AC_MSG_ERROR("libevent libraries not found.")]
+  )
+])
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644 (file)
index 0000000..f063083
--- /dev/null
@@ -0,0 +1,63 @@
+# List of files which contain translatable strings.
+# Copyright (C) 1999,2000 Ivo Timmermans
+
+# Package source files
+
+lib/dropin.c
+lib/utils.c
+lib/memcmp.c
+lib/getopt.c
+lib/avl_tree.c
+lib/list.c
+lib/getopt1.c
+lib/xmalloc.c
+lib/fake-getaddrinfo.c
+lib/alloca.c
+lib/splay_tree.c
+lib/fake-getnameinfo.c
+lib/malloc.c
+lib/realloc.c
+src/tincctl.c
+src/subnet.c
+src/logger.c
+src/edge.c
+src/device.c
+src/protocol_key.c
+src/protocol_edge.c
+src/control.c
+src/connection.c
+src/openssl/rsa.c
+src/openssl/digest.c
+src/openssl/cipher.c
+src/openssl/crypto.c
+src/protocol_auth.c
+src/net_packet.c
+src/rsa.c
+src/bsd/device.c
+src/graph.c
+src/raw_socket/device.c
+src/net_setup.c
+src/digest.c
+src/conf.c
+src/gcrypt/rsa.c
+src/gcrypt/digest.c
+src/gcrypt/cipher.c
+src/gcrypt/crypto.c
+src/uml_socket/device.c
+src/mingw/device.c
+src/net_socket.c
+src/meta.c
+src/netutl.c
+src/cygwin/device.c
+src/node.c
+src/protocol.c
+src/process.c
+src/protocol_subnet.c
+src/cipher.c
+src/net.c
+src/linux/device.c
+src/tincd.c
+src/crypto.c
+src/solaris/device.c
+src/route.c
+src/protocol_misc.c
diff --git a/po/nl.po b/po/nl.po
new file mode 100644 (file)
index 0000000..d67f25e
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,1618 @@
+# Dutch messages for tinc
+# Copyright (C) 1999-2007 Ivo Timmermans, Guus Sliepen.
+# Ivo Timmermans <ivo@tinc-vpn.org>, 1999-2006.
+# Guus Sliepen <guus@tinc-vpn.org>, 2000-2007.
+msgid ""
+msgstr ""
+"Project-Id-Version: tinc 1.0-svn\n"
+"Report-Msgid-Bugs-To: tinc-devel@tinc-vpn.org\n"
+"POT-Creation-Date: 2007-03-07 18:48+0100\n"
+"PO-Revision-Date: 2007-01-05 14:14+0100\n"
+"Last-Translator: Guus Sliepen <guus@tinc-vpn.org>\n"
+"Language-Team: Dutch\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/utils.c:93
+msgid "(unable to format errormessage)"
+msgstr "(kon foutmelding niet samenstellen)"
+
+#: src/conf.c:160
+#, c-format
+msgid "\"yes\" or \"no\" expected for configuration variable %s in %s line %d"
+msgstr ""
+"\"ja\" of \"nee\" verwacht voor configuratievariabele %s in %s regel %d"
+
+#: src/conf.c:176
+#, c-format
+msgid "Integer expected for configuration variable %s in %s line %d"
+msgstr "Geheel getal verwacht voor configuratievariabele %s in %s regel %d"
+
+#: src/conf.c:210
+#, c-format
+msgid ""
+"Hostname or IP address expected for configuration variable %s in %s line %d"
+msgstr ""
+"Hostnaam of IP adres verwacht voor configuratievariabele %s in %s regel %d"
+
+#: src/conf.c:226
+#, c-format
+msgid "Subnet expected for configuration variable %s in %s line %d"
+msgstr "Subnet verwacht voor configuratievariabele %s in %s regel %d"
+
+#: src/conf.c:237
+#, c-format
+msgid ""
+"Network address and prefix length do not match for configuration variable %s "
+"in %s line %d"
+msgstr ""
+"Netwerk adres en prefix lengte komen niet overeen bij configuratievariabele %"
+"s in %s regel %d"
+
+#: src/conf.c:337
+#, c-format
+msgid "Cannot open config file %s: %s"
+msgstr "Kan configuratie bestand %s niet openen: %s"
+
+#: src/conf.c:391
+#, c-format
+msgid "No value for variable `%s' on line %d while reading config file %s"
+msgstr ""
+"Geen waarde voor variabele `%s' op regel %d tijdens het lezen van "
+"configuratie bestand %s"
+
+#: src/conf.c:422
+#, c-format
+msgid "Failed to read `%s': %s"
+msgstr "Lezen van `%s' mislukte: %s"
+
+#: src/conf.c:444
+#, c-format
+msgid "Please enter a file to save %s to [%s]: "
+msgstr "Geef een bestand om de %s naar de schrijven [%s]: "
+
+#: src/conf.c:451
+#, c-format
+msgid "Error while reading stdin: %s\n"
+msgstr "Fout tijdens lezen van standaardinvoer: %s\n"
+
+#: src/conf.c:483
+#, c-format
+msgid "Error opening file `%s': %s\n"
+msgstr "Fout bij het openen van het bestand `%s': %s\n"
+
+#: src/connection.c:49
+msgid "everyone"
+msgstr "iedereen"
+
+#: src/connection.c:50
+msgid "BROADCAST"
+msgstr "BROADCAST"
+
+#: src/connection.c:115
+msgid "Connections:"
+msgstr "Verbindingen:"
+
+#: src/connection.c:119
+#, c-format
+msgid " %s at %s options %lx socket %d status %04x outbuf %d/%d/%d"
+msgstr " %s op %s opties %lx socket %d status %04x outbuf %d/%d/%d"
+
+#: src/connection.c:124
+msgid "End of connections."
+msgstr "Einde van verbindingen."
+
+#: src/meta.c:44
+#, c-format
+msgid "Sending %d bytes of metadata to %s (%s)"
+msgstr "Verzenden van %d bytes metadata naar %s (%s)"
+
+#: src/meta.c:66
+#, c-format
+msgid "Error while encrypting metadata to %s (%s): %s"
+msgstr "Fout tijdens versleutelen van metadata naar %s (%s): %s"
+
+#: src/meta.c:70
+msgid "Encrypted data too long! Heap corrupted!"
+msgstr "Versleutelde data te lang! Heap gecorrumpeerd!"
+
+#: src/meta.c:86
+#, c-format
+msgid "Flushing %d bytes to %s (%s)"
+msgstr "Wegschrijven %d bytes naar %s (%s)"
+
+#: src/meta.c:93 src/meta.c:156
+#, c-format
+msgid "Connection closed by %s (%s)"
+msgstr "Verbinding beëindigd door %s (%s)"
+
+#: src/meta.c:99
+#, c-format
+msgid "Flushing %d bytes to %s (%s) would block"
+msgstr "Wegschrijven %d bytes naar %s (%s) zou blokkeren"
+
+#: src/meta.c:104
+#, c-format
+msgid "Flushing meta data to %s (%s) failed: %s"
+msgstr "Wegschrijven metadata naar %s (%s) faalde: %s"
+
+#: src/meta.c:161
+#, c-format
+msgid "Metadata socket read error for %s (%s): %s"
+msgstr "Fout op metadata socket voor %s (%s) tijdens lezen: %s"
+
+#: src/meta.c:176
+#, c-format
+msgid "Error while decrypting metadata from %s (%s): %s"
+msgstr "Fout tijdens ontsleutelen van metadata van %s (%s): %s"
+
+#: src/meta.c:229
+#, c-format
+msgid "Metadata read buffer overflow for %s (%s)"
+msgstr "Metadata leesbuffer overloop voor %s (%s)"
+
+#: src/net.c:60
+msgid "Purging unreachable nodes"
+msgstr "Verwijderen onbereikbare nodes"
+
+#: src/net.c:69
+#, c-format
+msgid "Purging node %s (%s)"
+msgstr "Verwijdering node %s (%s)"
+
+#: src/net.c:159
+#, c-format
+msgid "Closing connection with %s (%s)"
+msgstr "Beëindigen verbinding met %s (%s)"
+
+#: src/net.c:230
+#, c-format
+msgid "%s (%s) didn't respond to PING in %ld seconds"
+msgstr "%s (%s) antwoordde niet binnen %ld seconden op PING"
+
+#: src/net.c:239
+#, c-format
+msgid "Old connection_t for %s (%s) status %04x still lingering, deleting..."
+msgstr ""
+"Oude connection_t voor %s (%s) status %04x nog steeds aanwezig, wordt "
+"verwijderd..."
+
+#: src/net.c:244
+#, c-format
+msgid "Timeout from %s (%s) during authentication"
+msgstr "Timeout van %s (%s) tijdens authenticatie"
+
+#: src/net.c:259
+#, c-format
+msgid "%s (%s) could not flush for %ld seconds (%d bytes remaining)"
+msgstr "%s (%s) kon niet binnen %ld seconden wegschrijven (%d bytes over)"
+
+#: src/net.c:286
+#, c-format
+msgid "Error while connecting to %s (%s): %s"
+msgstr "Fout tijdens schrijven naar %s (%s): %s"
+
+#: src/net.c:345
+#, fuzzy, c-format
+msgid "Error building fdset: %s"
+msgstr "Fout tijdens lezen van standaardinvoer: %s\n"
+
+#: src/net.c:354
+#, c-format
+msgid "Error while waiting for input: %s"
+msgstr "Fout tijdens wachten op invoer: %s"
+
+#: src/net.c:383
+msgid "Regenerating symmetric key"
+msgstr "Hergenereren symmetrische sleutel"
+
+#: src/net.c:400
+msgid "Flushing event queue"
+msgstr "Legen taakrij"
+
+#: src/net.c:419
+msgid "Unable to reread configuration file, exitting."
+msgstr "Kan configuratiebestand niet herlezen, beëindigen."
+
+#: src/net_packet.c:75
+#, c-format
+msgid "No response to MTU probes from %s (%s)"
+msgstr "Geen antwoord van %s (%s) op MTU probes"
+
+#: src/net_packet.c:82
+#, c-format
+msgid "Fixing MTU of %s (%s) to %d after %d probes"
+msgstr "MTU van %s (%s) vastgezet op %d na %d probes"
+
+#: src/net_packet.c:94
+#, c-format
+msgid "Sending MTU probe length %d to %s (%s)"
+msgstr "Verzending MTU probe lengte %d naar %s (%s)"
+
+#: src/net_packet.c:107
+#, c-format
+msgid "Got MTU probe length %d from %s (%s)"
+msgstr "Kreeg MTU probe met verkeerde lengte %d van %s (%s)"
+
+#: src/net_packet.c:164
+#, c-format
+msgid "Received packet of %d bytes from %s (%s)"
+msgstr "Ontvangst pakket van %d bytes van %s (%s)"
+
+#: src/net_packet.c:185 src/route.c:92
+#, c-format
+msgid "Got too short packet from %s (%s)"
+msgstr "Kreeg te kort pakket van %s (%s)"
+
+#: src/net_packet.c:198
+#, c-format
+msgid "Got unauthenticated packet from %s (%s)"
+msgstr "Kreeg niet-geauthenticeerd pakket van %s (%s)"
+
+#: src/net_packet.c:213
+#, c-format
+msgid "Error decrypting packet from %s (%s): %s"
+msgstr "Fout tijdens ontsleutelen pakket van %s (%s): %s"
+
+#: src/net_packet.c:229
+#, c-format
+msgid "Lost %d packets from %s (%s)"
+msgstr "%d pakketten van %s (%s) verloren"
+
+#: src/net_packet.c:235
+#, c-format
+msgid "Got late or replayed packet from %s (%s), seqno %d, last received %d"
+msgstr ""
+"Kreeg laat of gedupliceerd pakket van %s (%s), seqno %d, laatste ontvangen %d"
+
+#: src/net_packet.c:259
+#, c-format
+msgid "Error while uncompressing packet from %s (%s)"
+msgstr "Fout tijdens decomprimeren pakket van %s (%s)"
+
+#: src/net_packet.c:308
+#, c-format
+msgid "No valid key known yet for %s (%s), queueing packet"
+msgstr ""
+"Nog geen geldige sleutel bekend voor %s (%s), pakket wordt in wachtrij gezet"
+
+#: src/net_packet.c:337
+#, c-format
+msgid "Error while compressing packet to %s (%s)"
+msgstr "Fout tijdens comprimeren pakket naar %s (%s)"
+
+#: src/net_packet.c:359
+#, c-format
+msgid "Error while encrypting packet to %s (%s): %s"
+msgstr "Fout tijdens versleutelen pakket naar %s (%s): %s"
+
+#: src/net_packet.c:391
+#, c-format
+msgid "Setting outgoing packet priority to %d"
+msgstr "Instellen prioriteit uitgaand pakket op %d"
+
+#: src/net_packet.c:393 src/net_setup.c:487 src/net_socket.c:129
+#: src/net_socket.c:158 src/tincd.c:435 src/tincd.c:477 src/process.c:198
+#: src/process.c:231 src/process.c:430 src/bsd/device.c:93
+#: src/bsd/device.c:112 src/cygwin/device.c:140 src/cygwin/device.c:171
+#: src/mingw/device.c:73 src/mingw/device.c:82 src/mingw/device.c:87
+#: src/mingw/device.c:256 src/mingw/device.c:263 src/mingw/device.c:268
+#: src/mingw/device.c:275 src/mingw/device.c:284 src/mingw/device.c:291
+#: src/uml_socket/device.c:89 src/uml_socket/device.c:103
+#: src/uml_socket/device.c:130 src/uml_socket/device.c:194
+#, c-format
+msgid "System call `%s' failed: %s"
+msgstr "Systeemaanroep `%s' mislukte: %s"
+
+#: src/net_packet.c:404
+#, c-format
+msgid "Error sending packet to %s (%s): %s"
+msgstr "Fout tijdens verzenden pakket naar %s (%s): %s"
+
+#: src/net_packet.c:427
+#, c-format
+msgid "Sending packet of %d bytes to %s (%s)"
+msgstr "Verzending pakket van %d bytes naar %s (%s)"
+
+#: src/net_packet.c:431
+#, c-format
+msgid "Node %s (%s) is not reachable"
+msgstr "Node %s (%s) is niet bereikbaar"
+
+#: src/net_packet.c:439
+#, c-format
+msgid "Sending packet to %s via %s (%s)"
+msgstr "Verzending pakket naar %s via %s (%s)"
+
+#: src/net_packet.c:458
+#, c-format
+msgid "Broadcasting packet of %d bytes from %s (%s)"
+msgstr "Verspreiding pakket van %d bytes van %s (%s)"
+
+#: src/net_packet.c:478
+#, c-format
+msgid "Flushing queue for %s (%s)"
+msgstr "Legen van wachtrij voor %s (%s)"
+
+#: src/net_packet.c:500
+#, c-format
+msgid "Receiving packet failed: %s"
+msgstr "Ontvangst pakket mislukt: %s"
+
+#: src/net_packet.c:510
+#, c-format
+msgid "Received UDP packet from unknown source %s"
+msgstr "Ontvangst UDP pakket van onbekende oorsprong %s"
+
+#: src/net_setup.c:78 src/net_setup.c:95
+#, c-format
+msgid "Error reading RSA public key file `%s': %s"
+msgstr "Fout tijdens lezen RSA publieke sleutel bestand `%s': %s"
+
+#: src/net_setup.c:110
+#, c-format
+msgid "Reading RSA public key file `%s' failed: %s"
+msgstr "Lezen RSA publieke sleutel bestand `%s' mislukt: %s"
+
+#: src/net_setup.c:146
+#, c-format
+msgid "No public key for %s specified!"
+msgstr "Geen publieke sleutel bekend voor %s gespecificeerd!"
+
+#: src/net_setup.c:161
+msgid "PrivateKey used but no PublicKey found!"
+msgstr "PrivateKey gebruikt maar geen PublicKey gevonden!"
+
+#: src/net_setup.c:180
+#, c-format
+msgid "Error reading RSA private key file `%s': %s"
+msgstr "Fout tijdens lezen RSA privé sleutel bestand `%s': %s"
+
+#: src/net_setup.c:188
+#, c-format
+msgid "Could not stat RSA private key file `%s': %s'"
+msgstr "Kon gegevens RSA privé sleutel bestand `%s' niet opvragen: %s"
+
+#: src/net_setup.c:195
+#, c-format
+msgid "Warning: insecure file permissions for RSA private key file `%s'!"
+msgstr ""
+"Waarschuwing: onveilige permissies voor RSA privé sleutel bestand `%s'!"
+
+#: src/net_setup.c:202
+#, c-format
+msgid "Reading RSA private key file `%s' failed: %s"
+msgstr "Fout tijdens lezen RSA privé sleutel bestand `%s': %s"
+
+#: src/net_setup.c:232 src/net_setup.c:233
+msgid "MYSELF"
+msgstr "MIJZELF"
+
+#: src/net_setup.c:239
+msgid "Name for tinc daemon required!"
+msgstr "Naam voor tinc daemon verplicht!"
+
+#: src/net_setup.c:244
+msgid "Invalid name for myself!"
+msgstr "Ongeldige naam voor mijzelf!"
+
+#: src/net_setup.c:253
+msgid "Cannot open host configuration file for myself!"
+msgstr "Kan host configuratie bestand voor mijzelf niet openen!"
+
+#: src/net_setup.c:306
+msgid "Invalid routing mode!"
+msgstr "Ongeldige routing modus!"
+
+#: src/net_setup.c:317
+msgid "PriorityInheritance not supported on this platform"
+msgstr "PriorityInheritance wordt niet ondersteund op dit platform"
+
+#: src/net_setup.c:325
+msgid "Bogus maximum timeout!"
+msgstr "Onzinnige maximum timeout!"
+
+#: src/net_setup.c:339
+msgid "Invalid address family!"
+msgstr "Ongeldige adresfamilie!"
+
+#: src/net_setup.c:357
+msgid "Unrecognized cipher type!"
+msgstr "Onbekend cipher type!"
+
+#: src/net_setup.c:382 src/protocol_auth.c:194
+#, c-format
+msgid "Error during initialisation of cipher for %s (%s): %s"
+msgstr "Fout tijdens initialisatie van cipher voor %s (%s): %s"
+
+#: src/net_setup.c:399
+msgid "Unrecognized digest type!"
+msgstr "Onbekend digest type!"
+
+#: src/net_setup.c:412
+msgid "MAC length exceeds size of digest!"
+msgstr "MAC lengte is groter dan dat van digest!"
+
+#: src/net_setup.c:415
+msgid "Bogus MAC length!"
+msgstr "Onzinnige MAC lengte!"
+
+#: src/net_setup.c:429
+msgid "Bogus compression level!"
+msgstr "Onzinnig compressieniveau!"
+
+#: src/net_setup.c:454 src/net_setup.c:514 src/net_setup.c:525
+#, fuzzy, c-format
+msgid "event_add failed: %s"
+msgstr "Ontvangst pakket mislukt: %s"
+
+#: src/net_setup.c:534
+#, c-format
+msgid "Listening on %s"
+msgstr "Luisterend op %s"
+
+#: src/net_setup.c:545
+msgid "Ready"
+msgstr "Gereed"
+
+#: src/net_setup.c:547
+msgid "Unable to create any listening socket!"
+msgstr "Kon geen enkele luistersocket aanmaken!"
+
+#: src/net_socket.c:63
+#, c-format
+msgid "fcntl for %s: %s"
+msgstr "fcntl voor %s: %s"
+
+#: src/net_socket.c:90
+#, c-format
+msgid "Creating metasocket failed: %s"
+msgstr "Aanmaak van metasocket mislukt: %s"
+
+#: src/net_socket.c:109 src/net_socket.c:201
+#, c-format
+msgid "Can't bind to interface %s: %s"
+msgstr "Kan niet aan interface %s binden: %s"
+
+#: src/net_socket.c:114
+msgid "BindToInterface not supported on this platform"
+msgstr "BindToInterface wordt niet ondersteund op dit platform"
+
+#: src/net_socket.c:121
+#, c-format
+msgid "Can't bind to %s/tcp: %s"
+msgstr "Kan niet aan %s/tcp binden: %s"
+
+#: src/net_socket.c:148
+#, c-format
+msgid "Creating UDP socket failed: %s"
+msgstr "Aanmaak UDP socket mislukte: %s"
+
+#: src/net_socket.c:212
+#, c-format
+msgid "Can't bind to %s/udp: %s"
+msgstr "Kan niet aan %s/udp binden: %s"
+
+#: src/net_socket.c:239
+#, c-format
+msgid "Trying to re-establish outgoing connection in %d seconds"
+msgstr "Poging tot herstellen van uitgaande verbinding over %d seconden"
+
+#: src/net_socket.c:247
+#, c-format
+msgid "Connected to %s (%s)"
+msgstr "Verbonden met %s (%s)"
+
+#: src/net_socket.c:266
+#, c-format
+msgid "Could not set up a meta connection to %s"
+msgstr "Kon geen metaverbinding aangaan met %s"
+
+#: src/net_socket.c:300
+#, c-format
+msgid "Trying to connect to %s (%s)"
+msgstr "Poging tot verbinden met %s (%s)"
+
+#: src/net_socket.c:306
+#, c-format
+msgid "Creating socket for %s failed: %s"
+msgstr "Aanmaken socket voor %s mislukt: %s"
+
+#: src/net_socket.c:328
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: src/net_socket.c:349
+#, c-format
+msgid "Already connected to %s"
+msgstr "Reeds verbonden met %s"
+
+#: src/net_socket.c:368
+#, c-format
+msgid "No address specified for %s"
+msgstr "Geen adres gespecificeerd voor %s"
+
+#: src/net_socket.c:399
+#, c-format
+msgid "Accepting a new connection failed: %s"
+msgstr "Aanname van nieuwe verbinding is mislukt: %s"
+
+#: src/net_socket.c:417
+#, c-format
+msgid "Connection from %s"
+msgstr "Verbinding van %s"
+
+#: src/net_socket.c:440
+#, c-format
+msgid "Invalid name for outgoing connection in %s line %d"
+msgstr "Ongeldige naam voor uitgaande verbinding in %s regel %d"
+
+#: src/netutl.c:50
+#, c-format
+msgid "Error looking up %s port %s: %s"
+msgstr "Fout bij het opzoeken van %s poort %s: %s"
+
+#: src/netutl.c:105
+#, c-format
+msgid "Error while translating addresses: %s"
+msgstr "Fout tijdens vertalen adressen: %s"
+
+#: src/netutl.c:131 src/netutl.c:142
+#, c-format
+msgid "%s port %s"
+msgstr "%s poort %s"
+
+#: src/netutl.c:138
+#, c-format
+msgid "Error while looking up hostname: %s"
+msgstr "Fout bij het opzoeken van hostnaam: %s"
+
+#: src/netutl.c:187
+#, c-format
+msgid "sockaddrcmp() was called with unknown address family %d, exitting!"
+msgstr ""
+"sockaddrcmp() werd aangeroepen met onbekende adresfamilie %d, beëindigen!"
+
+#: src/protocol.c:87
+#, c-format
+msgid "Output buffer overflow while sending request to %s (%s)"
+msgstr "Uitvoer buffer overvol tijdens zenden verzoek naar %s (%s)"
+
+#: src/protocol.c:95
+#, c-format
+msgid "Sending %s to %s (%s): %s"
+msgstr "Verzending %s naar %s (%s): %s"
+
+#: src/protocol.c:98
+#, c-format
+msgid "Sending %s to %s (%s)"
+msgstr "Verzending %s naar %s (%s)"
+
+#: src/protocol.c:120
+#, c-format
+msgid "Forwarding %s from %s (%s): %s"
+msgstr "Doorsturen %s van %s (%s): %s"
+
+#: src/protocol.c:124
+#, c-format
+msgid "Forwarding %s from %s (%s)"
+msgstr "Doorsturen %s van %s (%s)"
+
+#: src/protocol.c:142
+#, c-format
+msgid "Unknown request from %s (%s): %s"
+msgstr "Onbekend verzoek van %s (%s): %s"
+
+#: src/protocol.c:145
+#, c-format
+msgid "Unknown request from %s (%s)"
+msgstr "Onbekend verzoek van %s (%s)"
+
+#: src/protocol.c:152
+#, c-format
+msgid "Got %s from %s (%s): %s"
+msgstr "Kreeg %s van %s (%s): %s"
+
+#: src/protocol.c:156
+#, c-format
+msgid "Got %s from %s (%s)"
+msgstr "Kreeg %s van %s (%s)"
+
+#: src/protocol.c:162
+#, c-format
+msgid "Unauthorized request from %s (%s)"
+msgstr "Niet toegestaan verzoek van %s (%s)"
+
+#: src/protocol.c:170
+#, c-format
+msgid "Error while processing %s from %s (%s)"
+msgstr "Fout tijdens afhandelen %s van %s (%s)"
+
+#: src/protocol.c:175
+#, c-format
+msgid "Bogus data received from %s (%s)"
+msgstr "Onzinnige data ontvangen van %s (%s)"
+
+#: src/protocol.c:221
+msgid "Already seen request"
+msgstr "Verzoek reeds gezien"
+
+#: src/protocol.c:251
+#, c-format
+msgid "Aging past requests: deleted %d, left %d"
+msgstr "Veroudering vorige verzoeken: %d gewist, %d overgebleven"
+
+#: src/protocol_auth.c:58 src/protocol_auth.c:214 src/protocol_auth.c:345
+#: src/protocol_auth.c:412 src/protocol_auth.c:538 src/protocol_edge.c:73
+#: src/protocol_edge.c:188 src/protocol_key.c:62 src/protocol_key.c:105
+#: src/protocol_key.c:173 src/protocol_misc.c:56 src/protocol_misc.c:85
+#: src/protocol_misc.c:176 src/protocol_subnet.c:58 src/protocol_subnet.c:170
+#, c-format
+msgid "Got bad %s from %s (%s)"
+msgstr "Kreeg verkeerde %s van %s (%s)"
+
+#: src/protocol_auth.c:66 src/protocol_edge.c:81 src/protocol_edge.c:87
+#: src/protocol_edge.c:196 src/protocol_edge.c:202 src/protocol_subnet.c:66
+#: src/protocol_subnet.c:74 src/protocol_subnet.c:178
+#: src/protocol_subnet.c:199
+#, c-format
+msgid "Got bad %s from %s (%s): %s"
+msgstr "Kreeg verkeerde %s van %s (%s): %s"
+
+#: src/protocol_auth.c:75
+#, c-format
+msgid "Peer %s is %s instead of %s"
+msgstr "Ander %s is %s in plaats van %s"
+
+#: src/protocol_auth.c:88
+#, c-format
+msgid "Peer %s (%s) uses incompatible version %d"
+msgstr "Ander %s (%s) gebruikt incompatibele versie %d"
+
+#: src/protocol_auth.c:104
+#, c-format
+msgid "Peer %s had unknown identity (%s)"
+msgstr "Ander %s heeft onbekende identiteit (%s)"
+
+#: src/protocol_auth.c:158
+#, c-format
+msgid "Generated random meta key (unencrypted): %s"
+msgstr "Willekeurige meta sleutel aangemaakt (niet versleuteld): %s"
+
+#: src/protocol_auth.c:170 src/protocol_auth.c:243
+#, c-format
+msgid "Error during encryption of meta key for %s (%s)"
+msgstr "Fout tijdens versleutelen van meta key voor %s (%s)"
+
+#: src/protocol_auth.c:224 src/protocol_auth.c:355 src/protocol_auth.c:420
+#: src/protocol_auth.c:442
+#, c-format
+msgid "Possible intruder %s (%s): %s"
+msgstr "Mogelijke indringer %s (%s): %s"
+
+#: src/protocol_auth.c:251
+#, c-format
+msgid "Received random meta key (unencrypted): %s"
+msgstr "Ontving willekeurige meta key (niet versleuteld): %s"
+
+#: src/protocol_auth.c:262
+#, c-format
+msgid "%s (%s) uses unknown cipher!"
+msgstr "%s (%s) gebruikt onbekende cipher!"
+
+#: src/protocol_auth.c:270
+#, c-format
+msgid "Error during initialisation of cipher from %s (%s): %s"
+msgstr "Fout tijdens initalisatie van cipher van %s (%s): %s"
+
+#: src/protocol_auth.c:286 src/protocol_key.c:243
+#, c-format
+msgid "Node %s (%s) uses unknown digest!"
+msgstr "Node %s (%s) gebruikt onbekende digest!"
+
+#: src/protocol_auth.c:291
+#, c-format
+msgid "%s (%s) uses bogus MAC length!"
+msgstr "%s (%s) gebruikt onzinnige MAC lengte!"
+
+#: src/protocol_auth.c:388
+#, c-format
+msgid "Error during calculation of response for %s (%s): %s"
+msgstr "Fout tijdens berekenen van antwoord voor %s (%s): %s"
+
+#: src/protocol_auth.c:421
+msgid "wrong challenge reply length"
+msgstr "verkeerde lengte antwoord op uitdaging"
+
+#: src/protocol_auth.c:434
+#, c-format
+msgid "Error during calculation of response from %s (%s): %s"
+msgstr "Fout tijdens narekenen van antwoord van %s (%s): %s"
+
+#: src/protocol_auth.c:443
+msgid "wrong challenge reply"
+msgstr "verkeerd antwoord op uitdaging"
+
+#: src/protocol_auth.c:448
+#, c-format
+msgid "Expected challenge reply: %s"
+msgstr "Verwachtte antwoord op uitdaging: %s"
+
+#: src/protocol_auth.c:554
+#, c-format
+msgid "Established a second connection with %s (%s), closing old connection"
+msgstr "Tweede verbinding met %s (%s) gemaakt, oude verbinding wordt gesloten"
+
+#: src/protocol_auth.c:577
+#, c-format
+msgid "Connection with %s (%s) activated"
+msgstr "Verbinding met %s (%s) geactiveerd"
+
+#: src/protocol_edge.c:82 src/protocol_edge.c:88 src/protocol_edge.c:197
+#: src/protocol_edge.c:203 src/protocol_subnet.c:67 src/protocol_subnet.c:179
+msgid "invalid name"
+msgstr "ongeldige naam"
+
+#: src/protocol_edge.c:127
+#, c-format
+msgid "Got %s from %s (%s) for ourself which does not match existing entry"
+msgstr ""
+"Kreeg %s van %s (%s) voor onszelf welke niet overeenkomt met reeds bekende"
+
+#: src/protocol_edge.c:132
+#, c-format
+msgid "Got %s from %s (%s) which does not match existing entry"
+msgstr "Kreeg %s van %s (%s) welke niet overeenkomt met reeds bekende"
+
+#: src/protocol_edge.c:140
+#, c-format
+msgid "Got %s from %s (%s) for ourself which does not exist"
+msgstr "Kreeg %s van %s (%s) voor onszelf welke niet bestaat"
+
+#: src/protocol_edge.c:215 src/protocol_edge.c:223 src/protocol_edge.c:236
+#, c-format
+msgid "Got %s from %s (%s) which does not appear in the edge tree"
+msgstr "Kreeg %s van %s (%s) welke niet voorkomt in de edge tree"
+
+#: src/protocol_edge.c:242 src/protocol_subnet.c:103 src/protocol_subnet.c:222
+#, c-format
+msgid "Got %s from %s (%s) for ourself"
+msgstr "Kreeg %s van %s (%s) voor onszelf"
+
+#: src/protocol_key.c:73
+#, c-format
+msgid "Got %s from %s (%s) origin %s which does not exist"
+msgstr "Kreeg %s van %s (%s) herkomst %s welke niet bestaat"
+
+#: src/protocol_key.c:113 src/protocol_key.c:181
+#, c-format
+msgid ""
+"Got %s from %s (%s) origin %s which does not exist in our connection list"
+msgstr ""
+"Kreeg %s van %s (%s) herkomst %s welke niet voorkomt in de verbindingslijst"
+
+#: src/protocol_key.c:121 src/protocol_key.c:189
+#, c-format
+msgid ""
+"Got %s from %s (%s) destination %s which does not exist in our connection "
+"list"
+msgstr ""
+"Kreeg %s van %s (%s) doel %s welke niet voorkomt in de verbindingslijst"
+
+#: src/protocol_key.c:223
+#, c-format
+msgid "Node %s (%s) uses unknown cipher!"
+msgstr "Node %s (%s) gebruikt onbekende cipher!"
+
+#: src/protocol_key.c:229
+#, c-format
+msgid "Node %s (%s) uses wrong keylength!"
+msgstr "Node %s (%s) gebruikt verkeerde lengte sleutel!"
+
+#: src/protocol_key.c:249
+#, c-format
+msgid "Node %s (%s) uses bogus MAC length!"
+msgstr "Node %s (%s) gebruikt onzinnige MAC lengte!"
+
+#: src/protocol_key.c:258
+#, c-format
+msgid "Node %s (%s) uses bogus compression level!"
+msgstr "Node %s (%s) gebruikt onzinnig compressieniveau!"
+
+#: src/protocol_key.c:266
+#, c-format
+msgid "Error during initialisation of key from %s (%s): %s"
+msgstr "Fout tijdens initialisatie van sleutel van %s (%s): %s"
+
+#: src/protocol_misc.c:61
+#, c-format
+msgid "Status message from %s (%s): %d: %s"
+msgstr "Statusmelding van %s (%s): %d: %s"
+
+#: src/protocol_misc.c:90
+#, c-format
+msgid "Error message from %s (%s): %d: %s"
+msgstr "Foutmelding van %s (%s): %d: %s"
+
+#: src/protocol_subnet.c:75 src/protocol_subnet.c:200
+msgid "invalid subnet string"
+msgstr "ongeldige subnet string"
+
+#: src/protocol_subnet.c:188
+#, c-format
+msgid "Got %s from %s (%s) for %s which is not in our node tree"
+msgstr "Kreeg %s van %s (%s) voor %s welke niet voorkomt in de node boom"
+
+#: src/protocol_subnet.c:214
+#, c-format
+msgid "Got %s from %s (%s) for %s which does not appear in his subnet tree"
+msgstr "Kreeg %s van %s (%s) voor %s welke niet voorkomt in zijn subnet boom"
+
+#: src/subnet.c:105
+#, c-format
+msgid "subnet_compare() was called with unknown subnet type %d, exitting!"
+msgstr ""
+"subnet_compare() werd aangeroepen met onbekend subnet type %d, beëindigen!"
+
+#: src/subnet.c:251
+#, c-format
+msgid "net2str() was called with netstr=%p, subnet=%p!\n"
+msgstr "net2str() werd aangeroepen met netstr=%p, subnet=%p!\n"
+
+#: src/subnet.c:288
+#, c-format
+msgid "net2str() was called with unknown subnet type %d, exiting!"
+msgstr "net2str() werd aangeroepen met onbekend subnet type %d, beëindigen!"
+
+#: src/subnet.c:449
+msgid "Subnet list:"
+msgstr "Subnet lijst:"
+
+#: src/subnet.c:455
+#, c-format
+msgid " %s owner %s"
+msgstr " %s eigenaar %s"
+
+#: src/subnet.c:458
+msgid "End of subnet list."
+msgstr "Einde van subnet lijst."
+
+#: src/tincd.c:109
+#, c-format
+msgid "Try `%s --help' for more information.\n"
+msgstr "Probeer `%s --help' voor meer informatie.\n"
+
+#: src/tincd.c:112
+#, c-format
+msgid ""
+"Usage: %s [option]...\n"
+"\n"
+msgstr ""
+"Gebruik: %s [optie]...\n"
+"\n"
+
+#: src/tincd.c:113
+#, c-format
+msgid ""
+"  -c, --config=DIR           Read configuration options from DIR.\n"
+"  -D, --no-detach            Don't fork and detach.\n"
+"  -d, --debug[=LEVEL]        Increase debug level or set it to LEVEL.\n"
+"  -k, --kill[=SIGNAL]        Attempt to kill a running tincd and exit.\n"
+"  -n, --net=NETNAME          Connect to net NETNAME.\n"
+"  -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
+"  -L, --mlock                Lock tinc into main memory.\n"
+"      --logfile[=FILENAME]   Write log entries to a logfile.\n"
+"      --pidfile=FILENAME     Write PID to FILENAME.\n"
+"      --help                 Display this help and exit.\n"
+"      --version              Output version information and exit.\n"
+"\n"
+msgstr ""
+"  -c, --config=MAP           Lees configuratie uit MAP.\n"
+"  -D, --no-detach            Start geen nieuw proces.\n"
+"  -d, --debug[=NIVEAU]       Verhoog debugniveau of stel het in op NIVEAU.\n"
+"  -k, --kill[=SIGNAAL]       Poging tot zenden signaal naar lopende tincd en "
+"beëindig.\n"
+"  -n, --net=NETNAAM          Verbind met net NETNAAM.\n"
+"  -K, --generate-keys[=BITS] Genereer publiek/privé RSA sleutelpaar.\n"
+"  -L, --mlock                Houd tinc vast in het centrale geheugen.\n"
+"      --logfile[=BESTAND]    Schrijf log naar BESTAND.\n"
+"      --pidfile=BESTAND      Schrijf PID naar BESTAND.\n"
+"      --help                 Geef deze hulp en beëindig.\n"
+"      --version              Geef versie informatie en beëindig.\n"
+"\n"
+
+#: src/tincd.c:124
+#, c-format
+msgid "Report bugs to tinc@tinc-vpn.org.\n"
+msgstr ""
+"Meld fouten in het programma aan tinc@tinc-vpn.org;\n"
+"Meld fouten in de vertaling aan vertaling@nl.linux.org.\n"
+
+#: src/tincd.c:180
+#, c-format
+msgid ""
+"Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, "
+"USR1, USR2, WINCH, INT or ALRM.\n"
+msgstr ""
+"Ongeldig argument `%s'; SIGNAAL moet een getal zijn of Ã©Ã©n van HUP, TERM, "
+"KILL, USR1, USR2, WINCH, INT of ALRM.\n"
+
+#: src/tincd.c:202
+#, c-format
+msgid ""
+"Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"
+msgstr ""
+"Ongeldig argument `%s'; BITS moet een nummer zijn gelijk aan of groter dan "
+"512.\n"
+
+#: src/tincd.c:295
+#, c-format
+msgid "Generating %d bits keys:\n"
+msgstr "Bezig met genereren van een %d bits sleutel:\n"
+
+#: src/tincd.c:299
+#, c-format
+msgid "Error during key generation!\n"
+msgstr "Fout tijdens genereren sleutel!\n"
+
+#: src/tincd.c:302
+#, c-format
+msgid "Done.\n"
+msgstr "Klaar.\n"
+
+#: src/tincd.c:305
+msgid "private RSA key"
+msgstr "geheime RSA sleutel"
+
+#: src/tincd.c:316 src/tincd.c:335
+#, c-format
+msgid ""
+"Appending key to existing contents.\n"
+"Make sure only one key is stored in the file.\n"
+msgstr ""
+"Sleutel wordt toegevoegd aan bestaande inhoud.\n"
+"Let er op dat er slechts Ã©Ã©n sleutel in het bestand is.\n"
+
+#: src/tincd.c:329
+msgid "public RSA key"
+msgstr "openbare RSA sleutel"
+
+#: src/tincd.c:388
+msgid "Both netname and configuration directory given, using the latter..."
+msgstr ""
+"Zowel netnaam als configuratiemap zijn gegeven, laatste wordt gebruikt..."
+
+#: src/tincd.c:409
+#, c-format
+msgid "%s version %s (built %s %s, protocol %d)\n"
+msgstr "%s versie %s (gemaakt %s %s, protocol %d)\n"
+
+#: src/tincd.c:411
+#, c-format
+msgid ""
+"Copyright (C) 1998-2007 Ivo Timmermans, Guus Sliepen and others.\n"
+"See the AUTHORS file for a complete list.\n"
+"\n"
+"tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
+"and you are welcome to redistribute it under certain conditions;\n"
+"see the file COPYING for details.\n"
+msgstr ""
+"Copyright (C) 1998-2007 Ivo Timmermans, Guus Sliepen en anderen.\n"
+"Zie het bestand AUTHORS voor een volledige lijst.\n"
+"\n"
+"tinc wordt gedistribueerd ZONDER ENIGE GARANTIE.  Dit is vrije "
+"programmatuur,\n"
+"en je bent welkom om het te distribueren onder bepaalde voorwaarden;\n"
+"zie het bestand COPYING voor details.\n"
+
+#: src/tincd.c:439
+msgid "mlockall() not supported on this platform!"
+msgstr "mlockall() wordt niet ondersteund op dit platform!"
+
+#: src/tincd.c:466
+#, fuzzy
+msgid "Error initializing libevent!"
+msgstr "Fout tijdens initialiseren LZO compressor!"
+
+#: src/tincd.c:471
+msgid "Error initializing LZO compressor!"
+msgstr "Fout tijdens initialiseren LZO compressor!"
+
+#: src/tincd.c:512
+msgid "Terminating"
+msgstr "Beëindigen"
+
+#: src/process.c:54
+#, c-format
+msgid "Memory exhausted (couldn't allocate %d bytes), exitting."
+msgstr "Geheugen uitgeput (kon geen %d bytes reserveren), beëindigen."
+
+#: src/process.c:79 src/process.c:129
+#, c-format
+msgid "Could not open service manager: %s"
+msgstr "Kon service manager niet openen: %s"
+
+#: src/process.c:110
+#, c-format
+msgid "Could not create %s service: %s"
+msgstr "Kon %s service niet aanmaken: %s"
+
+#: src/process.c:116
+#, c-format
+msgid "%s service installed"
+msgstr "%s service geïnstalleerd"
+
+#: src/process.c:119
+#, c-format
+msgid "Could not start %s service: %s"
+msgstr "Kon %s service niet starten: %s"
+
+#: src/process.c:121
+#, c-format
+msgid "%s service started"
+msgstr "%s service gestart"
+
+#: src/process.c:136
+#, c-format
+msgid "Could not open %s service: %s"
+msgstr "Kon %s service niet openen: %s"
+
+#: src/process.c:141
+#, c-format
+msgid "Could not stop %s service: %s"
+msgstr "Kon %s service niet stoppen: %s"
+
+#: src/process.c:143
+#, c-format
+msgid "%s service stopped"
+msgstr "%s service gestopt"
+
+#: src/process.c:146
+#, c-format
+msgid "Could not remove %s service: %s"
+msgstr "Kon %s service niet verwijderen: %s"
+
+#: src/process.c:150
+#, c-format
+msgid "%s service removed"
+msgstr "%s service verwijderd"
+
+#: src/process.c:158 src/process.c:161
+#, c-format
+msgid "Got %s request"
+msgstr "Kreeg %s verzoek"
+
+#: src/process.c:164
+#, c-format
+msgid "Got unexpected request %d"
+msgstr "Kreeg onverwacht verzoek %d"
+
+#: src/process.c:252
+#, c-format
+msgid "A tincd is already running for net `%s' with pid %ld.\n"
+msgstr "Een tincd draait al voor net `%s' met pid %ld.\n"
+
+#: src/process.c:255
+#, c-format
+msgid "A tincd is already running with pid %ld.\n"
+msgstr "Een tincd draait al met pid %ld.\n"
+
+#: src/process.c:261
+#, c-format
+msgid "Could write pid file %s: %s\n"
+msgstr "Kon pid bestand %s niet openen: %s\n"
+
+#: src/process.c:283
+#, c-format
+msgid "No other tincd is running for net `%s'.\n"
+msgstr "Geen andere tincd draait voor net `%s'.\n"
+
+#: src/process.c:286
+#, c-format
+msgid "No other tincd is running.\n"
+msgstr "Geen andere tincd draait.\n"
+
+#: src/process.c:295
+#, c-format
+msgid "The tincd for net `%s' is no longer running. "
+msgstr "De tincd voor net `%s' draait niet meer. "
+
+#: src/process.c:298
+#, c-format
+msgid "The tincd is no longer running. "
+msgstr "De tincd draait niet meer. "
+
+#: src/process.c:300
+#, c-format
+msgid "Removing stale lock file.\n"
+msgstr "Verwijdering oud vergrendelingsbestand.\n"
+
+#: src/process.c:333
+#, c-format
+msgid "Couldn't detach from terminal: %s"
+msgstr "Kon niet ontkoppelen van terminal: %s"
+
+#: src/process.c:341
+#, c-format
+msgid "Could not write pid file %s: %s\n"
+msgstr "Kon pid bestand %s niet schrijven: %s\n"
+
+#: src/process.c:352
+#, c-format
+msgid "tincd %s (%s %s) starting, debug level %d"
+msgstr "tincd %s (%s %s) start, debug niveau %d"
+
+#: src/process.c:387
+#, c-format
+msgid "Executing script %s"
+msgstr "Uitvoeren script %s"
+
+#: src/process.c:417
+#, c-format
+msgid "Script %s exited with non-zero status %d"
+msgstr "Script %s beëindigde met status %d"
+
+#: src/process.c:422
+#, c-format
+msgid "Script %s was killed by signal %d (%s)"
+msgstr "Script %s was gestopt door signaal %d (%s)"
+
+#: src/process.c:426
+#, c-format
+msgid "Script %s terminated abnormally"
+msgstr "Script %s abnormaal beëindigd"
+
+#: src/process.c:446 src/process.c:455 src/process.c:496 src/process.c:502
+#: src/process.c:520
+#, c-format
+msgid "Got %s signal"
+msgstr "Kreeg %s signaal"
+
+#: src/process.c:464
+#, c-format
+msgid "Got another fatal signal %d (%s): not restarting."
+msgstr "Kreeg nog een fataal signaal %d (%s): geen herstart."
+
+#: src/process.c:473
+#, c-format
+msgid "Got fatal signal %d (%s)"
+msgstr "Kreeg fataal signaal %d (%s)"
+
+#: src/process.c:477
+msgid "Trying to re-execute in 5 seconds..."
+msgstr "Poging tot herstarten over 5 seconden..."
+
+#: src/process.c:489
+msgid "Not restarting."
+msgstr "Geen herstart."
+
+#: src/process.c:505
+#, c-format
+msgid "Reverting to old debug level (%d)"
+msgstr "Herstellen van oud debug niveau (%d)"
+
+#: src/process.c:511
+#, c-format
+msgid ""
+"Temporarily setting debug level to 5.  Kill me with SIGINT again to go back "
+"to level %d."
+msgstr ""
+"Tijdelijk instellen debug niveau op 5. Zend nog een SIGINT signaal om niveau "
+"%d te herstellen."
+
+#: src/process.c:544
+#, c-format
+msgid "Got unexpected signal %d (%s)"
+msgstr "Kreeg onverwacht signaal %d (%s)"
+
+#: src/process.c:550
+#, c-format
+msgid "Ignored signal %d (%s)"
+msgstr "Signaal %d (%s) genegeerd"
+
+#: src/process.c:604
+#, c-format
+msgid "Installing signal handler for signal %d (%s) failed: %s\n"
+msgstr "Installeren van signaal afhandelaar voor signaal %d (%s) faalde: %s\n"
+
+#: src/route.c:111
+#, c-format
+msgid "Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"
+msgstr "Nieuw MAC adres %hx:%hx:%hx:%hx:%hx:%hx geleerd"
+
+#: src/route.c:149
+#, c-format
+msgid "Subnet %s expired"
+msgstr "Subnet %s is verlopen"
+
+#: src/route.c:190 src/route.c:345 src/route.c:489
+#, c-format
+msgid "Packet looping back to %s (%s)!"
+msgstr "Pakket komt terug naar %s (%s)!"
+
+#: src/route.c:289
+#, c-format
+msgid "Length of packet (%d) doesn't match length in IPv4 header (%zd)"
+msgstr ""
+"Lengte van pakket (%d) komt niet overeen met lengte in IPv4 header (%zd)"
+
+#: src/route.c:293
+#, c-format
+msgid "Fragmenting packet of %d bytes to %s (%s)"
+msgstr "Fragmentatie pakket van %d bytes naar %s (%s)"
+
+#: src/route.c:333
+#, c-format
+msgid ""
+"Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%"
+"d"
+msgstr ""
+"Kan pakket van %s (%s) niet routeren: onbekend IPv4 doeladres %d.%d.%d.%d"
+
+#: src/route.c:358 src/route.c:499
+#, c-format
+msgid "Packet for %s (%s) length %d larger than MTU %d"
+msgstr "Packet voor %s (%s) lengte %d groter dan MTU %d"
+
+#: src/route.c:473
+#, c-format
+msgid ""
+"Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%"
+"hx:%hx:%hx:%hx:%hx:%hx"
+msgstr ""
+"Kan pakket van %s (%s) niet routeren: onbekend IPv6 doeladres %hx:%hx:%hx:%"
+"hx:%hx:%hx:%hx:%hx"
+
+#: src/route.c:531
+#, c-format
+msgid "Got neighbor solicitation request from %s (%s) while in router mode!"
+msgstr ""
+"Kreeg neighbor solicitation request van %s (%s) terwijl we in router mode "
+"werken!"
+
+#: src/route.c:550
+msgid ""
+"Cannot route packet: received unknown type neighbor solicitation request"
+msgstr ""
+"Kan pakket niet routeren: ontvangst van onbekend type neighbor solicitation "
+"verzoek"
+
+#: src/route.c:569
+msgid "Cannot route packet: checksum error for neighbor solicitation request"
+msgstr ""
+"Kan pakket niet routeren: checksum fout voor neighbor solicitation verzoek"
+
+#: src/route.c:578
+#, c-format
+msgid ""
+"Cannot route packet: neighbor solicitation request for unknown address %hx:%"
+"hx:%hx:%hx:%hx:%hx:%hx:%hx"
+msgstr ""
+"Kan pakket niet routeren: neighbor solicitation verzoek voor onbekend adres %"
+"hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"
+
+#: src/route.c:665
+#, c-format
+msgid "Got ARP request from %s (%s) while in router mode!"
+msgstr "Kreeg ARP request van %s (%s) terwijl we in router mode werken!"
+
+#: src/route.c:682
+msgid "Cannot route packet: received unknown type ARP request"
+msgstr "Kan pakket niet routeren: ontvangst van onbekend type ARP verzoek"
+
+#: src/route.c:691
+#, c-format
+msgid "Cannot route packet: ARP request for unknown address %d.%d.%d.%d"
+msgstr "Kan pakket niet routeren: ARP verzoek voor onbekend adres %d.%d.%d.%d"
+
+#: src/route.c:747
+#, c-format
+msgid "Cannot route packet from %s (%s): unknown type %hx"
+msgstr "Kan pakket van %s (%s) niet routeren: onbekend type %hx"
+
+#: src/node.c:183
+msgid "Nodes:"
+msgstr "Nodes:"
+
+#: src/node.c:187
+#, c-format
+msgid ""
+" %s at %s cipher %d digest %d maclength %d compression %d options %lx status "
+"%04x nexthop %s via %s pmtu %d (min %d max %d)"
+msgstr ""
+" %s op %s cipher %d digest %d maclengte %d compressie %d opties %lx status %"
+"04x nexthop %s via %s pmtu %d (min %d max %d)"
+
+#: src/node.c:194
+msgid "End of nodes."
+msgstr "Einde van nodes."
+
+#: src/edge.c:148
+msgid "Edges:"
+msgstr "Edges:"
+
+#: src/edge.c:155
+#, c-format
+msgid " %s to %s at %s options %lx weight %d"
+msgstr " %s naar %s op %s opties %lx gewicht %d"
+
+#: src/edge.c:161
+msgid "End of edges."
+msgstr "Einde van edges."
+
+#: src/graph.c:270
+#, c-format
+msgid "Node %s (%s) became reachable"
+msgstr "Node %s (%s) werd bereikbaar"
+
+#: src/graph.c:274
+#, c-format
+msgid "Node %s (%s) became unreachable"
+msgstr "Node %s (%s) is niet meer bereikbaar"
+
+#: src/linux/device.c:72 src/solaris/device.c:57 src/bsd/device.c:63
+#: src/raw_socket/device.c:59 src/uml_socket/device.c:122
+#, c-format
+msgid "Could not open %s: %s"
+msgstr "Kon `%s' niet openen: %s"
+
+#: src/linux/device.c:83
+msgid "Linux tun/tap device (tun mode)"
+msgstr "Linux tun/tap apparaat (tun modus)"
+
+#: src/linux/device.c:87
+msgid "Linux tun/tap device (tap mode)"
+msgstr "Linux tun/tap apparaat (tap modus)"
+
+#: src/linux/device.c:97
+#, c-format
+msgid "Old ioctl() request was needed for %s"
+msgstr "Oud ioctl() verzoek was nodig voor %s"
+
+#: src/linux/device.c:105
+msgid "Linux ethertap device"
+msgstr "Linux ethertap apparaat"
+
+#: src/linux/device.c:110 src/solaris/device.c:106 src/bsd/device.c:133
+#: src/raw_socket/device.c:83 src/uml_socket/device.c:149
+#, c-format
+msgid "%s is a %s"
+msgstr "%s is een %s"
+
+#: src/linux/device.c:133 src/linux/device.c:144 src/linux/device.c:155
+#: src/solaris/device.c:125 src/bsd/device.c:152 src/bsd/device.c:181
+#: src/bsd/device.c:210 src/cygwin/device.c:239 src/mingw/device.c:110
+#: src/mingw/device.c:323 src/raw_socket/device.c:102
+#: src/uml_socket/device.c:239
+#, c-format
+msgid "Error while reading from %s %s: %s"
+msgstr "Fout tijdens lezen van %s %s: %s"
+
+#: src/linux/device.c:166 src/solaris/device.c:150 src/bsd/device.c:224
+#: src/cygwin/device.c:248 src/mingw/device.c:333 src/raw_socket/device.c:111
+#: src/uml_socket/device.c:249
+#, c-format
+msgid "Read packet of %d bytes from %s"
+msgstr "Pakket van %d bytes gelezen van %s"
+
+#: src/linux/device.c:176 src/solaris/device.c:160 src/bsd/device.c:234
+#: src/cygwin/device.c:260 src/mingw/device.c:346 src/raw_socket/device.c:121
+#: src/uml_socket/device.c:267
+#, c-format
+msgid "Writing packet of %d bytes to %s"
+msgstr "Pakket van %d bytes geschreven naar %s"
+
+#: src/linux/device.c:183 src/linux/device.c:190 src/linux/device.c:199
+#: src/solaris/device.c:164 src/bsd/device.c:268 src/raw_socket/device.c:125
+#: src/uml_socket/device.c:272
+#, c-format
+msgid "Can't write to %s %s: %s"
+msgstr "Kan niet schrijven naar %s %s: %s"
+
+#: src/linux/device.c:215 src/solaris/device.c:178 src/bsd/device.c:296
+#: src/cygwin/device.c:277 src/mingw/device.c:363 src/raw_socket/device.c:139
+#: src/uml_socket/device.c:288
+#, c-format
+msgid "Statistics for %s %s:"
+msgstr "Statistieken voor %s %s:"
+
+#: src/linux/device.c:216 src/solaris/device.c:179 src/bsd/device.c:297
+#: src/cygwin/device.c:278 src/mingw/device.c:364 src/raw_socket/device.c:140
+#: src/uml_socket/device.c:289
+#, c-format
+msgid " total bytes in:  %10d"
+msgstr " totaal aantal bytes in:  %10d"
+
+#: src/linux/device.c:217 src/solaris/device.c:180 src/bsd/device.c:298
+#: src/cygwin/device.c:279 src/mingw/device.c:365 src/raw_socket/device.c:141
+#: src/uml_socket/device.c:290
+#, c-format
+msgid " total bytes out: %10d"
+msgstr " totaal aantal bytes uit: %10d"
+
+#: src/solaris/device.c:69
+#, c-format
+msgid "Could not open /dev/ip: %s"
+msgstr "Kon /dev/ip niet openen: %s"
+
+#: src/solaris/device.c:75
+#, c-format
+msgid "Can't assign new interface: %s"
+msgstr "Kan geen nieuwe interface toekennen: %s"
+
+#: src/solaris/device.c:80
+#, c-format
+msgid "Could not open %s twice: %s"
+msgstr "Kon `%s' niet twee keer openen: %s"
+
+#: src/solaris/device.c:86
+#, c-format
+msgid "Can't push IP module: %s"
+msgstr "Kan IP module niet invoegen: %s"
+
+#: src/solaris/device.c:92
+#, c-format
+msgid "Can't set PPA %d: %s"
+msgstr "Kon PPA %d niet instellen: %s"
+
+#: src/solaris/device.c:97
+#, c-format
+msgid "Can't link TUN device to IP: %s"
+msgstr "Kan TUN apparaat niet koppelen aan IP: %s"
+
+#: src/solaris/device.c:104
+msgid "Solaris tun device"
+msgstr "Solaris tun apparaat"
+
+#: src/solaris/device.c:141 src/bsd/device.c:168
+#, c-format
+msgid "Unknown IP version %d while reading packet from %s %s"
+msgstr "Onbekende IP versie %d tijdens ontvangst pakket van %s %s"
+
+#: src/bsd/device.c:77
+#, c-format
+msgid "Unknown device type %s!"
+msgstr "Onbekend apparaattype %s!"
+
+#: src/bsd/device.c:105 src/bsd/device.c:124
+msgid "Generic BSD tun device"
+msgstr "Generiek BSD tun apparaat"
+
+#: src/bsd/device.c:129
+msgid "Generic BSD tap device"
+msgstr "Generiek BSD tap apparaat"
+
+#: src/bsd/device.c:199
+#, c-format
+msgid "Unknown address family %x while reading packet from %s %s"
+msgstr "Onbekende adresfamilie %x tijdens ontvangst pakket van %s %s"
+
+#: src/bsd/device.c:240 src/bsd/device.c:277 src/cygwin/device.c:264
+#: src/mingw/device.c:350
+#, c-format
+msgid "Error while writing to %s %s: %s"
+msgstr "Fout tijdens schrijven naar %s %s: %s"
+
+#: src/bsd/device.c:262
+#, c-format
+msgid "Unknown address family %x while writing packet to %s %s"
+msgstr "Onbekende adresfamiliy %x tijdens versturen pakket naar %s %s"
+
+#: src/cygwin/device.c:71 src/mingw/device.c:157
+#, c-format
+msgid "Unable to read registry: %s"
+msgstr "Kon registry niet lezen: %s"
+
+#: src/cygwin/device.c:123 src/mingw/device.c:208
+msgid "No Windows tap device found!"
+msgstr "Geen Windows tap apparaat gevonden!"
+
+#: src/cygwin/device.c:149
+#, c-format
+msgid "Could not open Windows tap device %s (%s) for writing: %s"
+msgstr "Kon Windows tap apparaat %s (%s) niet openen om te schrijven: %s"
+
+#: src/cygwin/device.c:158 src/mingw/device.c:233
+#, c-format
+msgid "Could not get MAC address from Windows tap device %s (%s): %s"
+msgstr "Kon MAC adres niet achterhalen van Windows tap apparaat %s (%s): %s"
+
+#: src/cygwin/device.c:187
+#, c-format
+msgid "Could not open Windows tap device %s (%s) for reading: %s"
+msgstr "Kon Windows tap apparaat %s (%s) niet openen om te lezen: %s"
+
+#: src/cygwin/device.c:193
+msgid "Tap reader forked and running."
+msgstr "Taplezer is geforked en draait."
+
+#: src/cygwin/device.c:210
+msgid "Tap reader failed!"
+msgstr "Taplezer faalde!"
+
+#: src/cygwin/device.c:214 src/mingw/device.c:302
+msgid "Windows tap device"
+msgstr "Windows tap apparaat"
+
+#: src/cygwin/device.c:216 src/mingw/device.c:304
+#, c-format
+msgid "%s (%s) is a %s"
+msgstr "%s (%s) is een %s"
+
+#: src/mingw/device.c:91
+msgid "Tap reader running"
+msgstr "Taplezer draait"
+
+#: src/mingw/device.c:226
+#, c-format
+msgid "%s (%s) is not a usable Windows tap device: %s"
+msgstr "%s (%s) is geen bruikbaar Windows tap apparaat: %s"
+
+#: src/raw_socket/device.c:56
+msgid "raw socket"
+msgstr "raw socket"
+
+#: src/raw_socket/device.c:68
+#, c-format
+msgid "Can't find interface %s: %s"
+msgstr "Kan interface %s niet vinden: %s"
+
+#: src/raw_socket/device.c:79 src/uml_socket/device.c:137
+#, c-format
+msgid "Could not bind %s to %s: %s"
+msgstr "Kon %s niet aan %s binden: %s"
+
+#: src/uml_socket/device.c:78
+msgid "UML network socket"
+msgstr "UML network socket"
+
+#: src/uml_socket/device.c:81
+#, c-format
+msgid "Could not open write %s: %s"
+msgstr "Kon `%s' niet openen: %s"
+
+#: src/uml_socket/device.c:95
+#, c-format
+msgid "Could not open data %s: %s"
+msgstr "Kon data-%s niet openen: %s"
+
+#: src/uml_socket/device.c:116
+#, c-format
+msgid "Could not bind data %s: %s"
+msgstr "Kon data-%s niet binden: %s"
+
+#: src/uml_socket/device.c:142
+#, c-format
+msgid "Could not listen on %s %s: %s"
+msgstr "Kon niet luisteren op %s %s: %s"
+
+#: src/uml_socket/device.c:189
+#, c-format
+msgid "Could not accept connection to %s %s: %s"
+msgstr "Kon verbinding met %s %s niet accepteren: %s"
+
+#: src/uml_socket/device.c:209
+#, c-format
+msgid "Error while reading request from %s %s: %s"
+msgstr "Fout tijdens lezen verzoek van %s %s: %s"
+
+#: src/uml_socket/device.c:216
+#, c-format
+msgid "Unknown magic %x, version %d, request type %d from %s %s"
+msgstr "Onbekende magische waarde %x, versie %d, verzoektype %d van %s %s"
+
+#: src/uml_socket/device.c:223
+#, c-format
+msgid "Could not bind write %s: %s"
+msgstr "Kon niet aan schrijf-%s binden: %s"
+
+#: src/uml_socket/device.c:231
+msgid "Connection with UML established"
+msgstr "Verbinding met UML geactiveerd"
+
+#: src/uml_socket/device.c:262
+#, c-format
+msgid "Dropping packet of %d bytes to %s: not connected to UML yet"
+msgstr "Pakket van %d bytes naar %s laten vallen: nog niet verbonden met UML"
index aca0e2dca3d39539fcfe8343a61764888d696a63..ba8b34793cd68cf6827d02bb2afa54b66b103b52 100644 (file)
@@ -1,11 +1,13 @@
 ## Produce this file with automake to get Makefile.in
 
-sbin_PROGRAMS = tincd
+sbin_PROGRAMS = tincd tincctl sptps_test
 
-EXTRA_DIST = linux/device.c bsd/device.c solaris/device.c cygwin/device.c mingw/device.c mingw/common.h
+EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt
 
-tincd_SOURCES = conf.c connection.c edge.c event.c graph.c logger.c meta.c net.c net_packet.c net_setup.c      \
-       net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c       \
+tincd_SOURCES = \
+       utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
+       buffer.c conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
+       net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
        protocol_key.c protocol_subnet.c route.c subnet.c tincd.c \
        dummy_device.c raw_socket_device.c
        
@@ -17,29 +19,45 @@ if VDE
 tincd_SOURCES += vde_device.c
 endif
 
+nodist_tincd_SOURCES = \
+       device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
+
+tincctl_SOURCES = \
+       utils.c getopt.c getopt1.c dropin.c \
+       list.c tincctl.c top.c
+
+nodist_tincctl_SOURCES = \
+       ecdsagen.c rsagen.c
+
+sptps_test_SOURCES = \
+       logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \
+       sptps.c sptps_test.c
+
 if TUNEMU
 tincd_SOURCES += bsd/tunemu.c
 endif
 
-nodist_tincd_SOURCES = device.c
+tincctl_LDADD = $(CURSES_LIBS)
 
 DEFAULT_INCLUDES =
 
-INCLUDES = @INCLUDES@ -I$(top_builddir) -I$(top_srcdir)/lib
+INCLUDES = @INCLUDES@ -I$(top_builddir)
+
+noinst_HEADERS = \
+       xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
+       buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
+       protocol.h route.h subnet.h tincctl.h top.h bsd/tunemu.h
 
-noinst_HEADERS = conf.h connection.h device.h edge.h event.h graph.h logger.h meta.h net.h netutl.h node.h process.h   \
-       protocol.h route.h subnet.h bsd/tunemu.h
+nodist_noinst_HEADERS = \
+       cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h
 
-LIBS = @LIBS@
+LIBS = @LIBS@ @LIBGCRYPT_LIBS@
 
 if TUNEMU
 LIBS += -lpcap
 endif
 
-tincd_LDADD = \
-       $(top_builddir)/lib/libvpn.a
-
-AM_CFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\"
+AM_CFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
 
 dist-hook:
        rm -f `find . -type l`
index 45c2d5f656b5b90338e7cce3de62a526c34d26dd..9a1688a805624a0146f08e93ef09e8e62ee30754 100644 (file)
@@ -195,19 +195,19 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
 #ifdef HAVE_TUNEMU
                case DEVICE_TYPE_TUNEMU:
                        if(device_type == DEVICE_TYPE_TUNEMU)
-                               lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14);
+                               inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
                        else
 #endif
-                               lenin = read(device_fd, packet->data + 14, MTU - 14);
+                               inlen = read(device_fd, packet->data + 14, MTU - 14);
 
-                       if(lenin <= 0) {
+                       if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
@@ -229,14 +229,14 @@ static bool read_packet(vpn_packet_t *packet) {
                                        return false;
                        }
 
-                       packet->len = lenin + 14;
+                       packet->len = inlen + 14;
                        break;
 
                case DEVICE_TYPE_TUNIFHEAD: {
                        u_int32_t type;
-                       struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}};
+                       struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}};
 
-                       if((lenin = readv(device_fd, vector, 2)) <= 0) {
+                       if((inlen = readv(device_fd, vector, 2)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
@@ -260,18 +260,18 @@ static bool read_packet(vpn_packet_t *packet) {
                                        return false;
                        }
 
-                       packet->len = lenin + 10;
+                       packet->len = inlen + 10;
                        break;
                }
 
                case DEVICE_TYPE_TAP:
-                       if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
+                       if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
                        }
 
-                       packet->len = lenin;
+                       packet->len = inlen;
                        break;
 
                default:
@@ -301,7 +301,7 @@ static bool write_packet(vpn_packet_t *packet) {
 
                case DEVICE_TYPE_TUNIFHEAD: {
                        u_int32_t type;
-                       struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, packet->len - 14}};
+                       struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}};
                        int af;
                        
                        af = (packet->data[12] << 8) + packet->data[13];
diff --git a/src/buffer.c b/src/buffer.c
new file mode 100644 (file)
index 0000000..3d4c329
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+    buffer.c -- buffer management
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "buffer.h"
+#include "xalloc.h"
+
+void buffer_compact(buffer_t *buffer, int maxsize) {
+       if(buffer->len >= maxsize || buffer->offset / 7 > buffer->len / 8) {
+               memmove(buffer->data, buffer->data + buffer->offset, buffer->len - buffer->offset);
+               buffer->len -= buffer->offset;
+               buffer->offset = 0;
+       }
+}
+
+// Make sure we can add size bytes to the buffer, and return a pointer to the start of those bytes.
+
+char *buffer_prepare(buffer_t *buffer, int size) {
+       if(!buffer->data) {
+               buffer->maxlen = size;
+               buffer->data = xmalloc(size);
+       } else {
+               if(buffer->offset && buffer->len + size > buffer->maxlen) {
+                       memmove(buffer->data, buffer->data + buffer->offset, buffer->len - buffer->offset);
+                       buffer->len -= buffer->offset;
+                       buffer->offset = 0;
+               }
+
+               if(buffer->len + size > buffer->maxlen) {
+                       buffer->maxlen = buffer->len + size;
+                       buffer->data = xrealloc(buffer->data, buffer->maxlen);
+               }
+       }
+
+       char *start = buffer->data + buffer->len;
+
+       buffer->len += size;
+
+       return start;
+}
+
+// Copy data into the buffer.
+                       
+void buffer_add(buffer_t *buffer, const char *data, int size) {
+       memcpy(buffer_prepare(buffer, size), data, size);
+}
+
+// Remove given number of bytes from the buffer, return a pointer to the start of them.
+
+static char *buffer_consume(buffer_t *buffer, int size) {
+       char *start = buffer->data + buffer->offset;
+
+       buffer->offset += size;
+
+       if(buffer->offset >= buffer->len) {
+               buffer->offset = 0;
+               buffer->len = 0;
+       }
+
+       return start;
+}
+
+// Check if there is a complete line in the buffer, and if so, return it NULL-terminated.
+
+char *buffer_readline(buffer_t *buffer) {
+       char *newline = memchr(buffer->data + buffer->offset, '\n', buffer->len - buffer->offset);
+
+       if(!newline)
+               return NULL;
+
+       int len = newline + 1 - (buffer->data + buffer->offset);
+       *newline = 0;
+       return buffer_consume(buffer, len);
+}
+
+// Check if we have enough bytes in the buffer, and if so, return a pointer to the start of them.
+
+char *buffer_read(buffer_t *buffer, int size) {
+       if(buffer->len - buffer->offset < size)
+               return NULL;
+
+       return buffer_consume(buffer, size);
+}
+
+void buffer_clear(buffer_t *buffer) {
+       free(buffer->data);
+       buffer->data = NULL;
+       buffer->maxlen = 0;
+       buffer->len = 0;
+       buffer->offset = 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
new file mode 100644 (file)
index 0000000..a96c15a
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __TINC_BUFFER_H__
+#define __TINC_BUFFER_H__
+
+typedef struct buffer_t {
+       char *data;
+       int maxlen;
+       int len;
+       int offset;
+} buffer_t;
+
+extern void buffer_compact(buffer_t *buffer, int maxsize);
+extern char *buffer_prepare(buffer_t *buffer, int size);
+extern void buffer_add(buffer_t *buffer, const char *data, int size);
+extern char *buffer_readline(buffer_t *buffer);
+extern char *buffer_read(buffer_t *buffer, int size);
+extern void buffer_clear(buffer_t *buffer);
+
+#endif
index 1560541a101c315b63c2950eb48758cb56b2d515..f47faefba0f1bc854f62629f6d144b9f9793d515 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "connection.h"
 #include "conf.h"
 #include "list.h"
@@ -33,7 +33,7 @@
 #include "utils.h"                             /* for cp */
 #include "xalloc.h"
 
-avl_tree_t *config_tree;
+splay_tree_t *config_tree;
 
 int pinginterval = 0;                  /* seconds between pings */
 int pingtimeout = 0;                   /* seconds to wait for response */
@@ -63,12 +63,12 @@ static int config_compare(const config_t *a, const config_t *b) {
                return a->file ? strcmp(a->file, b->file) : 0;
 }
 
-void init_configuration(avl_tree_t ** config_tree) {
-       *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
+void init_configuration(splay_tree_t ** config_tree) {
+       *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
 }
 
-void exit_configuration(avl_tree_t ** config_tree) {
-       avl_delete_tree(*config_tree);
+void exit_configuration(splay_tree_t ** config_tree) {
+       splay_delete_tree(*config_tree);
        *config_tree = NULL;
 }
 
@@ -89,18 +89,18 @@ void free_config(config_t *cfg) {
        free(cfg);
 }
 
-void config_add(avl_tree_t *config_tree, config_t *cfg) {
-       avl_insert(config_tree, cfg);
+void config_add(splay_tree_t *config_tree, config_t *cfg) {
+       splay_insert(config_tree, cfg);
 }
 
-config_t *lookup_config(const avl_tree_t *config_tree, char *variable) {
+config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
        config_t cfg, *found;
 
        cfg.variable = variable;
        cfg.file = NULL;
        cfg.line = 0;
 
-       found = avl_search_closest_greater(config_tree, &cfg);
+       found = splay_search_closest_greater(config_tree, &cfg);
 
        if(!found)
                return NULL;
@@ -111,11 +111,11 @@ config_t *lookup_config(const avl_tree_t *config_tree, char *variable) {
        return found;
 }
 
-config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg) {
-       avl_node_t *node;
+config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
+       splay_node_t *node;
        config_t *found;
 
-       node = avl_search_node(config_tree, cfg);
+       node = splay_search_node(config_tree, cfg);
 
        if(node) {
                if(node->next) {
@@ -203,9 +203,9 @@ bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
        /* Teach newbies what subnets are... */
 
        if(((subnet.type == SUBNET_IPV4)
-               && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
+               && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
                || ((subnet.type == SUBNET_IPV6)
-               && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
+               && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
                logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
                           cfg->variable, cfg->file, cfg->line);
                return false;
@@ -286,7 +286,7 @@ config_t *parse_config_line(char *line, const char *fname, int lineno) {
   Parse a configuration file and put the results in the configuration tree
   starting at *base.
 */
-bool read_config_file(avl_tree_t *config_tree, const char *fname) {
+bool read_config_file(splay_tree_t *config_tree, const char *fname) {
        FILE *fp;
        char buffer[MAX_STRING_SIZE];
        char *line;
@@ -338,7 +338,7 @@ bool read_config_file(avl_tree_t *config_tree, const char *fname) {
        return result;
 }
 
-void read_config_options(avl_tree_t *config_tree, const char *prefix) {
+void read_config_options(splay_tree_t *config_tree, const char *prefix) {
        list_node_t *node, *next;
        size_t prefix_len = prefix ? strlen(prefix) : 0;
 
@@ -400,64 +400,22 @@ bool read_connection_config(connection_t *c) {
        return x;
 }
 
-FILE *ask_and_open(const char *filename, const char *what) {
-       FILE *r;
-       char *directory;
-       char line[PATH_MAX];
-       const char *fn;
-
-       /* Check stdin and stdout */
-       if(!isatty(0) || !isatty(1)) {
-               /* Argh, they are running us from a script or something.  Write
-                  the files to the current directory and let them burn in hell
-                  for ever. */
-               fn = filename;
-       } else {
-               /* Ask for a file and/or directory name. */
-               fprintf(stdout, "Please enter a file to save %s to [%s]: ",
-                               what, filename);
-               fflush(stdout);
-
-               fn = readline(stdin, line, sizeof line);
-
-               if(!fn) {
-                       fprintf(stderr, "Error while reading stdin: %s\n",
-                                       strerror(errno));
-                       return NULL;
-               }
+bool append_config_file(const char *name, const char *key, const char *value) {
+       char *fname;
+       xasprintf(&fname, "%s/hosts/%s", confbase, name);
 
-               if(!strlen(fn))
-                       /* User just pressed enter. */
-                       fn = filename;
-       }
+       FILE *fp = fopen(fname, "a");
 
-#ifdef HAVE_MINGW
-       if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
-#else
-       if(fn[0] != '/') {
-#endif
-               /* The directory is a relative path or a filename. */
-               char *p;
-
-               directory = get_current_dir_name();
-               xasprintf(&p, "%s/%s", directory, fn);
-               free(directory);
-               fn = p;
+       if(!fp) {
+               logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
+       } else {
+               fprintf(fp, "\n# The following line was automatically added by tinc\n%s = %s\n", key, value);
+               fclose(fp);
        }
 
-       umask(0077);                            /* Disallow everything for group and other */
-
-       /* Open it first to keep the inode busy */
-
-       r = fopen(fn, "r+") ?: fopen(fn, "w+");
-
-       if(!r) {
-               fprintf(stderr, "Error opening file `%s': %s\n",
-                               fn, strerror(errno));
-               return NULL;
-       }
+       free(fname);
 
-       return r;
+       return fp;
 }
 
 bool disable_old_keys(FILE *f) {
index 5b0796e5bfbbf4fe5fb38f082a3a0c08390328f6..bd3850bfaca12c94648996bba05c55edc65ced90 100644 (file)
@@ -21,7 +21,7 @@
 #ifndef __TINC_CONF_H__
 #define __TINC_CONF_H__
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "list.h"
 
 typedef struct config_t {
@@ -33,7 +33,7 @@ typedef struct config_t {
 
 #include "subnet.h"
 
-extern avl_tree_t *config_tree;
+extern splay_tree_t *config_tree;
 
 extern int pinginterval;
 extern int pingtimeout;
@@ -43,13 +43,13 @@ extern char *confbase;
 extern char *netname;
 extern list_t *cmdline_conf;
 
-extern void init_configuration(avl_tree_t **);
-extern void exit_configuration(avl_tree_t **);
+extern void init_configuration(splay_tree_t **);
+extern void exit_configuration(splay_tree_t **);
 extern config_t *new_config(void) __attribute__ ((__malloc__));
 extern void free_config(config_t *);
-extern void config_add(avl_tree_t *, config_t *);
-extern config_t *lookup_config(const avl_tree_t *, char *);
-extern config_t *lookup_config_next(const avl_tree_t *, const config_t *);
+extern void config_add(splay_tree_t *, config_t *);
+extern config_t *lookup_config(splay_tree_t *, char *);
+extern config_t *lookup_config_next(splay_tree_t *, const config_t *);
 extern bool get_config_bool(const config_t *, bool *);
 extern bool get_config_int(const config_t *, int *);
 extern bool get_config_string(const config_t *, char **);
@@ -57,11 +57,12 @@ extern bool get_config_address(const config_t *, struct addrinfo **);
 extern bool get_config_subnet(const config_t *, struct subnet_t **);
 
 extern config_t *parse_config_line(char *, const char *, int);
-extern bool read_config_file(avl_tree_t *, const char *);
-extern void read_config_options(avl_tree_t *, const char *);
+extern bool read_config_file(splay_tree_t *, const char *);
+extern void read_config_options(splay_tree_t *, const char *);
 extern bool read_server_config(void);
 extern bool read_connection_config(struct connection_t *);
-extern FILE *ask_and_open(const char *, const char *);
+extern bool append_config_file(const char *, const char *, const char *);
+extern FILE *ask_and_open(const char *, const char *, const char *);
 extern bool is_safe_path(const char *);
 extern bool disable_old_keys(FILE *);
 
index 0211845b89521aa6a077a20a4a9b3bd83559f7a4..9587819d03e3d8bedf04890b8ad3bc526b9631ac 100644 (file)
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "conf.h"
+#include "control_common.h"
+#include "list.h"
 #include "logger.h"
 #include "subnet.h"
 #include "utils.h"
 #include "xalloc.h"
 
-avl_tree_t *connection_tree;   /* Meta connections */
+splay_tree_t *connection_tree; /* Meta connections */
 connection_t *everyone;
 
 static int connection_compare(const connection_t *a, const connection_t *b) {
@@ -36,55 +39,39 @@ static int connection_compare(const connection_t *a, const connection_t *b) {
 }
 
 void init_connections(void) {
-       connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection);
+       connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection);
        everyone = new_connection();
        everyone->name = xstrdup("everyone");
        everyone->hostname = xstrdup("BROADCAST");
 }
 
 void exit_connections(void) {
-       avl_delete_tree(connection_tree);
+       splay_delete_tree(connection_tree);
        free_connection(everyone);
 }
 
 connection_t *new_connection(void) {
-       connection_t *c;
-
-       c = xmalloc_and_zero(sizeof(connection_t));
-
-       if(!c)
-               return NULL;
-
-       gettimeofday(&c->start, NULL);
-
-       return c;
+       return xmalloc_and_zero(sizeof(connection_t));
 }
 
 void free_connection(connection_t *c) {
+       if(!c)
+               return;
+
        if(c->name)
                free(c->name);
 
        if(c->hostname)
                free(c->hostname);
 
-       if(c->inkey)
-               free(c->inkey);
+       cipher_close(&c->incipher);
+       digest_close(&c->indigest);
+       cipher_close(&c->outcipher);
+       digest_close(&c->outdigest);
 
-       if(c->outkey)
-               free(c->outkey);
-
-       if(c->inctx) {
-               EVP_CIPHER_CTX_cleanup(c->inctx);
-               free(c->inctx);
-       }
-
-       if(c->outctx) {
-               EVP_CIPHER_CTX_cleanup(c->outctx);
-               free(c->outctx);
-       }
-
-       if(c->mychallenge)
-               free(c->mychallenge);
+       ecdh_free(&c->ecdh);
+       ecdsa_free(&c->ecdsa);
+       rsa_free(&c->rsa);
 
        if(c->hischallenge)
                free(c->hischallenge);
@@ -92,35 +79,40 @@ void free_connection(connection_t *c) {
        if(c->config_tree)
                exit_configuration(&c->config_tree);
 
-       if(c->outbuf)
-               free(c->outbuf);
+       buffer_clear(&c->inbuf);
+       buffer_clear(&c->outbuf);
+       
+       if(event_initialized(&c->inevent))
+               event_del(&c->inevent);
+
+       if(event_initialized(&c->outevent))
+               event_del(&c->outevent);
 
-       if(c->rsa_key)
-               RSA_free(c->rsa_key);
+       if(c->socket > 0)
+               closesocket(c->socket);
 
        free(c);
 }
 
 void connection_add(connection_t *c) {
-       avl_insert(connection_tree, c);
+       splay_insert(connection_tree, c);
 }
 
 void connection_del(connection_t *c) {
-       avl_delete(connection_tree, c);
+       splay_delete(connection_tree, c);
 }
 
-void dump_connections(void) {
-       avl_node_t *node;
+bool dump_connections(connection_t *cdump) {
+       splay_node_t *node;
        connection_t *c;
 
-       logger(LOG_DEBUG, "Connections:");
-
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
-               logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d",
-                          c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status),
-                          c->outbufsize, c->outbufstart, c->outbuflen);
+               send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
+                               CONTROL, REQ_DUMP_CONNECTIONS,
+                               c->name, c->hostname, c->options, c->socket,
+                               bitfield_to_int(&c->status, sizeof c->status));
        }
 
-       logger(LOG_DEBUG, "End of connections.");
+       return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
 }
index ff2c9ccdcb34bf965033ca012bce9fcaab3d5aea..20e00763c0a1d9881b15509b34061b0327b0bce0 100644 (file)
 #ifndef __TINC_CONNECTION_H__
 #define __TINC_CONNECTION_H__
 
-#include <openssl/rsa.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "digest.h"
+#include "rsa.h"
+#include "splay_tree.h"
 
 #define OPTION_INDIRECT                0x0001
 #define OPTION_TCPONLY         0x0002
 #define OPTION_CLAMP_MSS       0x0008
 
 typedef struct connection_status_t {
-       unsigned int pinged:1;                          /* sent ping */
-       unsigned int active:1;                          /* 1 if active.. */
-       unsigned int connecting:1;                      /* 1 if we are waiting for a non-blocking connect() to finish */
-       unsigned int termreq:1;                         /* the termination of this connection was requested */
-       unsigned int remove:1;                          /* Set to 1 if you want this connection removed */
-       unsigned int timeout:1;                         /* 1 if gotten timeout */
-       unsigned int encryptout:1;                      /* 1 if we can encrypt outgoing traffic */
-       unsigned int decryptin:1;                       /* 1 if we have to decrypt incoming traffic */
-       unsigned int mst:1;                             /* 1 if this connection is part of a minimum spanning tree */
-       unsigned int unused:23;
+               unsigned int pinged:1;                  /* sent ping */
+               unsigned int active:1;                  /* 1 if active.. */
+               unsigned int connecting:1;              /* 1 if we are waiting for a non-blocking connect() to finish */
+               unsigned int termreq:1;                 /* the termination of this connection was requested */
+               unsigned int remove_unused:1;           /* Set to 1 if you want this connection removed */
+               unsigned int timeout_unused:1;          /* 1 if gotten timeout */
+               unsigned int encryptout:1;              /* 1 if we can encrypt outgoing traffic */
+               unsigned int decryptin:1;               /* 1 if we have to decrypt incoming traffic */
+               unsigned int mst:1;                     /* 1 if this connection is part of a minimum spanning tree */
+               unsigned int control:1;
+               unsigned int pcap:1;
+               unsigned int unused:21;
 } connection_status_t;
 
+#include "ecdh.h"
+#include "ecdsa.h"
 #include "edge.h"
 #include "net.h"
 #include "node.h"
@@ -53,7 +58,8 @@ typedef struct connection_t {
 
        union sockaddr_t address;                       /* his real (internet) ip */
        char *hostname;                         /* the hostname of its real ip */
-       int protocol_version;           /* used protocol */
+       int protocol_major;             /* used protocol */
+       int protocol_minor;             /* used protocol */
 
        int socket;                                     /* socket used for this connection */
        uint32_t options;                       /* options for this connection */
@@ -65,42 +71,34 @@ typedef struct connection_t {
        struct node_t *node;            /* node associated with the other end */
        struct edge_t *edge;            /* edge associated with this connection */
 
-       RSA *rsa_key;                           /* his public/private key */
-       const EVP_CIPHER *incipher;     /* Cipher he will use to send data to us */
-       const EVP_CIPHER *outcipher;    /* Cipher we will use to send data to him */
-       EVP_CIPHER_CTX *inctx;          /* Context of encrypted meta data that will come from him to us */
-       EVP_CIPHER_CTX *outctx;         /* Context of encrypted meta data that will be sent from us to him */
-       char *inkey;                            /* His symmetric meta key + iv */
-       char *outkey;                           /* Our symmetric meta key + iv */
-       int inkeylength;                        /* Length of his key + iv */
-       int outkeylength;                       /* Length of our key + iv */
-       const EVP_MD *indigest;
-       const EVP_MD *outdigest;
+       rsa_t rsa;                      /* his public RSA key */
+       ecdsa_t ecdsa;                  /* his public ECDSA key */
+       ecdsa_t ecdh;                   /* state for ECDH key exchange */
+       cipher_t incipher;              /* Cipher he will use to send data to us */
+       cipher_t outcipher;             /* Cipher we will use to send data to him */
+       digest_t indigest;
+       digest_t outdigest;
+
        int inmaclength;
        int outmaclength;
        int incompression;
        int outcompression;
-       char *mychallenge;                      /* challenge we received from him */
-       char *hischallenge;                     /* challenge we sent to him */
 
-       char buffer[MAXBUFSIZE];        /* metadata input buffer */
-       int buflen;                                     /* bytes read into buffer */
-       int reqlen;                                     /* length of incoming request */
+       char *hischallenge;             /* The challenge we sent to him */
+
+       struct buffer_t inbuf;
+       struct buffer_t outbuf;
+       struct event inevent;                           /* input event on this metadata connection */
+       struct event outevent;                          /* output event on this metadata connection */
        int tcplen;                                     /* length of incoming TCPpacket */
        int allow_request;                      /* defined if there's only one request possible */
 
-       char *outbuf;                           /* metadata output buffer */
-       int outbufstart;                        /* index of first meaningful byte in output buffer */
-       int outbuflen;                          /* number of meaningful bytes in output buffer */
-       int outbufsize;                         /* number of bytes allocated to output buffer */
-
        time_t last_ping_time;          /* last time we saw some activity from the other end or pinged them */
-       time_t last_flushed_time;       /* last time buffer was empty. Only meaningful if outbuflen > 0 */
 
-       avl_tree_t *config_tree;        /* Pointer to configuration tree belonging to him */
+       splay_tree_t *config_tree;      /* Pointer to configuration tree belonging to him */
 } connection_t;
 
-extern avl_tree_t *connection_tree;
+extern splay_tree_t *connection_tree;
 extern connection_t *everyone;
 
 extern void init_connections(void);
@@ -109,6 +107,6 @@ extern connection_t *new_connection(void) __attribute__ ((__malloc__));
 extern void free_connection(connection_t *);
 extern void connection_add(connection_t *);
 extern void connection_del(connection_t *);
-extern void dump_connections(void);
+extern bool dump_connections(struct connection_t *);
 
 #endif                                                 /* __TINC_CONNECTION_H__ */
diff --git a/src/control.c b/src/control.c
new file mode 100644 (file)
index 0000000..86224c2
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+    control.c -- Control socket handling.
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+#include "crypto.h"
+#include "conf.h"
+#include "control.h"
+#include "control_common.h"
+#include "graph.h"
+#include "logger.h"
+#include "meta.h"
+#include "net.h"
+#include "netutl.h"
+#include "protocol.h"
+#include "route.h"
+#include "splay_tree.h"
+#include "utils.h"
+#include "xalloc.h"
+
+char controlcookie[65];
+extern char *pidfilename;
+
+static bool control_return(connection_t *c, int type, int error) {
+       return send_request(c, "%d %d %d", CONTROL, type, error);
+}
+
+static bool control_ok(connection_t *c, int type) {
+       return control_return(c, type, 0);
+}
+
+bool control_h(connection_t *c, char *request) {
+       int type;
+
+       if(!c->status.control || c->allow_request != CONTROL) {
+               logger(LOG_ERR, "Unauthorized control request from %s (%s)", c->name, c->hostname);
+               return false;
+       }
+
+       if(sscanf(request, "%*d %d", &type) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "CONTROL", c->name, c->hostname);
+               return false;
+       }
+
+       switch (type) {
+               case REQ_STOP:
+                       event_loopexit(NULL);
+                       return control_ok(c, REQ_STOP);
+
+               case REQ_DUMP_NODES:
+                       return dump_nodes(c);
+                       
+               case REQ_DUMP_EDGES:
+                       return dump_edges(c);
+
+               case REQ_DUMP_SUBNETS:
+                       return dump_subnets(c);
+
+               case REQ_DUMP_CONNECTIONS:
+                       return dump_connections(c);
+
+               case REQ_PURGE:
+                       purge();
+                       return control_ok(c, REQ_PURGE);
+
+               case REQ_SET_DEBUG: {
+                       int new_level;
+                       if(sscanf(request, "%*d %*d %d", &new_level) != 1)
+                               return false;
+                       send_request(c, "%d %d %d", CONTROL, REQ_SET_DEBUG, debug_level);
+                       if(new_level >= 0)
+                               debug_level = new_level;
+                       return true;
+               }
+
+               case REQ_RETRY:
+                       retry();
+                       return control_ok(c, REQ_RETRY);
+
+               case REQ_RELOAD:
+                       logger(LOG_NOTICE, "Got '%s' command", "reload");
+                       int result = reload_configuration();
+                       return control_return(c, REQ_RELOAD, result);
+
+               case REQ_DISCONNECT: {
+                       char name[MAX_STRING_SIZE];
+                       connection_t *other;
+                       splay_node_t *node, *next;
+                       bool found = false;
+
+                       if(sscanf(request, "%*d %*d " MAX_STRING, name) != 1)
+                               return control_return(c, REQ_DISCONNECT, -1);
+
+                       for(node = connection_tree->head; node; node = next) {
+                               next = node->next;
+                               other = node->data;
+                               if(strcmp(other->name, name))
+                                       continue;
+                               terminate_connection(other, other->status.active);
+                               found = true;
+                       }
+
+                       return control_return(c, REQ_DISCONNECT, found ? 0 : -2);
+               }
+
+               case REQ_DUMP_TRAFFIC:
+                       return dump_traffic(c);
+
+               case REQ_PCAP:
+                       c->status.pcap = true;
+                       pcap = true;
+                       return true;
+
+               default:
+                       return send_request(c, "%d %d", CONTROL, REQ_INVALID);
+       }
+}
+
+bool init_control(void) {
+       randomize(controlcookie, sizeof controlcookie / 2);
+       bin2hex(controlcookie, controlcookie, sizeof controlcookie / 2);
+
+       FILE *f = fopen(pidfilename, "w");
+       if(!f) {
+               logger(LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
+               return false;
+       }
+
+#ifdef HAVE_FCHMOD
+       fchmod(fileno(f), 0600);
+#else
+       chmod(pidfilename, 0600);
+#endif
+       // Get the address and port of the first listening socket
+
+       char *localhost = NULL;
+       sockaddr_t sa;
+       socklen_t len = sizeof sa;
+
+       // Make sure we have a valid address, and map 0.0.0.0 and :: to 127.0.0.1 and ::1.
+
+       if(getsockname(listen_socket[0].tcp, (struct sockaddr *)&sa, &len)) {
+               xasprintf(&localhost, "127.0.0.1 port %d", myport);
+       } else {
+               if(sa.sa.sa_family == AF_INET) {
+                       if(sa.in.sin_addr.s_addr == 0)
+                               sa.in.sin_addr.s_addr = htonl(0x7f000001);
+               } else if(sa.sa.sa_family == AF_INET6) {
+                       static const uint8_t zero[16] = {0};
+                       if(!memcmp(sa.in6.sin6_addr.s6_addr, zero, sizeof zero))
+                               sa.in6.sin6_addr.s6_addr[15] = 1;
+               }
+
+               localhost = sockaddr2hostname(&sa);
+       }
+
+       fprintf(f, "%d %s %s\n", (int)getpid(), controlcookie, localhost);
+
+       free(localhost);
+       fclose(f);
+
+       return true;
+}
+
+void exit_control(void) {
+       unlink(pidfilename);
+}
diff --git a/src/control.h b/src/control.h
new file mode 100644 (file)
index 0000000..ce8145a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    control.h -- header for control.c.
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CONTROL_H__
+#define __TINC_CONTROL_H__
+
+extern bool init_control();
+extern void exit_control();
+extern char controlcookie[];
+
+#endif
diff --git a/src/control_common.h b/src/control_common.h
new file mode 100644 (file)
index 0000000..615e10a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    control_protocol.h -- control socket protocol.
+    Copyright (C) 2007 Scott Lamb <slamb@slamb.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CONTROL_PROTOCOL_H__
+#define __TINC_CONTROL_PROTOCOL_H__
+
+#include "protocol.h"
+
+enum request_type {
+       REQ_INVALID = -1,
+       REQ_STOP = 0,
+       REQ_RELOAD,
+       REQ_RESTART,
+       REQ_DUMP_NODES,
+       REQ_DUMP_EDGES,
+       REQ_DUMP_SUBNETS,
+       REQ_DUMP_CONNECTIONS,
+       REQ_DUMP_GRAPH,
+       REQ_PURGE,
+       REQ_SET_DEBUG,
+       REQ_RETRY,
+       REQ_CONNECT,
+       REQ_DISCONNECT,
+       REQ_DUMP_TRAFFIC,
+       REQ_PCAP,
+};
+
+#define TINC_CTL_VERSION_CURRENT 0
+
+#endif
index 4365399df44dcc3a0e7f85e9ec24ce93687dfed0..cf9f1b579c497338eda1dfcfd60837110d017e20 100644 (file)
@@ -69,18 +69,18 @@ static bool setup_device(void) {
        }
 
        for (i = 0; ; i++) {
-               len = sizeof(adapterid);
+               len = sizeof adapterid;
                if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
                        break;
 
                /* Find out more about this adapter */
 
-               snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
+               snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
 
                 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
                        continue;
 
-               len = sizeof(adaptername);
+               len = sizeof adaptername;
                err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
 
                RegCloseKey(key2);
@@ -104,7 +104,7 @@ static bool setup_device(void) {
                                continue;
                }
 
-               snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
+               snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
                if(device_handle != INVALID_HANDLE_VALUE) {
                        CloseHandle(device_handle);
@@ -126,7 +126,7 @@ static bool setup_device(void) {
        if(!iface)
                iface = xstrdup(adaptername);
 
-       snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
+       snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
        
        /* Now we are going to open this device twice: once for reading and once for writing.
           We do this because apparently it isn't possible to check for activity in the select() loop.
@@ -150,7 +150,7 @@ static bool setup_device(void) {
 
        /* Get MAC address from tap device */
 
-       if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
+       if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
                logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
                return false;
        }
@@ -173,7 +173,7 @@ static bool setup_device(void) {
                   It passes everything it reads to the socket. */
        
                char buf[MTU];
-               long lenin;
+               long inlen;
 
                CloseHandle(device_handle);
 
@@ -196,8 +196,8 @@ static bool setup_device(void) {
                /* Pass packets */
 
                for(;;) {
-                       ReadFile(device_handle, buf, MTU, &lenin, NULL);
-                       write(sp[1], buf, lenin);
+                       ReadFile(device_handle, buf, MTU, &inlen, NULL);
+                       write(sp[1], buf, inlen);
                }
        }
 
@@ -226,15 +226,15 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
 
-       if((lenin = read(sp[0], packet->data, MTU)) <= 0) {
+       if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
        }
        
-       packet->len = lenin;
+       packet->len = inlen;
 
        device_total_in += packet->len;
 
@@ -245,12 +245,12 @@ static bool read_packet(vpn_packet_t *packet) {
 }
 
 static bool write_packet(vpn_packet_t *packet) {
-       long lenout;
+       long outlen;
 
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(!WriteFile (device_handle, packet->data, packet->len, &lenout, NULL)) {
+       if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
                logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
index eaeca1883fcfb90b4cc289cec854627b9ec530e4..993d4ce82698a49bba569b3c03bd024ab9790422 100644 (file)
 
 extern int device_fd;
 extern char *device;
-
 extern char *iface;
 
+extern uint64_t device_in_packets;
+extern uint64_t device_in_bytes;
+extern uint64_t device_out_packets;
+extern uint64_t device_out_bytes;
+
 typedef struct devops_t {
        bool (*setup)(void);
        void (*close)(void);
diff --git a/src/dropin.c b/src/dropin.c
new file mode 100644 (file)
index 0000000..eb17aca
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+    dropin.c -- a set of drop-in replacements for libc functions
+    Copyright (C) 2000-2005 Ivo Timmermans,
+                  2000-2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "xalloc.h"
+
+#ifndef HAVE_DAEMON
+/*
+  Replacement for the daemon() function.
+  
+  The daemon() function is for programs wishing to detach themselves
+  from the controlling terminal and run in the background as system
+  daemons.
+
+  Unless the argument nochdir is non-zero, daemon() changes the
+  current working directory to the root (``/'').
+
+  Unless the argument noclose is non-zero, daemon() will redirect
+  standard input, standard output and standard error to /dev/null.
+*/
+int daemon(int nochdir, int noclose) {
+#ifdef HAVE_FORK
+       pid_t pid;
+       int fd;
+
+       pid = fork();
+
+       /* Check if forking failed */
+       if(pid < 0) {
+               perror("fork");
+               exit(-1);
+       }
+
+       /* If we are the parent, terminate */
+       if(pid)
+               exit(0);
+
+       /* Detach by becoming the new process group leader */
+       if(setsid() < 0) {
+               perror("setsid");
+               return -1;
+       }
+
+       /* Change working directory to the root (to avoid keeping mount
+          points busy) */
+       if(!nochdir) {
+               chdir("/");
+       }
+
+       /* Redirect stdin/out/err to /dev/null */
+       if(!noclose) {
+               fd = open("/dev/null", O_RDWR);
+
+               if(fd < 0) {
+                       perror("opening /dev/null");
+                       return -1;
+               } else {
+                       dup2(fd, 0);
+                       dup2(fd, 1);
+                       dup2(fd, 2);
+               }
+       }
+
+       return 0;
+#else
+       return -1;
+#endif
+}
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+/*
+  Replacement for the GNU get_current_dir_name function:
+
+  get_current_dir_name will malloc(3) an array big enough to hold the
+  current directory name.  If the environment variable PWD is set, and
+  its value is correct, then that value will be returned.
+*/
+char *get_current_dir_name(void) {
+       size_t size;
+       char *buf;
+       char *r;
+
+       /* Start with 100 bytes.  If this turns out to be insufficient to
+          contain the working directory, double the size.  */
+       size = 100;
+       buf = xmalloc(size);
+
+       errno = 0;                                      /* Success */
+       r = getcwd(buf, size);
+
+       /* getcwd returns NULL and sets errno to ERANGE if the bufferspace
+          is insufficient to contain the entire working directory.  */
+       while(r == NULL && errno == ERANGE) {
+               free(buf);
+               size <<= 1;                             /* double the size */
+               buf = xmalloc(size);
+               r = getcwd(buf, size);
+       }
+
+       return buf;
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int asprintf(char **buf, const char *fmt, ...) {
+       int result;
+       va_list ap;
+       va_start(ap, fmt);
+       result = vasprintf(buf, fmt, ap);
+       va_end(ap);
+       return result;
+}
+
+int vasprintf(char **buf, const char *fmt, va_list ap) {
+       int status;
+       va_list aq;
+       int len;
+
+       len = 4096;
+       *buf = xmalloc(len);
+
+       va_copy(aq, ap);
+       status = vsnprintf(*buf, len, fmt, aq);
+       va_end(aq);
+
+       if(status >= 0)
+               *buf = xrealloc(*buf, status + 1);
+
+       if(status > len - 1) {
+               len = status;
+               va_copy(aq, ap);
+               status = vsnprintf(*buf, len, fmt, aq);
+               va_end(aq);
+       }
+
+       return status;
+}
+#endif
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *tv, void *tz) {
+       tv->tv_sec = time(NULL);
+       tv->tv_usec = 0;
+       return 0;
+}
+#endif
+
+#ifndef HAVE_USLEEP
+int usleep(long long usec) {
+       struct timeval tv = {usec / 1000000, (usec / 1000) % 1000};
+       select(0, NULL, NULL, NULL, &tv);
+       return 0;
+}
+#endif
diff --git a/src/dropin.h b/src/dropin.h
new file mode 100644 (file)
index 0000000..3617b70
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    dropin.h -- header file for dropin.c
+    Copyright (C) 2000-2005 Ivo Timmermans,
+                  2000-2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __DROPIN_H__
+#define __DROPIN_H__
+
+#include "fake-getaddrinfo.h"
+#include "fake-getnameinfo.h"
+
+#ifndef HAVE_DAEMON
+extern int daemon(int, int);
+#endif
+
+#ifndef HAVE_GET_CURRENT_DIR_NAME
+extern char *get_current_dir_name(void);
+#endif
+
+#ifndef HAVE_ASPRINTF
+extern int asprintf(char **, const char *, ...);
+extern int vasprintf(char **, const char *, va_list ap);
+#endif
+
+#ifndef HAVE_GETTIMEOFDAY
+extern int gettimeofday(struct timeval *, void *);
+#endif
+
+#ifndef HAVE_USLEEP
+extern int usleep(long long usec);
+#endif
+
+#endif                                                 /* __DROPIN_H__ */
index e42dbd1739d5e6a24b7c496fce9df03652169266..f5aa0994412e2b0416467cf03ada7ce423cc3f6b 100644 (file)
@@ -20,7 +20,8 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "control_common.h"
 #include "edge.h"
 #include "logger.h"
 #include "netutl.h"
@@ -28,7 +29,7 @@
 #include "utils.h"
 #include "xalloc.h"
 
-avl_tree_t *edge_weight_tree;  /* Tree with all edges, sorted on weight */
+splay_tree_t *edge_weight_tree;        /* Tree with all edges, sorted on weight */
 
 static int edge_compare(const edge_t *a, const edge_t *b) {
        return strcmp(a->to->name, b->to->name);
@@ -51,19 +52,19 @@ static int edge_weight_compare(const edge_t *a, const edge_t *b) {
 }
 
 void init_edges(void) {
-       edge_weight_tree = avl_alloc_tree((avl_compare_t) edge_weight_compare, NULL);
+       edge_weight_tree = splay_alloc_tree((splay_compare_t) edge_weight_compare, NULL);
 }
 
-avl_tree_t *new_edge_tree(void) {
-       return avl_alloc_tree((avl_compare_t) edge_compare, (avl_action_t) free_edge);
+splay_tree_t *new_edge_tree(void) {
+       return splay_alloc_tree((splay_compare_t) edge_compare, (splay_action_t) free_edge);
 }
 
-void free_edge_tree(avl_tree_t *edge_tree) {
-       avl_delete_tree(edge_tree);
+void free_edge_tree(splay_tree_t *edge_tree) {
+       splay_delete_tree(edge_tree);
 }
 
 void exit_edges(void) {
-       avl_delete_tree(edge_weight_tree);
+       splay_delete_tree(edge_weight_tree);
 }
 
 /* Creation and deletion of connection elements */
@@ -79,8 +80,8 @@ void free_edge(edge_t *e) {
 }
 
 void edge_add(edge_t *e) {
-       avl_insert(edge_weight_tree, e);
-       avl_insert(e->from->edge_tree, e);
+       splay_insert(edge_weight_tree, e);
+       splay_insert(e->from->edge_tree, e);
 
        e->reverse = lookup_edge(e->to, e->from);
 
@@ -92,8 +93,8 @@ void edge_del(edge_t *e) {
        if(e->reverse)
                e->reverse->reverse = NULL;
 
-       avl_delete(edge_weight_tree, e);
-       avl_delete(e->from->edge_tree, e);
+       splay_delete(edge_weight_tree, e);
+       splay_delete(e->from->edge_tree, e);
 }
 
 edge_t *lookup_edge(node_t *from, node_t *to) {
@@ -102,27 +103,27 @@ edge_t *lookup_edge(node_t *from, node_t *to) {
        v.from = from;
        v.to = to;
 
-       return avl_search(from->edge_tree, &v);
+       return splay_search(from->edge_tree, &v);
 }
 
-void dump_edges(void) {
-       avl_node_t *node, *node2;
+bool dump_edges(connection_t *c) {
+       splay_node_t *node, *node2;
        node_t *n;
        edge_t *e;
        char *address;
 
-       logger(LOG_DEBUG, "Edges:");
-
        for(node = node_tree->head; node; node = node->next) {
                n = node->data;
                for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
                        e = node2->data;
                        address = sockaddr2hostname(&e->address);
-                       logger(LOG_DEBUG, " %s to %s at %s options %x weight %d",
-                                  e->from->name, e->to->name, address, e->options, e->weight);
+                       send_request(c, "%d %d %s to %s at %s options %x weight %d",
+                                       CONTROL, REQ_DUMP_EDGES,
+                                       e->from->name, e->to->name, address,
+                                       e->options, e->weight);
                        free(address);
                }
        }
 
-       logger(LOG_DEBUG, "End of edges.");
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_EDGES);
 }
index 4c65213b9f1dba9a0d9db39e76d7946bbe448286..ea45f497ec4442da08e084bda52f60c2384ff062 100644 (file)
@@ -21,7 +21,7 @@
 #ifndef __TINC_EDGE_H__
 #define __TINC_EDGE_H__
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "connection.h"
 #include "net.h"
 #include "node.h"
@@ -38,17 +38,17 @@ typedef struct edge_t {
        struct edge_t *reverse;         /* edge in the opposite direction, if available */
 } edge_t;
 
-extern avl_tree_t *edge_weight_tree;   /* Tree with all known edges sorted on weight */
+extern splay_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
 
 extern void init_edges(void);
 extern void exit_edges(void);
 extern edge_t *new_edge(void) __attribute__ ((__malloc__));
 extern void free_edge(edge_t *);
-extern avl_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
-extern void free_edge_tree(avl_tree_t *);
+extern splay_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
+extern void free_edge_tree(splay_tree_t *);
 extern void edge_add(edge_t *);
 extern void edge_del(edge_t *);
 extern edge_t *lookup_edge(struct node_t *, struct node_t *);
-extern void dump_edges(void);
+extern bool dump_edges(struct connection_t *);
 
 #endif                                                 /* __TINC_EDGE_H__ */
diff --git a/src/ethernet.h b/src/ethernet.h
new file mode 100644 (file)
index 0000000..eef5f42
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    ethernet.h -- missing Ethernet related definitions
+    Copyright (C) 2005 Ivo Timmermans
+                  2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_ETHERNET_H__
+#define __TINC_ETHERNET_H__
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#ifndef ARPHRD_ETHER
+#define ARPHRD_ETHER 1
+#endif
+
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
+
+#ifndef ETH_P_ARP
+#define ETH_P_ARP 0x0806
+#endif
+
+#ifndef ETH_P_IPV6
+#define ETH_P_IPV6 0x86DD
+#endif
+
+#ifndef HAVE_STRUCT_ETHER_HEADER
+struct ether_header {
+       uint8_t ether_dhost[ETH_ALEN];
+       uint8_t ether_shost[ETH_ALEN];
+       uint16_t ether_type;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef HAVE_STRUCT_ARPHDR
+struct arphdr {
+       uint16_t ar_hrd;
+       uint16_t ar_pro;
+       uint8_t ar_hln;
+       uint8_t ar_pln; 
+       uint16_t ar_op; 
+} __attribute__ ((__packed__));
+
+#define ARPOP_REQUEST 1 
+#define ARPOP_REPLY 2 
+#define ARPOP_RREQUEST 3 
+#define ARPOP_RREPLY 4 
+#define ARPOP_InREQUEST 8 
+#define ARPOP_InREPLY 9 
+#define ARPOP_NAK 10 
+#endif
+
+#ifndef HAVE_STRUCT_ETHER_ARP
+struct  ether_arp {
+       struct  arphdr ea_hdr;
+       uint8_t arp_sha[ETH_ALEN];
+       uint8_t arp_spa[4];
+       uint8_t arp_tha[ETH_ALEN];
+       uint8_t arp_tpa[4];
+} __attribute__ ((__packed__));
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op ea_hdr.ar_op
+#endif
+
+#endif /* __TINC_ETHERNET_H__ */
diff --git a/src/event.c b/src/event.c
deleted file mode 100644 (file)
index 89ee022..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-    event.c -- event queue
-    Copyright (C) 2002-2009 Guus Sliepen <guus@tinc-vpn.org>,
-                  2002-2005 Ivo Timmermans
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "system.h"
-
-#include "avl_tree.h"
-#include "event.h"
-#include "utils.h"
-#include "xalloc.h"
-
-avl_tree_t *event_tree;
-extern time_t now;
-
-static int id;
-
-static int event_compare(const event_t *a, const event_t *b) {
-       if(a->time > b->time)
-               return 1;
-
-       if(a->time < b->time)
-               return -1;
-
-       return a->id - b->id;
-}
-
-void init_events(void) {
-       event_tree = avl_alloc_tree((avl_compare_t) event_compare, (avl_action_t) free_event);
-}
-
-void exit_events(void) {
-       avl_delete_tree(event_tree);
-}
-
-void expire_events(void) {
-       avl_node_t *node;
-       event_t *event;
-       time_t diff;
-
-       /*
-        * Make all events appear expired by substracting the difference between
-         * the expiration time of the last event and the current time.
-        */
-
-       if(!event_tree->tail)
-               return;
-
-       event = event_tree->tail->data;
-       if(event->time <= now)
-               return;
-
-       diff = event->time - now;
-       
-       for(node = event_tree->head; node; node = node->next) {
-               event = node->data;
-               event->time -= diff;
-       }
-}
-
-event_t *new_event(void) {
-       return xmalloc_and_zero(sizeof(event_t));
-}
-
-void free_event(event_t *event) {
-       free(event);
-}
-
-void event_add(event_t *event) {
-       event->id = ++id;
-       avl_insert(event_tree, event);
-}
-
-void event_del(event_t *event) {
-       avl_delete(event_tree, event);
-}
-
-event_t *get_expired_event(void) {
-       event_t *event;
-
-       if(event_tree->head) {
-               event = event_tree->head->data;
-
-               if(event->time <= now) {
-                       avl_node_t *node = event_tree->head;
-                       avl_unlink_node(event_tree, node);
-                       free(node);
-                       return event;
-               }
-       }
-
-       return NULL;
-}
-
-event_t *peek_next_event(void) {
-       if (event_tree->head)
-               return event_tree->head->data;
-       return NULL;
-}
diff --git a/src/event.h b/src/event.h
deleted file mode 100644 (file)
index 38a4eb4..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-    event.h -- header for event.c
-    Copyright (C) 2002-2009 Guus Sliepen <guus@tinc-vpn.org>,
-                  2002-2005 Ivo Timmermans
-
-    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 of the License, 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.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef __TINC_EVENT_H__
-#define __TINC_EVENT_H__
-
-#include "avl_tree.h"
-
-extern avl_tree_t *event_tree;
-
-typedef void (*event_handler_t)(void *);
-
-typedef struct event {
-       time_t time;
-       int id;
-       event_handler_t handler;
-       void *data;
-} event_t;
-
-extern void init_events(void);
-extern void exit_events(void);
-extern void expire_events(void);
-extern event_t *new_event(void) __attribute__ ((__malloc__));
-extern void free_event(event_t *);
-extern void event_add(event_t *);
-extern void event_del(event_t *);
-extern event_t *get_expired_event(void);
-extern event_t *peek_next_event(void);
-
-#endif                                                 /* __TINC_EVENT_H__ */
diff --git a/src/fake-gai-errnos.h b/src/fake-gai-errnos.h
new file mode 100644 (file)
index 0000000..4ffabb6
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * fake library for ssh
+ *
+ * This file is included in getaddrinfo.c and getnameinfo.c.
+ * See getaddrinfo.c and getnameinfo.c.
+ */
+
+/* for old netdb.h */
+#ifndef EAI_NODATA
+#define EAI_NODATA     1
+#endif
+
+#ifndef EAI_MEMORY
+#define EAI_MEMORY     2
+#endif
+
+#ifndef EAI_FAMILY
+#define EAI_FAMILY     3
+#endif
diff --git a/src/fake-getaddrinfo.c b/src/fake-getaddrinfo.c
new file mode 100644 (file)
index 0000000..df3d347
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * fake library for ssh
+ *
+ * This file includes getaddrinfo(), freeaddrinfo() and gai_strerror().
+ * These funtions are defined in rfc2133.
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For exapmle, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#include "system.h"
+
+#include "ipv4.h"
+#include "ipv6.h"
+#include "fake-getaddrinfo.h"
+#include "xalloc.h"
+
+
+#if !HAVE_DECL_GAI_STRERROR
+char *gai_strerror(int ecode) {
+       switch (ecode) {
+               case EAI_NODATA:
+                       return "No address associated with hostname";
+               case EAI_MEMORY:
+                       return "Memory allocation failure";
+               case EAI_FAMILY:
+                       return "Address family not supported";
+               default:
+                       return "Unknown error";
+       }
+}    
+#endif /* !HAVE_GAI_STRERROR */
+
+#if !HAVE_DECL_FREEADDRINFO
+void freeaddrinfo(struct addrinfo *ai) {
+       struct addrinfo *next;
+
+       while(ai) {
+               next = ai->ai_next;
+               free(ai);
+               ai = next;
+       }
+}
+#endif /* !HAVE_FREEADDRINFO */
+
+#if !HAVE_DECL_GETADDRINFO
+static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr) {
+       struct addrinfo *ai;
+
+       ai = xmalloc_and_zero(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+       
+       ai->ai_addr = (struct sockaddr *)(ai + 1);
+       ai->ai_addrlen = sizeof(struct sockaddr_in);
+       ai->ai_addr->sa_family = ai->ai_family = AF_INET;
+
+       ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
+       ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
+       
+       return ai;
+}
+
+int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) {
+       struct addrinfo *prev = NULL;
+       struct hostent *hp;
+       struct in_addr in = {0};
+       int i;
+       uint16_t port = 0;
+
+       if(hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC)
+               return EAI_FAMILY;
+
+       if (servname)
+               port = htons(atoi(servname));
+
+       if (hints && hints->ai_flags & AI_PASSIVE) {
+               *res = malloc_ai(port, htonl(0x00000000));
+               return 0;
+       }
+               
+       if (!hostname) {
+               *res = malloc_ai(port, htonl(0x7f000001));
+               return 0;
+       }
+       
+       hp = gethostbyname(hostname);
+
+       if(!hp || !hp->h_addr_list || !hp->h_addr_list[0])
+               return EAI_NODATA;
+
+       for (i = 0; hp->h_addr_list[i]; i++) {
+               *res = malloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr);
+
+               if(prev)
+                       prev->ai_next = *res;
+
+               prev = *res;
+       }
+
+       return 0;
+}
+#endif /* !HAVE_GETADDRINFO */
diff --git a/src/fake-getaddrinfo.h b/src/fake-getaddrinfo.h
new file mode 100644 (file)
index 0000000..5af7491
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef _FAKE_GETADDRINFO_H
+#define _FAKE_GETADDRINFO_H
+
+#include "fake-gai-errnos.h"
+
+#ifndef AI_PASSIVE
+# define AI_PASSIVE        1
+# define AI_CANONNAME      2
+#endif
+
+#ifndef NI_NUMERICHOST
+# define NI_NUMERICHOST    2
+# define NI_NAMEREQD       4
+# define NI_NUMERICSERV    8
+#endif
+
+#ifndef AI_NUMERICHOST
+#define AI_NUMERICHOST 4
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo {
+       int     ai_flags;       /* AI_PASSIVE, AI_CANONNAME */
+       int     ai_family;      /* PF_xxx */
+       int     ai_socktype;    /* SOCK_xxx */
+       int     ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+       size_t  ai_addrlen;     /* length of ai_addr */
+       char    *ai_canonname;  /* canonical name for hostname */
+       struct sockaddr *ai_addr;       /* binary address */
+       struct addrinfo *ai_next;       /* next structure in linked list */
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#if !HAVE_DECL_GETADDRINFO
+int getaddrinfo(const char *hostname, const char *servname, 
+                const struct addrinfo *hints, struct addrinfo **res);
+#endif /* !HAVE_GETADDRINFO */
+
+#if !HAVE_DECL_GAI_STRERROR
+char *gai_strerror(int ecode);
+#endif /* !HAVE_GAI_STRERROR */
+
+#if !HAVE_DECL_FREEADDRINFO
+void freeaddrinfo(struct addrinfo *ai);
+#endif /* !HAVE_FREEADDRINFO */
+
+#endif /* _FAKE_GETADDRINFO_H */
diff --git a/src/fake-getnameinfo.c b/src/fake-getnameinfo.c
new file mode 100644 (file)
index 0000000..1eba492
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * fake library for ssh
+ *
+ * This file includes getnameinfo().
+ * These funtions are defined in rfc2133.
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For exapmle, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#include "system.h"
+
+#include "fake-getnameinfo.h"
+#include "fake-getaddrinfo.h"
+
+#if !HAVE_DECL_GETNAMEINFO
+
+int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) {
+       struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+       struct hostent *hp;
+       int len;
+
+       if(sa->sa_family != AF_INET)
+               return EAI_FAMILY;
+
+       if(serv && servlen) {
+               len = snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
+               if(len < 0 || len >= servlen)
+                       return EAI_MEMORY;
+       }
+
+       if(!host || !hostlen)
+               return 0;
+
+       if(flags & NI_NUMERICHOST) {
+               len = snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
+               if(len < 0 || len >= hostlen)
+                       return EAI_MEMORY;
+               return 0;
+       }
+
+       hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
+       
+       if(!hp || !hp->h_name || !hp->h_name[0])
+               return EAI_NODATA;
+       
+       len = snprintf(host, hostlen, "%s", hp->h_name);
+       if(len < 0 || len >= hostlen)
+               return EAI_MEMORY;
+
+       return 0;
+}
+#endif /* !HAVE_GETNAMEINFO */
diff --git a/src/fake-getnameinfo.h b/src/fake-getnameinfo.h
new file mode 100644 (file)
index 0000000..4389a8f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _FAKE_GETNAMEINFO_H
+#define _FAKE_GETNAMEINFO_H
+
+#if !HAVE_DECL_GETNAMEINFO
+int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, 
+                size_t hostlen, char *serv, size_t servlen, int flags);
+#endif /* !HAVE_GETNAMEINFO */
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif /* !NI_MAXSERV */
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif /* !NI_MAXHOST */
+
+#endif /* _FAKE_GETNAMEINFO_H */
diff --git a/src/gcrypt/cipher.c b/src/gcrypt/cipher.c
new file mode 100644 (file)
index 0000000..6a2cc5a
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+    cipher.c -- Symmetric block cipher handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "cipher.h"
+#include "logger.h"
+#include "xalloc.h"
+
+static struct {
+       const char *name;
+       int algo;
+       int mode;
+       int nid;
+} ciphertable[] = {
+       {"none", GCRY_CIPHER_NONE, GCRY_CIPHER_MODE_NONE, 0},
+
+       {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 92},
+       {"blowfish", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 91},
+       {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93},
+       {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94},
+
+       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418},
+       {"aes", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419},
+       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421},
+       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420},
+
+       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422},
+       {"aes192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423},
+       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425},
+       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424},
+
+       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426},
+       {"aes256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427},
+       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429},
+       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428},
+};
+
+static bool nametocipher(const char *name, int *algo, int *mode) {
+       size_t i;
+
+       for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
+               if(ciphertable[i].name && !strcasecmp(name, ciphertable[i].name)) {
+                       *algo = ciphertable[i].algo;
+                       *mode = ciphertable[i].mode;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool nidtocipher(int nid, int *algo, int *mode) {
+       size_t i;
+
+       for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
+               if(nid == ciphertable[i].nid) {
+                       *algo = ciphertable[i].algo;
+                       *mode = ciphertable[i].mode;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool ciphertonid(int algo, int mode, int *nid) {
+       size_t i;
+
+       for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
+               if(algo == ciphertable[i].algo && mode == ciphertable[i].mode) {
+                       *nid = ciphertable[i].nid;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool cipher_open(cipher_t *cipher, int algo, int mode) {
+       gcry_error_t err;
+
+       if(!ciphertonid(algo, mode, &cipher->nid)) {
+               logger(LOG_DEBUG, "Cipher %d mode %d has no corresponding nid!", algo, mode);
+               return false;
+       }
+
+       if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) {
+               logger(LOG_DEBUG, "Unable to intialise cipher %d mode %d: %s", algo, mode, gcry_strerror(err));
+               return false;
+       }
+
+       cipher->keylen = gcry_cipher_get_algo_keylen(algo);
+       cipher->blklen = gcry_cipher_get_algo_blklen(algo);
+       cipher->key = xmalloc(cipher->keylen + cipher->blklen);
+       cipher->padding = mode == GCRY_CIPHER_MODE_ECB || mode == GCRY_CIPHER_MODE_CBC;
+
+       return true;
+}
+
+bool cipher_open_by_name(cipher_t *cipher, const char *name) {
+       int algo, mode;
+
+       if(!nametocipher(name, &algo, &mode)) {
+               logger(LOG_DEBUG, "Unknown cipher name '%s'!", name);
+               return false;
+       }
+
+       return cipher_open(cipher, algo, mode);
+}
+
+bool cipher_open_by_nid(cipher_t *cipher, int nid) {
+       int algo, mode;
+
+       if(!nidtocipher(nid, &algo, &mode)) {
+               logger(LOG_DEBUG, "Unknown cipher ID %d!", nid);
+               return false;
+       }
+
+       return cipher_open(cipher, algo, mode);
+}
+
+bool cipher_open_blowfish_ofb(cipher_t *cipher) {
+       return cipher_open(cipher, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB);
+}
+
+void cipher_close(cipher_t *cipher) {
+       if(cipher->handle) {
+               gcry_cipher_close(cipher->handle);
+               cipher->handle = NULL;
+       }
+
+       if(cipher->key) {
+               free(cipher->key);
+               cipher->key = NULL;
+       }
+}
+
+size_t cipher_keylength(const cipher_t *cipher) {
+       return cipher->keylen + cipher->blklen;
+}
+
+void cipher_get_key(const cipher_t *cipher, void *key) {
+       memcpy(key, cipher->key, cipher->keylen + cipher->blklen);
+}
+
+bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
+       memcpy(cipher->key, key, cipher->keylen + cipher->blklen);
+
+       gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
+       gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
+
+       return true;
+}
+
+bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
+       memcpy(cipher->key, key + len - cipher->keylen, cipher->keylen + cipher->blklen);
+       memcpy(cipher->key + cipher->keylen, key + len - cipher->keylen - cipher->blklen, cipher->blklen);
+
+       gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
+       gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
+
+       return true;
+}
+
+bool cipher_regenerate_key(cipher_t *cipher, bool encrypt) {
+       gcry_create_nonce(cipher->key, cipher->keylen + cipher->blklen);
+
+       gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
+       gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
+
+       return true;
+}
+
+bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
+       gcry_error_t err;
+       uint8_t pad[cipher->blklen];
+
+       if(cipher->padding) {
+               if(!oneshot)
+                       return false;
+
+               size_t reqlen = ((inlen + cipher->blklen) / cipher->blklen) * cipher->blklen;
+
+               if(*outlen < reqlen) {
+                       logger(LOG_ERR, "Error while encrypting: not enough room for padding");
+                       return false;
+               }
+
+               uint8_t padbyte = reqlen - inlen;
+               inlen = reqlen - cipher->blklen;
+
+               for(int i = 0; i < cipher->blklen; i++)
+                       if(i < cipher->blklen - padbyte)
+                               pad[i] = ((uint8_t *)indata)[inlen + i];
+                       else
+                               pad[i] = padbyte;
+       }
+       
+       if(oneshot)
+               gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
+
+       if((err = gcry_cipher_encrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
+               logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
+               return false;
+       }
+
+       if(cipher->padding) {
+               if((err = gcry_cipher_encrypt(cipher->handle, outdata + inlen, cipher->blklen, pad, cipher->blklen))) {
+                       logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
+                       return false;
+               }
+
+               inlen += cipher->blklen;
+       }
+
+       *outlen = inlen;
+       return true;
+}
+
+bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
+       gcry_error_t err;
+
+       if(oneshot)
+               gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
+
+       if((err = gcry_cipher_decrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
+               logger(LOG_ERR, "Error while decrypting: %s", gcry_strerror(err));
+               return false;
+       }
+
+       if(cipher->padding) {
+               if(!oneshot)
+                       return false;
+
+               uint8_t padbyte = ((uint8_t *)outdata)[inlen - 1];
+
+               if(padbyte == 0 || padbyte > cipher->blklen || padbyte > inlen) {
+                       logger(LOG_ERR, "Error while decrypting: invalid padding");
+                       return false;
+               }
+
+               size_t origlen = inlen - padbyte;
+
+               for(int i = inlen - 1; i >= origlen; i--)
+                       if(((uint8_t *)outdata)[i] != padbyte) {
+                               logger(LOG_ERR, "Error while decrypting: invalid padding");
+                               return false;
+                       }
+
+               *outlen = origlen;
+       } else
+               *outlen = inlen;
+
+       return true;
+}
+
+int cipher_get_nid(const cipher_t *cipher) {
+       return cipher->nid;
+}
+
+bool cipher_active(const cipher_t *cipher) {
+       return cipher->nid != 0;
+}
diff --git a/src/gcrypt/cipher.h b/src/gcrypt/cipher.h
new file mode 100644 (file)
index 0000000..8e4a2eb
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    cipher.h -- header file cipher.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CIPHER_H__
+#define __TINC_CIPHER_H__
+
+#include <gcrypt.h>
+
+#define CIPHER_MAX_BLOCK_SIZE 32
+#define CIPHER_MAX_IV_SIZE 16
+#define CIPHER_MAX_KEY_SIZE 32
+
+typedef struct cipher {
+       gcry_cipher_hd_t handle;
+       char *key;
+       int nid;
+       uint16_t keylen;
+       uint16_t blklen;
+       bool padding;
+} cipher_t;
+
+extern bool cipher_open_by_name(struct cipher *, const char *);
+extern bool cipher_open_by_nid(struct cipher *, int);
+extern bool cipher_open_blowfish_ofb(struct cipher *);
+extern void cipher_close(struct cipher *);
+extern size_t cipher_keylength(const struct cipher *);
+extern void cipher_get_key(const struct cipher *, void *);
+extern bool cipher_set_key(struct cipher *, void *, bool);
+extern bool cipher_set_key_from_rsa(struct cipher *, void *, size_t, bool);
+extern bool cipher_regenerate_key(struct cipher *, bool);
+extern bool cipher_encrypt(struct cipher *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot);
+extern bool cipher_decrypt(struct cipher *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot);
+extern int cipher_get_nid(const struct cipher *);
+extern bool cipher_active(const struct cipher *);
+
+#endif
diff --git a/src/gcrypt/crypto.c b/src/gcrypt/crypto.c
new file mode 100644 (file)
index 0000000..dc92b3e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+    crypto.c -- Cryptographic miscellaneous functions and initialisation
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <gcrypt.h>
+
+#include "crypto.h"
+
+void crypto_init() {
+}
+
+void crypto_exit() {
+}
+
+void randomize(void *out, size_t outlen) {
+       gcry_create_nonce(out, outlen);
+}
diff --git a/src/gcrypt/crypto.h b/src/gcrypt/crypto.h
new file mode 100644 (file)
index 0000000..8047bfb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    crypto.h -- header for crypto.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CRYPTO_H__
+#define __TINC_CRYPTO_H__
+
+extern void crypto_init();
+extern void crypto_exit();
+extern void randomize(void *, size_t);
+
+#endif
diff --git a/src/gcrypt/digest.c b/src/gcrypt/digest.c
new file mode 100644 (file)
index 0000000..066600f
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+    digest.c -- Digest handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "digest.h"
+#include "logger.h"
+
+static struct {
+       const char *name;
+       int algo;
+       int nid;
+} digesttable[] = {
+       {"none", GCRY_MD_NONE, 0},
+       {"sha1", GCRY_MD_SHA1, 64},
+       {"sha256", GCRY_MD_SHA256, 672},
+       {"sha384", GCRY_MD_SHA384, 673},
+       {"sha512", GCRY_MD_SHA512, 674},
+};
+
+static bool nametodigest(const char *name, int *algo) {
+       int i;
+
+       for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
+               if(digesttable[i].name && !strcasecmp(name, digesttable[i].name)) {
+                       *algo = digesttable[i].algo;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool nidtodigest(int nid, int *algo) {
+       int i;
+
+       for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
+               if(nid == digesttable[i].nid) {
+                       *algo = digesttable[i].algo;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool digesttonid(int algo, int *nid) {
+       int i;
+
+       for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
+               if(algo == digesttable[i].algo) {
+                       *nid = digesttable[i].nid;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool digest_open(digest_t *digest, int algo, int maclength) {
+       if(!digesttonid(algo, &digest->nid)) {
+               logger(LOG_DEBUG, "Digest %d has no corresponding nid!", algo);
+               return false;
+       }
+
+       unsigned int len = gcry_md_get_algo_dlen(algo);
+
+       if(maclength > len || maclength < 0)
+               digest->maclength = len;
+       else
+               digest->maclength = maclength;
+       
+       digest->algo = algo;
+       digest->hmac = NULL;
+
+       return true;
+}
+
+bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
+       int algo;
+
+       if(!nametodigest(name, &algo)) {
+               logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
+               return false;
+       }
+
+       return digest_open(digest, algo, maclength);
+}
+
+bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
+       int algo;
+
+       if(!nidtodigest(nid, &algo)) {
+               logger(LOG_DEBUG, "Unknown digest ID %d!", nid);
+               return false;
+       }
+
+       return digest_open(digest, algo, maclength);
+}
+
+bool digest_open_sha1(digest_t *digest, int maclength) {
+       return digest_open(digest, GCRY_MD_SHA1, maclength);
+}
+
+void digest_close(digest_t *digest) {
+       if(digest->hmac)
+               gcry_md_close(digest->hmac);
+       digest->hmac = NULL;
+}
+
+bool digest_set_key(digest_t *digest, const void *key, size_t len) {
+       if(!digest->hmac)
+               gcry_md_open(&digest->hmac, digest->algo, GCRY_MD_FLAG_HMAC);
+       if(!digest->hmac)
+               return false;
+
+       return !gcry_md_setkey(digest->hmac, key, len);
+}
+
+bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
+       unsigned int len = gcry_md_get_algo_dlen(digest->algo);
+
+       if(digest->hmac) {
+               char *tmpdata;
+               gcry_md_reset(digest->hmac);
+               gcry_md_write(digest->hmac, indata, inlen);
+               tmpdata = gcry_md_read(digest->hmac, digest->algo);
+               if(!tmpdata)
+                       return false;
+               memcpy(outdata, tmpdata, digest->maclength);
+       } else {
+               char tmpdata[len];
+               gcry_md_hash_buffer(digest->algo, tmpdata, indata, inlen);
+               memcpy(outdata, tmpdata, digest->maclength);
+       }
+
+       return true;
+}
+
+bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
+       unsigned int len = digest->maclength;
+       char outdata[len];
+
+       return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, len);
+}
+
+int digest_get_nid(const digest_t *digest) {
+       return digest->nid;
+}
+
+size_t digest_length(const digest_t *digest) {
+       return digest->maclength;
+}
+
+bool digest_active(const digest_t *digest) {
+       return digest->algo != GCRY_MD_NONE;
+}
diff --git a/src/gcrypt/digest.h b/src/gcrypt/digest.h
new file mode 100644 (file)
index 0000000..1188496
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    digest.h -- header file digest.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_DIGEST_H__
+#define __TINC_DIGEST_H__
+
+#include <gcrypt.h>
+
+#define DIGEST_MAX_SIZE 64
+
+typedef struct digest {
+       int algo;
+       int nid;
+       int maclength;
+       gcry_md_hd_t hmac;
+} digest_t;
+
+extern bool digest_open_by_name(struct digest *, const char *name, int maclength);
+extern bool digest_open_by_nid(struct digest *, int nid, int maclength);
+extern bool digest_open_sha1(struct digest *, int maclength);
+extern void digest_close(struct digest *);
+extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
+extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
+extern bool digest_set_key(struct digest *, const void *key, size_t len);
+extern int digest_get_nid(const struct digest *);
+extern size_t digest_length(const struct digest *);
+extern bool digest_active(const struct digest *);
+
+#endif
diff --git a/src/gcrypt/rsa.c b/src/gcrypt/rsa.c
new file mode 100644 (file)
index 0000000..ceb1638
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+    rsa.c -- RSA key handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <gcrypt.h>
+
+#include "logger.h"
+#include "rsa.h"
+
+// Base64 decoding table
+
+static const uint8_t b64d[128] = {
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+  0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+  0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+  0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+  0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+  0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+  0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+  0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+  0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+  0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+  0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
+  0xff, 0xff
+};
+
+// PEM encoding/decoding functions
+
+static bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
+       bool decode = false;
+       char line[1024];
+       uint16_t word = 0;
+       int shift = 10;
+       size_t i, j = 0;
+
+       while(!feof(fp)) {
+               if(!fgets(line, sizeof line, fp))
+                       return false;
+
+               if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
+                       if(!strncmp(line + 11, header, strlen(header)))
+                               decode = true;
+                       continue;
+               }
+
+               if(decode && !strncmp(line, "-----END", 8)) {
+                       break;
+               }
+
+               if(!decode)
+                       continue;
+
+               for(i = 0; line[i] >= ' '; i++) {
+                       if((signed char)line[i] < 0 || b64d[(int)line[i]] == 0xff)
+                               break;
+                       word |= b64d[(int)line[i]] << shift;
+                       shift -= 6;
+                       if(shift <= 2) {
+                               if(j > size) {
+                                       errno = ENOMEM;
+                                       return false;
+                               }
+
+                               buf[j++] = word >> 8;
+                               word <<= 8;
+                               shift += 8;
+                       }
+               }
+       }
+
+       if(outsize)
+               *outsize = j;
+       return true;
+}
+
+
+// BER decoding functions
+
+static int ber_read_id(unsigned char **p, size_t *buflen) {
+       if(*buflen <= 0)
+               return -1;
+
+       if((**p & 0x1f) == 0x1f) {
+               int id = 0;
+               bool more;
+               while(*buflen > 0) {
+                       id <<= 7;
+                       id |= **p & 0x7f;
+                       more = *(*p)++ & 0x80;
+                       (*buflen)--;
+                       if(!more)
+                               break;
+               }
+               return id;
+       } else {
+               (*buflen)--;
+               return *(*p)++ & 0x1f;
+       }
+}
+
+static size_t ber_read_len(unsigned char **p, size_t *buflen) {
+       if(*buflen <= 0)
+               return -1;
+
+       if(**p & 0x80) {
+               size_t result = 0;
+               int len = *(*p)++ & 0x7f;
+               (*buflen)--;
+               if(len > *buflen)
+                       return 0;
+
+               while(len--) {
+                       result <<= 8;
+                       result |= *(*p)++;
+                       (*buflen)--;
+               }
+
+               return result;
+       } else {
+               (*buflen)--;
+               return *(*p)++;
+       }
+}
+       
+
+static bool ber_read_sequence(unsigned char **p, size_t *buflen, size_t *result) {
+       int tag = ber_read_id(p, buflen);
+       size_t len = ber_read_len(p, buflen);
+
+       if(tag == 0x10) {
+               if(result)
+                       *result = len;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) {
+       int tag = ber_read_id(p, buflen);
+       size_t len = ber_read_len(p, buflen);
+       gcry_error_t err = 0;
+
+       if(tag != 0x02 || len > *buflen)
+               return false;
+
+       if(mpi)
+               err = gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, *p, len, NULL);
+       
+       *p += len;
+       *buflen -= len;
+
+       return mpi ? !err : true;
+}
+
+bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
+       gcry_error_t err = 0;
+
+       err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
+               ?: gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL);
+
+       if(err) {
+               logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
+               return false;
+       }
+
+       return true;
+}
+
+bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
+       gcry_error_t err = 0;
+
+       err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
+               ?: gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL)
+               ?: gcry_mpi_scan(&rsa->d, GCRYMPI_FMT_HEX, d, 0, NULL);
+
+       if(err) {
+               logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
+               return false;
+       }
+
+       return true;
+}
+
+// Read PEM RSA keys
+
+bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
+       uint8_t derbuf[8096], *derp = derbuf;
+       size_t derlen;
+
+       if(!pem_decode(fp, "RSA PUBLIC KEY", derbuf, sizeof derbuf, &derlen)) {
+               logger(LOG_ERR, "Unable to read RSA public key: %s", strerror(errno));
+               return NULL;
+       }
+
+       if(!ber_read_sequence(&derp, &derlen, NULL)
+                       || !ber_read_mpi(&derp, &derlen, &rsa->n)
+                       || !ber_read_mpi(&derp, &derlen, &rsa->e)
+                       || derlen) {
+               logger(LOG_ERR, "Error while decoding RSA public key");
+               return NULL;
+       }
+
+       return true;
+}
+
+bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
+       uint8_t derbuf[8096], *derp = derbuf;
+       size_t derlen;
+
+       if(!pem_decode(fp, "RSA PRIVATE KEY", derbuf, sizeof derbuf, &derlen)) {
+               logger(LOG_ERR, "Unable to read RSA private key: %s", strerror(errno));
+               return NULL;
+       }
+
+       if(!ber_read_sequence(&derp, &derlen, NULL)
+                       || !ber_read_mpi(&derp, &derlen, NULL)
+                       || !ber_read_mpi(&derp, &derlen, &rsa->n)
+                       || !ber_read_mpi(&derp, &derlen, &rsa->e)
+                       || !ber_read_mpi(&derp, &derlen, &rsa->d)
+                       || !ber_read_mpi(&derp, &derlen, NULL) // p
+                       || !ber_read_mpi(&derp, &derlen, NULL) // q
+                       || !ber_read_mpi(&derp, &derlen, NULL)
+                       || !ber_read_mpi(&derp, &derlen, NULL)
+                       || !ber_read_mpi(&derp, &derlen, NULL) // u
+                       || derlen) {
+               logger(LOG_ERR, "Error while decoding RSA private key");
+               return NULL;
+       }
+
+       return true;
+}
+
+size_t rsa_size(rsa_t *rsa) {
+       return (gcry_mpi_get_nbits(rsa->n) + 7) / 8;
+}
+
+/* Well, libgcrypt has functions to handle RSA keys, but they suck.
+ * So we just use libgcrypt's mpi functions, and do the math ourselves.
+ */
+
+// TODO: get rid of this macro, properly clean up gcry_ structures after use
+#define check(foo) { gcry_error_t err = (foo); if(err) {logger(LOG_ERR, "gcrypt error %s/%s at %s:%d", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }}
+
+bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
+       gcry_mpi_t inmpi;
+       check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL));
+
+       gcry_mpi_t outmpi = gcry_mpi_new(len * 8);
+       gcry_mpi_powm(outmpi, inmpi, rsa->e, rsa->n);
+
+       int pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
+       while(pad--)
+               *(char *)out++ = 0;
+
+       check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi));
+
+       return true;
+}
+
+bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
+       gcry_mpi_t inmpi;
+       check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL));
+
+       gcry_mpi_t outmpi = gcry_mpi_new(len * 8);
+       gcry_mpi_powm(outmpi, inmpi, rsa->d, rsa->n);
+
+       int pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
+       while(pad--)
+               *(char *)out++ = 0;
+
+       check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi));
+
+       return true;
+}
diff --git a/src/gcrypt/rsa.h b/src/gcrypt/rsa.h
new file mode 100644 (file)
index 0000000..143f015
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    rsa.h -- RSA key handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_RSA_H__
+#define __TINC_RSA_H__
+
+#include <gcrypt.h>
+
+typedef struct rsa {
+       gcry_mpi_t n;
+       gcry_mpi_t e;
+       gcry_mpi_t d;
+} rsa_t;
+
+extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e);
+extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d);
+extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp);
+extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp);
+extern size_t rsa_size(rsa_t *rsa);
+extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out);
+extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out);
+
+#endif
diff --git a/src/gcrypt/rsagen.c b/src/gcrypt/rsagen.c
new file mode 100644 (file)
index 0000000..7d37d19
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+    rsagen.c -- RSA key generation and export
+    Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <gcrypt.h>
+
+#include "rsagen.h"
+
+#if 0
+// Base64 encoding table
+
+static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// PEM encoding
+
+static bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
+       bool decode = false;
+       char line[1024];
+       uint32_t word = 0;
+       int shift = 0;
+       size_t i, j = 0;
+
+       fprintf(fp, "-----BEGIN %s-----\n", header);
+
+       for(i = 0; i < size; i += 3) {
+               if(i <= size - 3) {
+                       word = buf[i] << 16 | buf[i + 1] << 8 | buf[i + 2];
+               } else {
+                       word = buf[i] << 16;
+                       if(i == size - 2)
+                               word |= buf[i + 1] << 8;
+               }
+
+               line[j++] = b64e[(word >> 18)       ];
+               line[j++] = b64e[(word >> 12) & 0x3f];
+               line[j++] = b64e[(word >>  6) & 0x3f];
+               line[j++] = b64e[(word      ) & 0x3f];
+
+               if(j >= 64) {
+                       line[j++] = '\n';
+                       line[j] = 0;
+                       fputs(line, fp);
+                       j = 0;
+               }
+       }
+
+       if(size % 3 > 0) {
+               if(size % 3 > 1)
+                       line[j++] = '=';
+               line[j++] = '=';
+       }
+
+       if(j) {
+               line[j++] = '\n';
+               line[j] = 0;
+               fputs(line, fp);
+       }
+
+       fprintf(fp, "-----END %s-----\n", header);
+
+       return true;
+}
+
+
+// BER encoding functions
+
+static bool ber_write_id(uint8_t **p, size_t *buflen, int id) {
+       if(*buflen <= 0)
+               return false;
+
+       if(id >= 0x1f) {
+               while(id) {
+                       if(*buflen <= 0)
+                               return false;
+
+                       (*buflen)--;
+                       **p = id & 0x7f;
+                       id >>= 7;
+                       if(id)
+                               **p |= 0x80;
+                       (*p)++;
+               }
+       } else {
+               (*buflen)--;
+               *(*p)++ = id;
+       }
+
+       return true;
+}
+
+static bool ber_write_len(uint8_t **p, size_t *buflen, size_t len) {
+       do {
+               if(*buflen <= 0)
+                       return false;
+
+               (*buflen)--;
+               **p = len & 0x7f;
+               len >>= 7;
+               if(len)
+                       **p |= 0x80;
+               (*p)++;
+       } while(len);
+
+       return true;
+}
+
+static bool ber_write_sequence(uint8_t **p, size_t *buflen, uint8_t *seqbuf, size_t seqlen) {
+       if(!ber_write_id(p, buflen, 0x10) || !ber_write_len(p, buflen, seqlen) || *buflen < seqlen)
+               return false;
+
+       memcpy(*p, seqbuf, seqlen);
+       *p += seqlen;
+       *buflen -= seqlen;
+
+       return true;
+}
+
+static bool ber_write_mpi(uint8_t **p, size_t *buflen, gcry_mpi_t mpi) {
+       uint8_t tmpbuf[1024];
+       size_t tmplen = sizeof tmpbuf;
+       gcry_error_t err;
+
+       err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &tmpbuf, &tmplen, mpi);
+       if(err)
+               return false;
+
+       if(!ber_write_id(p, buflen, 0x02) || !ber_write_len(p, buflen, tmplen) || *buflen < tmplen)
+               return false;
+
+       memcpy(*p, tmpbuf, tmplen);
+       *p += tmplen;
+       *buflen -= tmplen;
+
+       return true;
+}
+
+// Write PEM RSA keys
+
+bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
+       uint8_t derbuf1[8096];
+       uint8_t derbuf2[8096];
+       uint8_t *derp1 = derbuf1;
+       uint8_t *derp2 = derbuf2;
+       size_t derlen1 = sizeof derbuf1;
+       size_t derlen2 = sizeof derbuf2;
+
+       if(!ber_write_mpi(&derp1, &derlen1, &rsa->n)
+                       || !ber_write_mpi(&derp1, &derlen1, &rsa->e)
+                       || !ber_write_sequence(&derp2, &derlen2, derbuf1, derlen1)) {
+               logger(LOG_ERR, "Error while encoding RSA public key");
+               return false;
+       }
+
+       if(!pem_encode(fp, "RSA PUBLIC KEY", derbuf2, derlen2)) {
+               logger(LOG_ERR, "Unable to write RSA public key: %s", strerror(errno));
+               return false;
+       }
+
+       return true;
+}
+
+bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
+       uint8_t derbuf1[8096];
+       uint8_t derbuf2[8096];
+       uint8_t *derp1 = derbuf1;
+       uint8_t *derp2 = derbuf2;
+       size_t derlen1 = sizeof derbuf1;
+       size_t derlen2 = sizeof derbuf2;
+
+       if(!ber_write_mpi(&derp1, &derlen1, &bits)
+                       || ber_write_mpi(&derp1, &derlen1, &rsa->n) // modulus
+                       || ber_write_mpi(&derp1, &derlen1, &rsa->e) // public exponent
+                       || ber_write_mpi(&derp1, &derlen1, &rsa->d) // private exponent
+                       || ber_write_mpi(&derp1, &derlen1, &p)
+                       || ber_write_mpi(&derp1, &derlen1, &q)
+                       || ber_write_mpi(&derp1, &derlen1, &exp1)
+                       || ber_write_mpi(&derp1, &derlen1, &exp2)
+                       || ber_write_mpi(&derp1, &derlen1, &coeff))
+               logger(LOG_ERR, "Error while encoding RSA private key");
+               return false;
+       }
+
+       if(!pem_encode(fp, "RSA PRIVATE KEY", derbuf2, derlen2)) {
+               logger(LOG_ERR, "Unable to write RSA private key: %s", strerror(errno));
+               return false;
+       }
+
+       return true;
+}
+#endif
+
+bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
+       return false;
+}
+
+bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
+       return false;
+}
+
+bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) {
+       fprintf(stderr, "Generating RSA keys with libgcrypt not implemented yet\n");
+       return false;
+}
diff --git a/src/gcrypt/rsagen.h b/src/gcrypt/rsagen.h
new file mode 100644 (file)
index 0000000..422d156
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    rsagen.h -- RSA key generation and export
+    Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_RSAGEN_H__
+#define __TINC_RSAGEN_H__
+
+#include "rsa.h"
+
+extern bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent);
+extern bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp);
+extern bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp);
+
+#endif
diff --git a/src/getopt.c b/src/getopt.c
new file mode 100644 (file)
index 0000000..a6782ed
--- /dev/null
@@ -0,0 +1,1048 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+       Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+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.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library.  */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really.  See?  Capital letters.  */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+\f
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define        my_index        strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+extern pid_t __libc_pid;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void
+__attribute__ ((__unused__))
+store_args_and_env (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args_and_env);
+
+# define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)                                               \
+    {                                                                        \
+      char __tmp = __getopt_nonoption_flags[ch1];                            \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
+      __getopt_nonoption_flags[ch2] = __tmp;                                 \
+    }
+#else  /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#ifdef _LIBC
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+        presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+       nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+       {
+         memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
+         memset (&new_str[nonoption_flags_max_len], '\0',
+                 top + 1 - nonoption_flags_max_len);
+         nonoption_flags_max_len = top + 1;
+         __getopt_nonoption_flags = new_str;
+       }
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+             SWAP_FLAGS (bottom + i, middle + i);
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+       {
+         if (__getopt_nonoption_flags == NULL
+             || __getopt_nonoption_flags[0] == '\0')
+           nonoption_flags_max_len = -1;
+         else
+           {
+             const char *orig_str = __getopt_nonoption_flags;
+             int len = nonoption_flags_max_len = strlen (orig_str);
+             if (nonoption_flags_max_len < argc)
+               nonoption_flags_max_len = argc;
+             __getopt_nonoption_flags =
+               (char *) malloc (nonoption_flags_max_len);
+             if (__getopt_nonoption_flags == NULL)
+               nonoption_flags_max_len = -1;
+             else
+               {
+                 memcpy (__getopt_nonoption_flags, orig_str, len);
+                 memset (&__getopt_nonoption_flags[len], '\0',
+                         nonoption_flags_max_len - len);
+               }
+           }
+       }
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+       optind = 1;     /* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
+                    || (optind < nonoption_flags_len                         \
+                        && __getopt_nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+        moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+       last_nonopt = optind;
+      if (first_nonopt > optind)
+       first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc && NONOPTION_P)
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* The special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return -1;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+       {
+         if (ordering == REQUIRE_ORDER)
+           return -1;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+       /* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+        or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, nextchar, nameend - nextchar))
+         {
+           if ((unsigned int) (nameend - nextchar)
+               == (unsigned int) strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second or later nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, "%s: option `%s' is ambiguous\n",
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*nameend)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = nameend + 1;
+             else
+               {
+                 if (opterr)
+                   {
+                    if (argv[optind - 1][1] == '-')
+                     /* --option */
+                     fprintf (stderr,
+                      "%s: option `--%s' doesn't allow an argument\n",
+                      argv[0], pfound->name);
+                    else
+                     /* +option or -option */
+                     fprintf (stderr,
+                      "%s: option `%c%s' doesn't allow an argument\n",
+                      argv[0], argv[optind - 1][0], pfound->name);
+                   }
+
+                 nextchar += strlen (nextchar);
+
+                 optopt = pfound->val;
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr,
+                          "%s: option `%s' requires an argument\n",
+                          argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 optopt = pfound->val;
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, "%s: unrecognized option `--%s'\n",
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+           if (posixly_correct)
+             /* 1003.2 specifies the format of this message.  */
+             fprintf (stderr, "%s: illegal option -- %c\n",
+                      argv[0], c);
+           else
+             fprintf (stderr, "%s: invalid option -- %c\n",
+                      argv[0], c);
+         }
+       optopt = c;
+       return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+       char *nameend;
+       const struct option *p;
+       const struct option *pfound = NULL;
+       int exact = 0;
+       int ambig = 0;
+       int indfound = 0;
+       int option_index;
+
+       /* This is an option that requires an argument.  */
+       if (*nextchar != '\0')
+         {
+           optarg = nextchar;
+           /* If we end this ARGV-element by taking the rest as an arg,
+              we must advance to the next element now.  */
+           optind++;
+         }
+       else if (optind == argc)
+         {
+           if (opterr)
+             {
+               /* 1003.2 specifies the format of this message.  */
+               fprintf (stderr, "%s: option requires an argument -- %c\n",
+                        argv[0], c);
+             }
+           optopt = c;
+           if (optstring[0] == ':')
+             c = ':';
+           else
+             c = '?';
+           return c;
+         }
+       else
+         /* We already incremented `optind' once;
+            increment it again when taking next ARGV-elt as argument.  */
+         optarg = argv[optind++];
+
+       /* optarg is now the argument, see if it's in the
+          table of longopts.  */
+
+       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+         /* Do nothing.  */ ;
+
+       /* Test all long options for either exact match
+          or abbreviated matches.  */
+       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+         if (!strncmp (p->name, nextchar, nameend - nextchar))
+           {
+             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+               {
+                 /* Exact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+                 exact = 1;
+                 break;
+               }
+             else if (pfound == NULL)
+               {
+                 /* First nonexact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+               }
+             else
+               /* Second or later nonexact match found.  */
+               ambig = 1;
+           }
+       if (ambig && !exact)
+         {
+           if (opterr)
+             fprintf (stderr, "%s: option `-W %s' is ambiguous\n",
+                      argv[0], argv[optind]);
+           nextchar += strlen (nextchar);
+           optind++;
+           return '?';
+         }
+       if (pfound != NULL)
+         {
+           option_index = indfound;
+           if (*nameend)
+             {
+               /* Don't test has_arg with >, because some C compilers don't
+                  allow it to be used on enums.  */
+               if (pfound->has_arg)
+                 optarg = nameend + 1;
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr,
+                              "%s: option `-W %s' doesn't allow an argument\n",
+                              argv[0], pfound->name);
+
+                   nextchar += strlen (nextchar);
+                   return '?';
+                 }
+             }
+           else if (pfound->has_arg == 1)
+             {
+               if (optind < argc)
+                 optarg = argv[optind++];
+               else
+                 {
+                   if (opterr)
+                     fprintf (stderr,
+                              "%s: option `%s' requires an argument\n",
+                              argv[0], argv[optind - 1]);
+                   nextchar += strlen (nextchar);
+                   return optstring[0] == ':' ? ':' : '?';
+                 }
+             }
+           nextchar += strlen (nextchar);
+           if (longind != NULL)
+             *longind = option_index;
+           if (pfound->flag)
+             {
+               *(pfound->flag) = pfound->val;
+               return 0;
+             }
+           return pfound->val;
+         }
+         nextchar = NULL;
+         return 'W';   /* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = NULL;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr,
+                          "%s: option requires an argument -- %c\n",
+                          argv[0], c);
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/getopt.h b/src/getopt.h
new file mode 100644 (file)
index 0000000..ddf6fdd
--- /dev/null
@@ -0,0 +1,133 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+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.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/src/getopt1.c b/src/getopt1.c
new file mode 100644 (file)
index 0000000..86545f2
--- /dev/null
@@ -0,0 +1,189 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+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.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+\f
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
index 9aadcd8616eaa99ea86dd5bf9db8accb526ae058..bb55dfdcba1c821d25fcf1f27e4c81fc5478fb7f 100644 (file)
@@ -44,7 +44,7 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "config.h"
 #include "connection.h"
 #include "device.h"
 #include "subnet.h"
 #include "utils.h"
 #include "xalloc.h"
-
-static bool graph_changed = true;
+#include "graph.h"
 
 /* Implementation of Kruskal's algorithm.
-   Running time: O(EN)
+   Running time: O(E)
    Please note that sorting on weight is already done by add_edge().
 */
 
-static void mst_kruskal(void) {
-       avl_node_t *node, *next;
+void mst_kruskal(void) {
+       splay_node_t *node, *next;
        edge_t *e;
        node_t *n;
        connection_t *c;
-       int nodes = 0;
-       int safe_edges = 0;
-       bool skipped;
 
        /* Clear MST status on connections */
 
@@ -82,11 +78,6 @@ static void mst_kruskal(void) {
                c->status.mst = false;
        }
 
-       /* Do we have something to do at all? */
-
-       if(!edge_weight_tree->head)
-               return;
-
        ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:");
 
        /* Clear visited status on nodes */
@@ -94,29 +85,16 @@ static void mst_kruskal(void) {
        for(node = node_tree->head; node; node = node->next) {
                n = node->data;
                n->status.visited = false;
-               nodes++;
-       }
-
-       /* Starting point */
-
-       for(node = edge_weight_tree->head; node; node = node->next) {
-               e = node->data;
-               if(e->from->status.reachable) {
-                       e->from->status.visited = true;
-                       break;
-               }
        }
 
        /* Add safe edges */
 
-       for(skipped = false, node = edge_weight_tree->head; node; node = next) {
+       for(node = edge_weight_tree->head; node; node = next) {
                next = node->next;
                e = node->data;
 
-               if(!e->reverse || e->from->status.visited == e->to->status.visited) {
-                       skipped = true;
+               if(!e->reverse || (e->from->status.visited && e->to->status.visited))
                        continue;
-               }
 
                e->from->status.visited = true;
                e->to->status.visited = true;
@@ -127,37 +105,124 @@ static void mst_kruskal(void) {
                if(e->reverse->connection)
                        e->reverse->connection->status.mst = true;
 
-               safe_edges++;
-
                ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name,
                                   e->to->name, e->weight);
+       }
+}
 
-               if(skipped) {
-                       skipped = false;
-                       next = edge_weight_tree->head;
-                       continue;
+/* Implementation of Dijkstra's algorithm.
+   Running time: O(N^2)
+*/
+
+static void sssp_dijkstra(void) {
+       splay_node_t *node, *to;
+       edge_t *e;
+       node_t *n, *m;
+       list_t *todo_list;
+       list_node_t *lnode, *nnode;
+       bool indirect;
+
+       todo_list = list_alloc(NULL);
+
+       ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Dijkstra's algorithm:");
+
+       /* Clear visited status on nodes */
+
+       for(node = node_tree->head; node; node = node->next) {
+               n = node->data;
+               n->status.visited = false;
+               n->status.indirect = true;
+               n->distance = -1;
+       }
+
+       /* Begin with myself */
+
+       myself->status.indirect = false;
+       myself->nexthop = myself;
+       myself->via = myself;
+       myself->distance = 0;
+       list_insert_head(todo_list, myself);
+
+       /* Loop while todo_list is filled */
+
+       while(todo_list->head) {
+               n = NULL;
+               nnode = NULL;
+
+               /* Select node from todo_list with smallest distance */
+
+               for(lnode = todo_list->head; lnode; lnode = lnode->next) {
+                       m = lnode->data;
+                       if(!n || m->status.indirect < n->status.indirect || m->distance < n->distance) {
+                               n = m;
+                               nnode = lnode;
+                       }
+               }
+
+               /* Mark this node as visited and remove it from the todo_list */
+
+               n->status.visited = true;
+               list_unlink_node(todo_list, nnode);
+
+               /* Update distance of neighbours and add them to the todo_list */
+
+               for(to = n->edge_tree->head; to; to = to->next) {       /* "to" is the edge connected to "from" */
+                       e = to->data;
+
+                       if(e->to->status.visited || !e->reverse)
+                               continue;
+
+                       /* Situation:
+
+                                  /
+                                 /
+                          ----->(n)---e-->(e->to)
+                                 \
+                                  \
+
+                          Where e is an edge, (n) and (e->to) are nodes.
+                          n->address is set to the e->address of the edge left of n to n.
+                          We are currently examining the edge e right of n from n:
+
+                          - If edge e provides for better reachability of e->to, update e->to.
+                        */
+
+                       if(e->to->distance < 0)
+                               list_insert_tail(todo_list, e->to);
+
+                       indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address));
+
+                       if(e->to->distance >= 0 && (!e->to->status.indirect || indirect) && e->to->distance <= n->distance + e->weight)
+                               continue;
+
+                       e->to->distance = n->distance + e->weight;
+                       e->to->status.indirect = indirect;
+                       e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
+                       e->to->via = indirect ? n->via : e->to;
+                       e->to->options = e->options;
+
+                       if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
+                               update_node_udp(e->to, &e->address);
+
+                       ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Updating edge %s - %s weight %d distance %d", e->from->name,
+                                          e->to->name, e->weight, e->to->distance);
                }
        }
 
-       ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes,
-                          safe_edges);
+       list_free(todo_list);
 }
 
 /* Implementation of a simple breadth-first search algorithm.
    Running time: O(E)
 */
 
-static void sssp_bfs(void) {
-       avl_node_t *node, *next, *to;
+void sssp_bfs(void) {
+       splay_node_t *node, *to;
        edge_t *e;
        node_t *n;
        list_t *todo_list;
        list_node_t *from, *todonext;
        bool indirect;
-       char *name;
-       char *address, *port;
-       char *envp[7];
-       int i;
 
        todo_list = list_alloc(NULL);
 
@@ -228,6 +293,15 @@ static void sssp_bfs(void) {
        }
 
        list_free(todo_list);
+}
+
+static void check_reachability(void) {
+       splay_node_t *node, *next;
+       node_t *n;
+       char *name;
+       char *address, *port;
+       char *envp[7];
+       int i;
 
        /* Check reachability status. */
 
@@ -255,10 +329,8 @@ static void sssp_bfs(void) {
                        n->minmtu = 0;
                        n->mtuprobes = 0;
 
-                       if(n->mtuevent) {
-                               event_del(n->mtuevent);
-                               n->mtuevent = NULL;
-                       }
+                       if(timeout_initialized(&n->mtuevent))
+                               event_del(&n->mtuevent);
 
                        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
                        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
@@ -295,70 +367,7 @@ static void sssp_bfs(void) {
 
 void graph(void) {
        subnet_cache_flush();
-       sssp_bfs();
+       sssp_dijkstra();
+       check_reachability();
        mst_kruskal();
-       graph_changed = true;
-}
-
-
-
-/* Dump nodes and edges to a graphviz file.
-          
-   The file can be converted to an image with
-   dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true
-*/
-
-void dump_graph(void) {
-       avl_node_t *node;
-       node_t *n;
-       edge_t *e;
-       char *filename = NULL, *tmpname = NULL;
-       FILE *file;
-       
-       if(!graph_changed || !get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename))
-               return;
-
-       graph_changed = false;
-
-       ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph");
-       
-       if(filename[0] == '|') {
-               file = popen(filename + 1, "w");
-       } else {
-               xasprintf(&tmpname, "%s.new", filename);
-               file = fopen(tmpname, "w");
-       }
-
-       if(!file) {
-               logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno));
-               free(tmpname);
-               return;
-       }
-
-       fprintf(file, "digraph {\n");
-       
-       /* dump all nodes first */
-       for(node = node_tree->head; node; node = node->next) {
-               n = node->data;
-               fprintf(file, " %s [label = \"%s\"];\n", n->name, n->name);
-       }
-
-       /* now dump all edges */
-       for(node = edge_weight_tree->head; node; node = node->next) {
-               e = node->data;
-               fprintf(file, " %s -> %s;\n", e->from->name, e->to->name);
-       }
-
-       fprintf(file, "}\n");   
-       
-       if(filename[0] == '|') {
-               pclose(file);
-       } else {
-               fclose(file);
-#ifdef HAVE_MINGW
-               unlink(filename);
-#endif
-               rename(tmpname, filename);
-               free(tmpname);
-       }
 }
diff --git a/src/ipv4.h b/src/ipv4.h
new file mode 100644 (file)
index 0000000..57d236d
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+    ipv4.h -- missing IPv4 related definitions
+    Copyright (C) 2005 Ivo Timmermans
+                  2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_IPV4_H__
+#define __TINC_IPV4_H__
+
+#ifndef AF_INET
+#define AF_INET 2
+#endif
+
+#ifndef IPPROTO_ICMP
+#define IPPROTO_ICMP 1
+#endif
+
+#ifndef ICMP_DEST_UNREACH
+#define ICMP_DEST_UNREACH 3
+#endif
+
+#ifndef ICMP_FRAG_NEEDED
+#define ICMP_FRAG_NEEDED 4
+#endif
+
+#ifndef ICMP_NET_UNKNOWN
+#define ICMP_NET_UNKNOWN 6
+#endif
+
+#ifndef ICMP_TIME_EXCEEDED
+#define ICMP_TIME_EXCEEDED 11
+#endif
+
+#ifndef ICMP_EXC_TTL
+#define ICMP_EXC_TTL 0
+#endif
+
+#ifndef ICMP_NET_UNREACH
+#define ICMP_NET_UNREACH 0
+#endif
+
+#ifndef ICMP_NET_ANO
+#define ICMP_NET_ANO 9
+#endif
+
+#ifndef IP_MSS
+#define       IP_MSS          576
+#endif
+
+#ifndef HAVE_STRUCT_IP
+struct ip {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       unsigned int ip_hl:4;
+       unsigned int ip_v:4;
+#else
+       unsigned int ip_v:4;
+       unsigned int ip_hl:4;
+#endif
+       uint8_t ip_tos;
+       uint16_t ip_len;
+       uint16_t ip_id; 
+       uint16_t ip_off;
+#define IP_RF 0x8000
+#define IP_DF 0x4000
+#define IP_MF 0x2000
+       uint8_t ip_ttl;
+       uint8_t ip_p;
+       uint16_t ip_sum;
+       struct in_addr ip_src, ip_dst;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef IP_OFFMASK
+#define IP_OFFMASK 0x1fff
+#endif
+
+#ifndef HAVE_STRUCT_ICMP
+struct icmp {
+       uint8_t icmp_type;
+       uint8_t icmp_code;
+       uint16_t icmp_cksum;
+       union {
+               uint8_t ih_pptr;
+               struct in_addr ih_gwaddr;
+               struct ih_idseq {
+                       uint16_t icd_id;
+                       uint16_t icd_seq;
+               } ih_idseq;
+               uint32_t ih_void;
+
+
+               struct ih_pmtu {
+                       uint16_t ipm_void;
+                       uint16_t ipm_nextmtu;
+               } ih_pmtu;
+
+               struct ih_rtradv {
+                       uint8_t irt_num_addrs;
+                       uint8_t irt_wpa;
+                       uint16_t irt_lifetime;
+               } ih_rtradv;
+       } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+       union {
+               struct {
+                       uint32_t its_otime;
+                       uint32_t its_rtime;
+                       uint32_t its_ttime;
+               } id_ts;
+               struct {
+                       struct ip idi_ip;
+               } id_ip;
+               uint32_t id_mask;
+               uint8_t id_data[1];
+       } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_radv icmp_dun.id_radv
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+} __attribute__ ((__packed__));
+#endif
+
+#endif /* __TINC_IPV4_H__ */
diff --git a/src/ipv6.h b/src/ipv6.h
new file mode 100644 (file)
index 0000000..d98001d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    ipv6.h -- missing IPv6 related definitions
+    Copyright (C) 2005 Ivo Timmermans
+                  2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_IPV6_H__
+#define __TINC_IPV6_H__
+
+#ifndef AF_INET6
+#define AF_INET6 10
+#endif
+
+#ifndef IPPROTO_ICMPV6
+#define IPPROTO_ICMPV6 58
+#endif
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+       union {
+               uint8_t u6_addr8[16];
+               uint16_t u6_addr16[8];
+               uint32_t u6_addr32[4];
+       } in6_u;
+} __attribute__ ((__packed__));
+#define s6_addr in6_u.u6_addr8
+#define s6_addr16 in6_u.u6_addr16
+#define s6_addr32 in6_u.u6_addr32
+#endif
+
+#ifndef HAVE_STRUCT_SOCKADDR_IN6
+struct sockaddr_in6 {
+       uint16_t sin6_family;
+       uint16_t sin6_port;
+       uint32_t sin6_flowinfo;
+       struct in6_addr sin6_addr;
+       uint32_t sin6_scope_id;
+} __attribute__ ((__packed__));
+#endif
+
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+        ((((__const uint32_t *) (a))[0] == 0) \
+        && (((__const uint32_t *) (a))[1] == 0) \
+        && (((__const uint32_t *) (a))[2] == htonl (0xffff)))
+#endif
+
+#ifndef HAVE_STRUCT_IP6_HDR
+struct ip6_hdr {
+       union {
+               struct ip6_hdrctl {
+                       uint32_t ip6_un1_flow;
+                       uint16_t ip6_un1_plen;
+                       uint8_t ip6_un1_nxt;
+                       uint8_t ip6_un1_hlim;
+               } ip6_un1;
+               uint8_t ip6_un2_vfc;
+       } ip6_ctlun;
+       struct in6_addr ip6_src;
+       struct in6_addr ip6_dst;
+} __attribute__ ((__packed__));
+#define ip6_vfc ip6_ctlun.ip6_un2_vfc
+#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
+#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
+#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
+#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
+#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
+#endif
+
+#ifndef HAVE_STRUCT_ICMP6_HDR
+struct icmp6_hdr {
+       uint8_t icmp6_type;
+       uint8_t icmp6_code;
+       uint16_t icmp6_cksum;
+       union {
+               uint32_t icmp6_un_data32[1];
+               uint16_t icmp6_un_data16[2];
+               uint8_t icmp6_un_data8[4];
+       } icmp6_dataun;
+} __attribute__ ((__packed__));
+#define ICMP6_DST_UNREACH_NOROUTE 0
+#define ICMP6_DST_UNREACH 1
+#define ICMP6_PACKET_TOO_BIG 2
+#define ICMP6_DST_UNREACH_ADMIN 1
+#define ICMP6_DST_UNREACH_ADDR 3
+#define ND_NEIGHBOR_SOLICIT 135
+#define ND_NEIGHBOR_ADVERT 136
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+#define icmp6_mtu icmp6_data32[0]
+#endif
+
+#ifndef HAVE_STRUCT_ND_NEIGHBOR_SOLICIT
+struct nd_neighbor_solicit {
+       struct icmp6_hdr nd_ns_hdr;
+       struct in6_addr nd_ns_target;
+} __attribute__ ((__packed__));
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define nd_ns_type nd_ns_hdr.icmp6_type
+#define nd_ns_code nd_ns_hdr.icmp6_code
+#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
+#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
+#endif
+
+#ifndef HAVE_STRUCT_ND_OPT_HDR
+struct nd_opt_hdr {
+       uint8_t nd_opt_type;
+       uint8_t nd_opt_len;
+} __attribute__ ((__packed__));
+#endif
+
+#endif /* __TINC_IPV6_H__ */
index cbf215298dd635e8ff1d2cb869c6d93d114e4862..ccb9a3f008d99f54b42ca1cc7dcd6aa51c0f417b 100644 (file)
 
 #include "system.h"
 
-#ifdef HAVE_LINUX_IF_TUN_H
 #include <linux/if_tun.h>
 #define DEFAULT_DEVICE "/dev/net/tun"
-#else
-#define DEFAULT_DEVICE "/dev/tap0"
-#endif
 
 #include "conf.h"
 #include "device.h"
@@ -34,9 +30,9 @@
 #include "route.h"
 #include "utils.h"
 #include "xalloc.h"
+#include "device.h"
 
 typedef enum device_type_t {
-       DEVICE_TYPE_ETHERTAP,
        DEVICE_TYPE_TUN,
        DEVICE_TYPE_TAP,
 } device_type_t;
@@ -49,13 +45,12 @@ static char *type = NULL;
 static char ifrname[IFNAMSIZ];
 static char *device_info;
 
-static uint64_t device_total_in = 0;
-static uint64_t device_total_out = 0;
+uint64_t device_in_packets = 0;
+uint64_t device_in_bytes = 0;
+uint64_t device_out_packets = 0;
+uint64_t device_out_bytes = 0;
 
 static bool setup_device(void) {
-       struct ifreq ifr;
-       bool t1q = false;
-
        if(!get_config_string(lookup_config(config_tree, "Device"), &device))
                device = xstrdup(DEFAULT_DEVICE);
 
@@ -77,10 +72,7 @@ static bool setup_device(void) {
        fcntl(device_fd, F_SETFD, FD_CLOEXEC);
 #endif
 
-#ifdef HAVE_LINUX_IF_TUN_H
-       /* Ok now check if this is an old ethertap or a new tun/tap thingie */
-
-       memset(&ifr, 0, sizeof(ifr));
+       struct ifreq ifr = {{{0}}};
 
        get_config_string(lookup_config(config_tree, "DeviceType"), &type);
 
@@ -101,6 +93,8 @@ static bool setup_device(void) {
 
 #ifdef IFF_ONE_QUEUE
        /* Set IFF_ONE_QUEUE flag... */
+
+       bool t1q = false;
        if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q)
                ifr.ifr_flags |= IFF_ONE_QUEUE;
 #endif
@@ -117,16 +111,6 @@ static bool setup_device(void) {
                strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
                if(iface) free(iface);
                iface = xstrdup(ifrname);
-       } else
-#endif
-       {
-               if(routing_mode == RMODE_ROUTER)
-                       overwrite_mac = true;
-               device_info = "Linux ethertap device";
-               device_type = DEVICE_TYPE_ETHERTAP;
-               if(iface)
-                       free(iface);
-               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
        }
 
        logger(LOG_INFO, "%s is a %s", device, device_info);
@@ -143,45 +127,37 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
        
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       lenin = read(device_fd, packet->data + 10, MTU - 10);
+                       inlen = read(device_fd, packet->data + 10, MTU - 10);
 
-                       if(lenin <= 0) {
+                       if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s",
                                           device_info, device, strerror(errno));
                                return false;
                        }
 
-                       packet->len = lenin + 10;
+                       packet->len = inlen + 10;
                        break;
                case DEVICE_TYPE_TAP:
-                       lenin = read(device_fd, packet->data, MTU);
+                       inlen = read(device_fd, packet->data, MTU);
 
-                       if(lenin <= 0) {
+                       if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s",
                                           device_info, device, strerror(errno));
                                return false;
                        }
 
-                       packet->len = lenin;
-                       break;
-               case DEVICE_TYPE_ETHERTAP:
-                       lenin = read(device_fd, packet->data - 2, MTU + 2);
-
-                       if(lenin <= 0) {
-                               logger(LOG_ERR, "Error while reading from %s %s: %s",
-                                          device_info, device, strerror(errno));
-                               return false;
-                       }
-
-                       packet->len = lenin - 2;
+                       packet->len = inlen;
                        break;
+               default:
+                       abort();
        }
 
-       device_total_in += packet->len;
+       device_in_packets++;
+       device_in_bytes += packet->len;
 
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
                           device_info);
@@ -209,26 +185,20 @@ static bool write_packet(vpn_packet_t *packet) {
                                return false;
                        }
                        break;
-               case DEVICE_TYPE_ETHERTAP:
-                       *(short int *)(packet->data - 2) = packet->len;
-
-                       if(write(device_fd, packet->data - 2, packet->len + 2) < 0) {
-                               logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
-                                          strerror(errno));
-                               return false;
-                       }
-                       break;
+               default:
+                       abort();
        }
 
-       device_total_out += packet->len;
+       device_out_packets++;
+       device_out_bytes += packet->len;
 
        return true;
 }
 
 static void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_in_bytes);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
 }
 
 const devops_t os_devops = {
diff --git a/src/list.c b/src/list.c
new file mode 100644 (file)
index 0000000..9b67791
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+    list.c -- functions to deal with double linked lists
+    Copyright (C) 2000-2005 Ivo Timmermans
+                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "list.h"
+#include "xalloc.h"
+
+/* (De)constructors */
+
+list_t *list_alloc(list_action_t delete) {
+       list_t *list;
+
+       list = xmalloc_and_zero(sizeof(list_t));
+       list->delete = delete;
+
+       return list;
+}
+
+void list_free(list_t *list) {
+       free(list);
+}
+
+list_node_t *list_alloc_node(void) {
+       return xmalloc_and_zero(sizeof(list_node_t));
+}
+
+void list_free_node(list_t *list, list_node_t *node) {
+       if(node->data && list->delete)
+               list->delete(node->data);
+
+       free(node);
+}
+
+/* Insertion and deletion */
+
+list_node_t *list_insert_head(list_t *list, void *data) {
+       list_node_t *node;
+
+       node = list_alloc_node();
+
+       node->data = data;
+       node->prev = NULL;
+       node->next = list->head;
+       list->head = node;
+
+       if(node->next)
+               node->next->prev = node;
+       else
+               list->tail = node;
+
+       list->count++;
+
+       return node;
+}
+
+list_node_t *list_insert_tail(list_t *list, void *data) {
+       list_node_t *node;
+
+       node = list_alloc_node();
+
+       node->data = data;
+       node->next = NULL;
+       node->prev = list->tail;
+       list->tail = node;
+
+       if(node->prev)
+               node->prev->next = node;
+       else
+               list->head = node;
+
+       list->count++;
+
+       return node;
+}
+
+list_node_t *list_insert_after(list_t *list, list_node_t *after, void *data) {
+       list_node_t *node;
+
+       node = list_alloc_node();
+
+       node->data = data;
+       node->next = after->next;
+       node->prev = after;
+       after->next = node;
+
+       if(node->next)
+               node->next->prev = node;
+       else
+               list->tail = node;
+
+       list->count++;
+
+       return node;
+}
+
+list_node_t *list_insert_before(list_t *list, list_node_t *before, void *data) {
+       list_node_t *node;
+
+       node = list_alloc_node();
+
+       node->data = data;
+       node->next = before;
+       node->prev = before->prev;
+       before->prev = node;
+
+       if(node->prev)
+               node->prev->next = node;
+       else
+               list->head = node;
+
+       list->count++;
+
+       return node;
+}
+
+void list_unlink_node(list_t *list, list_node_t *node) {
+       if(node->prev)
+               node->prev->next = node->next;
+       else
+               list->head = node->next;
+
+       if(node->next)
+               node->next->prev = node->prev;
+       else
+               list->tail = node->prev;
+
+       list->count--;
+}
+
+void list_delete_node(list_t *list, list_node_t *node) {
+       list_unlink_node(list, node);
+       list_free_node(list, node);
+}
+
+void list_delete_head(list_t *list) {
+       list_delete_node(list, list->head);
+}
+
+void list_delete_tail(list_t *list) {
+       list_delete_node(list, list->tail);
+}
+
+/* Head/tail lookup */
+
+void *list_get_head(list_t *list) {
+       if(list->head)
+               return list->head->data;
+       else
+               return NULL;
+}
+
+void *list_get_tail(list_t *list) {
+       if(list->tail)
+               return list->tail->data;
+       else
+               return NULL;
+}
+
+/* Fast list deletion */
+
+void list_delete_list(list_t *list) {
+       list_node_t *node, *next;
+
+       for(node = list->head; node; node = next) {
+               next = node->next;
+               list_free_node(list, node);
+       }
+
+       list_free(list);
+}
+
+/* Traversing */
+
+void list_foreach_node(list_t *list, list_action_node_t action) {
+       list_node_t *node, *next;
+
+       for(node = list->head; node; node = next) {
+               next = node->next;
+               action(node);
+       }
+}
+
+void list_foreach(list_t *list, list_action_t action) {
+       list_node_t *node, *next;
+
+       for(node = list->head; node; node = next) {
+               next = node->next;
+               if(node->data)
+                       action(node->data);
+       }
+}
diff --git a/src/list.h b/src/list.h
new file mode 100644 (file)
index 0000000..4fe48db
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+    list.h -- header file for list.c
+    Copyright (C) 2000-2005 Ivo Timmermans
+                  2000-2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_LIST_H__
+#define __TINC_LIST_H__
+
+typedef struct list_node_t {
+       struct list_node_t *prev;
+       struct list_node_t *next;
+
+       /* Payload */
+
+       void *data;
+} list_node_t;
+
+typedef void (*list_action_t)(const void *);
+typedef void (*list_action_node_t)(const list_node_t *);
+
+typedef struct list_t {
+       list_node_t *head;
+       list_node_t *tail;
+       int count;
+
+       /* Callbacks */
+
+       list_action_t delete;
+} list_t;
+
+/* (De)constructors */
+
+extern list_t *list_alloc(list_action_t) __attribute__ ((__malloc__));
+extern void list_free(list_t *);
+extern list_node_t *list_alloc_node(void);
+extern void list_free_node(list_t *, list_node_t *);
+
+/* Insertion and deletion */
+
+extern list_node_t *list_insert_head(list_t *, void *);
+extern list_node_t *list_insert_tail(list_t *, void *);
+extern list_node_t *list_insert_after(list_t *, list_node_t *, void *);
+extern list_node_t *list_insert_before(list_t *, list_node_t *, void *);
+
+extern void list_unlink_node(list_t *, list_node_t *);
+extern void list_delete_node(list_t *, list_node_t *);
+
+extern void list_delete_head(list_t *);
+extern void list_delete_tail(list_t *);
+
+/* Head/tail lookup */
+
+extern void *list_get_head(list_t *);
+extern void *list_get_tail(list_t *);
+
+/* Fast list deletion */
+
+extern void list_delete_list(list_t *);
+
+/* Traversing */
+
+extern void list_foreach(list_t *, list_action_t);
+extern void list_foreach_node(list_t *, list_action_node_t);
+
+#endif                                                 /* __TINC_LIST_H__ */
index f886ba4c0d7d38dd434a4cf61bef68bd11f6d512..08f9795410b4a992db950887ae8fdee395f23244 100644 (file)
@@ -108,7 +108,7 @@ void logger(int priority, const char *format, ...) {
                        {
                                char message[4096];
                                const char *messages[] = {message};
-                               vsnprintf(message, sizeof(message), format, ap);
+                               vsnprintf(message, sizeof message, format, ap);
                                ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
                        }
 #else
@@ -118,7 +118,7 @@ void logger(int priority, const char *format, ...) {
 #else
                        {
                                char message[4096];
-                               vsnprintf(message, sizeof(message), format, ap);
+                               vsnprintf(message, sizeof message, format, ap);
                                syslog(priority, "%s", message);
                        }
 #endif
index 4c52464c33d2a62f663f54c2b920832b8a20e910..29dd824042754358bba1a0efaec4698b782e70b1 100644 (file)
 
 #include "system.h"
 
-#include <openssl/err.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "connection.h"
 #include "logger.h"
 #include "meta.h"
@@ -34,9 +32,6 @@
 #include "xalloc.h"
 
 bool send_meta(connection_t *c, const char *buffer, int length) {
-       int outlen;
-       int result;
-
        if(!c) {
                logger(LOG_ERR, "send_meta() called with NULL pointer!");
                abort();
@@ -45,77 +40,27 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
        ifdebug(META) logger(LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
                           c->name, c->hostname);
 
-       if(!c->outbuflen)
-               c->last_flushed_time = now;
-
-       /* Find room in connection's buffer */
-       if(length + c->outbuflen > c->outbufsize) {
-               c->outbufsize = length + c->outbuflen;
-               c->outbuf = xrealloc(c->outbuf, c->outbufsize);
-       }
-
-       if(length + c->outbuflen + c->outbufstart > c->outbufsize) {
-               memmove(c->outbuf, c->outbuf + c->outbufstart, c->outbuflen);
-               c->outbufstart = 0;
-       }
-
        /* Add our data to buffer */
        if(c->status.encryptout) {
-               result = EVP_EncryptUpdate(c->outctx, (unsigned char *)c->outbuf + c->outbufstart + c->outbuflen,
-                               &outlen, (unsigned char *)buffer, length);
-               if(!result || outlen < length) {
-                       logger(LOG_ERR, "Error while encrypting metadata to %s (%s): %s",
-                                       c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
-                       return false;
-               } else if(outlen > length) {
-                       logger(LOG_EMERG, "Encrypted data too long! Heap corrupted!");
-                       abort();
-               }
-               c->outbuflen += outlen;
-       } else {
-               memcpy(c->outbuf + c->outbufstart + c->outbuflen, buffer, length);
-               c->outbuflen += length;
-       }
-
-       return true;
-}
-
-bool flush_meta(connection_t *c) {
-       int result;
-       
-       ifdebug(META) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s)",
-                        c->outbuflen, c->name, c->hostname);
-
-       while(c->outbuflen) {
-               result = send(c->socket, c->outbuf + c->outbufstart, c->outbuflen, 0);
-               if(result <= 0) {
-                       if(!errno || errno == EPIPE) {
-                               ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
-                                                  c->name, c->hostname);
-                       } else if(errno == EINTR) {
-                               continue;
-                       } else if(sockwouldblock(sockerrno)) {
-                               ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s) would block",
-                                               c->outbuflen, c->name, c->hostname);
-                               return true;
-                       } else {
-                               logger(LOG_ERR, "Flushing meta data to %s (%s) failed: %s", c->name,
-                                          c->hostname, sockstrerror(sockerrno));
-                       }
+               size_t outlen = length;
 
+               if(!cipher_encrypt(&c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
+                       logger(LOG_ERR, "Error while encrypting metadata to %s (%s)",
+                                       c->name, c->hostname);
                        return false;
                }
 
-               c->outbufstart += result;
-               c->outbuflen -= result;
+       } else {
+               buffer_add(&c->outbuf, buffer, length);
        }
 
-       c->outbufstart = 0; /* avoid unnecessary memmoves */
+       event_add(&c->outevent, NULL);
+
        return true;
 }
 
 void broadcast_meta(connection_t *from, const char *buffer, int length) {
-       avl_node_t *node;
+       splay_node_t *node;
        connection_t *c;
 
        for(node = connection_tree->head; node; node = node->next) {
@@ -127,10 +72,9 @@ void broadcast_meta(connection_t *from, const char *buffer, int length) {
 }
 
 bool receive_meta(connection_t *c) {
-       int oldlen, i, result;
-       int lenin, lenout, reqlen;
-       bool decrypted = false;
+       int inlen;
        char inbuf[MAXBUFSIZE];
+       char *bufp = inbuf, *endp;
 
        /* Strategy:
           - Read as much as possible from the TCP socket in one go.
@@ -141,10 +85,17 @@ bool receive_meta(connection_t *c) {
           - If not, keep stuff in buffer and exit.
         */
 
-       lenin = recv(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen, 0);
+       buffer_compact(&c->inbuf, MAXBUFSIZE);
+
+       if(sizeof inbuf <= c->inbuf.len) {
+               logger(LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
+               return false;
+       }
+
+       inlen = recv(c->socket, inbuf, sizeof inbuf - c->inbuf.len, 0);
 
-       if(lenin <= 0) {
-               if(!lenin || !errno) {
+       if(inlen <= 0) {
+               if(!inlen || !errno) {
                        ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
                                           c->name, c->hostname);
                } else if(sockwouldblock(sockerrno))
@@ -152,76 +103,61 @@ bool receive_meta(connection_t *c) {
                else
                        logger(LOG_ERR, "Metadata socket read error for %s (%s): %s",
                                   c->name, c->hostname, sockstrerror(sockerrno));
-
                return false;
        }
 
-       oldlen = c->buflen;
-       c->buflen += lenin;
+       do {
+               if(!c->status.decryptin) {
+                       endp = memchr(bufp, '\n', inlen);
+                       if(endp)
+                               endp++;
+                       else
+                               endp = bufp + inlen;
+
+                       buffer_add(&c->inbuf, bufp, endp - bufp);
 
-       while(lenin > 0) {
-               /* Decrypt */
+                       inlen -= endp - bufp;
+                       bufp = endp;
+               } else {
+                       size_t outlen = inlen;
+                       ifdebug(META) logger(LOG_DEBUG, "Received encrypted %d bytes", inlen);
 
-               if(c->status.decryptin && !decrypted) {
-                       result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin);
-                       if(!result || lenout != lenin) {
-                               logger(LOG_ERR, "Error while decrypting metadata from %s (%s): %s",
-                                               c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
+                       if(!cipher_decrypt(&c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
+                               logger(LOG_ERR, "Error while decrypting metadata from %s (%s)",
+                                          c->name, c->hostname);
                                return false;
                        }
-                       memcpy(c->buffer + oldlen, inbuf, lenin);
-                       decrypted = true;
+
+                       inlen = 0;
                }
 
-               /* Are we receiving a TCPpacket? */
+               while(c->inbuf.len) {
+                       /* Are we receiving a TCPpacket? */
+
+                       if(c->tcplen) {
+                               char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
+                               if(tcpbuffer) {
+                                       receive_tcppacket(c, tcpbuffer, c->tcplen);
+                                       c->tcplen = 0;
+                                       continue;
+                               } else {
+                                       break;
+                               }
+                       }
 
-               if(c->tcplen) {
-                       if(c->tcplen <= c->buflen) {
-                               receive_tcppacket(c, c->buffer, c->tcplen);
+                       /* Otherwise we are waiting for a request */
 
-                               c->buflen -= c->tcplen;
-                               lenin -= c->tcplen - oldlen;
-                               memmove(c->buffer, c->buffer + c->tcplen, c->buflen);
-                               oldlen = 0;
-                               c->tcplen = 0;
+                       char *request = buffer_readline(&c->inbuf);
+                       if(request) {
+                               bool result = receive_request(c, request);
+                               if(!result)
+                                       return false;
                                continue;
                        } else {
                                break;
                        }
                }
-
-               /* Otherwise we are waiting for a request */
-
-               reqlen = 0;
-
-               for(i = oldlen; i < c->buflen; i++) {
-                       if(c->buffer[i] == '\n') {
-                               c->buffer[i] = '\0';    /* replace end-of-line by end-of-string so we can use sscanf */
-                               reqlen = i + 1;
-                               break;
-                       }
-               }
-
-               if(reqlen) {
-                       c->reqlen = reqlen;
-                       if(!receive_request(c))
-                               return false;
-
-                       c->buflen -= reqlen;
-                       lenin -= reqlen - oldlen;
-                       memmove(c->buffer, c->buffer + reqlen, c->buflen);
-                       oldlen = 0;
-                       continue;
-               } else {
-                       break;
-               }
-       }
-
-       if(c->buflen >= MAXBUFSIZE) {
-               logger(LOG_ERR, "Metadata read buffer overflow for %s (%s)",
-                          c->name, c->hostname);
-               return false;
-       }
+       } while(inlen);
 
        return true;
 }
index fcb09fc26b4fd320f5b730f0f6b9914ae3e2be08..bc81a6acda3456b77e46b0aab223ab8b6b73801d 100644 (file)
@@ -25,7 +25,6 @@
 
 extern bool send_meta(struct connection_t *, const char *, int);
 extern void broadcast_meta(struct connection_t *, const char *, int);
-extern bool flush_meta(struct connection_t *);
 extern bool receive_meta(struct connection_t *);
 
 #endif                                                 /* __TINC_META_H__ */
index b9588318f15ad894d3cb2d2da193c75c16dbd7f4..e468338668b2bdd83bc21fbfadd4b8d7804aadd3 100644 (file)
@@ -110,18 +110,18 @@ static bool setup_device(void) {
        }
 
        for (i = 0; ; i++) {
-               len = sizeof(adapterid);
+               len = sizeof adapterid;
                if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
                        break;
 
                /* Find out more about this adapter */
 
-               snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
+               snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
 
                 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
                        continue;
 
-               len = sizeof(adaptername);
+               len = sizeof adaptername;
                err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
 
                RegCloseKey(key2);
@@ -145,7 +145,7 @@ static bool setup_device(void) {
                                continue;
                }
 
-               snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
+               snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
                if(device_handle != INVALID_HANDLE_VALUE) {
                        found = true;
@@ -169,7 +169,7 @@ static bool setup_device(void) {
        /* Try to open the corresponding tap device */
 
        if(device_handle == INVALID_HANDLE_VALUE) {
-               snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
+               snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
        }
        
@@ -180,7 +180,7 @@ static bool setup_device(void) {
 
        /* Get MAC address from tap device */
 
-       if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
+       if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
                logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
                return false;
        }
@@ -201,7 +201,7 @@ static bool setup_device(void) {
        /* Set media status for newer TAP-Win32 devices */
 
        status = true;
-       DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
+       DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
 
        device_info = "Windows tap device";
 
@@ -222,13 +222,13 @@ static bool read_packet(vpn_packet_t *packet) {
 }
 
 static bool write_packet(vpn_packet_t *packet) {
-       long lenout;
+       long outlen;
        OVERLAPPED overlapped = {0};
 
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(!WriteFile(device_handle, packet->data, packet->len, &lenout, &overlapped)) {
+       if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
                logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
index fc9ec574987a97f81d42f757e19648bd800299b0..336bf9eec312bcc381751fd5d2fba8c8306d078a 100644 (file)
--- a/src/net.c
+++ b/src/net.c
 
 #include "system.h"
 
-#include <openssl/rand.h>
-
 #include "utils.h"
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "conf.h"
 #include "connection.h"
 #include "device.h"
-#include "event.h"
 #include "graph.h"
 #include "logger.h"
 #include "meta.h"
 #include "netutl.h"
 #include "process.h"
 #include "protocol.h"
-#include "route.h"
 #include "subnet.h"
 #include "xalloc.h"
 
-bool do_purge = false;
-volatile bool running = false;
-#ifdef HAVE_PSELECT
-bool graph_dump = false;
-#endif
-
-time_t now = 0;
 int contradicting_add_edge = 0;
 int contradicting_del_edge = 0;
 static int sleeptime = 10;
 
 /* Purge edges and subnets of unreachable nodes. Use carefully. */
 
-static void purge(void) {
-       avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
+void purge(void) {
+       splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
        node_t *n;
        edge_t *e;
        subnet_t *s;
@@ -112,52 +101,6 @@ static void purge(void) {
        }
 }
 
-/*
-  put all file descriptors in an fd_set array
-  While we're at it, purge stuff that needs to be removed.
-*/
-static int build_fdset(fd_set *readset, fd_set *writeset) {
-       avl_node_t *node, *next;
-       connection_t *c;
-       int i, max = 0;
-
-       FD_ZERO(readset);
-       FD_ZERO(writeset);
-
-       for(node = connection_tree->head; node; node = next) {
-               next = node->next;
-               c = node->data;
-
-               if(c->status.remove) {
-                       connection_del(c);
-                       if(!connection_tree->head)
-                               purge();
-               } else {
-                       FD_SET(c->socket, readset);
-                       if(c->outbuflen > 0)
-                               FD_SET(c->socket, writeset);
-                       if(c->socket > max)
-                               max = c->socket;
-               }
-       }
-
-       for(i = 0; i < listen_sockets; i++) {
-               FD_SET(listen_socket[i].tcp, readset);
-               if(listen_socket[i].tcp > max)
-                       max = listen_socket[i].tcp;
-               FD_SET(listen_socket[i].udp, readset);
-               if(listen_socket[i].udp > max)
-                       max = listen_socket[i].udp;
-       }
-
-       if(device_fd >= 0)
-               FD_SET(device_fd, readset);
-       if(device_fd > max)
-               max = device_fd;
-       
-       return max;
-}
-
 /*
   Terminate a connection:
   - Close the socket
@@ -166,21 +109,14 @@ static int build_fdset(fd_set *readset, fd_set *writeset) {
   - Deactivate the host
 */
 void terminate_connection(connection_t *c, bool report) {
-       if(c->status.remove)
-               return;
-
        ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Closing connection with %s (%s)",
                           c->name, c->hostname);
 
-       c->status.remove = true;
        c->status.active = false;
 
        if(c->node)
                c->node->connection = NULL;
 
-       if(c->socket)
-               closesocket(c->socket);
-
        if(c->edge) {
                if(report && !tunnelserver)
                        send_del_edge(everyone, c->edge);
@@ -206,16 +142,10 @@ void terminate_connection(connection_t *c, bool report) {
 
        /* Check if this was our outgoing connection */
 
-       if(c->outgoing) {
+       if(c->outgoing)
                retry_outgoing(c->outgoing);
-               c->outgoing = NULL;
-       }
 
-       free(c->outbuf);
-       c->outbuf = NULL;
-       c->outbuflen = 0;
-       c->outbufsize = 0;
-       c->outbufstart = 0;
+       connection_del(c);
 }
 
 /*
@@ -226,9 +156,10 @@ void terminate_connection(connection_t *c, bool report) {
   end does not reply in time, we consider them dead
   and close the connection.
 */
-static void check_dead_connections(void) {
-       avl_node_t *node, *next;
+static void timeout_handler(int fd, short events, void *event) {
+       splay_node_t *node, *next;
        connection_t *c;
+       time_t now = time(NULL);
 
        for(node = connection_tree->head; node; node = next) {
                next = node->next;
@@ -239,374 +170,223 @@ static void check_dead_connections(void) {
                                if(c->status.pinged) {
                                        ifdebug(CONNECTIONS) logger(LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
                                                           c->name, c->hostname, now - c->last_ping_time);
-                                       c->status.timeout = true;
                                        terminate_connection(c, true);
+                                       continue;
                                } else if(c->last_ping_time + pinginterval <= now) {
                                        send_ping(c);
                                }
                        } else {
-                               if(c->status.remove) {
-                                       logger(LOG_WARNING, "Old connection_t for %s (%s) status %04x still lingering, deleting...",
-                                                  c->name, c->hostname, bitfield_to_int(&c->status, sizeof c->status));
-                                       connection_del(c);
-                                       continue;
-                               }
-                               ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication",
-                                                  c->name, c->hostname);
                                if(c->status.connecting) {
+                                       ifdebug(CONNECTIONS)
+                                               logger(LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
                                        c->status.connecting = false;
                                        closesocket(c->socket);
                                        do_outgoing_connection(c);
                                } else {
+                                       ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
                                        terminate_connection(c, false);
+                                       continue;
                                }
                        }
                }
-
-               if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout <= now) {
-                       if(c->status.active) {
-                               ifdebug(CONNECTIONS) logger(LOG_INFO,
-                                               "%s (%s) could not flush for %ld seconds (%d bytes remaining)",
-                                               c->name, c->hostname, now - c->last_flushed_time, c->outbuflen);
-                               c->status.timeout = true;
-                               terminate_connection(c, true);
-                       }
-               }
        }
-}
 
-/*
-  check all connections to see if anything
-  happened on their sockets
-*/
-static void check_network_activity(fd_set * readset, fd_set * writeset) {
-       connection_t *c;
-       avl_node_t *node;
-       int result, i;
-       socklen_t len = sizeof(result);
-       vpn_packet_t packet;
-       static int errors = 0;
-
-       /* check input from kernel */
-       if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
-               if(devops.read(&packet)) {
-                       errors = 0;
-                       packet.priority = 0;
-                       route(myself, &packet);
-               } else {
-                       usleep(errors * 50000);
-                       errors++;
-                       if(errors > 10) {
-                               logger(LOG_ERR, "Too many errors from %s, exiting!", device);
-                               running = false;
-                       }
-               }
+       if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
+               logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
+               usleep(sleeptime * 1000000LL);
+               sleeptime *= 2;
+               if(sleeptime < 0)
+                       sleeptime = 3600;
+       } else {
+               sleeptime /= 2;
+               if(sleeptime < 10)
+                       sleeptime = 10;
        }
 
-       /* check meta connections */
-       for(node = connection_tree->head; node; node = node->next) {
-               c = node->data;
-
-               if(c->status.remove)
-                       continue;
+       contradicting_add_edge = 0;
+       contradicting_del_edge = 0;
 
-               if(FD_ISSET(c->socket, readset)) {
-                       if(c->status.connecting) {
-                               c->status.connecting = false;
-                               getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
-
-                               if(!result)
-                                       finish_connecting(c);
-                               else {
-                                       ifdebug(CONNECTIONS) logger(LOG_DEBUG,
-                                                          "Error while connecting to %s (%s): %s",
-                                                          c->name, c->hostname, sockstrerror(result));
-                                       closesocket(c->socket);
-                                       do_outgoing_connection(c);
-                                       continue;
-                               }
-                       }
-
-                       if(!receive_meta(c)) {
-                               terminate_connection(c, c->status.active);
-                               continue;
-                       }
-               }
+       event_add(event, &(struct timeval){pingtimeout, 0});
+}
 
-               if(FD_ISSET(c->socket, writeset)) {
-                       if(!flush_meta(c)) {
-                               terminate_connection(c, c->status.active);
-                               continue;
-                       }
+void handle_meta_connection_data(int fd, short events, void *data) {
+       connection_t *c = data;
+       int result;
+       socklen_t len = sizeof result;
+
+       if(c->status.connecting) {
+               c->status.connecting = false;
+
+               getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
+
+               if(!result)
+                       finish_connecting(c);
+               else {
+                       ifdebug(CONNECTIONS) logger(LOG_DEBUG,
+                                          "Error while connecting to %s (%s): %s",
+                                          c->name, c->hostname, sockstrerror(result));
+                       closesocket(c->socket);
+                       do_outgoing_connection(c);
+                       return;
                }
        }
 
-       for(i = 0; i < listen_sockets; i++) {
-               if(FD_ISSET(listen_socket[i].udp, readset))
-                       handle_incoming_vpn_data(listen_socket[i].udp);
-
-               if(FD_ISSET(listen_socket[i].tcp, readset))
-                       handle_new_meta_connection(listen_socket[i].tcp);
+       if (!receive_meta(c)) {
+               terminate_connection(c, c->status.active);
+               return;
        }
 }
 
-/*
-  this is where it all happens...
-*/
-int main_loop(void) {
-       fd_set readset, writeset;
-#ifdef HAVE_PSELECT
-       struct timespec tv;
-       sigset_t omask, block_mask;
-       time_t next_event;
-#else
-       struct timeval tv;
-#endif
-       int r, maxfd;
-       time_t last_ping_check, last_config_check, last_graph_dump;
-       event_t *event;
+static void sigterm_handler(int signal, short events, void *data) {
+       logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+       event_loopexit(NULL);
+}
 
-       last_ping_check = now;
-       last_config_check = now;
-       last_graph_dump = now;
-       
-       srand(now);
-
-#ifdef HAVE_PSELECT
-       if(lookup_config(config_tree, "GraphDumpFile"))
-               graph_dump = true;
-       /* Block SIGHUP & SIGALRM */
-       sigemptyset(&block_mask);
-       sigaddset(&block_mask, SIGHUP);
-       sigaddset(&block_mask, SIGALRM);
-       sigprocmask(SIG_BLOCK, &block_mask, &omask);
-#endif
+static void sighup_handler(int signal, short events, void *data) {
+       logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+       reopenlogger();
+       reload_configuration();
+}
 
-       running = true;
-
-       while(running) {
-#ifdef HAVE_PSELECT
-               next_event = last_ping_check + pingtimeout;
-               if(graph_dump && next_event > last_graph_dump + 60)
-                       next_event = last_graph_dump + 60;
-
-               if((event = peek_next_event()) && next_event > event->time)
-                       next_event = event->time;
-
-               if(next_event <= now)
-                       tv.tv_sec = 0;
-               else
-                       tv.tv_sec = next_event - now;
-               tv.tv_nsec = 0;
-#else
-               tv.tv_sec = 1;
-               tv.tv_usec = 0;
-#endif
+static void sigalrm_handler(int signal, short events, void *data) {
+       logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
+       retry();
+}
 
-               maxfd = build_fdset(&readset, &writeset);
+int reload_configuration(void) {
+       connection_t *c;
+       splay_node_t *node, *next;
+       char *fname;
+       struct stat s;
+       static time_t last_config_check = 0;
 
-#ifdef HAVE_MINGW
-               LeaveCriticalSection(&mutex);
-#endif
-#ifdef HAVE_PSELECT
-               r = pselect(maxfd + 1, &readset, &writeset, NULL, &tv, &omask);
-#else
-               r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
-#endif
-               now = time(NULL);
-#ifdef HAVE_MINGW
-               EnterCriticalSection(&mutex);
-#endif
+       /* Reread our own configuration file */
 
-               if(r < 0) {
-                       if(!sockwouldblock(sockerrno)) {
-                               logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
-                               dump_connections();
-                               return 1;
-                       }
-               }
+       exit_configuration(&config_tree);
+       init_configuration(&config_tree);
 
-               if(r > 0)
-                       check_network_activity(&readset, &writeset);
+       if(!read_server_config()) {
+               logger(LOG_ERR, "Unable to reread configuration file, exitting.");
+               event_loopexit(NULL);
+               return EINVAL;
+       }
 
-               if(do_purge) {
-                       purge();
-                       do_purge = false;
+       /* Close connections to hosts that have a changed or deleted host config file */
+       
+       for(node = connection_tree->head; node; node = next) {
+               c = node->data;
+               next = node->next;
+               
+               if(c->outgoing) {
+                       free(c->outgoing->name);
+                       if(c->outgoing->ai)
+                               freeaddrinfo(c->outgoing->ai);
+                       free(c->outgoing);
+                       c->outgoing = NULL;
                }
+               
+               xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
+               if(stat(fname, &s) || s.st_mtime > last_config_check)
+                       terminate_connection(c, c->status.active);
+               free(fname);
+       }
 
-               /* Let's check if everybody is still alive */
-
-               if(last_ping_check + pingtimeout <= now) {
-                       check_dead_connections();
-                       last_ping_check = now;
-
-                       if(routing_mode == RMODE_SWITCH)
-                               age_subnets();
-
-                       age_past_requests();
-
-                       /* Should we regenerate our key? */
-
-                       if(keyexpires <= now) {
-                               avl_node_t *node;
-                               node_t *n;
-
-                               ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
+       last_config_check = time(NULL);
 
-                               for(node = node_tree->head; node; node = node->next) {
-                                       n = node->data;
-                                       if(n->inkey) {
-                                               free(n->inkey);
-                                               n->inkey = NULL;
-                                       }
-                               }
+       /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
 
-                               send_key_changed();
-                               keyexpires = now + keylifetime;
-                       }
+       if(strictsubnets) {
+               subnet_t *subnet;
 
-                       /* Detect ADD_EDGE/DEL_EDGE storms that are caused when
-                        * two tinc daemons with the same name are on the VPN.
-                        * If so, sleep a while. If this happens multiple times
-                        * in a row, sleep longer. */
-
-                       if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
-                               logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
-                               usleep(sleeptime * 1000000LL);
-                               sleeptime *= 2;
-                               if(sleeptime < 0)
-                                       sleeptime = 3600;
-                       } else {
-                               sleeptime /= 2;
-                               if(sleeptime < 10)
-                                       sleeptime = 10;
-                       }
 
-                       contradicting_add_edge = 0;
-                       contradicting_del_edge = 0;
+               for(node = subnet_tree->head; node; node = node->next) {
+                       subnet = node->data;
+                       subnet->expires = 1;
                }
 
-               if(sigalrm) {
-                       avl_node_t *node;
-                       logger(LOG_INFO, "Flushing event queue");
-                       expire_events();
-                       for(node = connection_tree->head; node; node = node->next) {
-                               connection_t *c = node->data;
-                               send_ping(c);
+               load_all_subnets();
+
+               for(node = subnet_tree->head; node; node = next) {
+                       next = node->next;
+                       subnet = node->data;
+                       if(subnet->expires == 1) {
+                               send_del_subnet(everyone, subnet);
+                               if(subnet->owner->status.reachable)
+                                       subnet_update(subnet->owner, subnet, false);
+                               subnet_del(subnet->owner, subnet);
+                       } else if(subnet->expires == -1) {
+                               subnet->expires = 0;
+                       } else {
+                               send_add_subnet(everyone, subnet);
+                               if(subnet->owner->status.reachable)
+                                       subnet_update(subnet->owner, subnet, true);
                        }
-                       sigalrm = false;
                }
+       }
 
-               while((event = get_expired_event())) {
-                       event->handler(event->data);
-                       free_event(event);
-               }
-
-               if(sighup) {
-                       connection_t *c;
-                       avl_node_t *node, *next;
-                       char *fname;
-                       struct stat s;
-                       
-                       sighup = false;
-
-                       reopenlogger();
-                       
-                       /* Reread our own configuration file */
-
-                       exit_configuration(&config_tree);
-                       init_configuration(&config_tree);
-
-                       if(!read_server_config()) {
-                               logger(LOG_ERR, "Unable to reread configuration file, exitting.");
-                               return 1;
-                       }
-
-                       /* Cancel non-active outgoing connections */
-
-                       for(node = connection_tree->head; node; node = next) {
-                               next = node->next;
-                               c = node->data;
-
-                               c->outgoing = NULL;
-
-                               if(c->status.connecting) {
-                                       terminate_connection(c, false);
-                                       connection_del(c);
-                               }
-                       }
-
-                       /* Wipe list of outgoing connections */
-
-                       for(list_node_t *node = outgoing_list->head; node; node = node->next) {
-                               outgoing_t *outgoing = node->data;
-
-                               if(outgoing->event)
-                                       event_del(outgoing->event);
-                       }
-
-                       list_delete_list(outgoing_list);
-
-                       /* Close connections to hosts that have a changed or deleted host config file */
-                       
-                       for(node = connection_tree->head; node; node = node->next) {
-                               c = node->data;
-                               
-                               xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
-                               if(stat(fname, &s) || s.st_mtime > last_config_check)
-                                       terminate_connection(c, c->status.active);
-                               free(fname);
-                       }
-
-                       last_config_check = now;
-
-                       /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
-
-                       if(strictsubnets) {
-                               subnet_t *subnet;
+       /* Try to make outgoing connections */
+       
+       try_outgoing_connections();
 
-                               for(node = subnet_tree->head; node; node = node->next) {
-                                       subnet = node->data;
-                                       subnet->expires = 1;
-                               }
+       return 0;
+}
 
-                               load_all_subnets();
-
-                               for(node = subnet_tree->head; node; node = next) {
-                                       next = node->next;
-                                       subnet = node->data;
-                                       if(subnet->expires == 1) {
-                                               send_del_subnet(everyone, subnet);
-                                               if(subnet->owner->status.reachable)
-                                                       subnet_update(subnet->owner, subnet, false);
-                                               subnet_del(subnet->owner, subnet);
-                                       } else if(subnet->expires == -1) {
-                                               subnet->expires = 0;
-                                       } else {
-                                               send_add_subnet(everyone, subnet);
-                                               if(subnet->owner->status.reachable)
-                                                       subnet_update(subnet->owner, subnet, true);
-                                       }
-                               }
-                       }
+void retry(void) {
+       connection_t *c;
+       splay_node_t *node;
 
-                       /* Try to make outgoing connections */
-                       
-                       try_outgoing_connections();
-               }
+       for(node = connection_tree->head; node; node = node->next) {
+               c = node->data;
                
-               /* Dump graph if wanted every 60 seconds*/
-
-               if(last_graph_dump + 60 <= now) {
-                       dump_graph();
-                       last_graph_dump = now;
+               if(c->outgoing && !c->node) {
+                       if(timeout_initialized(&c->outgoing->ev))
+                               event_del(&c->outgoing->ev);
+                       if(c->status.connecting)
+                               close(c->socket);
+                       c->outgoing->timeout = 0;
+                       do_outgoing_connection(c);
                }
        }
+}
+
+/*
+  this is where it all happens...
+*/
+int main_loop(void) {
+       struct event timeout_event;
+
+       timeout_set(&timeout_event, timeout_handler, &timeout_event);
+       event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
+
+#ifndef HAVE_MINGW
+       struct event sighup_event;
+       struct event sigterm_event;
+       struct event sigquit_event;
+       struct event sigalrm_event;
+
+       signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
+       signal_add(&sighup_event, NULL);
+       signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
+       signal_add(&sigterm_event, NULL);
+       signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
+       signal_add(&sigquit_event, NULL);
+       signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
+       signal_add(&sigalrm_event, NULL);
+#endif
+
+       if(event_loop(0) < 0) {
+               logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+               return 1;
+       }
 
-#ifdef HAVE_PSELECT
-       /* Restore SIGHUP & SIGALARM mask */
-       sigprocmask(SIG_SETMASK, &omask, NULL);
+#ifndef HAVE_MINGW
+       signal_del(&sighup_event);
+       signal_del(&sigterm_event);
+       signal_del(&sigquit_event);
+       signal_del(&sigalrm_event);
 #endif
 
+       event_del(&timeout_event);
+
        return 0;
 }
index b831cdd8da55063b964071fe6c48ffcf6d027964..c511a5fc8342f6084525bbbbe7300afb63c1ca85 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -21,9 +21,9 @@
 #ifndef __TINC_NET_H__
 #define __TINC_NET_H__
 
-#include <openssl/evp.h>
-
 #include "ipv6.h"
+#include "cipher.h"
+#include "digest.h"
 
 #ifdef ENABLE_JUMBOGRAMS
 #define MTU 9018                               /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
 #define MTU 1518                               /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
 #endif
 
-#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20)       /* MTU + seqno + padding + HMAC + compressor overhead */
+#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)      /* MTU + seqno + padding + HMAC + compressor overhead */
 #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128)   /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
 
-#define MAXSOCKETS 128                 /* Overkill... */
+#define MAXSOCKETS 8                   /* Probably overkill... */
 
 typedef struct mac_t {
        uint8_t x[6];
@@ -84,6 +84,8 @@ typedef struct vpn_packet_t {
 } vpn_packet_t;
 
 typedef struct listen_socket_t {
+       struct event ev_tcp;
+       struct event ev_udp;
        int tcp;
        int udp;
        sockaddr_t sa;
@@ -98,7 +100,7 @@ typedef struct outgoing_t {
        struct config_t *cfg;
        struct addrinfo *ai;
        struct addrinfo *aip;
-       struct event *event;
+       struct event ev;
 } outgoing_t;
 
 extern list_t *outgoing_list;
@@ -110,31 +112,26 @@ extern unsigned replaywin;
 
 extern listen_socket_t listen_socket[MAXSOCKETS];
 extern int listen_sockets;
-extern int keyexpires;
 extern int keylifetime;
 extern int udp_rcvbuf;
 extern int udp_sndbuf;
 extern bool do_prune;
-extern bool do_purge;
 extern char *myport;
-extern time_t now;
 extern int contradicting_add_edge;
 extern int contradicting_del_edge;
 
-extern volatile bool running;
-
 /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
 #include "connection.h"
 #include "node.h"
 
 extern void retry_outgoing(outgoing_t *);
-extern void handle_incoming_vpn_data(int);
+extern void handle_incoming_vpn_data(int, short, void *);
 extern void finish_connecting(struct connection_t *);
-extern void do_outgoing_connection(struct connection_t *);
-extern bool handle_new_meta_connection(int);
+extern bool do_outgoing_connection(struct connection_t *);
+extern void handle_new_meta_connection(int, short, void *);
 extern int setup_listen_socket(const sockaddr_t *);
 extern int setup_vpn_in_socket(const sockaddr_t *);
-extern void send_packet(const struct node_t *, vpn_packet_t *);
+extern void send_packet(struct node_t *, vpn_packet_t *);
 extern void receive_tcppacket(struct connection_t *, const char *, int);
 extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
 extern bool setup_network(void);
@@ -144,8 +141,16 @@ extern void close_network_connections(void);
 extern int main_loop(void);
 extern void terminate_connection(struct connection_t *, bool);
 extern void flush_queue(struct node_t *);
+extern bool node_read_ecdsa_public_key(struct node_t *);
+extern bool read_ecdsa_public_key(struct connection_t *);
 extern bool read_rsa_public_key(struct connection_t *);
 extern void send_mtu_probe(struct node_t *);
+extern void handle_device_data(int, short, void *);
+extern void handle_meta_connection_data(int, short, void *);
+extern void regenerate_key(void);
+extern void purge(void);
+extern void retry(void);
+extern int reload_configuration(void);
 extern void load_all_subnets(void);
 
 #ifndef HAVE_MINGW
index 360f318e1ffacf7c5dd06a234b2a856e9f047e94..f7d8640300a6bc5065b572e0828446e9b3cb7c0a 100644 (file)
 #include LZO1X_H
 #endif
 
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "conf.h"
 #include "connection.h"
+#include "crypto.h"
+#include "digest.h"
 #include "device.h"
 #include "ethernet.h"
-#include "event.h"
 #include "graph.h"
 #include "logger.h"
 #include "net.h"
@@ -53,7 +55,6 @@
 #include "xalloc.h"
 
 int keylifetime = 0;
-int keyexpires = 0;
 #ifdef HAVE_LZO
 static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
 #endif
@@ -69,13 +70,13 @@ unsigned replaywin = 16;
 // mtuprobes ==    32: send 1 burst, sleep pingtimeout second
 // mtuprobes ==    33: no response from other side, restart PMTU discovery process
 
-void send_mtu_probe(node_t *n) {
+static void send_mtu_probe_handler(int fd, short events, void *data) {
+       node_t *n = data;
        vpn_packet_t packet;
        int len, i;
        int timeout = 1;
        
        n->mtuprobes++;
-       n->mtuevent = NULL;
 
        if(!n->status.reachable || !n->status.validkey) {
                ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
@@ -128,7 +129,7 @@ void send_mtu_probe(node_t *n) {
                        len = 64;
                
                memset(packet.data, 0, 14);
-               RAND_pseudo_bytes(packet.data + 14, len - 14);
+               randomize(packet.data + 14, len - 14);
                packet.len = len;
                packet.priority = 0;
 
@@ -138,14 +139,16 @@ void send_mtu_probe(node_t *n) {
        }
 
 end:
-       n->mtuevent = new_event();
-       n->mtuevent->handler = (event_handler_t)send_mtu_probe;
-       n->mtuevent->data = n;
-       n->mtuevent->time = now + timeout;
-       event_add(n->mtuevent);
+       event_add(&n->mtuevent, &(struct timeval){timeout, 0});
 }
 
-void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
+void send_mtu_probe(node_t *n) {
+       if(!timeout_initialized(&n->mtuevent))
+               timeout_set(&n->mtuevent, send_mtu_probe_handler, n);
+       send_mtu_probe_handler(0, 0, n);
+}
+
+static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
        ifdebug(TRAFFIC) logger(LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
 
        if(!packet->data[0]) {
@@ -231,18 +234,17 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
                           packet->len, n->name, n->hostname);
 
+       n->in_packets++;
+       n->in_bytes += packet->len;
+
        route(n, packet);
 }
 
-static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) {
-       unsigned char hmac[EVP_MAX_MD_SIZE];
-
-       if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
+static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
+       if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
                return false;
 
-       HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
-
-       return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
+       return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
 }
 
 static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
@@ -250,11 +252,9 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
        int nextpkt = 0;
        vpn_packet_t *outpkt = pkt[0];
-       int outlen, outpad;
-       unsigned char hmac[EVP_MAX_MD_SIZE];
-       int i;
+       size_t outlen;
 
-       if(!n->inkey) {
+       if(!cipher_active(&n->incipher)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
                                        n->name, n->hostname);
                return;
@@ -262,7 +262,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
 
        /* Check packet length */
 
-       if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
+       if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
                                        n->name, n->hostname);
                return;
@@ -270,39 +270,31 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
 
        /* Check the message authentication code */
 
-       if(n->indigest && n->inmaclength) {
-               inpkt->len -= n->inmaclength;
-               HMAC(n->indigest, n->inkey, n->inkeylength,
-                        (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
-
-               if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
-                       ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)",
-                                          n->name, n->hostname);
+       if(digest_active(&n->indigest)) {
+               inpkt->len -= n->indigest.maclength;
+               if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
+                       ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
                        return;
                }
        }
-
        /* Decrypt the packet */
 
-       if(n->incipher) {
+       if(cipher_active(&n->incipher)) {
                outpkt = pkt[nextpkt++];
+               outlen = MAXSIZE;
 
-               if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL)
-                               || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
-                                       (unsigned char *) &inpkt->seqno, inpkt->len)
-                               || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
-                       ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s): %s",
-                                               n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
+               if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+                       ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
                        return;
                }
                
-               outpkt->len = outlen + outpad;
+               outpkt->len = outlen;
                inpkt = outpkt;
        }
 
        /* Check the sequence number */
 
-       inpkt->len -= sizeof(inpkt->seqno);
+       inpkt->len -= sizeof inpkt->seqno;
        inpkt->seqno = ntohl(inpkt->seqno);
 
        if(replaywin) {
@@ -323,7 +315,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                                        return;
                                }
                        } else {
-                               for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
+                               for(int i = n->received_seqno + 1; i < inpkt->seqno; i++)
                                        n->late[(i / 8) % replaywin] |= 1 << i % 8;
                        }
                }
@@ -336,7 +328,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                n->received_seqno = inpkt->seqno;
                        
        if(n->received_seqno > MAX_SEQNO)
-               keyexpires = 0;
+               regenerate_key();
 
        /* Decompress the packet */
 
@@ -383,12 +375,12 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        vpn_packet_t *inpkt = origpkt;
        int nextpkt = 0;
        vpn_packet_t *outpkt;
-       int origlen;
-       int outlen, outpad;
+       int origlen = origpkt->len;
+       size_t outlen;
 #if defined(SOL_IP) && defined(IP_TOS)
        static int priority = 0;
+       int origpriority = origpkt->priority;
 #endif
-       int origpriority;
 
        if(!n->status.reachable) {
                ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
@@ -398,6 +390,8 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        /* Make sure we have a valid key */
 
        if(!n->status.validkey) {
+               time_t now = time(NULL);
+
                ifdebug(TRAFFIC) logger(LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
@@ -425,9 +419,6 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                return;
        }
 
-       origlen = inpkt->len;
-       origpriority = inpkt->priority;
-
        /* Compress the packet */
 
        if(n->outcompression) {
@@ -445,32 +436,28 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        /* Add sequence number */
 
        inpkt->seqno = htonl(++(n->sent_seqno));
-       inpkt->len += sizeof(inpkt->seqno);
+       inpkt->len += sizeof inpkt->seqno;
 
        /* Encrypt the packet */
 
-       if(n->outcipher) {
+       if(cipher_active(&n->outcipher)) {
                outpkt = pkt[nextpkt++];
+               outlen = MAXSIZE;
 
-               if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL)
-                               || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
-                                       (unsigned char *) &inpkt->seqno, inpkt->len)
-                               || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
-                       ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s): %s",
-                                               n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
+               if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+                       ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
 
-               outpkt->len = outlen + outpad;
+               outpkt->len = outlen;
                inpkt = outpkt;
        }
 
        /* Add the message authentication code */
 
-       if(n->outdigest && n->outmaclength) {
-               HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
-                        inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
-               inpkt->len += n->outmaclength;
+       if(digest_active(&n->outdigest)) {
+               digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len);
+               inpkt->len += digest_length(&n->outdigest);
        }
 
        /* Determine which socket we have to use */
@@ -513,12 +500,14 @@ end:
 /*
   send a packet to the given vpn ip.
 */
-void send_packet(const node_t *n, vpn_packet_t *packet) {
+void send_packet(node_t *n, vpn_packet_t *packet) {
        node_t *via;
 
        if(n == myself) {
                if(overwrite_mac)
                         memcpy(packet->data, mymac.x, ETH_ALEN);
+               n->out_packets++;
+               n->out_bytes += packet->len;
                devops.write(packet);
                return;
        }
@@ -532,6 +521,9 @@ void send_packet(const node_t *n, vpn_packet_t *packet) {
                return;
        }
 
+       n->out_packets++;
+       n->out_bytes += packet->len;
+
        via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
 
        if(via != n)
@@ -548,7 +540,7 @@ void send_packet(const node_t *n, vpn_packet_t *packet) {
 /* Broadcast a packet using the minimum spanning tree */
 
 void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
-       avl_node_t *node;
+       splay_node_t *node;
        connection_t *c;
 
        ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
@@ -572,11 +564,12 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
 }
 
 static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
-       avl_node_t *node;
+       splay_node_t *node;
        edge_t *e;
        node_t *n = NULL;
        bool hard = false;
        static time_t last_hard_try = 0;
+       time_t now = time(NULL);
 
        for(node = edge_weight_tree->head; node; node = node->next) {
                e = node->data;
@@ -603,21 +596,24 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
        return n;
 }
 
-void handle_incoming_vpn_data(int sock) {
+void handle_incoming_vpn_data(int sock, short events, void *data) {
        vpn_packet_t pkt;
        char *hostname;
        sockaddr_t from;
-       socklen_t fromlen = sizeof(from);
+       socklen_t fromlen = sizeof from;
        node_t *n;
+       int len;
 
-       pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
+       len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
 
-       if(pkt.len < 0) {
+       if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
                        logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
                return;
        }
 
+       pkt.len = len;
+
        sockaddrunmap(&from);           /* Some braindead IPv6 implementations do stupid things. */
 
        n = lookup_node_udp(&from);
@@ -636,7 +632,19 @@ void handle_incoming_vpn_data(int sock) {
                        return;
        }
 
-       n->sock = sock;
+       n->sock = (intptr_t)data;
 
        receive_udppacket(n, &pkt);
 }
+
+void handle_device_data(int sock, short events, void *data) {
+       vpn_packet_t packet;
+
+       packet.priority = 0;
+
+       if(devops.read(&packet)) {
+               myself->in_packets++;
+               myself->in_bytes += packet.len;
+               route(myself, &packet);
+       }
+}
index 299e3729e0cb131555d99238e8b4c2745325c69b..207ce42543b4c9da1c542aa4f7484b580953a425 100644 (file)
 
 #include "system.h"
 
-#include <openssl/pem.h>
-#include <openssl/rsa.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "conf.h"
 #include "connection.h"
+#include "control.h"
 #include "device.h"
-#include "event.h"
+#include "digest.h"
+#include "ecdsa.h"
 #include "graph.h"
 #include "logger.h"
 #include "net.h"
 #include "process.h"
 #include "protocol.h"
 #include "route.h"
+#include "rsa.h"
 #include "subnet.h"
 #include "utils.h"
 #include "xalloc.h"
 
 char *myport;
+static struct event device_ev;
 devops_t devops;
 
-bool read_rsa_public_key(connection_t *c) {
+bool node_read_ecdsa_public_key(node_t *n) {
+       if(ecdsa_active(&n->ecdsa))
+               return true;
+
+       splay_tree_t *config_tree;
        FILE *fp;
        char *fname;
-       char *key;
+       char *p;
+       bool result = false;
 
-       if(!c->rsa_key) {
-               c->rsa_key = RSA_new();
-//             RSA_blinding_on(c->rsa_key, NULL);
-       }
+       xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
 
-       /* First, check for simple PublicKey statement */
+       init_configuration(&config_tree);
+       if(!read_config_file(config_tree, fname))
+               goto exit;
 
-       if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &key)) {
-               BN_hex2bn(&c->rsa_key->n, key);
-               BN_hex2bn(&c->rsa_key->e, "FFFF");
-               free(key);
-               return true;
+       /* First, check for simple ECDSAPublicKey statement */
+
+       if(get_config_string(lookup_config(config_tree, "ECDSAPublicKey"), &p)) {
+               result = ecdsa_set_base64_public_key(&n->ecdsa, p);
+               free(p);
+               goto exit;
        }
 
-       /* Else, check for PublicKeyFile statement and read it */
+       /* Else, check for ECDSAPublicKeyFile statement and read it */
 
-       if(get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname)) {
-               fp = fopen(fname, "r");
+       free(fname);
 
-               if(!fp) {
-                       logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
-                                  fname, strerror(errno));
-                       free(fname);
-                       return false;
-               }
+       if(!get_config_string(lookup_config(config_tree, "ECDSAPublicKeyFile"), &fname))
+               xasprintf(&fname, "%s/hosts/%s", confbase, n->name);
 
-               free(fname);
-               c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
-               fclose(fp);
+       fp = fopen(fname, "r");
 
-               if(c->rsa_key)
-                       return true;            /* Woohoo. */
+       if(!fp) {
+               logger(LOG_ERR, "Error reading ECDSA public key file `%s': %s", fname, strerror(errno));
+               goto exit;
+       }
 
-               /* If it fails, try PEM_read_RSA_PUBKEY. */
-               fp = fopen(fname, "r");
+       result = ecdsa_read_pem_public_key(&n->ecdsa, fp);
+       fclose(fp);
 
-               if(!fp) {
-                       logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
-                                  fname, strerror(errno));
-                       free(fname);
-                       return false;
-               }
+exit:
+       exit_configuration(&config_tree);
+       free(fname);
+       return result;
+}
 
-               free(fname);
-               c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
-               fclose(fp);
+bool read_ecdsa_public_key(connection_t *c) {
+       FILE *fp;
+       char *fname;
+       char *p;
+       bool result;
 
-               if(c->rsa_key) {
-//                             RSA_blinding_on(c->rsa_key, NULL);
-                       return true;
-               }
+       /* First, check for simple ECDSAPublicKey statement */
 
-               logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
-                          fname, strerror(errno));
-               return false;
+       if(get_config_string(lookup_config(c->config_tree, "ECDSAPublicKey"), &p)) {
+               result = ecdsa_set_base64_public_key(&c->ecdsa, p);
+               free(p);
+               return result;
        }
 
-       /* Else, check if a harnessed public key is in the config file */
+       /* Else, check for ECDSAPublicKeyFile statement and read it */
+
+       if(!get_config_string(lookup_config(c->config_tree, "ECDSAPublicKeyFile"), &fname))
+               xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 
-       xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
 
        if(!fp) {
-               logger(LOG_ERR, "Error reading RSA public key file `%s': %s", fname, strerror(errno));
+               logger(LOG_ERR, "Error reading ECDSA public key file `%s': %s",
+                          fname, strerror(errno));
                free(fname);
                return false;
        }
 
-       c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
+       result = ecdsa_read_pem_public_key(&c->ecdsa, fp);
        fclose(fp);
+
+       if(!result) 
+               logger(LOG_ERR, "Reading ECDSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
+       return result;
+}
 
-       if(c->rsa_key)
-               return true;
+bool read_rsa_public_key(connection_t *c) {
+       FILE *fp;
+       char *fname;
+       char *n;
+       bool result;
 
-       /* Try again with PEM_read_RSA_PUBKEY. */
+       /* First, check for simple PublicKey statement */
+
+       if(get_config_string(lookup_config(c->config_tree, "PublicKey"), &n)) {
+               result = rsa_set_hex_public_key(&c->rsa, n, "FFFF");
+               free(n);
+               return result;
+       }
+
+       /* Else, check for PublicKeyFile statement and read it */
+
+       if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
+               xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 
-       xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
        fp = fopen(fname, "r");
 
        if(!fp) {
@@ -138,39 +157,77 @@ bool read_rsa_public_key(connection_t *c) {
                return false;
        }
 
-       c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
-//     RSA_blinding_on(c->rsa_key, NULL);
+       result = rsa_read_pem_public_key(&c->rsa, fp);
        fclose(fp);
+
+       if(!result) 
+               logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
+       return result;
+}
 
-       if(c->rsa_key)
-               return true;
+static bool read_ecdsa_private_key(void) {
+       FILE *fp;
+       char *fname;
+       bool result;
 
-       logger(LOG_ERR, "No public key for %s specified!", c->name);
+       /* Check for PrivateKeyFile statement and read it */
+
+       if(!get_config_string(lookup_config(config_tree, "ECDSAPrivateKeyFile"), &fname))
+               xasprintf(&fname, "%s/ecdsa_key.priv", confbase);
+
+       fp = fopen(fname, "r");
+
+       if(!fp) {
+               logger(LOG_ERR, "Error reading ECDSA private key file `%s': %s", fname, strerror(errno));
+               free(fname);
+               return false;
+       }
+
+#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+       struct stat s;
+
+       if(fstat(fileno(fp), &s)) {
+               logger(LOG_ERR, "Could not stat ECDSA private key file `%s': %s'", fname, strerror(errno));
+               free(fname);
+               return false;
+       }
 
-       return false;
+       if(s.st_mode & ~0100700)
+               logger(LOG_WARNING, "Warning: insecure file permissions for ECDSA private key file `%s'!", fname);
+#endif
+
+       result = ecdsa_read_pem_private_key(&myself->connection->ecdsa, fp);
+       fclose(fp);
+
+       if(!result) 
+               logger(LOG_ERR, "Reading ECDSA private key file `%s' failed: %s", fname, strerror(errno));
+       free(fname);
+       return result;
 }
 
 static bool read_rsa_private_key(void) {
        FILE *fp;
-       char *fname, *key, *pubkey;
-       struct stat s;
+       char *fname;
+       char *n, *d;
+       bool result;
 
-       if(get_config_string(lookup_config(config_tree, "PrivateKey"), &key)) {
-               if(!get_config_string(lookup_config(config_tree, "PublicKey"), &pubkey)) {
+       /* First, check for simple PrivateKey statement */
+
+       if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
+               if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
                        logger(LOG_ERR, "PrivateKey used but no PublicKey found!");
+                       free(d);
                        return false;
                }
-               myself->connection->rsa_key = RSA_new();
-//             RSA_blinding_on(myself->connection->rsa_key, NULL);
-               BN_hex2bn(&myself->connection->rsa_key->d, key);
-               BN_hex2bn(&myself->connection->rsa_key->n, pubkey);
-               BN_hex2bn(&myself->connection->rsa_key->e, "FFFF");
-               free(key);
-               free(pubkey);
+               result = rsa_set_hex_private_key(&myself->connection->rsa, n, "FFFF", d);
+               free(n);
+               free(d);
                return true;
        }
 
+       /* Else, check for PrivateKeyFile statement and read it */
+
        if(!get_config_string(lookup_config(config_tree, "PrivateKeyFile"), &fname))
                xasprintf(&fname, "%s/rsa_key.priv", confbase);
 
@@ -184,9 +241,10 @@ static bool read_rsa_private_key(void) {
        }
 
 #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+       struct stat s;
+
        if(fstat(fileno(fp), &s)) {
-               logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'",
-                               fname, strerror(errno));
+               logger(LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
                free(fname);
                return false;
        }
@@ -195,18 +253,31 @@ static bool read_rsa_private_key(void) {
                logger(LOG_WARNING, "Warning: insecure file permissions for RSA private key file `%s'!", fname);
 #endif
 
-       myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+       result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
        fclose(fp);
 
-       if(!myself->connection->rsa_key) {
-               logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s",
-                          fname, strerror(errno));
-               free(fname);
-               return false;
+       if(!result) 
+               logger(LOG_ERR, "Reading RSA private key file `%s' failed: %s", fname, strerror(errno));
+       free(fname);
+       return result;
+}
+
+static struct event keyexpire_event;
+
+static void keyexpire_handler(int fd, short events, void *data) {
+       regenerate_key();
+}
+
+void regenerate_key(void) {
+       if(timeout_initialized(&keyexpire_event)) {
+               ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
+               event_del(&keyexpire_event);
+               send_key_changed();
+       } else {
+               timeout_set(&keyexpire_event, keyexpire_handler, NULL);
        }
 
-       free(fname);
-       return true;
+       event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
 }
 
 /*
@@ -217,7 +288,7 @@ void load_all_subnets(void) {
        struct dirent *ent;
        char *dname;
        char *fname;
-       avl_tree_t *config_tree;
+       splay_tree_t *config_tree;
        config_t *cfg;
        subnet_t *s, *s2;
        node_t *n;
@@ -293,7 +364,8 @@ static bool setup_myself(void) {
        myself->connection->hostname = xstrdup("MYSELF");
 
        myself->connection->options = 0;
-       myself->connection->protocol_version = PROT_CURRENT;
+       myself->connection->protocol_major = PROT_MAJOR;
+       myself->connection->protocol_minor = PROT_MINOR;
 
        if(!get_config_string(lookup_config(config_tree, "Name"), &name)) {     /* Not acceptable */
                logger(LOG_ERR, "Name for tinc daemon required!");
@@ -313,6 +385,11 @@ static bool setup_myself(void) {
        read_config_file(config_tree, fname);
        free(fname);
 
+       get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental);
+
+       if(experimental && !read_ecdsa_private_key())
+               return false;
+
        if(!read_rsa_private_key())
                return false;
 
@@ -456,65 +533,36 @@ static bool setup_myself(void) {
 
        /* Generate packet encryption key */
 
-       if(get_config_string
-          (lookup_config(config_tree, "Cipher"), &cipher)) {
-               if(!strcasecmp(cipher, "none")) {
-                       myself->incipher = NULL;
-               } else {
-                       myself->incipher = EVP_get_cipherbyname(cipher);
-
-                       if(!myself->incipher) {
-                               logger(LOG_ERR, "Unrecognized cipher type!");
-                               return false;
-                       }
-               }
-       } else
-               myself->incipher = EVP_bf_cbc();
-
-       if(myself->incipher)
-               myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
-       else
-               myself->inkeylength = 1;
+       if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
+               cipher = xstrdup("blowfish");
 
-       myself->connection->outcipher = EVP_bf_ofb();
+       if(!cipher_open_by_name(&myself->incipher, cipher)) {
+               logger(LOG_ERR, "Unrecognized cipher type!");
+               return false;
+       }
 
        if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
                keylifetime = 3600;
 
-       keyexpires = now + keylifetime;
-       
-       /* Check if we want to use message authentication codes... */
+       regenerate_key();
 
-       if(get_config_string(lookup_config(config_tree, "Digest"), &digest)) {
-               if(!strcasecmp(digest, "none")) {
-                       myself->indigest = NULL;
-               } else {
-                       myself->indigest = EVP_get_digestbyname(digest);
+       /* Check if we want to use message authentication codes... */
 
-                       if(!myself->indigest) {
-                               logger(LOG_ERR, "Unrecognized digest type!");
-                               return false;
-                       }
-               }
-       } else
-               myself->indigest = EVP_sha1();
+       int maclength = 4;
+       get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
 
-       myself->connection->outdigest = EVP_sha1();
+       if(maclength < 0) {
+               logger(LOG_ERR, "Bogus MAC length!");
+               return false;
+       }
 
-       if(get_config_int(lookup_config(config_tree, "MACLength"), &myself->inmaclength)) {
-               if(myself->indigest) {
-                       if(myself->inmaclength > myself->indigest->md_size) {
-                               logger(LOG_ERR, "MAC length exceeds size of digest!");
-                               return false;
-                       } else if(myself->inmaclength < 0) {
-                               logger(LOG_ERR, "Bogus MAC length!");
-                               return false;
-                       }
-               }
-       } else
-               myself->inmaclength = 4;
+       if(!get_config_string(lookup_config(config_tree, "Digest"), &digest))
+               digest = xstrdup("sha1");
 
-       myself->connection->outmaclength = 0;
+       if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
+               logger(LOG_ERR, "Unrecognized digest type!");
+               return false;
+       }
 
        /* Compression */
 
@@ -562,6 +610,16 @@ static bool setup_myself(void) {
        if(!devops.setup())
                return false;
 
+       if(device_fd >= 0) {
+               event_set(&device_ev, device_fd, EV_READ|EV_PERSIST, handle_device_data, NULL);
+
+               if (event_add(&device_ev, NULL) < 0) {
+                       logger(LOG_ERR, "event_add failed: %s", strerror(errno));
+                       devops.close();
+                       return false;
+               }
+       }
+
        /* Run tinc-up script to further initialize the tap interface */
        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
@@ -571,7 +629,7 @@ static bool setup_myself(void) {
 
        execute_script("tinc-up", envp);
 
-       for(i = 0; i < 5; i++)
+       for(i = 0; i < 4; i++)
                free(envp[i]);
 
        /* Run subnet-up scripts for our own subnets */
@@ -617,8 +675,28 @@ static bool setup_myself(void) {
                        listen_socket[listen_sockets].udp =
                                setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
 
-                       if(listen_socket[listen_sockets].udp < 0)
+                       if(listen_socket[listen_sockets].udp < 0) {
+                               close(listen_socket[listen_sockets].tcp);
                                continue;
+                       }
+
+                       event_set(&listen_socket[listen_sockets].ev_tcp,
+                                         listen_socket[listen_sockets].tcp,
+                                         EV_READ|EV_PERSIST,
+                                         handle_new_meta_connection, NULL);
+                       if(event_add(&listen_socket[listen_sockets].ev_tcp, NULL) < 0) {
+                               logger(LOG_ERR, "event_add failed: %s", strerror(errno));
+                               abort();
+                       }
+
+                       event_set(&listen_socket[listen_sockets].ev_udp,
+                                         listen_socket[listen_sockets].udp,
+                                         EV_READ|EV_PERSIST,
+                                         handle_incoming_vpn_data, (void *)(intptr_t)listen_sockets);
+                       if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
+                               logger(LOG_ERR, "event_add failed: %s", strerror(errno));
+                               abort();
+                       }
 
                        ifdebug(CONNECTIONS) {
                                hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
@@ -647,9 +725,6 @@ static bool setup_myself(void) {
   initialize network
 */
 bool setup_network(void) {
-       now = time(NULL);
-
-       init_events();
        init_connections();
        init_subnets();
        init_nodes();
@@ -681,7 +756,7 @@ bool setup_network(void) {
   close all open network connections
 */
 void close_network_connections(void) {
-       avl_node_t *node, *next;
+       splay_node_t *node, *next;
        connection_t *c;
        char *envp[5];
        int i;
@@ -693,13 +768,6 @@ void close_network_connections(void) {
                terminate_connection(c, false);
        }
 
-       for(list_node_t *node = outgoing_list->head; node; node = node->next) {
-               outgoing_t *outgoing = node->data;
-
-               if(outgoing->event)
-                       event_del(outgoing->event);
-       }
-
        list_delete_list(outgoing_list);
 
        if(myself && myself->connection) {
@@ -709,6 +777,8 @@ void close_network_connections(void) {
        }
 
        for(i = 0; i < listen_sockets; i++) {
+               event_del(&listen_socket[i].ev_tcp);
+               event_del(&listen_socket[i].ev_udp);
                close(listen_socket[i].tcp);
                close(listen_socket[i].udp);
        }
@@ -724,7 +794,6 @@ void close_network_connections(void) {
        exit_subnets();
        exit_nodes();
        exit_connections();
-       exit_events();
 
        execute_script("tinc-down", envp);
 
index f371dc12f91fe2e88322cfb99b64a27e1c960c02..730cf6b8e56f5f00ffce0fa12ec34e6a7334aedd 100644 (file)
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "conf.h"
 #include "connection.h"
-#include "event.h"
 #include "logger.h"
 #include "meta.h"
 #include "net.h"
@@ -70,12 +69,12 @@ static void configure_tcp(connection_t *c) {
 
 #if defined(SOL_TCP) && defined(TCP_NODELAY)
        option = 1;
-       setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
+       setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option);
 #endif
 
 #if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
        option = IPTOS_LOWDELAY;
-       setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option));
+       setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
 #endif
 }
 
@@ -128,7 +127,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
        /* Optimize TCP settings */
 
        option = 1;
-       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
 
 #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
        if(sa->sa.sa_family == AF_INET6)
@@ -140,10 +139,10 @@ int setup_listen_socket(const sockaddr_t *sa) {
 #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
                struct ifreq ifr;
 
-               memset(&ifr, 0, sizeof(ifr));
+               memset(&ifr, 0, sizeof ifr);
                strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
 
-               if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) {
+               if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
                        closesocket(nfd);
                        logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
                                   strerror(sockerrno));
@@ -210,7 +209,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
 #endif
 
        option = 1;
-       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
 
        if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
                logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
@@ -271,19 +270,18 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
        return nfd;
 } /* int setup_vpn_in_socket */
 
+static void retry_outgoing_handler(int fd, short events, void *data) {
+       setup_outgoing_connection(data);
+}
+
 void retry_outgoing(outgoing_t *outgoing) {
        outgoing->timeout += 5;
 
        if(outgoing->timeout > maxtimeout)
                outgoing->timeout = maxtimeout;
 
-       if(outgoing->event)
-               event_del(outgoing->event);
-       outgoing->event = new_event();
-       outgoing->event->handler = (event_handler_t) setup_outgoing_connection;
-       outgoing->event->time = now + outgoing->timeout;
-       outgoing->event->data = outgoing;
-       event_add(outgoing->event);
+       timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
+       event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
 
        ifdebug(CONNECTIONS) logger(LOG_NOTICE,
                           "Trying to re-establish outgoing connection in %d seconds",
@@ -295,12 +293,13 @@ void finish_connecting(connection_t *c) {
 
        configure_tcp(c);
 
-       c->last_ping_time = now;
+       c->last_ping_time = time(NULL);
+       c->status.connecting = false;
 
        send_id(c);
 }
 
-void do_outgoing_connection(connection_t *c) {
+bool do_outgoing_connection(connection_t *c) {
        char *address, *port, *space;
        int result;
 
@@ -314,10 +313,10 @@ begin:
                if(!c->outgoing->cfg) {
                        ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
                                           c->name);
-                       c->status.remove = true;
                        retry_outgoing(c->outgoing);
                        c->outgoing = NULL;
-                       return;
+                       connection_del(c);
+                       return false;
                }
 
                get_config_string(c->outgoing->cfg, &address);
@@ -387,7 +386,7 @@ begin:
        if(result == -1) {
                if(sockinprogress(sockerrno)) {
                        c->status.connecting = true;
-                       return;
+                       return true;
                }
 
                closesocket(c->socket);
@@ -399,14 +398,32 @@ begin:
 
        finish_connecting(c);
 
-       return;
+       return true;
+}
+
+static void handle_meta_write(int sock, short events, void *data) {
+       ifdebug(META) logger(LOG_DEBUG, "handle_meta_write() called");
+
+       connection_t *c = data;
+
+       ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
+       if(outlen <= 0) {
+               logger(LOG_ERR, "Onoes, outlen = %d (%s)", (int)outlen, strerror(errno));
+               terminate_connection(c, c->status.active);
+               return;
+       }
+
+       buffer_read(&c->outbuf, outlen);
+       if(!c->outbuf.len && event_initialized(&c->outevent))
+               event_del(&c->outevent);
 }
 
 void setup_outgoing_connection(outgoing_t *outgoing) {
        connection_t *c;
        node_t *n;
 
-       outgoing->event = NULL;
+       if(event_initialized(&outgoing->ev))
+               event_del(&outgoing->ev);
 
        n = lookup_node(outgoing->name);
 
@@ -437,28 +454,32 @@ void setup_outgoing_connection(outgoing_t *outgoing) {
        }
 
        c->outgoing = outgoing;
-       c->last_ping_time = now;
+       c->last_ping_time = time(NULL);
 
        connection_add(c);
 
-       do_outgoing_connection(c);
+       if (do_outgoing_connection(c)) {
+               event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
+               event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
+               event_add(&c->inevent, NULL);
+       }
 }
 
 /*
   accept a new tcp connect and create a
   new connection
 */
-bool handle_new_meta_connection(int sock) {
+void handle_new_meta_connection(int sock, short events, void *data) {
        connection_t *c;
        sockaddr_t sa;
        int fd;
-       socklen_t len = sizeof(sa);
+       socklen_t len = sizeof sa;
 
        fd = accept(sock, &sa.sa, &len);
 
        if(fd < 0) {
                logger(LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
-               return false;
+               return;
        }
 
        sockaddrunmap(&sa);
@@ -473,18 +494,20 @@ bool handle_new_meta_connection(int sock) {
        c->address = sa;
        c->hostname = sockaddr2hostname(&sa);
        c->socket = fd;
-       c->last_ping_time = now;
+       c->last_ping_time = time(NULL);
 
        ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection from %s", c->hostname);
 
+       event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
+       event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
+       event_add(&c->inevent, NULL);
+               
        configure_tcp(c);
 
        connection_add(c);
 
        c->allow_request = ID;
        send_id(c);
-
-       return true;
 }
 
 static void free_outgoing(outgoing_t *outgoing) {
@@ -515,7 +538,7 @@ void try_outgoing_connections(void) {
                        continue;
                }
 
-               outgoing = xmalloc_and_zero(sizeof(*outgoing));
+               outgoing = xmalloc_and_zero(sizeof *outgoing);
                outgoing->name = name;
                list_insert_tail(outgoing_list, outgoing);
                setup_outgoing_connection(outgoing);
index 11a06ed4847c44b69db822be634ab1a6297e751c..8db252dd134c900933ed0a23fd897dace44084b4 100644 (file)
@@ -88,7 +88,7 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
                return;
        }
 
-       err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+       err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV);
 
        if(err) {
                logger(LOG_ERR, "Error while translating addresses: %s",
@@ -118,7 +118,7 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
                return str;
        }
 
-       err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port),
+       err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port,
                                        hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
        if(err) {
                logger(LOG_ERR, "Error while looking up hostname: %s",
@@ -179,20 +179,20 @@ int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
                        return strcmp(a->unknown.port, b->unknown.port);
 
                case AF_INET:
-                       result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
+                       result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof a->in.sin_addr);
 
                        if(result)
                                return result;
 
-                       return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
+                       return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof a->in.sin_port);
 
                case AF_INET6:
-                       result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
+                       result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof a->in6.sin6_addr);
 
                        if(result)
                                return result;
 
-                       return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
+                       return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof a->in6.sin6_port);
 
                default:
                        logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
index cf70f8380e12df58641c01d03740135dc3252b13..38123e2028fca1b84ae9540d567d24c05f46e366 100644 (file)
@@ -20,7 +20,8 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "control_common.h"
+#include "splay_tree.h"
 #include "logger.h"
 #include "net.h"
 #include "netutl.h"
@@ -28,8 +29,8 @@
 #include "utils.h"
 #include "xalloc.h"
 
-avl_tree_t *node_tree;                 /* Known nodes, sorted by name */
-avl_tree_t *node_udp_tree;             /* Known nodes, sorted by address and port */
+splay_tree_t *node_tree;                       /* Known nodes, sorted by name */
+splay_tree_t *node_udp_tree;           /* Known nodes, sorted by address and port */
 
 node_t *myself;
 
@@ -38,27 +39,32 @@ static int node_compare(const node_t *a, const node_t *b) {
 }
 
 static int node_udp_compare(const node_t *a, const node_t *b) {
-       return sockaddrcmp(&a->address, &b->address);
+       int result;
+
+       result = sockaddrcmp(&a->address, &b->address);
+
+       if(result)
+               return result;
+
+       return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
 }
 
 void init_nodes(void) {
-       node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node);
-       node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL);
+       node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
+       node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL);
 }
 
 void exit_nodes(void) {
-       avl_delete_tree(node_udp_tree);
-       avl_delete_tree(node_tree);
+       splay_delete_tree(node_udp_tree);
+       splay_delete_tree(node_tree);
 }
 
 node_t *new_node(void) {
-       node_t *n = xmalloc_and_zero(sizeof(*n));
+       node_t *n = xmalloc_and_zero(sizeof *n);
 
        if(replaywin) n->late = xmalloc_and_zero(replaywin);
        n->subnet_tree = new_subnet_tree();
        n->edge_tree = new_edge_tree();
-       EVP_CIPHER_CTX_init(&n->inctx);
-       EVP_CIPHER_CTX_init(&n->outctx);
        n->mtu = MTU;
        n->maxmtu = MTU;
 
@@ -66,12 +72,6 @@ node_t *new_node(void) {
 }
 
 void free_node(node_t *n) {
-       if(n->inkey)
-               free(n->inkey);
-
-       if(n->outkey)
-               free(n->outkey);
-
        if(n->subnet_tree)
                free_subnet_tree(n->subnet_tree);
 
@@ -80,11 +80,16 @@ void free_node(node_t *n) {
 
        sockaddrfree(&n->address);
 
-       EVP_CIPHER_CTX_cleanup(&n->inctx);
-       EVP_CIPHER_CTX_cleanup(&n->outctx);
+       cipher_close(&n->incipher);
+       digest_close(&n->indigest);
+       cipher_close(&n->outcipher);
+       digest_close(&n->outdigest);
+
+       ecdh_free(&n->ecdh);
+       ecdsa_free(&n->ecdsa);
 
-       if(n->mtuevent)
-               event_del(n->mtuevent);
+       if(timeout_initialized(&n->mtuevent))
+               event_del(&n->mtuevent);
        
        if(n->hostname)
                free(n->hostname);
@@ -99,11 +104,11 @@ void free_node(node_t *n) {
 }
 
 void node_add(node_t *n) {
-       avl_insert(node_tree, n);
+       splay_insert(node_tree, n);
 }
 
 void node_del(node_t *n) {
-       avl_node_t *node, *next;
+       splay_node_t *node, *next;
        edge_t *e;
        subnet_t *s;
 
@@ -119,8 +124,8 @@ void node_del(node_t *n) {
                edge_del(e);
        }
 
-       avl_delete(node_udp_tree, n);
-       avl_delete(node_tree, n);
+       splay_delete(node_udp_tree, n);
+       splay_delete(node_tree, n);
 }
 
 node_t *lookup_node(char *name) {
@@ -128,7 +133,7 @@ node_t *lookup_node(char *name) {
 
        n.name = name;
 
-       return avl_search(node_tree, &n);
+       return splay_search(node_tree, &n);
 }
 
 node_t *lookup_node_udp(const sockaddr_t *sa) {
@@ -137,7 +142,7 @@ node_t *lookup_node_udp(const sockaddr_t *sa) {
        n.address = *sa;
        n.name = NULL;
 
-       return avl_search(node_udp_tree, &n);
+       return splay_search(node_udp_tree, &n);
 }
 
 void update_node_udp(node_t *n, const sockaddr_t *sa) {
@@ -146,7 +151,7 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
                return;
        }
 
-       avl_delete(node_udp_tree, n);
+       splay_delete(node_udp_tree, n);
 
        if(n->hostname)
                free(n->hostname);
@@ -154,7 +159,7 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
        if(sa) {
                n->address = *sa;
                n->hostname = sockaddr2hostname(&n->address);
-               avl_insert(node_udp_tree, n);
+               splay_insert(node_udp_tree, n);
                ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
        } else {
                memset(&n->address, 0, sizeof n->address);
@@ -163,20 +168,31 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
        }
 }
 
-void dump_nodes(void) {
-       avl_node_t *node;
+bool dump_nodes(connection_t *c) {
+       splay_node_t *node;
        node_t *n;
 
-       logger(LOG_DEBUG, "Nodes:");
-
        for(node = node_tree->head; node; node = node->next) {
                n = node->data;
-               logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)",
-                          n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0,
-                          n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression,
+               send_request(c, "%d %d %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)", CONTROL, REQ_DUMP_NODES,
+                          n->name, n->hostname, cipher_get_nid(&n->outcipher),
+                          digest_get_nid(&n->outdigest), (int)digest_length(&n->outdigest), n->outcompression,
                           n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
-                          n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
+                          n->via ? n->via->name ?: "-" : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
+       }
+
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
+}
+
+bool dump_traffic(connection_t *c) {
+       splay_node_t *node;
+       node_t *n;
+
+       for(node = node_tree->head; node; node = node->next) {
+               n = node->data;
+               send_request(c, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, CONTROL, REQ_DUMP_TRAFFIC,
+                          n->name, n->in_packets, n->in_bytes, n->out_packets, n->out_bytes);
        }
 
-       logger(LOG_DEBUG, "End of nodes.");
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
 }
index 66cb6c62f6024696149af1441133fc7e5a2c26f1..4464011fc1f60e3ad94a355510432cb6039b1b69 100644 (file)
 #ifndef __TINC_NODE_H__
 #define __TINC_NODE_H__
 
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "connection.h"
-#include "event.h"
+#include "digest.h"
+#include "ecdh.h"
 #include "subnet.h"
 
 typedef struct node_status_t {
-       unsigned int unused_active:1;                   /* 1 if active (not used for nodes) */
-       unsigned int validkey:1;                                /* 1 if we currently have a valid key for him */
-       unsigned int unused_waitingforkey:1;            /* 1 if we already sent out a request */
-       unsigned int visited:1;                         /* 1 if this node has been visited by one of the graph algorithms */
-       unsigned int reachable:1;                       /* 1 if this node is reachable in the graph */
-       unsigned int indirect:1;                                /* 1 if this node is not directly reachable by us */
-       unsigned int unused:26;
+       unsigned int unused_active:1;           /* 1 if active (not used for nodes) */
+       unsigned int validkey:1;                /* 1 if we currently have a valid key for him */
+       unsigned int unused_waitingforkey:1;    /* 1 if we already sent out a request */
+       unsigned int visited:1;                 /* 1 if this node has been visited by one of the graph algorithms */
+       unsigned int reachable:1;               /* 1 if this node is reachable in the graph */
+       unsigned int indirect:1;                /* 1 if this node is not directly reachable by us */
+       unsigned int ecdh:1;                    /* 1 if this node supports ECDH key exchange */
+       unsigned int unused:25;
 } node_status_t;
 
 typedef struct node_t {
@@ -47,31 +50,25 @@ typedef struct node_t {
        node_status_t status;
        time_t last_req_key;
 
-       const EVP_CIPHER *incipher;             /* Cipher type for UDP packets received from him */
-       char *inkey;                            /* Cipher key and iv */
-       int inkeylength;                        /* Cipher key and iv length */
-       EVP_CIPHER_CTX inctx;                   /* Cipher context */
-       
-       const EVP_CIPHER *outcipher;            /* Cipher type for UDP packets sent to him*/
-       char *outkey;                           /* Cipher key and iv */
-       int outkeylength;                       /* Cipher key and iv length */
-       EVP_CIPHER_CTX outctx;                  /* Cipher context */
-       
-       const EVP_MD *indigest;                 /* Digest type for MAC of packets received from him */
-       int inmaclength;                        /* Length of MAC */
-
-       const EVP_MD *outdigest;                /* Digest type for MAC of packets sent to him*/
-       int outmaclength;                       /* Length of MAC */
+       ecdsa_t ecdsa;                          /* His public ECDSA key */
+       ecdh_t ecdh;                            /* State for ECDH key exchange */
+
+       cipher_t incipher;                        /* Cipher for UDP packets */
+       digest_t indigest;                        /* Digest for UDP packets */  
+
+       cipher_t outcipher;                        /* Cipher for UDP packets */
+       digest_t outdigest;                        /* Digest for UDP packets */ 
 
        int incompression;                      /* Compressionlevel, 0 = no compression */
        int outcompression;                     /* Compressionlevel, 0 = no compression */
 
+       int distance;
        struct node_t *nexthop;                 /* nearest node from us to him */
        struct node_t *via;                     /* next hop for UDP packets */
 
-       avl_tree_t *subnet_tree;                /* Pointer to a tree of subnets belonging to this node */
+       splay_tree_t *subnet_tree;              /* Pointer to a tree of subnets belonging to this node */
 
-       avl_tree_t *edge_tree;                  /* Edges with this node as one of the endpoints */
+       splay_tree_t *edge_tree;                        /* Edges with this node as one of the endpoints */
 
        struct connection_t *connection;        /* Connection associated with this node (if a direct connection exists) */
 
@@ -84,12 +81,17 @@ typedef struct node_t {
        length_t minmtu;                        /* Probed minimum MTU */
        length_t maxmtu;                        /* Probed maximum MTU */
        int mtuprobes;                          /* Number of probes */
-       event_t *mtuevent;                      /* Probe event */
+       struct event mtuevent;                  /* Probe event */
+
+       uint64_t in_packets;
+       uint64_t in_bytes;
+       uint64_t out_packets;
+       uint64_t out_bytes;
 } node_t;
 
 extern struct node_t *myself;
-extern avl_tree_t *node_tree;
-extern avl_tree_t *node_udp_tree;
+extern splay_tree_t *node_tree;
+extern splay_tree_t *node_udp_tree;
 
 extern void init_nodes(void);
 extern void exit_nodes(void);
@@ -99,7 +101,8 @@ extern void node_add(node_t *);
 extern void node_del(node_t *);
 extern node_t *lookup_node(char *);
 extern node_t *lookup_node_udp(const sockaddr_t *);
+extern bool dump_nodes(struct connection_t *);
+extern bool dump_traffic(struct connection_t *);
 extern void update_node_udp(node_t *, const sockaddr_t *);
-extern void dump_nodes(void);
 
 #endif                                                 /* __TINC_NODE_H__ */
diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c
new file mode 100644 (file)
index 0000000..63892f3
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+    cipher.c -- Symmetric block cipher handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/rand.h>
+#include <openssl/err.h>
+
+#include "cipher.h"
+#include "logger.h"
+#include "xalloc.h"
+
+typedef struct cipher_counter {
+       unsigned char counter[EVP_MAX_IV_LENGTH];
+       unsigned char block[EVP_MAX_IV_LENGTH];
+       int n;
+} cipher_counter_t;
+
+static bool cipher_open(cipher_t *cipher) {
+       EVP_CIPHER_CTX_init(&cipher->ctx);
+
+       return true;
+}
+
+bool cipher_open_by_name(cipher_t *cipher, const char *name) {
+       cipher->cipher = EVP_get_cipherbyname(name);
+
+       if(cipher->cipher)
+               return cipher_open(cipher);
+
+       logger(LOG_ERR, "Unknown cipher name '%s'!", name);
+       return false;
+}
+
+bool cipher_open_by_nid(cipher_t *cipher, int nid) {
+       cipher->cipher = EVP_get_cipherbynid(nid);
+
+       if(cipher->cipher)
+               return cipher_open(cipher);
+
+       logger(LOG_ERR, "Unknown cipher nid %d!", nid);
+       return false;
+}
+
+bool cipher_open_blowfish_ofb(cipher_t *cipher) {
+       cipher->cipher = EVP_bf_ofb();
+       return cipher_open(cipher);
+}
+
+void cipher_close(cipher_t *cipher) {
+       EVP_CIPHER_CTX_cleanup(&cipher->ctx);
+       if(cipher->counter) {
+               free(cipher->counter);
+               cipher->counter = 0;
+       }
+}
+
+size_t cipher_keylength(const cipher_t *cipher) {
+       return cipher->cipher->key_len + cipher->cipher->block_size;
+}
+
+bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
+       bool result;
+
+       if(encrypt)
+               result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
+       else
+               result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
+
+       if(result)
+               return true;
+
+       logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
+       bool result;
+
+       if(encrypt)
+               result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
+       else
+               result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
+
+       if(result)
+               return true;
+
+       logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+bool cipher_set_counter_key(cipher_t *cipher, void *key) {
+       int result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, NULL);
+       if(!result) {
+               logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       if(!cipher->counter)
+               cipher->counter = xmalloc_and_zero(sizeof *cipher->counter);
+       else
+               cipher->counter->n = 0;
+
+       memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->block_size);
+
+       return true;
+}
+
+bool cipher_counter_xor(cipher_t *cipher, const void *indata, size_t inlen, void *outdata) {
+       if(!cipher->counter) {
+               logger(LOG_ERR, "Counter not initialized");
+               return false;
+       }
+
+       const unsigned char *in = indata;
+       unsigned char *out = outdata;
+
+       while(inlen--) {
+               // Encrypt the new counter value if we need it 
+               if(!cipher->counter->n) {
+                       int len;
+                       if(!EVP_EncryptUpdate(&cipher->ctx, cipher->counter->block, &len, cipher->counter->counter, cipher->cipher->block_size)) {
+                               logger(LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
+                               return false;
+                       }
+
+                       // Increase the counter value
+                       for(int i = 0; i < cipher->cipher->block_size; i++)
+                               if(++cipher->counter->counter[i])
+                                       break;
+               }
+
+               *out++ = *in++ ^ cipher->counter->counter[cipher->counter->n++];
+
+               if(cipher->counter->n >= cipher->cipher->block_size)
+                       cipher->counter->n = 0;
+       }
+
+       return true;
+}
+
+
+bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
+       if(oneshot) {
+               int len, pad;
+               if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
+                               && EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
+                               && EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
+                       if(outlen) *outlen = len + pad;
+                       return true;
+               }
+       } else {
+               int len;
+               if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
+                       if(outlen) *outlen = len;
+                       return true;
+               }
+       }
+
+       logger(LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
+       if(oneshot) {
+               int len, pad;
+               if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
+                               && EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
+                               && EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
+                       if(outlen) *outlen = len + pad;
+                       return true;
+               }
+       } else {
+               int len;
+               if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
+                       if(outlen) *outlen = len;
+                       return true;
+               }
+       }
+
+       logger(LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+int cipher_get_nid(const cipher_t *cipher) {
+       return cipher->cipher ? cipher->cipher->nid : 0;
+}
+
+bool cipher_active(const cipher_t *cipher) {
+       return cipher->cipher && cipher->cipher->nid != 0;
+}
diff --git a/src/openssl/cipher.h b/src/openssl/cipher.h
new file mode 100644 (file)
index 0000000..589ec4c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    cipher.h -- header file cipher.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CIPHER_H__
+#define __TINC_CIPHER_H__
+
+#include <openssl/evp.h>
+
+#define CIPHER_MAX_BLOCK_SIZE EVP_MAX_BLOCK_LENGTH
+#define CIPHER_MAX_KEY_SIZE EVP_MAX_KEY_LENGTH
+#define CIPHER_MAX_IV_SIZE EVP_MAX_IV_LENGTH
+
+typedef struct cipher {
+       EVP_CIPHER_CTX ctx;
+       const EVP_CIPHER *cipher;
+       struct cipher_counter *counter;
+} cipher_t;
+
+extern bool cipher_open_by_name(cipher_t *, const char *);
+extern bool cipher_open_by_nid(cipher_t *, int);
+extern bool cipher_open_blowfish_ofb(cipher_t *);
+extern void cipher_close(cipher_t *);
+extern size_t cipher_keylength(const cipher_t *);
+extern bool cipher_set_key(cipher_t *, void *, bool);
+extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool);
+extern bool cipher_set_counter_key(cipher_t *, void *);
+extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
+extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
+extern bool cipher_counter_xor(cipher_t *, const void *indata, size_t inlen, void *outdata);
+extern int cipher_get_nid(const cipher_t *);
+extern bool cipher_active(const cipher_t *);
+
+#endif
diff --git a/src/openssl/crypto.c b/src/openssl/crypto.c
new file mode 100644 (file)
index 0000000..db921d6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    crypto.c -- Cryptographic miscellaneous functions and initialisation
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+
+#include "crypto.h"
+
+void crypto_init(void) {
+        RAND_load_file("/dev/urandom", 1024);
+
+        ENGINE_load_builtin_engines();
+        ENGINE_register_all_complete();
+
+        OpenSSL_add_all_algorithms();
+}
+
+void crypto_exit(void) {
+       EVP_cleanup();
+}
+
+void randomize(void *out, size_t outlen) {
+       RAND_pseudo_bytes(out, outlen);
+}
diff --git a/src/openssl/crypto.h b/src/openssl/crypto.h
new file mode 100644 (file)
index 0000000..8047bfb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    crypto.h -- header for crypto.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_CRYPTO_H__
+#define __TINC_CRYPTO_H__
+
+extern void crypto_init();
+extern void crypto_exit();
+extern void randomize(void *, size_t);
+
+#endif
diff --git a/src/openssl/digest.c b/src/openssl/digest.c
new file mode 100644 (file)
index 0000000..88bdeb3
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    digest.c -- Digest handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+#include "utils.h"
+#include "xalloc.h"
+
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+
+#include "digest.h"
+#include "logger.h"
+
+static void set_maclength(digest_t *digest, int maclength) {
+       int digestlen = EVP_MD_size(digest->digest);
+
+       if(maclength > digestlen || maclength < 0)
+               digest->maclength = digestlen;
+       else
+               digest->maclength = maclength;
+}
+
+bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
+       digest->digest = EVP_get_digestbyname(name);
+       digest->key = NULL;
+
+       if(!digest->digest) {
+               logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
+               return false;
+       }
+
+       set_maclength(digest, maclength);
+       return true;
+}
+
+bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
+       digest->digest = EVP_get_digestbynid(nid);
+       digest->key = NULL;
+
+       if(!digest->digest) {
+               logger(LOG_DEBUG, "Unknown digest nid %d!", nid);
+               return false;
+       }
+
+       set_maclength(digest, maclength);
+       return true;
+}
+
+bool digest_open_sha1(digest_t *digest, int maclength) {
+       digest->digest = EVP_sha1();
+       digest->key = NULL;
+
+       set_maclength(digest, maclength);
+       return true;
+}
+
+bool digest_set_key(digest_t *digest, const void *key, size_t len) {
+       digest->key = xrealloc(digest->key, len);
+       memcpy(digest->key, key, len);
+       digest->keylength = len;
+       return true;
+}
+
+void digest_close(digest_t *digest) {
+       if(digest->key)
+               free(digest->key);
+       digest->key = NULL;
+}
+
+bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
+       size_t len = EVP_MD_size(digest->digest);
+       unsigned char tmpdata[len];
+
+       if(digest->key) {
+               HMAC(digest->digest, digest->key, digest->keylength, indata, inlen, tmpdata, NULL);
+       } else {
+               EVP_MD_CTX ctx;
+
+               if(!EVP_DigestInit(&ctx, digest->digest)
+                               || !EVP_DigestUpdate(&ctx, indata, inlen)
+                               || !EVP_DigestFinal(&ctx, tmpdata, NULL)) {
+                       logger(LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL));
+                       return false;
+               }
+       }
+
+       memcpy(outdata, tmpdata, digest->maclength);
+       return true;
+}
+
+bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
+       size_t len = digest->maclength;
+       unsigned char outdata[len];
+
+       return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, digest->maclength);
+}
+
+int digest_get_nid(const digest_t *digest) {
+       return digest->digest ? digest->digest->type : 0;
+}
+
+size_t digest_keylength(const digest_t *digest) {
+       return digest->digest->md_size;
+}
+
+size_t digest_length(const digest_t *digest) {
+       return digest->maclength;
+}
+
+bool digest_active(const digest_t *digest) {
+       return digest->digest && digest->digest->type != 0;
+}
diff --git a/src/openssl/digest.h b/src/openssl/digest.h
new file mode 100644 (file)
index 0000000..24562b6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    digest.h -- header file digest.c
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_DIGEST_H__
+#define __TINC_DIGEST_H__
+
+#include <openssl/evp.h>
+
+#define DIGEST_MAX_SIZE EVP_MAX_MD_SIZE
+
+typedef struct digest {
+       const EVP_MD *digest;
+       int maclength;
+       int keylength;
+       char *key;
+} digest_t;
+
+extern bool digest_open_by_name(struct digest *, const char *name, int maclength);
+extern bool digest_open_by_nid(struct digest *, int nid, int maclength);
+extern bool digest_open_sha1(struct digest *, int maclength);
+extern void digest_close(struct digest *);
+extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
+extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
+extern bool digest_set_key(struct digest *, const void *key, size_t len);
+extern int digest_get_nid(const struct digest *);
+extern size_t digest_keylength(const struct digest *);
+extern size_t digest_length(const struct digest *);
+extern bool digest_active(const struct digest *);
+
+#endif
diff --git a/src/openssl/ecdh.c b/src/openssl/ecdh.c
new file mode 100644 (file)
index 0000000..804605c
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+    ecdh.c -- Diffie-Hellman key exchange handling
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+#include "utils.h"
+#include "xalloc.h"
+
+#include <openssl/err.h>
+#include <openssl/ec.h>
+#include <openssl/obj_mac.h>
+
+#include "ecdh.h"
+#include "logger.h"
+
+bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey) {
+       *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
+       if(!EC_KEY_generate_key(*ecdh)) {
+               logger(LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+       
+       const EC_POINT *point = EC_KEY_get0_public_key(*ecdh);
+       if(!point) {
+               logger(LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       size_t result = EC_POINT_point2oct(EC_KEY_get0_group(*ecdh), point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL);
+       if(!result) {
+               logger(LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       return true;
+}
+
+bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
+       EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(*ecdh));
+       if(!point) {
+               logger(LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       int result = EC_POINT_oct2point(EC_KEY_get0_group(*ecdh), point, pubkey, ECDH_SIZE, NULL);
+       if(!result) {
+               logger(LOG_ERR, "Converting binary to EC_POINT failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       result = ECDH_compute_key(shared, ECDH_SIZE, point, *ecdh, NULL);
+       EC_POINT_free(point);
+       EC_KEY_free(*ecdh);
+       *ecdh = NULL;
+
+       if(!result) {
+               logger(LOG_ERR, "Computing Elliptic Curve Diffie-Hellman shared key failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       return true;
+}
+
+void ecdh_free(ecdh_t *ecdh) {
+       if(*ecdh) {
+               EC_KEY_free(*ecdh);
+               *ecdh = NULL;
+       }
+}
diff --git a/src/openssl/ecdh.h b/src/openssl/ecdh.h
new file mode 100644 (file)
index 0000000..ef7de6e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+    ecdh.h -- header file for ecdh.c
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_ECDH_H__
+#define __TINC_ECDH_H__
+
+#include <openssl/ecdh.h>
+
+#define ECDH_SIZE 67
+#define ECDH_SHARED_SIZE 66
+
+typedef EC_KEY *ecdh_t;
+
+extern bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey);
+extern bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared);
+extern void ecdh_free(ecdh_t *ecdh);
+
+#endif
diff --git a/src/openssl/ecdsa.c b/src/openssl/ecdsa.c
new file mode 100644 (file)
index 0000000..50165e5
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+    ecdsa.c -- ECDSA key handling
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#include "logger.h"
+#include "ecdsa.h"
+#include "utils.h"
+
+// Get and set ECDSA keys
+//
+bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
+       *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
+
+       int len = strlen(p);
+       unsigned char pubkey[len / 4 * 3 + 3];
+       const unsigned char *ppubkey = pubkey;
+       len = b64decode(p, (char *)pubkey, len);
+
+       if(!o2i_ECPublicKey(ecdsa, &ppubkey, len)) {
+               logger(LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       return true;
+}
+
+char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
+       unsigned char *pubkey = NULL;
+       int len = i2o_ECPublicKey(*ecdsa, &pubkey);
+
+       char *base64 = malloc(len * 4 / 3 + 5);
+       b64encode((char *)pubkey, base64, len);
+
+       free(pubkey);
+
+       return base64;
+}
+
+// Read PEM ECDSA keys
+
+bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
+       *ecdsa = PEM_read_EC_PUBKEY(fp, ecdsa, NULL, NULL);
+
+       if(*ecdsa)
+               return true;
+
+       logger(LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
+       *ecdsa = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);
+
+       if(*ecdsa)
+               return true;
+       
+       logger(LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+size_t ecdsa_size(ecdsa_t *ecdsa) {
+       return ECDSA_size(*ecdsa);
+}
+
+// TODO: standardise output format?
+
+bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t len, void *sig) {
+       unsigned int siglen = ECDSA_size(*ecdsa);
+
+       unsigned char hash[SHA512_DIGEST_LENGTH];
+       SHA512(in, len, hash);
+
+       memset(sig, 0, siglen);
+
+       if(!ECDSA_sign(0, hash, sizeof hash, sig, &siglen, *ecdsa)) {
+               logger(LOG_DEBUG, "ECDSA_sign() failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       return true;
+}
+
+bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t len, const void *sig) {
+       unsigned int siglen = ECDSA_size(*ecdsa);
+
+       unsigned char hash[SHA512_DIGEST_LENGTH];
+       SHA512(in, len, hash);
+
+       if(!ECDSA_verify(0, hash, sizeof hash, sig, siglen, *ecdsa)) {
+               logger(LOG_DEBUG, "ECDSA_verify() failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       return true;
+}
+
+bool ecdsa_active(ecdsa_t *ecdsa) {
+       return *ecdsa;
+}
+
+void ecdsa_free(ecdsa_t *ecdsa) {
+       if(*ecdsa) {
+               EC_KEY_free(*ecdsa);
+               *ecdsa = NULL;
+       }
+}
diff --git a/src/openssl/ecdsa.h b/src/openssl/ecdsa.h
new file mode 100644 (file)
index 0000000..04f9eb9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+    ecdsa.h -- ECDSA key handling
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_ECDSA_H__
+#define __TINC_ECDSA_H__
+
+#include <openssl/ec.h>
+
+typedef EC_KEY *ecdsa_t;
+
+extern bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p);
+extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
+extern bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp);
+extern bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp);
+extern size_t ecdsa_size(ecdsa_t *ecdsa);
+extern bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t inlen, void *out);
+extern bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t inlen, const void *out);
+extern bool ecdsa_active(ecdsa_t *ecdsa);
+extern void ecdsa_free(ecdsa_t *ecdsa);
+
+#endif
diff --git a/src/openssl/ecdsagen.c b/src/openssl/ecdsagen.c
new file mode 100644 (file)
index 0000000..883c77e
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+    ecdsagen.c -- ECDSA key generation and export
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/obj_mac.h>
+
+#include "ecdsagen.h"
+#include "utils.h"
+
+// Generate ECDSA key
+
+bool ecdsa_generate(ecdsa_t *ecdsa) {
+       *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
+
+       if(!EC_KEY_generate_key(*ecdsa)) {
+               fprintf(stderr, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
+               return false;
+       }
+
+       EC_KEY_set_asn1_flag(*ecdsa, OPENSSL_EC_NAMED_CURVE);
+       EC_KEY_set_conv_form(*ecdsa, POINT_CONVERSION_COMPRESSED);
+
+       return true;
+}
+
+// Write PEM ECDSA keys
+
+bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
+       BIO *out = BIO_new(BIO_s_file());
+       BIO_set_fp(out,fp,BIO_NOCLOSE);
+       PEM_write_bio_EC_PUBKEY(out, *ecdsa);
+       BIO_free(out);
+       return true;
+}
+
+bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
+       BIO *out = BIO_new(BIO_s_file());
+       BIO_set_fp(out,fp,BIO_NOCLOSE);
+       PEM_write_bio_ECPrivateKey(out, *ecdsa, NULL, NULL, 0, NULL, NULL);
+       BIO_free(out);
+       return true;
+}
+
+// Convert ECDSA public key to base64 format
+
+char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
+       unsigned char *pubkey = NULL;
+       int len = i2o_ECPublicKey(*ecdsa, &pubkey);
+
+       char *base64 = malloc(len * 4 / 3 + 5);
+       b64encode((char *)pubkey, base64, len);
+
+       free(pubkey);
+
+       return base64;
+}
diff --git a/src/openssl/ecdsagen.h b/src/openssl/ecdsagen.h
new file mode 100644 (file)
index 0000000..8a40e45
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+    ecdsagen.h -- ECDSA key generation and export
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_ECDSAGEN_H__
+#define __TINC_ECDSAGEN_H__
+
+#include "ecdsa.h"
+
+extern bool ecdsa_generate(ecdsa_t *ecdsa);
+extern bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp);
+extern bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp);
+extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
+
+#endif
diff --git a/src/openssl/prf.c b/src/openssl/prf.c
new file mode 100644 (file)
index 0000000..1c432c7
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+    prf.c -- Pseudo-Random Function for key material generation
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/obj_mac.h>
+
+#include "digest.h"
+#include "prf.h"
+
+/* Generate key material from a master secret and a seed, based on RFC 4346 section 5.
+   We use SHA512 instead of MD5 and SHA1.
+ */
+
+static bool prf_xor(int nid, const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
+       digest_t digest;
+       
+       if(!digest_open_by_nid(&digest, nid, -1))
+               return false;
+
+       if(!digest_set_key(&digest, secret, secretlen))
+               return false;
+
+       size_t len = digest_length(&digest);
+
+       /* Data is what the "inner" HMAC function processes.
+          It consists of the previous HMAC result plus the seed.
+        */
+
+       char data[len + seedlen];
+       memset(data, 0, len);
+       memcpy(data + len, seed, seedlen);
+
+       char hash[len];
+
+       while(outlen > 0) {
+               /* Inner HMAC */
+               digest_create(&digest, data, len + seedlen, data);
+
+               /* Outer HMAC */
+               digest_create(&digest, data, len + seedlen, hash);
+
+               /* XOR the results of the outer HMAC into the out buffer */
+               for(int i = 0; i < len && i < outlen; i++)
+                       *out++ ^= hash[i];
+
+               outlen -= len;
+       }
+
+       digest_close(&digest);
+       return true;
+}
+
+bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
+       /* This construction allows us to easily switch back to a scheme where the PRF is calculated using two different digest algorithms. */
+       memset(out, 0, outlen);
+
+       return prf_xor(NID_sha512, secret, secretlen, seed, seedlen, out, outlen);
+}
diff --git a/src/openssl/prf.h b/src/openssl/prf.h
new file mode 100644 (file)
index 0000000..6525505
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    prf.h -- header file for prf.c
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_PRF_H__
+#define __TINC_PRF_H__
+
+extern bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen);
+
+#endif
diff --git a/src/openssl/rsa.c b/src/openssl/rsa.c
new file mode 100644 (file)
index 0000000..c3ea692
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+    rsa.c -- RSA key handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#include "logger.h"
+#include "rsa.h"
+
+// Set RSA keys
+
+bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
+       *rsa = RSA_new();
+       BN_hex2bn(&(*rsa)->n, n);
+       BN_hex2bn(&(*rsa)->e, e);
+       return true;
+}
+
+bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
+       *rsa = RSA_new();
+       BN_hex2bn(&(*rsa)->n, n);
+       BN_hex2bn(&(*rsa)->e, e);
+       BN_hex2bn(&(*rsa)->d, d);
+       return true;
+}
+
+// Read PEM RSA keys
+
+bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
+       *rsa = PEM_read_RSAPublicKey(fp, rsa, NULL, NULL);
+
+       if(*rsa)
+               return true;
+       
+       *rsa = PEM_read_RSA_PUBKEY(fp, rsa, NULL, NULL);
+
+       if(*rsa)
+               return true;
+
+       logger(LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
+       *rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+
+       if(*rsa)
+               return true;
+       
+       logger(LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;
+}
+
+size_t rsa_size(rsa_t *rsa) {
+       return RSA_size(*rsa);
+}
+
+bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
+       if(RSA_public_encrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
+               return true;
+
+       logger(LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;   
+}
+
+bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
+       if(RSA_private_decrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
+               return true;
+
+       logger(LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL));
+       return false;   
+}
+
+bool rsa_active(rsa_t *rsa) {
+       return *rsa;
+}
+
+void rsa_free(rsa_t *rsa) {
+       if(*rsa) {
+               RSA_free(*rsa);
+               *rsa = NULL;
+       }
+}
diff --git a/src/openssl/rsa.h b/src/openssl/rsa.h
new file mode 100644 (file)
index 0000000..10fe346
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    rsa.h -- RSA key handling
+    Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_RSA_H__
+#define __TINC_RSA_H__
+
+#include <openssl/rsa.h>
+
+typedef RSA *rsa_t;
+
+extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e);
+extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d);
+extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp);
+extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp);
+extern size_t rsa_size(rsa_t *rsa);
+extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t inlen, void *out);
+extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t inlen, void *out);
+extern bool rsa_active(rsa_t *rsa);
+extern void rsa_free(rsa_t *rsa);
+
+
+#endif
diff --git a/src/openssl/rsagen.c b/src/openssl/rsagen.c
new file mode 100644 (file)
index 0000000..0f4a4fa
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    rsagen.c -- RSA key generation and export
+    Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#include "logger.h"
+#include "rsagen.h"
+
+/* This function prettyprints the key generation process */
+
+static void indicator(int a, int b, void *p) {
+       switch (a) {
+               case 0:
+                       fprintf(stderr, ".");
+                       break;
+
+               case 1:
+                       fprintf(stderr, "+");
+                       break;
+
+               case 2:
+                       fprintf(stderr, "-");
+                       break;
+
+               case 3:
+                       switch (b) {
+                               case 0:
+                                       fprintf(stderr, " p\n");
+                                       break;
+
+                               case 1:
+                                       fprintf(stderr, " q\n");
+                                       break;
+
+                               default:
+                                       fprintf(stderr, "?");
+                       }
+                       break;
+
+               default:
+                       fprintf(stderr, "?");
+       }
+}
+
+// Generate RSA key
+
+bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) {
+       *rsa = RSA_generate_key(bits, exponent, indicator, NULL);
+
+       return *rsa;
+}
+
+// Write PEM RSA keys
+
+bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
+       PEM_write_RSAPublicKey(fp, *rsa);
+
+       return true;
+}
+
+bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
+       PEM_write_RSAPrivateKey(fp, *rsa, NULL, NULL, 0, NULL, NULL);
+       return true;
+}
diff --git a/src/openssl/rsagen.h b/src/openssl/rsagen.h
new file mode 100644 (file)
index 0000000..422d156
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    rsagen.h -- RSA key generation and export
+    Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_RSAGEN_H__
+#define __TINC_RSAGEN_H__
+
+#include "rsa.h"
+
+extern bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent);
+extern bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp);
+extern bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp);
+
+#endif
index f33355ce1e739ff2837f8df919b3234f98fd37ca..a7d0f260ec448a07850c861e66b23fcb698afbca 100644 (file)
 
 #include "conf.h"
 #include "connection.h"
+#include "control.h"
 #include "device.h"
 #include "edge.h"
 #include "logger.h"
 #include "net.h"
 #include "node.h"
-#include "pidfile.h"
 #include "process.h"
 #include "subnet.h"
 #include "utils.h"
 
 /* If zero, don't detach from the terminal. */
 bool do_detach = true;
-bool sighup = false;
 bool sigalrm = false;
 
 extern char *identname;
-extern char *pidfilename;
 extern char **g_argv;
 extern bool use_logfile;
 
-#ifndef HAVE_MINGW
-static sigset_t emptysigset;
-#endif
-
-static int saved_debug_level = -1;
-
-static void memory_full(int size) {
-       logger(LOG_ERR, "Memory exhausted (couldn't allocate %d bytes), exitting.", size);
-       exit(1);
-}
-
 /* Some functions the less gifted operating systems might lack... */
 
 #ifdef HAVE_MINGW
@@ -66,7 +53,7 @@ static SC_HANDLE service = NULL;
 static SERVICE_STATUS status = {0};
 static SERVICE_STATUS_HANDLE statushandle = 0;
 
-bool install_service(void) {
+static bool install_service(void) {
        char command[4096] = "\"";
        char **argp;
        bool space;
@@ -126,35 +113,6 @@ bool install_service(void) {
        return true;
 }
 
-bool remove_service(void) {
-       manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
-       if(!manager) {
-               logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
-               return false;
-       }
-
-       service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
-
-       if(!service) {
-               logger(LOG_ERR, "Could not open %s service: %s", identname, winerror(GetLastError()));
-               return false;
-       }
-
-       if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
-               logger(LOG_ERR, "Could not stop %s service: %s", identname, winerror(GetLastError()));
-       else
-               logger(LOG_INFO, "%s service stopped", identname);
-
-       if(!DeleteService(service)) {
-               logger(LOG_ERR, "Could not remove %s service: %s", identname, winerror(GetLastError()));
-               return false;
-       }
-
-       logger(LOG_INFO, "%s service removed", identname);
-
-       return true;
-}
-
 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
        switch(request) {
                case SERVICE_CONTROL_INTERROGATE:
@@ -171,19 +129,11 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
                        return ERROR_CALL_NOT_IMPLEMENTED;
        }
 
-       if(running) {
-               running = false;
-               status.dwWaitHint = 30000; 
-               status.dwCurrentState = SERVICE_STOP_PENDING; 
-               SetServiceStatus(statushandle, &status);
-               return NO_ERROR;
-       } else {
-               status.dwWaitHint = 0; 
-               status.dwCurrentState = SERVICE_STOPPED; 
-               SetServiceStatus(statushandle, &status);
-               exit(1);
-       }
-
+       event_loopexit(NULL);
+       status.dwWaitHint = 30000; 
+       status.dwCurrentState = SERVICE_STOP_PENDING; 
+       SetServiceStatus(statushandle, &status);
+       return NO_ERROR;
 }
 
 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
@@ -240,85 +190,15 @@ bool init_service(void) {
 }
 #endif
 
-#ifndef HAVE_MINGW
-/*
-  check for an existing tinc for this net, and write pid to pidfile
-*/
-static bool write_pidfile(void) {
-       pid_t pid;
-
-       pid = check_pid(pidfilename);
-
-       if(pid) {
-               if(netname)
-                       fprintf(stderr, "A tincd is already running for net `%s' with pid %ld.\n",
-                                       netname, (long)pid);
-               else
-                       fprintf(stderr, "A tincd is already running with pid %ld.\n", (long)pid);
-               return false;
-       }
-
-       /* if it's locked, write-protected, or whatever */
-       if(!write_pid(pidfilename)) {
-               fprintf(stderr, "Could write pid file %s: %s\n", pidfilename, strerror(errno));
-               return false;
-       }
-
-       return true;
-}
-#endif
-
-/*
-  kill older tincd for this net
-*/
-bool kill_other(int signal) {
-#ifndef HAVE_MINGW
-       pid_t pid;
-
-       pid = read_pid(pidfilename);
-
-       if(!pid) {
-               if(netname)
-                       fprintf(stderr, "No other tincd is running for net `%s'.\n",
-                                       netname);
-               else
-                       fprintf(stderr, "No other tincd is running.\n");
-               return false;
-       }
-
-       errno = 0;                                      /* No error, sometimes errno is only changed on error */
-
-       /* ESRCH is returned when no process with that pid is found */
-       if(kill(pid, signal) && errno == ESRCH) {
-               if(netname)
-                       fprintf(stderr, "The tincd for net `%s' is no longer running. ",
-                                       netname);
-               else
-                       fprintf(stderr, "The tincd is no longer running. ");
-
-               fprintf(stderr, "Removing stale lock file.\n");
-               remove_pid(pidfilename);
-       }
-
-       return true;
-#else
-       return remove_service();
-#endif
-}
-
 /*
-  Detach from current terminal, write pidfile, kill parent
+  Detach from current terminal
 */
 bool detach(void) {
-       setup_signals();
-
-       /* First check if we can open a fresh new pidfile */
-
 #ifndef HAVE_MINGW
-       if(!write_pidfile())
-               return false;
-
-       /* If we succeeded in doing that, detach */
+       signal(SIGPIPE, SIG_IGN);
+       signal(SIGUSR1, SIG_IGN);
+       signal(SIGUSR2, SIG_IGN);
+       signal(SIGWINCH, SIG_IGN);
 
        closelogger();
 #endif
@@ -330,13 +210,6 @@ bool detach(void) {
                                        strerror(errno));
                        return false;
                }
-
-               /* Now UPDATE the pid in the pidfile, because we changed it... */
-
-               if(!write_pid(pidfilename)) {
-                       fprintf(stderr, "Could not write pid file %s: %s\n", pidfilename, strerror(errno));
-                       return false;
-               }
 #else
                if(!statushandle)
                        exit(install_service());
@@ -348,8 +221,6 @@ bool detach(void) {
        logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
                           VERSION, __DATE__, __TIME__, debug_level);
 
-       xalloc_fail_func = memory_full;
-
        return true;
 }
 
@@ -428,161 +299,3 @@ bool execute_script(const char *name, char **envp) {
 #endif
        return true;
 }
-
-
-/*
-  Signal handlers.
-*/
-
-#ifndef HAVE_MINGW
-static RETSIGTYPE sigterm_handler(int a) {
-       logger(LOG_NOTICE, "Got %s signal", "TERM");
-       if(running)
-               running = false;
-       else
-               exit(1);
-}
-
-static RETSIGTYPE sigquit_handler(int a) {
-       logger(LOG_NOTICE, "Got %s signal", "QUIT");
-       if(running)
-               running = false;
-       else
-               exit(1);
-}
-
-static RETSIGTYPE fatal_signal_square(int a) {
-       logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
-                  strsignal(a));
-       exit(1);
-}
-
-static RETSIGTYPE fatal_signal_handler(int a) {
-       struct sigaction act;
-       logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
-
-       if(do_detach) {
-               logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
-
-               act.sa_handler = fatal_signal_square;
-               act.sa_mask = emptysigset;
-               act.sa_flags = 0;
-               sigaction(SIGSEGV, &act, NULL);
-
-               close_network_connections();
-               sleep(5);
-               remove_pid(pidfilename);
-               execvp(g_argv[0], g_argv);
-       } else {
-               logger(LOG_NOTICE, "Not restarting.");
-               exit(1);
-       }
-}
-
-static RETSIGTYPE sighup_handler(int a) {
-       logger(LOG_NOTICE, "Got %s signal", "HUP");
-       sighup = true;
-}
-
-static RETSIGTYPE sigint_handler(int a) {
-       logger(LOG_NOTICE, "Got %s signal", "INT");
-
-       if(saved_debug_level != -1) {
-               logger(LOG_NOTICE, "Reverting to old debug level (%d)",
-                       saved_debug_level);
-               debug_level = saved_debug_level;
-               saved_debug_level = -1;
-       } else {
-               logger(LOG_NOTICE,
-                       "Temporarily setting debug level to 5.  Kill me with SIGINT again to go back to level %d.",
-                       debug_level);
-               saved_debug_level = debug_level;
-               debug_level = 5;
-       }
-}
-
-static RETSIGTYPE sigalrm_handler(int a) {
-       logger(LOG_NOTICE, "Got %s signal", "ALRM");
-       sigalrm = true;
-}
-
-static RETSIGTYPE sigusr1_handler(int a) {
-       dump_connections();
-}
-
-static RETSIGTYPE sigusr2_handler(int a) {
-       devops.dump_stats();
-       dump_nodes();
-       dump_edges();
-       dump_subnets();
-}
-
-static RETSIGTYPE sigwinch_handler(int a) {
-       do_purge = true;
-}
-
-static RETSIGTYPE unexpected_signal_handler(int a) {
-       logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
-}
-
-static RETSIGTYPE ignore_signal_handler(int a) {
-       ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
-}
-
-static struct {
-       int signal;
-       void (*handler)(int);
-} sighandlers[] = {
-       {SIGHUP, sighup_handler},
-       {SIGTERM, sigterm_handler},
-       {SIGQUIT, sigquit_handler},
-       {SIGSEGV, fatal_signal_handler},
-       {SIGBUS, fatal_signal_handler},
-       {SIGILL, fatal_signal_handler},
-       {SIGPIPE, ignore_signal_handler},
-       {SIGINT, sigint_handler},
-       {SIGUSR1, sigusr1_handler},
-       {SIGUSR2, sigusr2_handler},
-       {SIGCHLD, ignore_signal_handler},
-       {SIGALRM, sigalrm_handler},
-       {SIGWINCH, sigwinch_handler},
-       {SIGABRT, SIG_DFL},
-       {0, NULL}
-};
-#endif
-
-void setup_signals(void) {
-#ifndef HAVE_MINGW
-       int i;
-       struct sigaction act;
-
-       sigemptyset(&emptysigset);
-       act.sa_handler = NULL;
-       act.sa_mask = emptysigset;
-       act.sa_flags = 0;
-
-       /* Set a default signal handler for every signal, errors will be
-          ignored. */
-       for(i = 1; i < NSIG; i++) {
-               if(!do_detach)
-                       act.sa_handler = SIG_DFL;
-               else
-                       act.sa_handler = unexpected_signal_handler;
-               sigaction(i, &act, NULL);
-       }
-
-       /* If we didn't detach, allow coredumps */
-       if(!do_detach)
-               sighandlers[3].handler = SIG_DFL;
-
-       /* Then, for each known signal that we want to catch, assign a
-          handler to the signal, with error checking this time. */
-       for(i = 0; sighandlers[i].signal; i++) {
-               act.sa_handler = sighandlers[i].handler;
-               if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
-                       fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
-                                       sighandlers[i].signal, strsignal(sighandlers[i].signal),
-                                       strerror(errno));
-       }
-#endif
-}
index 8d2d56211323c577b7d742a14637ce04a37f287b..ea7815eb538669086d5f550f0c410d967a51d57a 100644 (file)
@@ -22,7 +22,6 @@
 #define __TINC_PROCESS_H__
 
 extern bool do_detach;
-extern bool sighup;
 extern bool sigalrm;
 
 extern void setup_signals(void);
index 7dbee7171d99f985b3a8acf7505fb48ab5053d4f..116e139eebc98c686652bb62e8b4d0095b3a525e 100644 (file)
 
 bool tunnelserver = false;
 bool strictsubnets = false;
+bool experimental = false;
 
 /* Jumptable for the request handlers */
 
-static bool (*request_handlers[])(connection_t *) = {
+static bool (*request_handlers[])(connection_t *, char *) = {
                id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
                status_h, error_h, termreq_h,
                ping_h, pong_h,
                add_subnet_h, del_subnet_h,
                add_edge_h, del_edge_h,
-               key_changed_h, req_key_h, ans_key_h, tcppacket_h,
+               key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
 };
 
 /* Request names */
@@ -49,10 +50,10 @@ static char (*request_name[]) = {
                "STATUS", "ERROR", "TERMREQ",
                "PING", "PONG",
                "ADD_SUBNET", "DEL_SUBNET",
-               "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET",
+               "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL",
 };
 
-static avl_tree_t *past_request_tree;
+static splay_tree_t *past_request_tree;
 
 bool check_id(const char *id) {
        for(; *id; id++)
@@ -67,15 +68,15 @@ bool check_id(const char *id) {
 
 bool send_request(connection_t *c, const char *format, ...) {
        va_list args;
-       char buffer[MAXBUFSIZE];
-       int len, request;
+       char request[MAXBUFSIZE];
+       int len;
 
        /* Use vsnprintf instead of vxasprintf: faster, no memory
           fragmentation, cleanup is automatic, and there is a limit on the
           input buffer anyway */
 
        va_start(args, format);
-       len = vsnprintf(buffer, MAXBUFSIZE, format, args);
+       len = vsnprintf(request, MAXBUFSIZE, format, args);
        va_end(args);
 
        if(len < 0 || len > MAXBUFSIZE - 1) {
@@ -85,51 +86,47 @@ bool send_request(connection_t *c, const char *format, ...) {
        }
 
        ifdebug(PROTOCOL) {
-               sscanf(buffer, "%d", &request);
                ifdebug(META)
                        logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
-                                  request_name[request], c->name, c->hostname, buffer);
+                                  request_name[atoi(request)], c->name, c->hostname, request);
                else
-                       logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[request],
+                       logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[atoi(request)],
                                   c->name, c->hostname);
        }
 
-       buffer[len++] = '\n';
+       request[len++] = '\n';
 
        if(c == everyone) {
-               broadcast_meta(NULL, buffer, len);
+               broadcast_meta(NULL, request, len);
                return true;
        } else
-               return send_meta(c, buffer, len);
+               return send_meta(c, request, len);
 }
 
-void forward_request(connection_t *from) {
-       int request;
-
+void forward_request(connection_t *from, char *request) {
+       /* Note: request is not zero terminated anymore after a call to this function! */
        ifdebug(PROTOCOL) {
-               sscanf(from->buffer, "%d", &request);
                ifdebug(META)
                        logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
-                                  request_name[request], from->name, from->hostname,
-                                  from->buffer);
+                                  request_name[atoi(request)], from->name, from->hostname, request);
                else
                        logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
-                                  request_name[request], from->name, from->hostname);
+                                  request_name[atoi(request)], from->name, from->hostname);
        }
 
-       from->buffer[from->reqlen - 1] = '\n';
-
-       broadcast_meta(from, from->buffer, from->reqlen);
+       int len = strlen(request);
+       request[len++] = '\n';
+       broadcast_meta(from, request, len);
 }
 
-bool receive_request(connection_t *c) {
-       int request;
+bool receive_request(connection_t *c, char *request) {
+       int reqno = atoi(request);
 
-       if(sscanf(c->buffer, "%d", &request) == 1) {
-               if((request < 0) || (request >= LAST) || !request_handlers[request]) {
+       if(reqno || *request == '0') {
+               if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
                        ifdebug(META)
                                logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
-                                          c->name, c->hostname, c->buffer);
+                                          c->name, c->hostname, request);
                        else
                                logger(LOG_ERR, "Unknown request from %s (%s)",
                                           c->name, c->hostname);
@@ -139,25 +136,24 @@ bool receive_request(connection_t *c) {
                        ifdebug(PROTOCOL) {
                                ifdebug(META)
                                        logger(LOG_DEBUG, "Got %s from %s (%s): %s",
-                                                  request_name[request], c->name, c->hostname,
-                                                  c->buffer);
+                                                  request_name[reqno], c->name, c->hostname, request);
                                else
                                        logger(LOG_DEBUG, "Got %s from %s (%s)",
-                                                  request_name[request], c->name, c->hostname);
+                                                  request_name[reqno], c->name, c->hostname);
                        }
                }
 
-               if((c->allow_request != ALL) && (c->allow_request != request)) {
+               if((c->allow_request != ALL) && (c->allow_request != reqno)) {
                        logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
                                   c->hostname);
                        return false;
                }
 
-               if(!request_handlers[request](c)) {
+               if(!request_handlers[reqno](c, request)) {
                        /* Something went wrong. Probably scriptkiddies. Terminate. */
 
                        logger(LOG_ERR, "Error while processing %s from %s (%s)",
-                                  request_name[request], c->name, c->hostname);
+                                  request_name[reqno], c->name, c->hostname);
                        return false;
                }
        } else {
@@ -180,42 +176,38 @@ static void free_past_request(past_request_t *r) {
        free(r);
 }
 
-void init_requests(void) {
-       past_request_tree = avl_alloc_tree((avl_compare_t) past_request_compare, (avl_action_t) free_past_request);
-}
-
-void exit_requests(void) {
-       avl_delete_tree(past_request_tree);
-}
+static struct event past_request_event;
 
 bool seen_request(char *request) {
        past_request_t *new, p = {NULL};
 
        p.request = request;
 
-       if(avl_search(past_request_tree, &p)) {
+       if(splay_search(past_request_tree, &p)) {
                ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Already seen request");
                return true;
        } else {
-               new = xmalloc(sizeof(*new));
+               new = xmalloc(sizeof *new);
                new->request = xstrdup(request);
-               new->firstseen = now;
-               avl_insert(past_request_tree, new);
+               new->firstseen = time(NULL);
+               splay_insert(past_request_tree, new);
+               event_add(&past_request_event, &(struct timeval){10, 0});
                return false;
        }
 }
 
-void age_past_requests(void) {
-       avl_node_t *node, *next;
+static void age_past_requests(int fd, short events, void *data) {
+       splay_node_t *node, *next;
        past_request_t *p;
        int left = 0, deleted = 0;
+       time_t now = time(NULL);
 
        for(node = past_request_tree->head; node; node = next) {
                next = node->next;
                p = node->data;
 
                if(p->firstseen + pinginterval <= now)
-                       avl_delete_node(past_request_tree, node), deleted++;
+                       splay_delete_node(past_request_tree, node), deleted++;
                else
                        left++;
        }
@@ -223,4 +215,20 @@ void age_past_requests(void) {
        if(left || deleted)
                ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Aging past requests: deleted %d, left %d",
                           deleted, left);
+
+       if(left)
+               event_add(&past_request_event, &(struct timeval){10, 0});
+}
+
+void init_requests(void) {
+       past_request_tree = splay_alloc_tree((splay_compare_t) past_request_compare, (splay_action_t) free_past_request);
+
+       timeout_set(&past_request_event, age_past_requests, NULL);
+}
+
+void exit_requests(void) {
+       splay_delete_tree(past_request_tree);
+
+       if(timeout_initialized(&past_request_event))
+               event_del(&past_request_event);
 }
index 6c46c726055605a191891197441c361a51b2c7d4..2c97641d97b9c5fc1a236dc367cc2932c7f2108f 100644 (file)
 #ifndef __TINC_PROTOCOL_H__
 #define __TINC_PROTOCOL_H__
 
-/* Protocol version. Different versions are incompatible,
-   incompatible version have different protocols.
- */
+/* Protocol version. Different major versions are incompatible. */
 
-#define PROT_CURRENT 17
+#define PROT_MAJOR 17
+#define PROT_MINOR 2
 
 /* Silly Windows */
 
@@ -44,6 +43,7 @@ typedef enum request_t {
        ADD_EDGE, DEL_EDGE,
        KEY_CHANGED, REQ_KEY, ANS_KEY,
        PACKET,
+       CONTROL,
        LAST                                            /* Guardian for the highest request number */
 } request_t;
 
@@ -54,6 +54,7 @@ typedef struct past_request_t {
 
 extern bool tunnelserver;
 extern bool strictsubnets;
+extern bool experimental;
 
 /* Maximum size of strings in a request.
  * scanf terminates %2048s with a NUL character,
@@ -71,19 +72,19 @@ extern bool strictsubnets;
 /* Basic functions */
 
 extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
-extern void forward_request(struct connection_t *);
-extern bool receive_request(struct connection_t *);
+extern void forward_request(struct connection_t *, char *);
+extern bool receive_request(struct connection_t *, char *);
 extern bool check_id(const char *);
 
 extern void init_requests(void);
 extern void exit_requests(void);
 extern bool seen_request(char *);
-extern void age_past_requests(void);
 
 /* Requests */
 
 extern bool send_id(struct connection_t *);
 extern bool send_metakey(struct connection_t *);
+extern bool send_metakey_ec(struct connection_t *);
 extern bool send_challenge(struct connection_t *);
 extern bool send_chal_reply(struct connection_t *);
 extern bool send_ack(struct connection_t *);
@@ -103,23 +104,24 @@ extern bool send_tcppacket(struct connection_t *, const struct vpn_packet_t *);
 
 /* Request handlers  */
 
-extern bool id_h(struct connection_t *);
-extern bool metakey_h(struct connection_t *);
-extern bool challenge_h(struct connection_t *);
-extern bool chal_reply_h(struct connection_t *);
-extern bool ack_h(struct connection_t *);
-extern bool status_h(struct connection_t *);
-extern bool error_h(struct connection_t *);
-extern bool termreq_h(struct connection_t *);
-extern bool ping_h(struct connection_t *);
-extern bool pong_h(struct connection_t *);
-extern bool add_subnet_h(struct connection_t *);
-extern bool del_subnet_h(struct connection_t *);
-extern bool add_edge_h(struct connection_t *);
-extern bool del_edge_h(struct connection_t *);
-extern bool key_changed_h(struct connection_t *);
-extern bool req_key_h(struct connection_t *);
-extern bool ans_key_h(struct connection_t *);
-extern bool tcppacket_h(struct connection_t *);
+extern bool id_h(struct connection_t *, char *);
+extern bool metakey_h(struct connection_t *, char *);
+extern bool challenge_h(struct connection_t *, char *);
+extern bool chal_reply_h(struct connection_t *, char *);
+extern bool ack_h(struct connection_t *, char *);
+extern bool status_h(struct connection_t *, char *);
+extern bool error_h(struct connection_t *, char *);
+extern bool termreq_h(struct connection_t *, char *);
+extern bool ping_h(struct connection_t *, char *);
+extern bool pong_h(struct connection_t *, char *);
+extern bool add_subnet_h(struct connection_t *, char *);
+extern bool del_subnet_h(struct connection_t *, char *);
+extern bool add_edge_h(struct connection_t *, char *);
+extern bool del_edge_h(struct connection_t *, char *);
+extern bool key_changed_h(struct connection_t *, char *);
+extern bool req_key_h(struct connection_t *, char *);
+extern bool ans_key_h(struct connection_t *, char *);
+extern bool tcppacket_h(struct connection_t *, char *);
+extern bool control_h(struct connection_t *, char *);
 
 #endif                                                 /* __TINC_PROTOCOL_H__ */
index b6018ca743e555107d326b39b875fa442ec285e1..a542ca9ea922813b75cc741f24dbe8d1f2550c9c 100644 (file)
 
 #include "system.h"
 
-#include <openssl/sha.h>
-#include <openssl/rand.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "conf.h"
 #include "connection.h"
+#include "control.h"
+#include "control_common.h"
+#include "cipher.h"
+#include "crypto.h"
+#include "digest.h"
 #include "edge.h"
 #include "graph.h"
 #include "logger.h"
 #include "net.h"
 #include "netutl.h"
 #include "node.h"
+#include "prf.h"
 #include "protocol.h"
+#include "rsa.h"
 #include "utils.h"
 #include "xalloc.h"
 
 bool send_id(connection_t *c) {
-       return send_request(c, "%d %s %d", ID, myself->connection->name,
-                                               myself->connection->protocol_version);
+       gettimeofday(&c->start, NULL);
+
+       int minor = 0;
+
+       if(experimental) {
+               if(c->config_tree && !read_ecdsa_public_key(c))
+                       minor = 1;
+               else
+                       minor = myself->connection->protocol_minor;
+       }
+
+       return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
 }
 
-bool id_h(connection_t *c) {
+bool id_h(connection_t *c, char *request) {
        char name[MAX_STRING_SIZE];
 
-       if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
+       if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
                           c->hostname);
                return false;
        }
 
+       /* Check if this is a control connection */
+
+       if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
+               c->status.control = true;
+               c->allow_request = CONTROL;
+               c->last_ping_time = time(NULL) + 3600;
+               return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
+       }
+
        /* Check if identity is a valid name */
 
        if(!check_id(name)) {
@@ -76,9 +97,9 @@ bool id_h(connection_t *c) {
 
        /* Check if version matches */
 
-       if(c->protocol_version != myself->connection->protocol_version) {
-               logger(LOG_ERR, "Peer %s (%s) uses incompatible version %d",
-                          c->name, c->hostname, c->protocol_version);
+       if(c->protocol_major != myself->connection->protocol_major) {
+               logger(LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
+                          c->name, c->hostname, c->protocol_major, c->protocol_minor);
                return false;
        }
 
@@ -89,6 +110,9 @@ bool id_h(connection_t *c) {
                return send_ack(c);
        }
 
+       if(!experimental)
+               c->protocol_minor = 0;
+
        if(!c->config_tree) {
                init_configuration(&c->config_tree);
 
@@ -97,34 +121,61 @@ bool id_h(connection_t *c) {
                                   c->name);
                        return false;
                }
-       }
 
-       if(!read_rsa_public_key(c)) {
-               return false;
+               if(experimental && c->protocol_minor >= 2)
+                       if(!read_ecdsa_public_key(c))
+                               return false;
+       } else {
+               if(c->protocol_minor && !ecdsa_active(&c->ecdsa))
+                       c->protocol_minor = 1;
        }
 
        c->allow_request = METAKEY;
 
-       return send_metakey(c);
+       if(c->protocol_minor >= 2)
+               return send_metakey_ec(c);
+       else
+               return send_metakey(c);
 }
 
-bool send_metakey(connection_t *c) {
-       bool x;
+bool send_metakey_ec(connection_t *c) {
+       logger(LOG_DEBUG, "Sending ECDH metakey to %s", c->name);
+
+       size_t siglen = ecdsa_size(&myself->connection->ecdsa);
+
+       char key[(ECDH_SIZE + siglen) * 2 + 1];
 
-       int len = RSA_size(c->rsa_key);
+       // TODO: include nonce? Use relevant parts of SSH or TLS protocol
 
-       /* Allocate buffers for the meta key */
+       if(!ecdh_generate_public(&c->ecdh, key))
+               return false;
+
+       if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
+               return false;
 
-       char buffer[2 * len + 1];
+       b64encode(key, key, ECDH_SIZE + siglen);
        
-       c->outkey = xrealloc(c->outkey, len);
+       return send_request(c, "%d %s", METAKEY, key);
+}
 
-       if(!c->outctx)
-               c->outctx = xmalloc_and_zero(sizeof(*c->outctx));
+bool send_metakey(connection_t *c) {
+       if(!read_rsa_public_key(c))
+               return false;
 
-       /* Copy random data to the buffer */
+       if(!cipher_open_blowfish_ofb(&c->outcipher))
+               return false;
+       
+       if(!digest_open_sha1(&c->outdigest, -1))
+               return false;
+
+       size_t len = rsa_size(&c->rsa);
+       char key[len];
+       char enckey[len];
+       char hexkey[2 * len + 1];
 
-       RAND_pseudo_bytes((unsigned char *)c->outkey, len);
+       /* Create a random key */
+
+       randomize(key, len);
 
        /* The message we send must be smaller than the modulus of the RSA key.
           By definition, for a key of k bits, the following formula holds:
@@ -136,13 +187,13 @@ bool send_metakey(connection_t *c) {
           This can be done by setting the most significant bit to zero.
         */
 
-       c->outkey[0] &= 0x7F;
+       key[0] &= 0x7F;
+
+       cipher_set_key_from_rsa(&c->outcipher, key, len, true);
 
        ifdebug(SCARY_THINGS) {
-               bin2hex(c->outkey, buffer, len);
-               buffer[len * 2] = '\0';
-               logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s",
-                          buffer);
+               bin2hex(key, hexkey, len);
+               logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
        }
 
        /* Encrypt the random data
@@ -152,132 +203,151 @@ bool send_metakey(connection_t *c) {
           with a length equal to that of the modulus of the RSA key.
         */
 
-       if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) {
-               logger(LOG_ERR, "Error during encryption of meta key for %s (%s)",
-                          c->name, c->hostname);
+       if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) {
+               logger(LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
                return false;
        }
 
        /* Convert the encrypted random data to a hexadecimal formatted string */
 
-       bin2hex(buffer, buffer, len);
-       buffer[len * 2] = '\0';
+       bin2hex(enckey, hexkey, len);
 
        /* Send the meta key */
 
-       x = send_request(c, "%d %d %d %d %d %s", METAKEY,
-                                        c->outcipher ? c->outcipher->nid : 0,
-                                        c->outdigest ? c->outdigest->type : 0, c->outmaclength,
-                                        c->outcompression, buffer);
+       bool result = send_request(c, "%d %d %d %d %d %s", METAKEY,
+                        cipher_get_nid(&c->outcipher),
+                        digest_get_nid(&c->outdigest), c->outmaclength,
+                        c->outcompression, hexkey);
+       
+       c->status.encryptout = true;
+       return result;
+}
 
-       /* Further outgoing requests are encrypted with the key we just generated */
+static bool metakey_ec_h(connection_t *c, const char *request) {
+       size_t siglen = ecdsa_size(&c->ecdsa);
+       char key[MAX_STRING_SIZE];
 
-       if(c->outcipher) {
-               if(!EVP_EncryptInit(c->outctx, c->outcipher,
-                                       (unsigned char *)c->outkey + len - c->outcipher->key_len,
-                                       (unsigned char *)c->outkey + len - c->outcipher->key_len -
-                                       c->outcipher->iv_len)) {
-                       logger(LOG_ERR, "Error during initialisation of cipher for %s (%s): %s",
-                                       c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
-                       return false;
-               }
+       logger(LOG_DEBUG, "Got ECDH metakey from %s", c->name);
 
-               c->status.encryptout = true;
+       if(sscanf(request, "%*d " MAX_STRING, key) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
+               return false;
        }
 
-       return x;
-}
+       int inlen = b64decode(key, key, sizeof key);
 
-bool metakey_h(connection_t *c) {
-       char buffer[MAX_STRING_SIZE];
-       int cipher, digest, maclength, compression;
-       int len;
+       if(inlen != (ECDH_SIZE + siglen)) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
+               return false;
+       }
 
-       if(sscanf(c->buffer, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) {
-               logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name,
-                          c->hostname);
+       if(!ecdsa_verify(&c->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "invalid ECDSA signature");
                return false;
        }
 
-       len = RSA_size(myself->connection->rsa_key);
+       char shared[ECDH_SHARED_SIZE];
 
-       /* Check if the length of the meta key is all right */
+       if(!ecdh_compute_shared(&c->ecdh, key, shared))
+               return false;
 
-       if(strlen(buffer) != len * 2) {
-               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
+       /* Update our crypto end */
+
+       if(!cipher_open_by_name(&c->incipher, "aes-256-ofb"))
+               return false;
+       if(!digest_open_by_name(&c->indigest, "sha512", -1))
                return false;
+       if(!cipher_open_by_name(&c->outcipher, "aes-256-ofb"))
+               return false;
+       if(!digest_open_by_name(&c->outdigest, "sha512", -1))
+               return false;
+
+       size_t mykeylen = cipher_keylength(&c->incipher);
+       size_t hiskeylen = cipher_keylength(&c->outcipher);
+
+       char *mykey;
+       char *hiskey;
+       char *seed;
+       
+       if(strcmp(myself->name, c->name) < 0) {
+               mykey = key;
+               hiskey = key + mykeylen * 2;
+               xasprintf(&seed, "tinc TCP key expansion %s %s", myself->name, c->name);
+       } else {
+               mykey = key + hiskeylen * 2;
+               hiskey = key;
+               xasprintf(&seed, "tinc TCP key expansion %s %s", c->name, myself->name);
        }
 
-       /* Allocate buffers for the meta key */
+       if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
+               return false;
 
-       c->inkey = xrealloc(c->inkey, len);
+       free(seed);
 
-       if(!c->inctx)
-               c->inctx = xmalloc_and_zero(sizeof(*c->inctx));
+       cipher_set_key(&c->incipher, mykey, false);
+       digest_set_key(&c->indigest, mykey + mykeylen, mykeylen);
 
-       /* Convert the challenge from hexadecimal back to binary */
+       cipher_set_key(&c->outcipher, hiskey, true);
+       digest_set_key(&c->outdigest, hiskey + hiskeylen, hiskeylen);
 
-       hex2bin(buffer, buffer, len);
+       c->status.decryptin = true;
+       c->status.encryptout = true;
+       c->allow_request = CHALLENGE;
 
-       /* Decrypt the meta key */
+       return send_challenge(c);
+}
 
-       if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) {  /* See challenge() */
-               logger(LOG_ERR, "Error during decryption of meta key for %s (%s)",
-                          c->name, c->hostname);
+bool metakey_h(connection_t *c, char *request) {
+       if(c->protocol_minor >= 2)
+               return metakey_ec_h(c, request);
+
+       char hexkey[MAX_STRING_SIZE];
+       int cipher, digest, maclength, compression;
+       size_t len = rsa_size(&myself->connection->rsa);
+       char enckey[len];
+       char key[len];
+
+       if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
                return false;
        }
 
-       ifdebug(SCARY_THINGS) {
-               bin2hex(c->inkey, buffer, len);
-               buffer[len * 2] = '\0';
-               logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", buffer);
-       }
+       /* Convert the challenge from hexadecimal back to binary */
 
-       /* All incoming requests will now be encrypted. */
+       int inlen = hex2bin(hexkey, enckey, sizeof enckey);
 
-       /* Check and lookup cipher and digest algorithms */
+       /* Check if the length of the meta key is all right */
 
-       if(cipher) {
-               c->incipher = EVP_get_cipherbynid(cipher);
-               
-               if(!c->incipher) {
-                       logger(LOG_ERR, "%s (%s) uses unknown cipher!", c->name, c->hostname);
-                       return false;
-               }
+       if(inlen != len) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
+               return false;
+       }
 
-               if(!EVP_DecryptInit(c->inctx, c->incipher,
-                                       (unsigned char *)c->inkey + len - c->incipher->key_len,
-                                       (unsigned char *)c->inkey + len - c->incipher->key_len -
-                                       c->incipher->iv_len)) {
-                       logger(LOG_ERR, "Error during initialisation of cipher from %s (%s): %s",
-                                       c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
-                       return false;
-               }
+       /* Decrypt the meta key */
 
-               c->status.decryptin = true;
-       } else {
-               c->incipher = NULL;
+       if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) {
+               logger(LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
+               return false;
        }
 
-       c->inmaclength = maclength;
+       ifdebug(SCARY_THINGS) {
+               bin2hex(key, hexkey, len);
+               logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
+       }
 
-       if(digest) {
-               c->indigest = EVP_get_digestbynid(digest);
+       /* Check and lookup cipher and digest algorithms */
 
-               if(!c->indigest) {
-                       logger(LOG_ERR, "Node %s (%s) uses unknown digest!", c->name, c->hostname);
-                       return false;
-               }
+       if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) {
+               logger(LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
+               return false;
+       }
 
-               if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) {
-                       logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname);
-                       return false;
-               }
-       } else {
-               c->indigest = NULL;
+       if(!digest_open_by_nid(&c->indigest, digest, -1)) {
+               logger(LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
+               return false;
        }
 
-       c->incompression = compression;
+       c->status.decryptin = true;
 
        c->allow_request = CHALLENGE;
 
@@ -285,134 +355,87 @@ bool metakey_h(connection_t *c) {
 }
 
 bool send_challenge(connection_t *c) {
-       /* CHECKME: what is most reasonable value for len? */
-
-       int len = RSA_size(c->rsa_key);
+       size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa);
+       char buffer[len * 2 + 1];
 
-       /* Allocate buffers for the challenge */
-
-       char buffer[2 * len + 1];
-
-       c->hischallenge = xrealloc(c->hischallenge, len);
+       if(!c->hischallenge)
+               c->hischallenge = xrealloc(c->hischallenge, len);
 
        /* Copy random data to the buffer */
 
-       RAND_pseudo_bytes((unsigned char *)c->hischallenge, len);
+       randomize(c->hischallenge, len);
 
        /* Convert to hex */
 
        bin2hex(c->hischallenge, buffer, len);
-       buffer[len * 2] = '\0';
 
        /* Send the challenge */
 
        return send_request(c, "%d %s", CHALLENGE, buffer);
 }
 
-bool challenge_h(connection_t *c) {
+bool challenge_h(connection_t *c, char *request) {
        char buffer[MAX_STRING_SIZE];
-       int len;
+       size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&myself->connection->rsa);
+       size_t digestlen = digest_length(&c->indigest);
+       char digest[digestlen];
 
-       if(sscanf(c->buffer, "%*d " MAX_STRING, buffer) != 1) {
-               logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name,
-                          c->hostname);
+       if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
                return false;
        }
 
-       len = RSA_size(myself->connection->rsa_key);
+       /* Convert the challenge from hexadecimal back to binary */
+
+       int inlen = hex2bin(buffer, buffer, sizeof buffer);
 
        /* Check if the length of the challenge is all right */
 
-       if(strlen(buffer) != len * 2) {
-               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
-                          c->hostname, "wrong challenge length");
+       if(inlen != len) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
                return false;
        }
 
-       /* Allocate buffers for the challenge */
-
-       c->mychallenge = xrealloc(c->mychallenge, len);
-
-       /* Convert the challenge from hexadecimal back to binary */
-
-       hex2bin(buffer, c->mychallenge, len);
-
        c->allow_request = CHAL_REPLY;
 
-       /* Rest is done by send_chal_reply() */
-
-       return send_chal_reply(c);
-}
-
-bool send_chal_reply(connection_t *c) {
-       char hash[EVP_MAX_MD_SIZE * 2 + 1];
-       EVP_MD_CTX ctx;
-
        /* Calculate the hash from the challenge we received */
 
-       if(!EVP_DigestInit(&ctx, c->indigest)
-                       || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key))
-                       || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) {
-               logger(LOG_ERR, "Error during calculation of response for %s (%s): %s",
-                       c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
+       digest_create(&c->indigest, buffer, len, digest);
 
        /* Convert the hash to a hexadecimal formatted string */
 
-       bin2hex(hash, hash, c->indigest->md_size);
-       hash[c->indigest->md_size * 2] = '\0';
+       bin2hex(digest, buffer, digestlen);
 
        /* Send the reply */
 
-       return send_request(c, "%d %s", CHAL_REPLY, hash);
+       return send_request(c, "%d %s", CHAL_REPLY, buffer);
 }
 
-bool chal_reply_h(connection_t *c) {
+bool chal_reply_h(connection_t *c, char *request) {
        char hishash[MAX_STRING_SIZE];
-       char myhash[EVP_MAX_MD_SIZE];
-       EVP_MD_CTX ctx;
 
-       if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) {
+       if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
                           c->hostname);
                return false;
        }
 
-       /* Check if the length of the hash is all right */
-
-       if(strlen(hishash) != c->outdigest->md_size * 2) {
-               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
-                          c->hostname, "wrong challenge reply length");
-               return false;
-       }
-
        /* Convert the hash to binary format */
 
-       hex2bin(hishash, hishash, c->outdigest->md_size);
+       int inlen = hex2bin(hishash, hishash, sizeof hishash);
 
-       /* Calculate the hash from the challenge we sent */
+       /* Check if the length of the hash is all right */
 
-       if(!EVP_DigestInit(&ctx, c->outdigest)
-                       || !EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key))
-                       || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) {
-               logger(LOG_ERR, "Error during calculation of response from %s (%s): %s",
-                       c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
+       if(inlen != digest_length(&c->outdigest)) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
                return false;
        }
 
-       /* Verify the incoming hash with the calculated hash */
-
-       if(memcmp(hishash, myhash, c->outdigest->md_size)) {
-               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
-                          c->hostname, "wrong challenge reply");
 
-               ifdebug(SCARY_THINGS) {
-                       bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
-                       hishash[SHA_DIGEST_LENGTH * 2] = '\0';
-                       logger(LOG_DEBUG, "Expected challenge reply: %s", hishash);
-               }
+       /* Verify the hash */
 
+       if(!digest_verify(&c->outdigest, c->hischallenge, c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa), hishash)) {
+               logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
                return false;
        }
 
@@ -420,12 +443,31 @@ bool chal_reply_h(connection_t *c) {
           Send an acknowledgement with the rest of the information needed.
         */
 
+       free(c->hischallenge);
+       c->hischallenge = NULL;
        c->allow_request = ACK;
 
        return send_ack(c);
 }
 
+static bool send_upgrade(connection_t *c) {
+       /* Special case when protocol_minor is 1: the other end is ECDSA capable,
+        * but doesn't know our key yet. So send it now. */
+
+       char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
+
+       if(!pubkey)
+               return false;
+
+       bool result = send_request(c, "%d %s", ACK, pubkey);
+       free(pubkey);
+       return result;
+}
+
 bool send_ack(connection_t *c) {
+       if(c->protocol_minor == 1)
+               return send_upgrade(c);
+
        /* ACK message contains rest of the information the other end needs
           to create node_t and edge_t structures. */
 
@@ -459,7 +501,7 @@ bool send_ack(connection_t *c) {
 }
 
 static void send_everything(connection_t *c) {
-       avl_node_t *node, *node2;
+       splay_node_t *node, *node2;
        node_t *n;
        subnet_t *s;
        edge_t *e;
@@ -490,7 +532,29 @@ static void send_everything(connection_t *c) {
        }
 }
 
-bool ack_h(connection_t *c) {
+static bool upgrade_h(connection_t *c, char *request) {
+       char pubkey[MAX_STRING_SIZE];
+
+       if(sscanf(request, "%*d " MAX_STRING, pubkey) != 1) {
+               logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
+               return false;
+       }
+
+       if(ecdsa_active(&c->ecdsa) || read_ecdsa_public_key(c)) {
+               logger(LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
+               return false;
+       }
+
+       logger(LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
+       append_config_file(c->name, "ECDSAPublicKey", pubkey);
+       c->allow_request = TERMREQ;
+       return send_termreq(c);
+}
+
+bool ack_h(connection_t *c, char *request) {
+       if(c->protocol_minor == 1)
+               return upgrade_h(c, request);
+
        char hisport[MAX_STRING_SIZE];
        char *hisaddress;
        int weight, mtu;
@@ -498,7 +562,7 @@ bool ack_h(connection_t *c) {
        node_t *n;
        bool choice;
 
-       if(sscanf(c->buffer, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
+       if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
                           c->hostname);
                return false;
@@ -515,8 +579,17 @@ bool ack_h(connection_t *c) {
        } else {
                if(n->connection) {
                        /* Oh dear, we already have a connection to this node. */
-                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection",
-                                          n->name, n->hostname);
+                       ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
+
+                       if(n->connection->outgoing) {
+                               if(c->outgoing)
+                                       logger(LOG_WARNING, "Two outgoing connections to the same node!");
+                               else
+                                       c->outgoing = n->connection->outgoing;
+
+                               n->connection->outgoing = NULL;
+                       }
+
                        terminate_connection(n->connection, false);
                        /* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
                        graph();
@@ -544,6 +617,9 @@ bool ack_h(connection_t *c) {
                        c->options &= ~OPTION_CLAMP_MSS;
        }
 
+       if(c->protocol_minor > 0)
+               c->node->status.ecdh = true;
+
        /* Activate this connection */
 
        c->allow_request = ALL;
index 5d81462b6c9bb019914860b82885f8ae35bc8286..e6736945323f733ad9ed1bb9c7494550838240ae 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "conf.h"
 #include "connection.h"
 #include "edge.h"
@@ -50,7 +50,7 @@ bool send_add_edge(connection_t *c, const edge_t *e) {
        return x;
 }
 
-bool add_edge_h(connection_t *c) {
+bool add_edge_h(connection_t *c, char *request) {
        edge_t *e;
        node_t *from, *to;
        char from_name[MAX_STRING_SIZE];
@@ -61,7 +61,7 @@ bool add_edge_h(connection_t *c) {
        uint32_t options;
        int weight;
 
-       if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
+       if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
                          from_name, to_name, to_address, to_port, &options, &weight) != 6) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
                           c->hostname);
@@ -76,7 +76,7 @@ bool add_edge_h(connection_t *c) {
                return false;
        }
 
-       if(seen_request(c->buffer))
+       if(seen_request(request))
                return true;
 
        /* Lookup nodes */
@@ -153,7 +153,7 @@ bool add_edge_h(connection_t *c) {
        /* Tell the rest about the new edge */
 
        if(!tunnelserver)
-               forward_request(c);
+               forward_request(c, request);
 
        /* Run MST before or after we tell the rest? */
 
@@ -167,13 +167,13 @@ bool send_del_edge(connection_t *c, const edge_t *e) {
                                                e->from->name, e->to->name);
 }
 
-bool del_edge_h(connection_t *c) {
+bool del_edge_h(connection_t *c, char *request) {
        edge_t *e;
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        node_t *from, *to;
 
-       if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
+       if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
                           c->hostname);
                return false;
@@ -187,7 +187,7 @@ bool del_edge_h(connection_t *c) {
                return false;
        }
 
-       if(seen_request(c->buffer))
+       if(seen_request(request))
                return true;
 
        /* Lookup nodes */
@@ -238,7 +238,7 @@ bool del_edge_h(connection_t *c) {
        /* Tell the rest about the deleted edge */
 
        if(!tunnelserver)
-               forward_request(c);
+               forward_request(c, request);
 
        /* Delete the edge */
 
index 1d471c80084181817ab3529821f9fac5675676f9..c392a469846e3878a53a65b84a110f43698df02e 100644 (file)
 
 #include "system.h"
 
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
 #include "connection.h"
+#include "crypto.h"
+#include "ecdh.h"
 #include "logger.h"
 #include "net.h"
 #include "netutl.h"
 #include "node.h"
+#include "prf.h"
 #include "protocol.h"
 #include "utils.h"
 #include "xalloc.h"
@@ -37,7 +37,7 @@
 static bool mykeyused = false;
 
 void send_key_changed(void) {
-       avl_node_t *node;
+       splay_node_t *node;
        connection_t *c;
 
        send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name);
@@ -51,22 +51,17 @@ void send_key_changed(void) {
        }
 }
 
-bool key_changed_h(connection_t *c) {
+bool key_changed_h(connection_t *c, char *request) {
        char name[MAX_STRING_SIZE];
        node_t *n;
 
-       if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
+       if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
                           c->name, c->hostname);
                return false;
        }
 
-       if(!check_id(name)) {
-               logger(LOG_ERR, "Got bad %s from %s (%s): %s", "KEY_CHANGED", c->name, c->hostname, "invalid name");
-               return false;
-       }
-
-       if(seen_request(c->buffer))
+       if(seen_request(request))
                return true;
 
        n = lookup_node(name);
@@ -83,21 +78,22 @@ bool key_changed_h(connection_t *c) {
        /* Tell the others */
 
        if(!tunnelserver)
-               forward_request(c);
+               forward_request(c, request);
 
        return true;
 }
 
 bool send_req_key(node_t *to) {
-       return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
+       return send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, experimental ? 1 : 0);
 }
 
-bool req_key_h(connection_t *c) {
+bool req_key_h(connection_t *c, char *request) {
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        node_t *from, *to;
+       int kx_version = 0;
 
-       if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
+       if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &kx_version) < 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
                           c->hostname);
                return false;
@@ -127,6 +123,10 @@ bool req_key_h(connection_t *c) {
        /* Check if this key request is for us */
 
        if(to == myself) {                      /* Yes, send our own key back */
+               if(experimental && kx_version >= 1) {
+                       logger(LOG_DEBUG, "Got ECDH key request from %s", from->name);
+                       from->status.ecdh = true;
+               }
                send_ans_key(from);
        } else {
                if(tunnelserver)
@@ -138,55 +138,80 @@ bool req_key_h(connection_t *c) {
                        return true;
                }
 
-               send_request(to->nexthop->connection, "%s", c->buffer);
+               send_request(to->nexthop->connection, "%s", request);
        }
 
        return true;
 }
 
+bool send_ans_key_ecdh(node_t *to) {
+       int siglen = ecdsa_size(&myself->connection->ecdsa);
+       char key[(ECDH_SIZE + siglen) * 2 + 1];
+
+       if(!ecdh_generate_public(&to->ecdh, key))
+               return false;
+
+       if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
+               return false;
+
+       b64encode(key, key, ECDH_SIZE + siglen);
+
+       char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
+
+       if(!pubkey)
+               return false;
+
+       int result = send_request(to->nexthop->connection, "%d %s %s ECDH:%s:%s %d %d %zu %d", ANS_KEY,
+                                               myself->name, to->name, key, pubkey,
+                                               cipher_get_nid(&myself->incipher),
+                                               digest_get_nid(&myself->indigest),
+                                               digest_length(&myself->indigest),
+                                               myself->incompression);
+
+       free(pubkey);
+       return result;
+}
+
 bool send_ans_key(node_t *to) {
-       // Set key parameters
-       to->incipher = myself->incipher;
-       to->inkeylength = myself->inkeylength;
-       to->indigest = myself->indigest;
-       to->inmaclength = myself->inmaclength;
+       if(experimental && to->status.ecdh)
+               return send_ans_key_ecdh(to);
+
+       size_t keylen = cipher_keylength(&myself->incipher);
+       char key[keylen * 2 + 1];
+
+       cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
+       digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
        to->incompression = myself->incompression;
 
-       // Allocate memory for key
-       to->inkey = xrealloc(to->inkey, to->inkeylength);
+       randomize(key, keylen);
+       cipher_set_key(&to->incipher, key, false);
+       digest_set_key(&to->indigest, key, keylen);
 
-       // Create a new key
-       RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
-       if(to->incipher)
-               EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
+       bin2hex(key, key, keylen);
 
        // Reset sequence number and late packet window
        mykeyused = true;
        to->received_seqno = 0;
        if(replaywin) memset(to->late, 0, replaywin);
 
-       // Convert to hexadecimal and send
-       char key[2 * to->inkeylength + 1];
-       bin2hex(to->inkey, key, to->inkeylength);
-       key[to->inkeylength * 2] = '\0';
-
-       return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
-                       myself->name, to->name, key,
-                       to->incipher ? to->incipher->nid : 0,
-                       to->indigest ? to->indigest->type : 0, to->inmaclength,
-                       to->incompression);
+       return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
+                                               myself->name, to->name, key,
+                                               cipher_get_nid(&to->incipher),
+                                               digest_get_nid(&to->indigest),
+                                               digest_length(&to->indigest),
+                                               to->incompression);
 }
 
-bool ans_key_h(connection_t *c) {
+bool ans_key_h(connection_t *c, char *request) {
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        char key[MAX_STRING_SIZE];
-       char address[MAX_STRING_SIZE] = "";
-       char port[MAX_STRING_SIZE] = "";
-       int cipher, digest, maclength, compression;
+        char address[MAX_STRING_SIZE] = "";
+        char port[MAX_STRING_SIZE] = "";
+       int cipher, digest, maclength, compression, keylen;
        node_t *from, *to;
 
-       if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
+       if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
                from_name, to_name, key, &cipher, &digest, &maclength,
                &compression, address, port) < 7) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
@@ -223,7 +248,7 @@ bool ans_key_h(connection_t *c) {
 
                if(!to->status.reachable) {
                        logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
-                               "ANS_KEY", c->name, c->hostname, to_name);
+                                  "ANS_KEY", c->name, c->hostname, to_name);
                        return true;
                }
 
@@ -231,74 +256,146 @@ bool ans_key_h(connection_t *c) {
                        char *address, *port;
                        ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
                        sockaddr2str(&from->address, &address, &port);
-                       send_request(to->nexthop->connection, "%s %s %s", c->buffer, address, port);
+                       send_request(to->nexthop->connection, "%s %s %s", request, address, port);
                        free(address);
                        free(port);
                        return true;
                }
 
-               return send_request(to->nexthop->connection, "%s", c->buffer);
+               return send_request(to->nexthop->connection, "%s", request);
        }
 
-       /* Update our copy of the origin's packet key */
-       from->outkey = xrealloc(from->outkey, strlen(key) / 2);
-       from->outkeylength = strlen(key) / 2;
-       hex2bin(key, from->outkey, from->outkeylength);
-
        /* Check and lookup cipher and digest algorithms */
 
-       if(cipher) {
-               from->outcipher = EVP_get_cipherbynid(cipher);
+       if(!cipher_open_by_nid(&from->outcipher, cipher)) {
+               logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
+               return false;
+       }
+
+       if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
+               logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
+               return false;
+       }
+
+       if(maclength != digest_length(&from->outdigest)) {
+               logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
+               return false;
+       }
 
-               if(!from->outcipher) {
-                       logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name,
-                                  from->hostname);
+       if(compression < 0 || compression > 11) {
+               logger(LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
+               return true;
+       }
+
+       from->outcompression = compression;
+
+       /* ECDH or old-style key exchange? */
+       
+       if(experimental && !strncmp(key, "ECDH:", 5)) {
+               char *pubkey = strchr(key + 5, ':');
+               if(pubkey)
+                       *pubkey++ = 0;
+                       
+               /* Check if we already have an ECDSA public key for this node.
+                * If not, use the one from the key exchange, and store it. */
+
+               if(!node_read_ecdsa_public_key(from)) {
+                       if(!pubkey) {
+                               logger(LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
+                               return true;
+                       }
+
+                       if(!ecdsa_set_base64_public_key(&from->ecdsa, pubkey))
+                               return true;
+
+                       append_config_file(from->name, "ECDSAPublicKey", pubkey);
+               }
+
+               int siglen = ecdsa_size(&from->ecdsa);
+               int keylen = b64decode(key + 5, key + 5, sizeof key - 5);
+
+               if(keylen != ECDH_SIZE + siglen) {
+                       logger(LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
                        return true;
                }
 
-               if(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
-                       logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name,
-                                  from->hostname);
+               if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
+                       logger(LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
+                       return true;
+               }
+
+               if(!ecdsa_verify(&from->ecdsa, key + 5, ECDH_SIZE, key + 5 + ECDH_SIZE)) {
+                       logger(LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
                        return true;
                }
-       } else {
-               from->outcipher = NULL;
-       }
 
-       from->outmaclength = maclength;
+               if(!from->ecdh) {
+                       from->status.ecdh = true;
+                       if(!send_ans_key(from))
+                               return true;
+               }
 
-       if(digest) {
-               from->outdigest = EVP_get_digestbynid(digest);
+               char shared[ECDH_SHARED_SIZE * 2 + 1];
 
-               if(!from->outdigest) {
-                       logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name,
-                                  from->hostname);
+               if(!ecdh_compute_shared(&from->ecdh, key + 5, shared))
                        return true;
+
+               /* Update our crypto end */
+
+               size_t mykeylen = cipher_keylength(&myself->incipher);
+               size_t hiskeylen = cipher_keylength(&from->outcipher);
+
+               char *mykey;
+               char *hiskey;
+               char *seed;
+               
+               if(strcmp(myself->name, from->name) < 0) {
+                       mykey = key;
+                       hiskey = key + mykeylen * 2;
+                       xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
+               } else {
+                       mykey = key + hiskeylen * 2;
+                       hiskey = key;
+                       xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
                }
 
-               if(from->outmaclength > from->outdigest->md_size || from->outmaclength < 0) {
-                       logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!",
-                                  from->name, from->hostname);
+               if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
                        return true;
-               }
-       } else {
-               from->outdigest = NULL;
-       }
 
-       if(compression < 0 || compression > 11) {
-               logger(LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
-               return true;
-       }
-       
-       from->outcompression = compression;
+               free(seed);
+
+               cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
+               digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
+               from->incompression = myself->incompression;
 
-       if(from->outcipher)
-               if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
-                       logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s",
-                                       from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
+               cipher_set_key(&from->incipher, mykey, false);
+               digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
+
+               cipher_set_key(&from->outcipher, hiskey, true);
+               digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
+
+               // Reset sequence number and late packet window
+               mykeyused = true;
+               from->received_seqno = 0;
+               if(replaywin)
+                       memset(from->late, 0, replaywin);
+
+               if(strcmp(myself->name, from->name) < 0)
+                       memmove(key, key + mykeylen * 2, hiskeylen * 2);
+       } else {
+               keylen = hex2bin(key, key, sizeof key);
+
+               if(keylen != cipher_keylength(&from->outcipher)) {
+                       logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
                        return true;
                }
 
+               /* Update our copy of the origin's packet key */
+
+               cipher_set_key(&from->outcipher, key, true);
+               digest_set_key(&from->outdigest, key, keylen);
+       }
+
        from->status.validkey = true;
        from->sent_seqno = 0;
 
@@ -308,7 +405,7 @@ bool ans_key_h(connection_t *c) {
                update_node_udp(from, &sa);
        }
 
-       if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuevent)
+       if(from->options & OPTION_PMTU_DISCOVERY)
                send_mtu_probe(from);
 
        return true;
index 3d405bc408140423b7561dfd67585f29741676b0..225d7b4e7f325e50dab0e4281bf48dc8f84050cc 100644 (file)
@@ -40,11 +40,11 @@ bool send_status(connection_t *c, int statusno, const char *statusstring) {
        return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
 }
 
-bool status_h(connection_t *c) {
+bool status_h(connection_t *c, char *request) {
        int statusno;
        char statusstring[MAX_STRING_SIZE];
 
-       if(sscanf(c->buffer, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
+       if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
                           c->name, c->hostname);
                return false;
@@ -63,11 +63,11 @@ bool send_error(connection_t *c, int err, const char *errstring) {
        return send_request(c, "%d %d %s", ERROR, err, errstring);
 }
 
-bool error_h(connection_t *c) {
+bool error_h(connection_t *c, char *request) {
        int err;
        char errorstring[MAX_STRING_SIZE];
 
-       if(sscanf(c->buffer, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
+       if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
                           c->name, c->hostname);
                return false;
@@ -76,29 +76,25 @@ bool error_h(connection_t *c) {
        ifdebug(ERROR) logger(LOG_NOTICE, "Error message from %s (%s): %d: %s",
                           c->name, c->hostname, err, errorstring);
 
-       terminate_connection(c, c->status.active);
-
-       return true;
+       return false;
 }
 
 bool send_termreq(connection_t *c) {
        return send_request(c, "%d", TERMREQ);
 }
 
-bool termreq_h(connection_t *c) {
-       terminate_connection(c, c->status.active);
-
-       return true;
+bool termreq_h(connection_t *c, char *request) {
+       return false;
 }
 
 bool send_ping(connection_t *c) {
        c->status.pinged = true;
-       c->last_ping_time = now;
+       c->last_ping_time = time(NULL);
 
        return send_request(c, "%d", PING);
 }
 
-bool ping_h(connection_t *c) {
+bool ping_h(connection_t *c, char *request) {
        return send_pong(c);
 }
 
@@ -106,7 +102,7 @@ bool send_pong(connection_t *c) {
        return send_request(c, "%d", PONG);
 }
 
-bool pong_h(connection_t *c) {
+bool pong_h(connection_t *c, char *request) {
        c->status.pinged = false;
 
        /* Succesful connection, reset timeout if this is an outgoing connection. */
@@ -123,19 +119,19 @@ bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
        /* If there already is a lot of data in the outbuf buffer, discard this packet.
            We use a very simple Random Early Drop algorithm. */
 
-       if(2.0 * c->outbuflen / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
+       if(2.0 * c->outbuf.len / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
                return true;
 
        if(!send_request(c, "%d %hd", PACKET, packet->len))
                return false;
 
-       return send_meta(c, (char *)packet->data, packet->len) && flush_meta(c);
+       return send_meta(c, (char *)packet->data, packet->len);
 }
 
-bool tcppacket_h(connection_t *c) {
+bool tcppacket_h(connection_t *c, char *request) {
        short int len;
 
-       if(sscanf(c->buffer, "%*d %hd", &len) != 1) {
+       if(sscanf(request, "%*d %hd", &len) != 1) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
                           c->hostname);
                return false;
index 62656c38302e8587ab10dfb4cb1f36e24ad2239e..6f9e626eb092191fc40b3b27dafce0493d8ffdf7 100644 (file)
@@ -41,13 +41,13 @@ bool send_add_subnet(connection_t *c, const subnet_t *subnet) {
        return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
 }
 
-bool add_subnet_h(connection_t *c) {
+bool add_subnet_h(connection_t *c, char *request) {
        char subnetstr[MAX_STRING_SIZE];
        char name[MAX_STRING_SIZE];
        node_t *owner;
        subnet_t s = {NULL}, *new, *old;
 
-       if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
+       if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
                           c->hostname);
                return false;
@@ -69,7 +69,7 @@ bool add_subnet_h(connection_t *c) {
                return false;
        }
 
-       if(seen_request(c->buffer))
+       if(seen_request(request))
                return true;
 
        /* Check if the owner of the new subnet is in the connection list */
@@ -117,7 +117,7 @@ bool add_subnet_h(connection_t *c) {
        if(strictsubnets) {
                logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
                                "ADD_SUBNET", c->name, c->hostname, subnetstr);
-               forward_request(c);
+               forward_request(c, request);
                return true;
        }
 
@@ -131,12 +131,13 @@ bool add_subnet_h(connection_t *c) {
 
        /* Tell the rest */
 
-       forward_request(c);
+       if(!tunnelserver)
+               forward_request(c, request);
 
        /* Fast handoff of roaming MAC addresses */
 
        if(s.type == SUBNET_MAC && owner != myself && (old = lookup_subnet(myself, &s)) && old->expires)
-               old->expires = now;
+               old->expires = 1;
 
        return true;
 }
@@ -150,13 +151,13 @@ bool send_del_subnet(connection_t *c, const subnet_t *s) {
        return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
 }
 
-bool del_subnet_h(connection_t *c) {
+bool del_subnet_h(connection_t *c, char *request) {
        char subnetstr[MAX_STRING_SIZE];
        char name[MAX_STRING_SIZE];
        node_t *owner;
        subnet_t s = {NULL}, *find;
 
-       if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
+       if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
                           c->hostname);
                return false;
@@ -178,7 +179,7 @@ bool del_subnet_h(connection_t *c) {
                return false;
        }
 
-       if(seen_request(c->buffer))
+       if(seen_request(request))
                return true;
 
        /* Check if the owner of the subnet being deleted is in the connection list */
@@ -208,7 +209,7 @@ bool del_subnet_h(connection_t *c) {
                ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for %s which does not appear in his subnet tree",
                                   "DEL_SUBNET", c->name, c->hostname, name);
                if(strictsubnets)
-                       forward_request(c);
+                       forward_request(c, request);
                return true;
        }
 
@@ -226,7 +227,8 @@ bool del_subnet_h(connection_t *c) {
 
        /* Tell the rest */
 
-       forward_request(c);
+       if(!tunnelserver)
+               forward_request(c, request);
        if(strictsubnets)
                return true;
 
index 1dd726f888de2d172efb9b7a8eeea2f7efaa9f29..3785dcf69782012ad78280b840d7ead2aeeed58e 100644 (file)
@@ -56,11 +56,12 @@ static bool setup_device(void) {
                return false;
        }
 
+       memset(&ifr, 0, sizeof ifr);
+
 #ifdef FD_CLOEXEC
        fcntl(device_fd, F_SETFD, FD_CLOEXEC);
 #endif
 
-       memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
        if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
                close(device_fd);
@@ -69,12 +70,12 @@ static bool setup_device(void) {
                return false;
        }
 
-       memset(&sa, '0', sizeof(sa));
+       memset(&sa, '0', sizeof sa);
        sa.sll_family = AF_PACKET;
        sa.sll_protocol = htons(ETH_P_ALL);
        sa.sll_ifindex = ifr.ifr_ifindex;
 
-       if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
+       if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof sa)) {
                logger(LOG_ERR, "Could not bind %s to %s: %s", device, iface, strerror(errno));
                return false;
        }
@@ -92,15 +93,15 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
 
-       if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
+       if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
        }
 
-       packet->len = lenin;
+       packet->len = inlen;
 
        device_total_in += packet->len;
 
index 0b77bd4a11bacd9ba6a8baa4510f8c57cc191b74..32be4dec404f06c2fffe6e53ef7c699343d064d0 100644 (file)
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
 #include "connection.h"
+#include "control_common.h"
 #include "ethernet.h"
 #include "ipv4.h"
 #include "ipv6.h"
 #include "logger.h"
+#include "meta.h"
 #include "net.h"
 #include "protocol.h"
 #include "route.h"
@@ -41,6 +43,7 @@ int macexpire = 600;
 bool overwrite_mac = false;
 bool broadcast = true;
 mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
+bool pcap = false;
 
 /* Sizes of various headers */
 
@@ -57,6 +60,8 @@ static const size_t opt_size = sizeof(struct nd_opt_hdr);
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
+static struct event age_subnets_event;
+
 /* RFC 1071 */
 
 static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
@@ -80,6 +85,7 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
 static bool ratelimit(int frequency) {
        static time_t lasttime = 0;
        static int count = 0;
+       time_t now = time(NULL);
        
        if(lasttime == now) {
                if(++count > frequency)
@@ -179,9 +185,43 @@ static void swap_mac_addresses(vpn_packet_t *packet) {
        memcpy(&packet->data[6], &tmp, sizeof tmp);
 }
        
+static void age_subnets(int fd, short events, void *data) {
+       subnet_t *s;
+       connection_t *c;
+       splay_node_t *node, *next, *node2;
+       bool left = false;
+       time_t now = time(NULL);
+
+       for(node = myself->subnet_tree->head; node; node = next) {
+               next = node->next;
+               s = node->data;
+               if(s->expires && s->expires < now) {
+                       ifdebug(TRAFFIC) {
+                               char netstr[MAXNETSTR];
+                               if(net2str(netstr, sizeof netstr, s))
+                                       logger(LOG_INFO, "Subnet %s expired", netstr);
+                       }
+
+                       for(node2 = connection_tree->head; node2; node2 = node2->next) {
+                               c = node2->data;
+                               if(c->status.active)
+                                       send_del_subnet(c, s);
+                       }
+
+                       subnet_del(myself, s);
+               } else {
+                       if(s->expires)
+                               left = true;
+               }
+       }
+
+       if(left)
+               event_add(&age_subnets_event, &(struct timeval){10, 0});
+}
+
 static void learn_mac(mac_t *address) {
        subnet_t *subnet;
-       avl_node_t *node;
+       splay_node_t *node;
        connection_t *c;
 
        subnet = lookup_subnet_mac(myself, address);
@@ -195,7 +235,7 @@ static void learn_mac(mac_t *address) {
 
                subnet = new_subnet();
                subnet->type = SUBNET_MAC;
-               subnet->expires = now + macexpire;
+               subnet->expires = time(NULL) + macexpire;
                subnet->net.mac.address = *address;
                subnet->weight = 10;
                subnet_add(myself, subnet);
@@ -208,36 +248,13 @@ static void learn_mac(mac_t *address) {
                        if(c->status.active)
                                send_add_subnet(c, subnet);
                }
-       }
 
-       if(subnet->expires)
-               subnet->expires = now + macexpire;
-}
-
-void age_subnets(void) {
-       subnet_t *s;
-       connection_t *c;
-       avl_node_t *node, *next, *node2;
-
-       for(node = myself->subnet_tree->head; node; node = next) {
-               next = node->next;
-               s = node->data;
-               if(s->expires && s->expires <= now) {
-                       ifdebug(TRAFFIC) {
-                               char netstr[MAXNETSTR];
-                               if(net2str(netstr, sizeof netstr, s))
-                                       logger(LOG_INFO, "Subnet %s expired", netstr);
-                       }
-
-                       for(node2 = connection_tree->head; node2; node2 = node2->next) {
-                               c = node2->data;
-                               if(c->status.active)
-                                       send_del_subnet(c, s);
-                       }
-
-                       subnet_update(myself, s, false);
-                       subnet_del(myself, s);
-               }
+               if(!timeout_initialized(&age_subnets_event))
+                       timeout_set(&age_subnets_event, age_subnets, NULL);
+               event_add(&age_subnets_event, &(struct timeval){10, 0});
+       } else {
+               if(subnet->expires)
+                       subnet->expires = time(NULL) + macexpire;
        }
 }
 
@@ -332,7 +349,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) {
        todo = ntohs(ip.ip_len) - ip_size;
 
        if(ether_size + ip_size + todo != packet->len) {
-               ifdebug(TRAFFIC) logger(LOG_WARNING, "Length of packet (%d) doesn't match length in IPv4 header (%zd)", packet->len, ether_size + ip_size + todo);
+               ifdebug(TRAFFIC) logger(LOG_WARNING, "Length of packet (%d) doesn't match length in IPv4 header (%d)", packet->len, (int)(ether_size + ip_size + todo));
                return;
        }
 
@@ -498,7 +515,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
 
        /* Generate checksum */
        
-       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
        checksum = inet_checksum(&icmp6, icmp6_size, checksum);
        checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
 
@@ -625,7 +642,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
 
        /* Generate checksum */
 
-       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
        checksum = inet_checksum(&ns, ns_size, checksum);
        if(has_opt) {
                checksum = inet_checksum(&opt, opt_size, checksum);
@@ -688,7 +705,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
 
        /* Generate checksum */
 
-       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
        checksum = inet_checksum(&ns, ns_size, checksum);
        if(has_opt) {
                checksum = inet_checksum(&opt, opt_size, checksum);
@@ -749,7 +766,7 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
        /* Check if this is a valid ARP request */
 
        if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
-          arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) {
+          arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) {
                ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type ARP request");
                return;
        }
@@ -773,9 +790,9 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
        memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN);        /* copy destination address */
        packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
 
-       memcpy(&addr, arp.arp_tpa, sizeof(addr));       /* save protocol addr */
-       memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */
-       memcpy(arp.arp_spa, &addr, sizeof(addr));       /* ... */
+       memcpy(&addr, arp.arp_tpa, sizeof addr);        /* save protocol addr */
+       memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr);  /* swap destination and source protocol address */
+       memcpy(arp.arp_spa, &addr, sizeof addr);        /* ... */
 
        memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN);     /* set target hard/proto addr */
        memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
@@ -849,6 +866,19 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
        send_packet(subnet->owner, packet);
 }
 
+static void send_pcap(vpn_packet_t *packet) {
+       pcap = false;
+       for(splay_node_t *node = connection_tree->head; node; node = node->next) {
+               connection_t *c = node->data;
+               if(!c->status.pcap)
+                       continue;
+               else
+                       pcap = true;
+               if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, packet->len))
+                       send_meta(c, (char *)packet->data, packet->len);
+       }
+}
+
 static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
        uint16_t type = packet->data[12] << 8 | packet->data[13];
 
@@ -894,6 +924,9 @@ static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
 }
 
 void route(node_t *source, vpn_packet_t *packet) {
+       if(pcap)
+               send_pcap(packet);
+
        if(forwarding_mode == FMODE_KERNEL && source != myself) {
                send_packet(myself, packet);
                return;
index c1481fa329df75abe2e4f761e7013fbd97e0678e..44023fdb3efff73dfd9dd474fa41a9c3c8527cd4 100644 (file)
@@ -44,10 +44,10 @@ extern bool overwrite_mac;
 extern bool broadcast;
 extern bool priorityinheritance;
 extern int macexpire;
+extern bool pcap;
 
 extern mac_t mymac;
 
-extern void age_subnets(void);
 extern void route(struct node_t *, struct vpn_packet_t *);
 
 #endif                                                 /* __TINC_ROUTE_H__ */
index 969b514ae39fa36c917fa92ea004bafd9d70667c..365561fdacb2f063074aed9e358b23d96968e4be 100644 (file)
@@ -127,9 +127,9 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
 
-       if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
+       if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
@@ -151,7 +151,7 @@ static bool read_packet(vpn_packet_t *packet) {
                        return false;
        }
 
-       packet->len = lenin + 14;
+       packet->len = inlen + 14;
 
        device_total_in += packet->len;
 
diff --git a/src/splay_tree.c b/src/splay_tree.c
new file mode 100644 (file)
index 0000000..135ba06
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+    splay_tree.c -- splay tree and linked list convenience
+    Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "splay_tree.h"
+#include "xalloc.h"
+
+/* Splay operation */
+
+static splay_node_t *splay_top_down(splay_tree_t *tree, const void *data, int *result) {
+       splay_node_t left = {NULL}, right = {NULL};
+       splay_node_t *leftbottom = &left, *rightbottom = &right, *child, *grandchild;
+       splay_node_t *root = tree->root;
+       int c;
+
+       if(!root) {
+               if(result)
+                       *result = 0;
+               return NULL;
+       }
+
+       while((c = tree->compare(data, root->data))) {
+               if(c < 0 && (child = root->left)) {
+                       c = tree->compare(data, child->data);
+
+                       if(c < 0 && (grandchild = child->left)) {
+                               rightbottom->left = child;
+                               child->parent = rightbottom;
+                               rightbottom = child;
+                               
+                               if((root->left = child->right))
+                                       child->right->parent = root;
+                               
+                               child->right = root;
+                               root->parent = child;
+
+                               child->left = NULL;
+                               grandchild->parent = NULL;
+
+                               root = grandchild;
+                       } else if (c > 0 && (grandchild = child->right)) {
+                               leftbottom->right = child;
+                               child->parent = leftbottom;
+                               leftbottom = child;
+
+                               child->right = NULL;
+                               grandchild->parent = NULL;
+
+                               rightbottom->left = root;
+                               root->parent = rightbottom;
+                               rightbottom = root;
+
+                               root->left = NULL;
+
+                               root = grandchild;
+                       } else {
+                               rightbottom->left = root;
+                               root->parent = rightbottom;
+                               rightbottom = root;
+                               
+                               root->left = NULL;
+                               child->parent = NULL;
+
+                               root = child;
+                               break;
+                       }
+               } else if(c > 0 && (child = root->right)) {
+                       c = tree->compare(data, child->data);
+
+                       if(c > 0 && (grandchild = child->right)) {
+                               leftbottom->right = child;
+                               child->parent = leftbottom;
+                               leftbottom = child;
+                               
+                               if((root->right = child->left))
+                                       child->left->parent = root;
+                               
+                               child->left = root;
+                               root->parent = child;
+
+                               child->right = NULL;
+                               grandchild->parent = NULL;
+
+                               root = grandchild;
+                       } else if (c < 0 && (grandchild = child->left)) {
+                               rightbottom->left = child;
+                               child->parent = rightbottom;
+                               rightbottom = child;
+
+                               child->left = NULL;
+                               grandchild->parent = NULL;
+
+                               leftbottom->right = root;
+                               root->parent = leftbottom;
+                               leftbottom = root;
+
+                               root->right = NULL;
+
+                               root = grandchild;
+                       } else {
+                               leftbottom->right = root;
+                               root->parent = leftbottom;
+                               leftbottom = root;
+
+                               root->right = NULL;
+                               child->parent = NULL;
+
+                               root = child;
+                               break;
+                       }
+               } else {
+                       break;
+               }
+       }
+
+       /* Merge trees */
+
+       if(left.right) {
+               if(root->left) {
+                       leftbottom->right = root->left;
+                       root->left->parent = leftbottom;
+               }
+               root->left = left.right;
+               left.right->parent = root;
+       }
+
+       if(right.left) {
+               if(root->right) {
+                       rightbottom->left = root->right;
+                       root->right->parent = rightbottom;
+               }
+               root->right = right.left;
+               right.left->parent = root;
+       }
+
+       /* Return result */
+
+       tree->root = root;
+       if(result)
+               *result = c;
+
+       return tree->root;
+}
+                       
+static void splay_bottom_up(splay_tree_t *tree, splay_node_t *node) {
+       splay_node_t *parent, *grandparent, *greatgrandparent;
+
+       while((parent = node->parent)) {
+               if(!(grandparent = parent->parent)) { /* zig */
+                       if(node == parent->left) {
+                               if((parent->left = node->right))                                
+                                       parent->left->parent = parent;
+                               node->right = parent;
+                       } else {
+                               if((parent->right = node->left))
+                                       parent->right->parent = parent;
+                               node->left = parent;
+                       }
+
+                       parent->parent = node;
+                       node->parent = NULL;
+               } else {
+                       greatgrandparent = grandparent->parent;
+
+                       if(node == parent->left && parent == grandparent->left) { /* left zig-zig */
+                               if((grandparent->left = parent->right))
+                                       grandparent->left->parent = grandparent;
+                               parent->right = grandparent;
+                               grandparent->parent = parent;
+
+                               if((parent->left = node->right))
+                                       parent->left->parent = parent;
+                               node->right = parent;
+                               parent->parent = node;
+                       } else if(node == parent->right && parent == grandparent->right) { /* right zig-zig */
+                               if((grandparent->right = parent->left))
+                                       grandparent->right->parent = grandparent;
+                               parent->left = grandparent;
+                               grandparent->parent = parent;
+
+                               if((parent->right = node->left))
+                                       parent->right->parent = parent;
+                               node->left = parent;
+                               parent->parent = node;
+                       } else if(node == parent->right && parent == grandparent->left) { /* left-right zig-zag */
+                               if((parent->right = node->left))
+                                       parent->right->parent = parent;
+                               node->left = parent;
+                               parent->parent = node;
+
+                               if((grandparent->left = node->right))
+                                       grandparent->left->parent = grandparent;
+                               node->right = grandparent;
+                               grandparent->parent = node;
+                       } else { /* right-left zig-zag */
+                               if((parent->left = node->right))
+                                       parent->left->parent = parent;
+                               node->right = parent;
+                               parent->parent = node;
+
+                               if((grandparent->right = node->left))
+                                       grandparent->right->parent = grandparent;
+                               node->left = grandparent;
+                               grandparent->parent = node;
+                       }
+
+                       if((node->parent = greatgrandparent)) {
+                               if(grandparent == greatgrandparent->left)
+                                       greatgrandparent->left = node;
+                               else
+                                       greatgrandparent->right = node;
+                       }
+               }
+       }
+
+       tree->root = node;
+}
+
+/* (De)constructors */
+
+splay_tree_t *splay_alloc_tree(splay_compare_t compare, splay_action_t delete) {
+       splay_tree_t *tree;
+
+       tree = xmalloc_and_zero(sizeof(splay_tree_t));
+       tree->compare = compare;
+       tree->delete = delete;
+
+       return tree;
+}
+
+void splay_free_tree(splay_tree_t *tree) {
+       free(tree);
+}
+
+splay_node_t *splay_alloc_node(void) {
+       return xmalloc_and_zero(sizeof(splay_node_t));
+}
+
+void splay_free_node(splay_tree_t *tree, splay_node_t *node) {
+       if(node->data && tree->delete)
+               tree->delete(node->data);
+
+       free(node);
+}
+
+/* Searching */
+
+void *splay_search(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+
+       node = splay_search_node(tree, data);
+
+       return node ? node->data : NULL;
+}
+
+void *splay_search_closest(splay_tree_t *tree, const void *data, int *result) {
+       splay_node_t *node;
+
+       node = splay_search_closest_node(tree, data, result);
+
+       return node ? node->data : NULL;
+}
+
+void *splay_search_closest_smaller(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+
+       node = splay_search_closest_smaller_node(tree, data);
+
+       return node ? node->data : NULL;
+}
+
+void *splay_search_closest_greater(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+
+       node = splay_search_closest_greater_node(tree, data);
+
+       return node ? node->data : NULL;
+}
+
+splay_node_t *splay_search_node(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+       int result;
+
+       node = splay_search_closest_node(tree, data, &result);
+
+       return result ? NULL : node;
+}
+
+splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *tree, const void *data, int *result) {
+       splay_node_t *node;
+       int c;
+
+       node = tree->root;
+
+       if(!node) {
+               if(result)
+                       *result = 0;
+               return NULL;
+       }
+
+       for(;;) {
+               c = tree->compare(data, node->data);
+
+               if(c < 0) {
+                       if(node->left)
+                               node = node->left;
+                       else
+                               break;
+               } else if(c > 0) {
+                       if(node->right)
+                               node = node->right;
+                       else 
+                               break;
+               } else {
+                       break;
+               }
+       }
+
+       if(result)
+               *result = c;
+       return node;
+}
+
+splay_node_t *splay_search_closest_node(splay_tree_t *tree, const void *data, int *result) {
+       return splay_top_down(tree, data, result);
+}
+
+splay_node_t *splay_search_closest_smaller_node(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+       int result;
+
+       node = splay_search_closest_node(tree, data, &result);
+
+       if(result < 0)
+               node = node->prev;
+
+       return node;
+}
+
+splay_node_t *splay_search_closest_greater_node(splay_tree_t *tree, const void *data) {
+       splay_node_t *node;
+       int result;
+
+       node = splay_search_closest_node(tree, data, &result);
+
+       if(result > 0)
+               node = node->next;
+
+       return node;
+}
+
+/* Insertion and deletion */
+
+splay_node_t *splay_insert(splay_tree_t *tree, void *data) {
+       splay_node_t *closest, *new;
+       int result;
+
+       if(!tree->root) {
+               new = splay_alloc_node();
+               new->data = data;
+               splay_insert_top(tree, new);
+       } else {
+               closest = splay_search_closest_node(tree, data, &result);
+
+               if(!result)
+                       return NULL;
+
+               new = splay_alloc_node();
+               new->data = data;
+               
+               if(result < 0)
+                       splay_insert_before(tree, closest, new);
+               else
+                       splay_insert_after(tree, closest, new);
+       }                       
+
+       return new;
+}
+
+splay_node_t *splay_insert_node(splay_tree_t *tree, splay_node_t *node) {
+       splay_node_t *closest;
+       int result;
+
+       if(!tree->root)
+               splay_insert_top(tree, node);
+       else {
+               closest = splay_search_closest_node(tree, node->data, &result);
+               
+               if(!result)
+                       return NULL;
+
+               if(result < 0)
+                       splay_insert_before(tree, closest, node);
+               else
+                       splay_insert_after(tree, closest, node);
+       }
+
+       return node;
+}
+
+void splay_insert_top(splay_tree_t *tree, splay_node_t *node) {
+       node->prev = node->next = node->left = node->right = node->parent = NULL;
+       tree->head = tree->tail = tree->root = node;
+}
+
+void splay_insert_before(splay_tree_t *tree, splay_node_t *before, splay_node_t *node) {
+       if(!before) {
+               if(tree->tail)
+                       splay_insert_after(tree, tree->tail, node);
+               else
+                       splay_insert_top(tree, node);
+               return;
+       }
+
+       node->next = before;
+       if((node->prev = before->prev))
+               before->prev->next = node;
+       else
+               tree->head = node;
+       before->prev = node;
+
+       splay_bottom_up(tree, before);
+
+       node->right = before;
+       before->parent = node;
+       if((node->left = before->left))
+               before->left->parent = node;
+       before->left = NULL;
+
+       node->parent = NULL;
+       tree->root = node;
+}
+
+void splay_insert_after(splay_tree_t *tree, splay_node_t *after, splay_node_t *node) {
+       if(!after) {
+               if(tree->head)
+                       splay_insert_before(tree, tree->head, node);
+               else
+                       splay_insert_top(tree, node);
+               return;
+       }
+
+       node->prev = after;
+       if((node->next = after->next))
+               after->next->prev = node;
+       else
+               tree->tail = node;
+       after->next = node;
+
+       splay_bottom_up(tree, after);
+
+       node->left = after;
+       after->parent = node;
+       if((node->right = after->right))
+               after->right->parent = node;
+       after->right = NULL;
+
+       node->parent = NULL;
+       tree->root = node;
+}
+
+splay_node_t *splay_unlink(splay_tree_t *tree, void *data) {
+       splay_node_t *node;
+
+       node = splay_search_node(tree, data);
+
+       if(node)
+               splay_unlink_node(tree, node);
+
+       return node;
+}
+
+void splay_unlink_node(splay_tree_t *tree, splay_node_t *node) {
+       if(node->prev)
+               node->prev->next = node->next;
+       else
+               tree->head = node->next;
+
+       if(node->next)
+               node->next->prev = node->prev;
+       else
+               tree->tail = node->prev;
+
+       splay_bottom_up(tree, node);
+
+       if(node->prev) {
+               node->left->parent = NULL;
+               tree->root = node->left;
+               if((node->prev->right = node->right))
+                       node->right->parent = node->prev;
+       } else if(node->next) {
+               tree->root = node->right;
+               node->right->parent = NULL;
+       } else {
+               tree->root = NULL;
+       }
+}
+
+void splay_delete_node(splay_tree_t *tree, splay_node_t *node) {
+       splay_unlink_node(tree, node);
+       splay_free_node(tree, node);
+}
+
+void splay_delete(splay_tree_t *tree, void *data) {
+       splay_node_t *node;
+
+       node = splay_search_node(tree, data);
+
+       if(node)
+               splay_delete_node(tree, node);
+}
+
+/* Fast tree cleanup */
+
+void splay_delete_tree(splay_tree_t *tree) {
+       splay_node_t *node, *next;
+
+       for(node = tree->head; node; node = next) {
+               next = node->next;
+               splay_free_node(tree, node);
+       }
+
+       splay_free_tree(tree);
+}
+
+/* Tree walking */
+
+void splay_foreach(const splay_tree_t *tree, splay_action_t action) {
+       splay_node_t *node, *next;
+
+       for(node = tree->head; node; node = next) {
+               next = node->next;
+               action(node->data);
+       }
+}
+
+void splay_foreach_node(const splay_tree_t *tree, splay_action_t action) {
+       splay_node_t *node, *next;
+
+       for(node = tree->head; node; node = next) {
+               next = node->next;
+               action(node);
+       }
+}
diff --git a/src/splay_tree.h b/src/splay_tree.h
new file mode 100644 (file)
index 0000000..e4af0c4
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+    splay_tree.h -- header file for splay_tree.c
+    Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+
+#ifndef __SPLAY_TREE_H__
+#define __SPLAY_TREE_H__
+
+typedef struct splay_node_t {
+
+       /* Linked list part */
+
+       struct splay_node_t *next;
+       struct splay_node_t *prev;
+
+       /* Tree part */
+
+       struct splay_node_t *parent;
+       struct splay_node_t *left;
+       struct splay_node_t *right;
+
+       /* Payload */
+
+       void *data;
+
+} splay_node_t;
+
+typedef int (*splay_compare_t)(const void *, const void *);
+typedef void (*splay_action_t)(const void *);
+typedef void (*splay_action_node_t)(const splay_node_t *);
+
+typedef struct splay_tree_t {
+
+       /* Linked list part */
+
+       splay_node_t *head;
+       splay_node_t *tail;
+
+       /* Tree part */
+
+       splay_node_t *root;
+
+       splay_compare_t compare;
+       splay_action_t delete;
+
+} splay_tree_t;
+
+/* (De)constructors */
+
+extern splay_tree_t *splay_alloc_tree(splay_compare_t, splay_action_t);
+extern void splay_free_tree(splay_tree_t *);
+
+extern splay_node_t *splay_alloc_node(void);
+extern void splay_free_node(splay_tree_t *tree, splay_node_t *);
+
+/* Insertion and deletion */
+
+extern splay_node_t *splay_insert(splay_tree_t *, void *);
+extern splay_node_t *splay_insert_node(splay_tree_t *, splay_node_t *);
+
+extern void splay_insert_top(splay_tree_t *, splay_node_t *);
+extern void splay_insert_before(splay_tree_t *, splay_node_t *, splay_node_t *);
+extern void splay_insert_after(splay_tree_t *, splay_node_t *, splay_node_t *);
+
+extern splay_node_t *splay_unlink(splay_tree_t *, void *);
+extern void splay_unlink_node(splay_tree_t *tree, splay_node_t *);
+extern void splay_delete(splay_tree_t *, void *);
+extern void splay_delete_node(splay_tree_t *, splay_node_t *);
+
+/* Fast tree cleanup */
+
+extern void splay_delete_tree(splay_tree_t *);
+
+/* Searching */
+
+extern void *splay_search(splay_tree_t *, const void *);
+extern void *splay_search_closest(splay_tree_t *, const void *, int *);
+extern void *splay_search_closest_smaller(splay_tree_t *, const void *);
+extern void *splay_search_closest_greater(splay_tree_t *, const void *);
+
+extern splay_node_t *splay_search_node(splay_tree_t *, const void *);
+extern splay_node_t *splay_search_closest_node(splay_tree_t *, const void *, int *);
+extern splay_node_t *splay_search_closest_node_nosplay(const splay_tree_t *, const void *, int *);
+extern splay_node_t *splay_search_closest_smaller_node(splay_tree_t *, const void *);
+extern splay_node_t *splay_search_closest_greater_node(splay_tree_t *, const void *);
+
+/* Tree walking */
+
+extern void splay_foreach(const splay_tree_t *, splay_action_t);
+extern void splay_foreach_node(const splay_tree_t *, splay_action_t);
+
+#endif
diff --git a/src/sptps.c b/src/sptps.c
new file mode 100644 (file)
index 0000000..d22390e
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+    sptps.c -- Simple Peer-to-Peer Security
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "cipher.h"
+#include "crypto.h"
+#include "digest.h"
+#include "ecdh.h"
+#include "ecdsa.h"
+#include "prf.h"
+#include "sptps.h"
+
+char *logfilename;
+#include "utils.c"
+
+/*
+   Nonce MUST be exchanged first (done)
+   Signatures MUST be done over both nonces, to guarantee the signature is fresh
+   Otherwise: if ECDHE key of one side is compromised, it can be reused!
+
+   Add explicit tag to beginning of structure to distinguish the client and server when signing. (done)
+
+   Sign all handshake messages up to ECDHE kex with long-term public keys. (done)
+
+   HMACed KEX finished message to prevent downgrade attacks and prove you have the right key material (done by virtue of ECDSA over the whole ECDHE exchange?)
+
+   Explicit close message needs to be added.
+
+   Maybe do add some alert messages to give helpful error messages? Not more than TLS sends.
+
+   Use counter mode instead of OFB. (done)
+
+   Make sure ECC operations are fixed time (aka prevent side-channel attacks).
+*/
+
+// Log an error message.
+static bool error(sptps_t *s, int s_errno, const char *msg) {
+       fprintf(stderr, "SPTPS error: %s\n", msg);
+       errno = s_errno;
+       return false;
+}
+
+// Send a record (private version, accepts all record types, handles encryption and authentication).
+static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+       char plaintext[len + 23];
+       char ciphertext[len + 19];
+
+       // Create header with sequence number, length and record type
+       uint32_t seqno = htonl(s->outseqno++);
+       uint16_t netlen = htons(len);
+
+       memcpy(plaintext, &seqno, 4);
+       memcpy(plaintext + 4, &netlen, 2);
+       plaintext[6] = type;
+
+       // Add plaintext (TODO: avoid unnecessary copy)
+       memcpy(plaintext + 7, data, len);
+
+       if(s->outstate) {
+               // If first handshake has finished, encrypt and HMAC
+               if(!digest_create(&s->outdigest, plaintext, len + 7, plaintext + 7 + len))
+                       return false;
+
+               if(!cipher_counter_xor(&s->outcipher, plaintext + 4, sizeof ciphertext, ciphertext))
+                       return false;
+
+               return s->send_data(s->handle, ciphertext, len + 19);
+       } else {
+               // Otherwise send as plaintext
+               return s->send_data(s->handle, plaintext + 4, len + 3);
+       }
+}
+
+// Send an application record.
+bool send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+       // Sanity checks: application cannot send data before handshake is finished,
+       // and only record types 0..127 are allowed.
+       if(!s->outstate)
+               return error(s, EINVAL, "Handshake phase not finished yet");
+
+       if(type >= SPTPS_HANDSHAKE)
+               return error(s, EINVAL, "Invalid application record type");
+
+       return send_record_priv(s, type, data, len);
+}
+
+// Send a Key EXchange record, containing a random nonce and an ECDHE public key.
+static bool send_kex(sptps_t *s) {
+       size_t keylen = ECDH_SIZE;
+
+       // Make room for our KEX message, which we will keep around since send_sig() needs it.
+       s->mykex = realloc(s->mykex, 1 + 32 + keylen);
+       if(!s->mykex)
+               return error(s, errno, strerror(errno));
+
+       // Set version byte to zero.
+       s->mykex[0] = SPTPS_VERSION;
+
+       // Create a random nonce.
+       randomize(s->mykex + 1, 32);
+
+       // Create a new ECDH public key.
+       if(!ecdh_generate_public(&s->ecdh, s->mykex + 1 + 32))
+               return false;
+
+       return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
+}
+
+// Send a SIGnature record, containing an ECDSA signature over both KEX records.
+static bool send_sig(sptps_t *s) {
+       size_t keylen = ECDH_SIZE;
+       size_t siglen = ecdsa_size(&s->mykey);
+
+       // Concatenate both KEX messages, plus tag indicating if it is from the connection originator
+       char msg[(1 + 32 + keylen) * 2 + 1];
+       char sig[siglen];
+
+       msg[0] = s->initiator;
+       memcpy(msg + 1, s->mykex, 1 + 32 + keylen);
+       memcpy(msg + 2 + 32 + keylen, s->hiskex, 1 + 32 + keylen);
+
+       // Sign the result.
+       if(!ecdsa_sign(&s->mykey, msg, sizeof msg, sig))
+               return false;
+
+       // Send the SIG exchange record.
+       return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof sig);
+}
+
+// Generate key material from the shared secret created from the ECDHE key exchange.
+static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
+       // Initialise cipher and digest structures if necessary
+       if(!s->outstate) {
+               bool result
+                       =  cipher_open_by_name(&s->incipher, "aes-256-ecb")
+                       && cipher_open_by_name(&s->outcipher, "aes-256-ecb")
+                       && digest_open_by_name(&s->indigest, "sha256", 16)
+                       && digest_open_by_name(&s->outdigest, "sha256", 16);
+               if(!result)
+                       return false;
+       }
+
+       // Allocate memory for key material
+       size_t keylen = digest_keylength(&s->indigest) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher) + cipher_keylength(&s->outcipher);
+
+       s->key = realloc(s->key, keylen);
+       if(!s->key)
+               return error(s, errno, strerror(errno));
+
+       // Create the HMAC seed, which is "key expansion" + session label + server nonce + client nonce
+       char seed[s->labellen + 64 + 13];
+       strcpy(seed, "key expansion");
+       if(s->initiator) {
+               memcpy(seed + 13, s->mykex + 1, 32);
+               memcpy(seed + 45, s->hiskex + 1, 32);
+       } else {
+               memcpy(seed + 13, s->hiskex + 1, 32);
+               memcpy(seed + 45, s->mykex + 1, 32);
+       }
+       memcpy(seed + 78, s->label, s->labellen);
+
+       // Use PRF to generate the key material
+       if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen))
+               return false;
+
+       return true;
+}
+
+// Send an ACKnowledgement record.
+static bool send_ack(sptps_t *s) {
+       return send_record_priv(s, SPTPS_HANDSHAKE, "", 0);
+}
+
+// Receive an ACKnowledgement record.
+static bool receive_ack(sptps_t *s, const char *data, uint16_t len) {
+       if(len)
+               return error(s, EIO, "Invalid ACK record length");
+
+       if(s->initiator) {
+               bool result
+                       = cipher_set_counter_key(&s->incipher, s->key)
+                       && digest_set_key(&s->indigest, s->key + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
+               if(!result)
+                       return false;
+       } else {
+               bool result
+                       = cipher_set_counter_key(&s->incipher, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest))
+                       && digest_set_key(&s->indigest, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
+               if(!result)
+                       return false;
+       }
+
+       free(s->key);
+       s->key = NULL;
+       s->instate = true;
+
+       return true;
+}
+
+// Receive a Key EXchange record, respond by sending a SIG record.
+static bool receive_kex(sptps_t *s, const char *data, uint16_t len) {
+       // Verify length of the HELLO record
+       if(len != 1 + 32 + ECDH_SIZE)
+               return error(s, EIO, "Invalid KEX record length");
+
+       // Ignore version number for now.
+
+       // Make a copy of the KEX message, send_sig() and receive_sig() need it
+       s->hiskex = realloc(s->hiskex, len);
+       if(!s->hiskex)
+               return error(s, errno, strerror(errno));
+
+       memcpy(s->hiskex, data, len);
+
+       return send_sig(s);
+}
+
+// Receive a SIGnature record, verify it, if it passed, compute the shared secret and calculate the session keys.
+static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
+       size_t keylen = ECDH_SIZE;
+       size_t siglen = ecdsa_size(&s->hiskey);
+
+       // Verify length of KEX record.
+       if(len != siglen)
+               return error(s, EIO, "Invalid KEX record length");
+
+       // Concatenate both KEX messages, plus tag indicating if it is from the connection originator
+       char msg[(1 + 32 + keylen) * 2 + 1];
+
+       msg[0] = !s->initiator;
+       memcpy(msg + 1, s->hiskex, 1 + 32 + keylen);
+       memcpy(msg + 2 + 32 + keylen, s->mykex, 1 + 32 + keylen);
+
+       // Verify signature.
+       if(!ecdsa_verify(&s->hiskey, msg, sizeof msg, data))
+               return false;
+
+       // Compute shared secret.
+       char shared[ECDH_SHARED_SIZE];
+       if(!ecdh_compute_shared(&s->ecdh, s->hiskex + 1 + 32, shared))
+               return false;
+
+       // Generate key material from shared secret.
+       if(!generate_key_material(s, shared, sizeof shared))
+               return false;
+
+       free(s->mykex);
+       free(s->hiskex);
+
+       s->mykex = NULL;
+       s->hiskex = NULL;
+
+       // Send cipher change record
+       if(!send_ack(s))
+               return false;
+
+       // TODO: only set new keys after ACK has been set/received
+       if(s->initiator) {
+               bool result
+                       = cipher_set_counter_key(&s->outcipher, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest))
+                       && digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest) + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
+               if(!result)
+                       return false;
+       } else {
+               bool result
+                       =  cipher_set_counter_key(&s->outcipher, s->key)
+                       && digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
+               if(!result)
+                       return false;
+       }
+
+       s->outstate = true;
+
+       return true;
+}
+
+// Force another Key EXchange (for testing purposes).
+bool force_kex(sptps_t *s) {
+       if(!s->outstate || s->state != SPTPS_SECONDARY_KEX)
+               return error(s, EINVAL, "Cannot force KEX in current state");
+
+       s->state = SPTPS_KEX;
+       return send_kex(s);
+}
+
+// Receive a handshake record.
+static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
+       // Only a few states to deal with handshaking.
+       fprintf(stderr, "Received handshake message, current state %d\n", s->state);
+       switch(s->state) {
+               case SPTPS_SECONDARY_KEX:
+                       // We receive a secondary KEX request, first respond by sending our own.
+                       if(!send_kex(s))
+                               return false;
+               case SPTPS_KEX:
+                       // We have sent our KEX request, we expect our peer to sent one as well.
+                       if(!receive_kex(s, data, len))
+                               return false;
+                       s->state = SPTPS_SIG;
+                       return true;
+               case SPTPS_SIG:
+                       // If we already sent our secondary public ECDH key, we expect the peer to send his.
+                       if(!receive_sig(s, data, len))
+                               return false;
+                       // s->state = SPTPS_ACK;
+                       s->state = SPTPS_ACK;
+                       return true;
+               case SPTPS_ACK:
+                       // We expect a handshake message to indicate transition to the new keys.
+                       if(!receive_ack(s, data, len))
+                               return false;
+                       s->state = SPTPS_SECONDARY_KEX;
+                       return true;
+               // TODO: split ACK into a VERify and ACK?
+               default:
+                       return error(s, EIO, "Invalid session state");
+       }
+}
+
+// Receive incoming data. Check if it contains a complete record, if so, handle it.
+bool receive_data(sptps_t *s, const char *data, size_t len) {
+       while(len) {
+               // First read the 2 length bytes.
+               if(s->buflen < 6) {
+                       size_t toread = 6 - s->buflen;
+                       if(toread > len)
+                               toread = len;
+
+                       if(s->instate) {
+                               if(!cipher_counter_xor(&s->incipher, data, toread, s->inbuf + s->buflen))
+                                       return false;
+                       } else {
+                               memcpy(s->inbuf + s->buflen, data, toread);
+                       }
+
+                       s->buflen += toread;
+                       len -= toread;
+                       data += toread;
+
+                       // Exit early if we don't have the full length.
+                       if(s->buflen < 6)
+                               return true;
+
+                       // If we have the length bytes, ensure our buffer can hold the whole request.
+                       uint16_t reclen;
+                       memcpy(&reclen, s->inbuf + 4, 2);
+                       reclen = htons(reclen);
+                       s->inbuf = realloc(s->inbuf, reclen + 23UL);
+                       if(!s->inbuf)
+                               return error(s, errno, strerror(errno));
+
+                       // Add sequence number.
+                       uint32_t seqno = htonl(s->inseqno++);
+                       memcpy(s->inbuf, &seqno, 4);
+
+                       // Exit early if we have no more data to process.
+                       if(!len)
+                               return true;
+               }
+
+               // Read up to the end of the record.
+               uint16_t reclen;
+               memcpy(&reclen, s->inbuf + 4, 2);
+               reclen = htons(reclen);
+               size_t toread = reclen + (s->instate ? 23UL : 7UL) - s->buflen;
+               if(toread > len)
+                       toread = len;
+
+               if(s->instate) {
+                       if(!cipher_counter_xor(&s->incipher, data, toread, s->inbuf + s->buflen))
+                               return false;
+               } else {
+                       memcpy(s->inbuf + s->buflen, data, toread);
+               }
+
+               s->buflen += toread;
+               len -= toread;
+               data += toread;
+
+               // If we don't have a whole record, exit.
+               if(s->buflen < reclen + (s->instate ? 23UL : 7UL))
+                       return true;
+
+               // Check HMAC.
+               if(s->instate)
+                       if(!digest_verify(&s->indigest, s->inbuf, reclen + 7UL, s->inbuf + reclen + 7UL))
+                               error(s, EIO, "Invalid HMAC");
+
+               uint8_t type = s->inbuf[6];
+
+               // Handle record.
+               if(type < SPTPS_HANDSHAKE) {
+                       if(!s->instate)
+                               return error(s, EIO, "Application record received before handshake finished");
+                       if(!s->receive_record(s->handle, type, s->inbuf + 7, reclen))
+                               return false;
+               } else if(type == SPTPS_HANDSHAKE) {
+                       if(!receive_handshake(s, s->inbuf + 7, reclen))
+                               return false;
+               } else {
+                       return error(s, EIO, "Invalid record type");
+               }
+
+               s->buflen = 4;
+       }
+
+       return true;
+}
+
+// Start a SPTPS session.
+bool start_sptps(sptps_t *s, void *handle, bool initiator, ecdsa_t mykey, ecdsa_t hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
+       // Initialise struct sptps
+       memset(s, 0, sizeof *s);
+
+       s->handle = handle;
+       s->initiator = initiator;
+       s->mykey = mykey;
+       s->hiskey = hiskey;
+
+       s->label = malloc(labellen);
+       if(!s->label)
+               return error(s, errno, strerror(errno));
+
+       s->inbuf = malloc(7);
+       if(!s->inbuf)
+               return error(s, errno, strerror(errno));
+       s->buflen = 4;
+       memset(s->inbuf, 0, 4);
+
+       memcpy(s->label, label, labellen);
+       s->labellen = labellen;
+
+       s->send_data = send_data;
+       s->receive_record = receive_record;
+
+       // Do first KEX immediately
+       s->state = SPTPS_KEX;
+       return send_kex(s);
+}
+
+// Stop a SPTPS session.
+bool stop_sptps(sptps_t *s) {
+       // Clean up any resources.
+       ecdh_free(&s->ecdh);
+       free(s->inbuf);
+       free(s->mykex);
+       free(s->hiskex);
+       free(s->key);
+       free(s->label);
+       return true;
+}
diff --git a/src/sptps.h b/src/sptps.h
new file mode 100644 (file)
index 0000000..51de575
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+    sptps.h -- Simple Peer-to-Peer Security
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "cipher.h"
+#include "digest.h"
+#include "ecdh.h"
+#include "ecdsa.h"
+
+#define SPTPS_VERSION 0
+
+// Record types
+#define SPTPS_HANDSHAKE 128   // Key exchange and authentication
+#define SPTPS_ALERT 129       // Warning or error messages
+#define SPTPS_CLOSE 130       // Application closed the connection
+
+// Key exchange states
+#define SPTPS_KEX 0           // Waiting for the first Key EXchange record
+#define SPTPS_SECONDARY_KEX 1 // Ready to receive a secondary Key EXchange record
+#define SPTPS_SIG 2           // Waiting for a SIGnature record
+#define SPTPS_ACK 3           // Waiting for an ACKnowledgement record
+
+typedef bool (*send_data_t)(void *handle, const char *data, size_t len);
+typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
+
+typedef struct sptps {
+       bool initiator;
+       int state;
+
+       char *inbuf;
+       size_t buflen;
+
+       bool instate;
+       cipher_t incipher;
+       digest_t indigest;
+       uint32_t inseqno;
+
+       bool outstate;
+       cipher_t outcipher;
+       digest_t outdigest;
+       uint32_t outseqno;
+
+       ecdsa_t mykey;
+       ecdsa_t hiskey;
+       ecdh_t ecdh;
+
+       char *mykex;
+       char *hiskex;
+       char *key;
+       char *label;
+       size_t labellen;
+
+       void *handle;
+       send_data_t send_data;
+       receive_record_t receive_record;
+} sptps_t;
+
+extern bool start_sptps(sptps_t *s, void *handle, bool initiator, ecdsa_t mykey, ecdsa_t hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record);
+extern bool stop_sptps(sptps_t *s);
+extern bool send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len);
+extern bool receive_data(sptps_t *s, const char *data, size_t len);
+extern bool force_kex(sptps_t *s);
diff --git a/src/sptps_test.c b/src/sptps_test.c
new file mode 100644 (file)
index 0000000..866159e
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+    sptps_test.c -- Simple Peer-to-Peer Security test program
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+#include "poll.h"
+
+#include "crypto.h"
+#include "ecdsa.h"
+#include "sptps.h"
+#include "utils.h"
+
+ecdsa_t mykey, hiskey;
+
+static bool send_data(void *handle, const char *data, size_t len) {
+       char hex[len * 2 + 1];
+       bin2hex(data, hex, len);
+       fprintf(stderr, "Sending %zu bytes of data:\n%s\n", len, hex);
+       const int *sock = handle;
+       if(send(*sock, data, len, 0) != len)
+               return false;
+       return true;
+}
+
+static bool receive_record(void *handle, uint8_t type, const char *data, uint16_t len) {
+       fprintf(stderr, "Received type %d record of %hu bytes:\n", type, len);
+       fwrite(data, len, 1, stdout);
+       return true;
+}
+
+int main(int argc, char *argv[]) {
+       bool initiator = false;
+
+       if(argc < 3) {
+               fprintf(stderr, "Usage: %s my_ecdsa_key_file his_ecdsa_key_file [host] port\n", argv[0]);
+               return 1;
+       }
+
+       if(argc > 4)
+               initiator = true;
+
+       struct addrinfo *ai, hint;
+       memset(&hint, 0, sizeof hint);
+
+       hint.ai_family = AF_UNSPEC;
+       hint.ai_socktype = SOCK_STREAM;
+       hint.ai_protocol = IPPROTO_TCP;
+       hint.ai_flags = initiator ? 0 : AI_PASSIVE;
+       
+       if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
+               fprintf(stderr, "getaddrinfo() failed: %s\n", strerror(errno));
+               return 1;
+       }
+
+       int sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+       if(sock < 0) {
+               fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
+               return 1;
+       }
+
+       int one = 1;
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
+
+       if(initiator) {
+               if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
+                       fprintf(stderr, "Could not connect to peer: %s\n", strerror(errno));
+                       return 1;
+               }
+               fprintf(stderr, "Connected\n");
+       } else {
+               if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
+                       fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
+                       return 1;
+               }
+               if(listen(sock, 1)) {
+                       fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
+                       return 1;
+               }
+               fprintf(stderr, "Listening...\n");
+
+               sock = accept(sock, NULL, NULL);
+               if(sock < 0) {
+                       fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
+                       return 1;
+               }
+
+               fprintf(stderr, "Connected\n");
+       }
+
+       crypto_init();
+
+       FILE *fp = fopen(argv[1], "r");
+       if(!ecdsa_read_pem_private_key(&mykey, fp))
+               return 1;
+       fclose(fp);
+
+       fp = fopen(argv[2], "r");
+       if(!ecdsa_read_pem_public_key(&hiskey, fp))
+               return 1;
+       fclose(fp);
+
+       fprintf(stderr, "Keys loaded\n");
+
+       sptps_t s;
+       if(!start_sptps(&s, &sock, initiator, mykey, hiskey, "sptps_test", 10, send_data, receive_record))
+               return 1;
+
+       while(true) {
+               char buf[65535] = "";
+
+               struct pollfd fds[2];
+               fds[0].fd = 0;
+               fds[0].events = POLLIN;
+               fds[1].fd = sock;
+               fds[1].events = POLLIN;
+               if(poll(fds, 2, -1) < 0)
+                       return 1;
+
+               if(fds[0].revents) {
+                       ssize_t len = read(0, buf, sizeof buf);
+                       if(len < 0) {
+                               fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
+                               return 1;
+                       }
+                       if(len == 0)
+                               break;
+                       if(buf[0] == '^')
+                               send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
+                       else if(buf[0] == '$')
+                               force_kex(&s);
+                       else
+                       if(!send_record(&s, buf[0] == '!' ? 1 : 0, buf, buf[0] == '\n' ? 0 : buf[0] == '*' ? sizeof buf : len))
+                               return 1;
+               }
+
+               if(fds[1].revents) {
+                       ssize_t len = recv(sock, buf, sizeof buf, 0);
+                       if(len < 0) {
+                               fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
+                               return 1;
+                       }
+                       if(len == 0) {
+                               fprintf(stderr, "Connection terminated by peer.\n");
+                               break;
+                       }
+                       char hex[len * 2 + 1];
+                       bin2hex(buf, hex, len);
+                       fprintf(stderr, "Received %zd bytes of data:\n%s\n", len, hex);
+                       if(!receive_data(&s, buf, len))
+                               return 1;
+               }
+       }
+
+       return 0;
+}
index 7fffe633c4ce377aeae9400c1536a00d20980c80..2c67dcc1c07115798f89c7963a4854a609c62f23 100644 (file)
@@ -20,7 +20,8 @@
 
 #include "system.h"
 
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "control_common.h"
 #include "device.h"
 #include "logger.h"
 #include "net.h"
@@ -33,7 +34,7 @@
 
 /* lists type of subnet */
 
-avl_tree_t *subnet_tree;
+splay_tree_t *subnet_tree;
 
 /* Subnet lookup cache */
 
@@ -63,7 +64,7 @@ void subnet_cache_flush(void) {
 static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
        int result;
 
-       result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
+       result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
 
        if(result)
                return result;
@@ -145,21 +146,21 @@ int subnet_compare(const subnet_t *a, const subnet_t *b) {
 /* Initialising trees */
 
 void init_subnets(void) {
-       subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet);
+       subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet);
 
        subnet_cache_flush();
 }
 
 void exit_subnets(void) {
-       avl_delete_tree(subnet_tree);
+       splay_delete_tree(subnet_tree);
 }
 
-avl_tree_t *new_subnet_tree(void) {
-       return avl_alloc_tree((avl_compare_t) subnet_compare, NULL);
+splay_tree_t *new_subnet_tree(void) {
+       return splay_alloc_tree((splay_compare_t) subnet_compare, NULL);
 }
 
-void free_subnet_tree(avl_tree_t *subnet_tree) {
-       avl_delete_tree(subnet_tree);
+void free_subnet_tree(splay_tree_t *subnet_tree) {
+       splay_delete_tree(subnet_tree);
 }
 
 /* Allocating and freeing space for subnets */
@@ -177,15 +178,15 @@ void free_subnet(subnet_t *subnet) {
 void subnet_add(node_t *n, subnet_t *subnet) {
        subnet->owner = n;
 
-       avl_insert(subnet_tree, subnet);
-       avl_insert(n->subnet_tree, subnet);
+       splay_insert(subnet_tree, subnet);
+       splay_insert(n->subnet_tree, subnet);
 
        subnet_cache_flush();
 }
 
 void subnet_del(node_t *n, subnet_t *subnet) {
-       avl_delete(n->subnet_tree, subnet);
-       avl_delete(subnet_tree, subnet);
+       splay_delete(n->subnet_tree, subnet);
+       splay_delete(subnet_tree, subnet);
 
        subnet_cache_flush();
 }
@@ -326,12 +327,12 @@ bool net2str(char *netstr, int len, const subnet_t *subnet) {
 /* Subnet lookup routines */
 
 subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
-       return avl_search(owner->subnet_tree, subnet);
+       return splay_search(owner->subnet_tree, subnet);
 }
 
 subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
        subnet_t *p, *r = NULL;
-       avl_node_t *n;
+       splay_node_t *n;
        int i;
 
        // Check if this address is cached
@@ -372,7 +373,7 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
 
 subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
        subnet_t *p, *r = NULL;
-       avl_node_t *n;
+       splay_node_t *n;
        int i;
 
        // Check if this address is cached
@@ -411,7 +412,7 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
 
 subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
        subnet_t *p, *r = NULL;
-       avl_node_t *n;
+       splay_node_t *n;
        int i;
 
        // Check if this address is cached
@@ -449,7 +450,7 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
 }
 
 void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
-       avl_node_t *node;
+       splay_node_t *node;
        int i;
        char *envp[9] = {NULL};
        char netstr[MAXNETSTR];
@@ -513,23 +514,23 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
                }
        }
 
-       for(i = 0; envp[i] && i < 9; i++)
+       for(i = 0; envp[i] && i < 8; i++)
                free(envp[i]);
 }
 
-void dump_subnets(void) {
+bool dump_subnets(connection_t *c) {
        char netstr[MAXNETSTR];
        subnet_t *subnet;
-       avl_node_t *node;
-
-       logger(LOG_DEBUG, "Subnet list:");
+       splay_node_t *node;
 
        for(node = subnet_tree->head; node; node = node->next) {
                subnet = node->data;
                if(!net2str(netstr, sizeof netstr, subnet))
                        continue;
-               logger(LOG_DEBUG, " %s owner %s", netstr, subnet->owner->name);
+               send_request(c, "%d %d %s owner %s",
+                               CONTROL, REQ_DUMP_SUBNETS,
+                               netstr, subnet->owner->name);
        }
 
-       logger(LOG_DEBUG, "End of subnet list.");
+       return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
 }
index e129a9592d3f98975db56aa0e7bf41dfff63bbd6..f22e6d58abfc0d36ee601519a999922cedf2612c 100644 (file)
@@ -64,15 +64,15 @@ typedef struct subnet_t {
 
 #define MAXNETSTR 64
 
-extern avl_tree_t *subnet_tree;
+extern splay_tree_t *subnet_tree;
 
 extern int subnet_compare(const struct subnet_t *, const struct subnet_t *);
 extern subnet_t *new_subnet(void) __attribute__ ((__malloc__));
 extern void free_subnet(subnet_t *);
 extern void init_subnets(void);
 extern void exit_subnets(void);
-extern avl_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__));
-extern void free_subnet_tree(avl_tree_t *);
+extern splay_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__));
+extern void free_subnet_tree(splay_tree_t *);
 extern void subnet_add(struct node_t *, subnet_t *);
 extern void subnet_del(struct node_t *, subnet_t *);
 extern void subnet_update(struct node_t *, subnet_t *, bool);
@@ -82,7 +82,7 @@ extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
 extern subnet_t *lookup_subnet_mac(const struct node_t *, const mac_t *);
 extern subnet_t *lookup_subnet_ipv4(const ipv4_t *);
 extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
-extern void dump_subnets(void);
+extern bool dump_subnets(struct connection_t *);
 extern void subnet_cache_flush(void);
 
 #endif                                                 /* __TINC_SUBNET_H__ */
diff --git a/src/tincctl.c b/src/tincctl.c
new file mode 100644 (file)
index 0000000..4f06daa
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+    tincctl.c -- Controlling a running tincd
+    Copyright (C) 2007-2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include <getopt.h>
+
+#include "xalloc.h"
+#include "protocol.h"
+#include "control_common.h"
+#include "ecdsagen.h"
+#include "rsagen.h"
+#include "utils.h"
+#include "tincctl.h"
+#include "top.h"
+
+/* The name this program was run with. */
+static char *program_name = NULL;
+
+/* If nonzero, display usage information and exit. */
+static bool show_help = false;
+
+/* If nonzero, print the version on standard output and exit.  */
+static bool show_version = false;
+
+static char *name = NULL;
+static char *identname = NULL;                         /* program name for syslog */
+static char *pidfilename = NULL;                       /* pid file location */
+static char controlcookie[1024];
+char *netname = NULL;
+char *confbase = NULL;
+
+#ifdef HAVE_MINGW
+static struct WSAData wsa_state;
+#endif
+
+static struct option const long_options[] = {
+       {"config", required_argument, NULL, 'c'},
+       {"net", required_argument, NULL, 'n'},
+       {"help", no_argument, NULL, 1},
+       {"version", no_argument, NULL, 2},
+       {"pidfile", required_argument, NULL, 5},
+       {NULL, 0, NULL, 0}
+};
+
+static void usage(bool status) {
+       if(status)
+               fprintf(stderr, "Try `%s --help\' for more information.\n",
+                               program_name);
+       else {
+               printf("Usage: %s [options] command\n\n", program_name);
+               printf("Valid options are:\n"
+                               "  -c, --config=DIR        Read configuration options from DIR.\n"
+                               "  -n, --net=NETNAME       Connect to net NETNAME.\n"
+                               "      --pidfile=FILENAME  Read control cookie from FILENAME.\n"
+                               "      --help              Display this help and exit.\n"
+                               "      --version           Output version information and exit.\n"
+                               "\n"
+                               "Valid commands are:\n"
+                               "  start                      Start tincd.\n"
+                               "  stop                       Stop tincd.\n"
+                               "  restart                    Restart tincd.\n"
+                               "  reload                     Partially reload configuration of running tincd.\n"
+                               "  pid                        Show PID of currently running tincd.\n"
+                               "  generate-keys [bits]       Generate new RSA and ECDSA public/private keypairs.\n"
+                               "  generate-rsa-keys [bits]   Generate a new RSA public/private keypair.\n"
+                               "  generate-ecdsa-keys        Generate a new ECDSA public/private keypair.\n"
+                               "  dump                       Dump a list of one of the following things:\n"
+                               "    nodes                    - all known nodes in the VPN\n"
+                               "    edges                    - all known connections in the VPN\n"
+                               "    subnets                  - all known subnets in the VPN\n"
+                               "    connections              - all meta connections with ourself\n"
+                               "    graph                    - graph of the VPN in dotty format\n"
+                               "  purge                      Purge unreachable nodes\n"
+                               "  debug N                    Set debug level\n"
+                               "  retry                      Retry all outgoing connections\n"
+                               "  disconnect NODE            Close meta connection with NODE\n"
+#ifdef HAVE_CURSES
+                               "  top                        Show real-time statistics\n"
+#endif
+                               "  pcap                       Dump traffic in pcap format\n"
+                               "\n");
+               printf("Report bugs to tinc@tinc-vpn.org.\n");
+       }
+}
+
+static bool parse_options(int argc, char **argv) {
+       int r;
+       int option_index = 0;
+
+       while((r = getopt_long(argc, argv, "c:n:", long_options, &option_index)) != EOF) {
+               switch (r) {
+                       case 0:                         /* long option */
+                               break;
+
+                       case 'c':                               /* config file */
+                               confbase = xstrdup(optarg);
+                               break;
+
+                       case 'n':                               /* net name given */
+                               netname = xstrdup(optarg);
+                               break;
+
+                       case 1:                                 /* show help */
+                               show_help = true;
+                               break;
+
+                       case 2:                                 /* show version */
+                               show_version = true;
+                               break;
+
+                       case 5:                                 /* open control socket here */
+                               pidfilename = xstrdup(optarg);
+                               break;
+
+                       case '?':
+                               usage(true);
+                               return false;
+
+                       default:
+                               break;
+               }
+       }
+
+       return true;
+}
+
+FILE *ask_and_open(const char *filename, const char *what, const char *mode) {
+       FILE *r;
+       char *directory;
+       char buf[PATH_MAX];
+       char buf2[PATH_MAX];
+
+       /* Check stdin and stdout */
+       if(isatty(0) && isatty(1)) {
+               /* Ask for a file and/or directory name. */
+               fprintf(stdout, "Please enter a file to save %s to [%s]: ",
+                               what, filename);
+               fflush(stdout);
+
+               if(fgets(buf, sizeof buf, stdin) == NULL) {
+                       fprintf(stderr, "Error while reading stdin: %s\n",
+                                       strerror(errno));
+                       return NULL;
+               }
+
+               size_t len = strlen(buf);
+               if(len)
+                       buf[--len] = 0;
+
+               if(len)
+                       filename = buf;
+       }
+
+#ifdef HAVE_MINGW
+       if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
+#else
+       if(filename[0] != '/') {
+#endif
+               /* The directory is a relative path or a filename. */
+               directory = get_current_dir_name();
+               snprintf(buf2, sizeof buf2, "%s/%s", directory, filename);
+               filename = buf2;
+       }
+
+       umask(0077);                            /* Disallow everything for group and other */
+
+       /* Open it first to keep the inode busy */
+
+       r = fopen(filename, mode);
+
+       if(!r) {
+               fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
+               return NULL;
+       }
+
+       return r;
+}
+
+/*
+  Generate a public/private ECDSA keypair, and ask for a file to store
+  them in.
+*/
+static bool ecdsa_keygen() {
+       ecdsa_t key;
+       FILE *f;
+       char *filename;
+
+       fprintf(stderr, "Generating ECDSA keypair:\n");
+
+       if(!ecdsa_generate(&key)) {
+               fprintf(stderr, "Error during key generation!\n");
+               return false;
+       } else
+               fprintf(stderr, "Done.\n");
+
+       xasprintf(&filename, "%s/ecdsa_key.priv", confbase);
+       f = ask_and_open(filename, "private ECDSA key", "a");
+
+       if(!f)
+               return false;
+  
+#ifdef HAVE_FCHMOD
+       /* Make it unreadable for others. */
+       fchmod(fileno(f), 0600);
+#endif
+               
+       if(ftell(f))
+               fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+       ecdsa_write_pem_private_key(&key, f);
+
+       fclose(f);
+       free(filename);
+
+       if(name)
+               xasprintf(&filename, "%s/hosts/%s", confbase, name);
+       else
+               xasprintf(&filename, "%s/ecdsa_key.pub", confbase);
+
+       f = ask_and_open(filename, "public ECDSA key", "a");
+
+       if(!f)
+               return false;
+
+       if(ftell(f))
+               fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+       char *pubkey = ecdsa_get_base64_public_key(&key);
+       fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
+       free(pubkey);
+
+       fclose(f);
+       free(filename);
+
+       return true;
+}
+
+/*
+  Generate a public/private RSA keypair, and ask for a file to store
+  them in.
+*/
+static bool rsa_keygen(int bits) {
+       rsa_t key;
+       FILE *f;
+       char *filename;
+
+       fprintf(stderr, "Generating %d bits keys:\n", bits);
+
+       if(!rsa_generate(&key, bits, 0x10001)) {
+               fprintf(stderr, "Error during key generation!\n");
+               return false;
+       } else
+               fprintf(stderr, "Done.\n");
+
+       xasprintf(&filename, "%s/rsa_key.priv", confbase);
+       f = ask_and_open(filename, "private RSA key", "a");
+
+       if(!f)
+               return false;
+  
+#ifdef HAVE_FCHMOD
+       /* Make it unreadable for others. */
+       fchmod(fileno(f), 0600);
+#endif
+               
+       if(ftell(f))
+               fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+       rsa_write_pem_private_key(&key, f);
+
+       fclose(f);
+       free(filename);
+
+       if(name)
+               xasprintf(&filename, "%s/hosts/%s", confbase, name);
+       else
+               xasprintf(&filename, "%s/rsa_key.pub", confbase);
+
+       f = ask_and_open(filename, "public RSA key", "a");
+
+       if(!f)
+               return false;
+
+       if(ftell(f))
+               fprintf(stderr, "Appending key to existing contents.\nMake sure only one key is stored in the file.\n");
+
+       rsa_write_pem_public_key(&key, f);
+
+       fclose(f);
+       free(filename);
+
+       return true;
+}
+
+/*
+  Set all files and paths according to netname
+*/
+static void make_names(void) {
+#ifdef HAVE_MINGW
+       HKEY key;
+       char installdir[1024] = "";
+       long len = sizeof installdir;
+#endif
+
+       if(netname)
+               xasprintf(&identname, "tinc.%s", netname);
+       else
+               identname = xstrdup("tinc");
+
+#ifdef HAVE_MINGW
+       if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
+               if(!RegQueryValueEx(key, NULL, 0, 0, installdir, &len)) {
+                       if(!confbase) {
+                               if(netname)
+                                       xasprintf(&confbase, "%s/%s", installdir, netname);
+                               else
+                                       xasprintf(&confbase, "%s", installdir);
+                       }
+               }
+               if(!pidfilename)
+                       xasprintf(&pidfilename, "%s/pid", confbase);
+               RegCloseKey(key);
+               if(*installdir)
+                       return;
+       }
+#endif
+
+       if(!pidfilename)
+               xasprintf(&pidfilename, "%s/run/%s.pid", LOCALSTATEDIR, identname);
+
+       if(netname) {
+               if(!confbase)
+                       xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
+               else
+                       fprintf(stderr, "Both netname and configuration directory given, using the latter...\n");
+       } else {
+               if(!confbase)
+                       xasprintf(&confbase, CONFDIR "/tinc");
+       }
+}
+
+static char buffer[4096];
+static size_t blen = 0;
+
+bool recvline(int fd, char *line, size_t len) {
+       char *newline = NULL;
+
+       while(!(newline = memchr(buffer, '\n', blen))) {
+               int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
+               if(result == -1 && errno == EINTR)
+                       continue;
+               else if(result <= 0)
+                       return false;
+               blen += result;
+       }
+
+       if(newline - buffer >= len)
+               return false;
+
+       len = newline - buffer;
+
+       memcpy(line, buffer, len);
+       line[len] = 0;
+       memmove(buffer, newline + 1, blen - len - 1);
+       blen -= len + 1;
+
+       return true;
+}
+
+bool recvdata(int fd, char *data, size_t len) {
+       while(blen < len) {
+               int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
+               if(result == -1 && errno == EINTR)
+                       continue;
+               else if(result <= 0)
+                       return false;
+               blen += result;
+       }
+
+       memcpy(data, buffer, len);
+       memmove(buffer, buffer + len, blen - len);
+       blen -= len;
+
+       return true;
+}
+
+bool sendline(int fd, char *format, ...) {
+       static char buffer[4096];
+       char *p = buffer;
+       int blen = 0;
+       va_list ap;
+
+       va_start(ap, format);
+       blen = vsnprintf(buffer, sizeof buffer, format, ap);
+       va_end(ap);
+
+       if(blen < 1 || blen >= sizeof buffer)
+               return false;
+
+       buffer[blen] = '\n';
+       blen++;
+
+       while(blen) {
+               int result = send(fd, p, blen, 0);
+               if(result == -1 && errno == EINTR)
+                       continue;
+               else if(result <= 0)
+                       return false;
+               p += result;
+               blen -= result;
+       }
+
+       return true;    
+}
+
+void pcap(int fd, FILE *out) {
+       sendline(fd, "%d %d", CONTROL, REQ_PCAP);
+       char data[9018];
+
+       struct {
+               uint32_t magic;
+               uint16_t major;
+               uint16_t minor;
+               uint32_t tz_offset;
+               uint32_t tz_accuracy;
+               uint32_t snaplen;
+               uint32_t ll_type;
+       } header = {
+               0xa1b2c3d4,
+               2, 4,
+               0, 0,
+               sizeof data,
+               1,
+       };
+
+       struct {
+               uint32_t tv_sec;
+               uint32_t tv_usec;
+               uint32_t len;
+               uint32_t origlen;
+       } packet;
+
+       struct timeval tv;
+
+       fwrite(&header, sizeof header, 1, out);
+       fflush(out);
+
+       char line[32];
+       while(recvline(fd, line, sizeof line)) {
+               int code, req, len;
+               int n = sscanf(line, "%d %d %d", &code, &req, &len);
+               gettimeofday(&tv, NULL);
+               if(n != 3 || code != CONTROL || req != REQ_PCAP || len < 0 || len > sizeof data)
+                       break;
+               if(!recvdata(fd, data, len))
+                       break;
+               packet.tv_sec = tv.tv_sec;
+               packet.tv_usec = tv.tv_usec;
+               packet.len = len;
+               packet.origlen = len;
+               fwrite(&packet, sizeof packet, 1, out);
+               fwrite(data, len, 1, out);
+               fflush(out);
+       }
+}
+
+#ifdef HAVE_MINGW
+static bool remove_service(void) {
+       SC_HANDLE manager = NULL;
+       SC_HANDLE service = NULL;
+       SERVICE_STATUS status = {0};
+
+       manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+       if(!manager) {
+               fprintf(stderr, "Could not open service manager: %s\n", winerror(GetLastError()));
+               return false;
+       }
+
+       service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
+
+       if(!service) {
+               fprintf(stderr, "Could not open %s service: %s\n", identname, winerror(GetLastError()));
+               return false;
+       }
+
+       if(!ControlService(service, SERVICE_CONTROL_STOP, &status))
+               fprintf(stderr, "Could not stop %s service: %s\n", identname, winerror(GetLastError()));
+       else
+               fprintf(stderr, "%s service stopped\n", identname);
+
+       if(!DeleteService(service)) {
+               fprintf(stderr, "Could not remove %s service: %s\n", identname, winerror(GetLastError()));
+               return false;
+       }
+
+       fprintf(stderr, "%s service removed\n", identname);
+
+       return true;
+}
+#endif
+
+int main(int argc, char *argv[]) {
+       int fd;
+       int result;
+       char host[128];
+       char port[128];
+       int pid;
+
+       program_name = argv[0];
+
+       if(!parse_options(argc, argv))
+               return 1;
+       
+       make_names();
+
+       if(show_version) {
+               printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
+                          VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
+               printf("Copyright (C) 1998-2009 Ivo Timmermans, Guus Sliepen and others.\n"
+                               "See the AUTHORS file for a complete list.\n\n"
+                               "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
+                               "and you are welcome to redistribute it under certain conditions;\n"
+                               "see the file COPYING for details.\n");
+
+               return 0;
+       }
+
+       if(show_help) {
+               usage(false);
+               return 0;
+       }
+
+       if(optind >= argc) {
+               fprintf(stderr, "Not enough arguments.\n");
+               usage(true);
+               return 1;
+       }
+
+       // First handle commands that don't involve connecting to a running tinc daemon.
+
+       if(!strcasecmp(argv[optind], "generate-rsa-keys")) {
+               return !rsa_keygen(optind > argc ? atoi(argv[optind + 1]) : 2048);
+       }
+
+       if(!strcasecmp(argv[optind], "generate-ecdsa-keys")) {
+               return !ecdsa_keygen();
+       }
+
+       if(!strcasecmp(argv[optind], "generate-keys")) {
+               return !(rsa_keygen(optind > argc ? atoi(argv[optind + 1]) : 2048) && ecdsa_keygen());
+       }
+
+       if(!strcasecmp(argv[optind], "start")) {
+               int i, j;
+               char *c;
+               char *slash = strrchr(argv[0], '/');
+#ifdef HAVE_MINGW
+               if ((c = strrchr(argv[0], '\\')) > slash)
+                       slash = c;
+#endif
+               if (slash++) {
+                       c = xmalloc((slash - argv[0]) + sizeof("tincd"));
+                       sprintf(c, "%.*stincd", (int)(slash - argv[0]), argv[0]);
+               }
+               else
+                       c = "tincd";
+               argv[0] = c;
+               for(i = j = 1; argv[i]; ++i)
+                       if (i != optind && strcmp(argv[i], "--") != 0)
+                               argv[j++] = argv[i];
+               argv[j] = NULL;
+               execvp(c, argv);
+               fprintf(stderr, "Could not start %s: %s\n", c, strerror(errno));
+               return 1;
+       }
+
+       /*
+        * Now handle commands that do involve connecting to a running tinc daemon.
+        * Authenticate the server by ensuring the parent directory can be
+        * traversed only by root. Note this is not totally race-free unless all
+        * ancestors are writable only by trusted users, which we don't verify.
+        */
+
+       FILE *f = fopen(pidfilename, "r");
+       if(!f) {
+               fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
+               return 1;
+       }
+       if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
+               fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
+               return 1;
+       }
+
+#ifdef HAVE_MINGW
+       if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
+               fprintf(stderr, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
+               return 1;
+       }
+#endif
+
+       struct addrinfo hints = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM,
+               .ai_protocol = IPPROTO_TCP,
+               .ai_flags = 0,
+       };
+
+       struct addrinfo *res = NULL;
+
+       if(getaddrinfo(host, port, &hints, &res) || !res) {
+               fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, strerror(errno));
+               return 1;
+       }
+
+       fd = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP);
+       if(fd < 0) {
+               fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
+               return 1;
+       }
+
+#ifdef HAVE_MINGW
+       unsigned long arg = 0;
+
+       if(ioctlsocket(fd, FIONBIO, &arg) != 0) {
+               fprintf(stderr, "ioctlsocket failed: %s", sockstrerror(sockerrno));
+       }
+#endif
+
+       if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+               fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
+               return 1;
+       }
+
+       freeaddrinfo(res);
+
+       char line[4096];
+       char data[4096];
+       int code, version, req;
+
+       if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %s %d", &code, data, &version) != 3 || code != 0) {
+               fprintf(stderr, "Cannot read greeting from control socket: %s\n",
+                               sockstrerror(sockerrno));
+               return 1;
+       }
+
+       sendline(fd, "%d ^%s %d", ID, controlcookie, TINC_CTL_VERSION_CURRENT);
+       
+       if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &version, &pid) != 3 || code != 4 || version != TINC_CTL_VERSION_CURRENT) {
+               fprintf(stderr, "Could not fully establish control socket connection\n");
+               return 1;
+       }
+
+       if(!strcasecmp(argv[optind], "pid")) {
+               printf("%d\n", pid);
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "stop")) {
+#ifndef HAVE_MINGW
+               sendline(fd, "%d %d", CONTROL, REQ_STOP);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_STOP || result) {
+                       fprintf(stderr, "Could not stop tinc daemon\n");
+                       return 1;
+               }
+#else
+               if(!remove_service())
+                       return 1;
+#endif
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "reload")) {
+               sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RELOAD || result) {
+                       fprintf(stderr, "Could not reload tinc daemon\n");
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "restart")) {
+               sendline(fd, "%d %d", CONTROL, REQ_RESTART);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RESTART || result) {
+                       fprintf(stderr, "Could not restart tinc daemon\n");
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "retry")) {
+               sendline(fd, "%d %d", CONTROL, REQ_RETRY);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_RETRY || result) {
+                       fprintf(stderr, "Could not retry outgoing connections\n");
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "dump")) {
+               if(argc < optind + 2) {
+                       fprintf(stderr, "Not enough arguments.\n");
+                       usage(true);
+                       return 1;
+               }
+
+               bool do_graph = false;
+
+               if(!strcasecmp(argv[optind+1], "nodes"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+               else if(!strcasecmp(argv[optind+1], "edges"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+               else if(!strcasecmp(argv[optind+1], "subnets"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
+               else if(!strcasecmp(argv[optind+1], "connections"))
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
+               else if(!strcasecmp(argv[optind+1], "graph")) {
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
+                       sendline(fd, "%d %d", CONTROL, REQ_DUMP_EDGES);
+                       do_graph = true;
+                       printf("digraph {\n");
+               } else {
+                       fprintf(stderr, "Unknown dump type '%s'.\n", argv[optind+1]);
+                       usage(true);
+                       return 1;
+               }
+
+               while(recvline(fd, line, sizeof line)) {
+                       char node1[4096], node2[4096];
+                       int n = sscanf(line, "%d %d %s to %s", &code, &req, node1, node2);
+                       if(n == 2) {
+                               if(do_graph && req == REQ_DUMP_NODES)
+                                       continue;
+                               else {
+                                       if(do_graph)
+                                               printf("}\n");
+                                       return 0;
+                               }
+                       }
+                       if(n < 2)
+                               break;
+
+                       if(!do_graph)
+                               printf("%s\n", line + 5);
+                       else {
+                               if(req == REQ_DUMP_NODES)
+                                       printf(" %s [label = \"%s\"];\n", node1, node1);
+                               else
+                                       printf(" %s -> %s;\n", node1, node2);
+                       }
+               }
+
+               fprintf(stderr, "Error receiving dump\n");
+               return 1;
+       }
+
+       if(!strcasecmp(argv[optind], "purge")) {
+               sendline(fd, "%d %d", CONTROL, REQ_PURGE);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_PURGE || result) {
+                       fprintf(stderr, "Could not purge tinc daemon\n");
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "debug")) {
+               int debuglevel, origlevel;
+
+               if(argc != optind + 2) {
+                       fprintf(stderr, "Invalid arguments.\n");
+                       return 1;
+               }
+               debuglevel = atoi(argv[optind+1]);
+
+               sendline(fd, "%d %d %d", CONTROL, REQ_SET_DEBUG, debuglevel);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &origlevel) != 3 || code != CONTROL || req != REQ_SET_DEBUG) {
+                       fprintf(stderr, "Could not purge tinc daemon\n");
+                       return 1;
+               }
+
+               fprintf(stderr, "Old level %d, new level %d\n", origlevel, debuglevel);
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "connect")) {
+               if(argc != optind + 2) {
+                       fprintf(stderr, "Invalid arguments.\n");
+                       return 1;
+               }
+               char *name = argv[optind + 1];
+
+               sendline(fd, "%d %d %s", CONTROL, REQ_CONNECT, name);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_CONNECT || result) {
+                       fprintf(stderr, "Could not connect to %s\n", name);
+                       return 1;
+               }
+               return 0;
+       }
+
+       if(!strcasecmp(argv[optind], "disconnect")) {
+               if(argc != optind + 2) {
+                       fprintf(stderr, "Invalid arguments.\n");
+                       return 1;
+               }
+               char *name = argv[optind + 1];
+
+               sendline(fd, "%d %d %s", CONTROL, REQ_DISCONNECT, name);
+               if(!recvline(fd, line, sizeof line) || sscanf(line, "%d %d %d", &code, &req, &result) != 3 || code != CONTROL || req != REQ_DISCONNECT || result) {
+                       fprintf(stderr, "Could not disconnect %s\n", name);
+                       return 1;
+               }
+               return 0;
+       }
+
+#ifdef HAVE_CURSES
+       if(!strcasecmp(argv[optind], "top")) {
+               top(fd);
+               return 0;
+       }
+#endif
+
+       if(!strcasecmp(argv[optind], "pcap")) {
+               pcap(fd, stdout);
+               return 0;
+       }
+
+       fprintf(stderr, "Unknown command `%s'.\n", argv[optind]);
+       usage(true);
+       
+       close(fd);
+
+       return 0;
+}
diff --git a/src/tincctl.h b/src/tincctl.h
new file mode 100644 (file)
index 0000000..114b931
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    tincctl.h -- header for tincctl.c.
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_TINCCTL_H__
+#define __TINC_TINCCTL_H__
+
+extern bool sendline(int fd, char *format, ...);
+extern bool recvline(int fd, char *line, size_t len);
+
+#endif
+
index a1be971acf493406fb5fe60c5c0786a5a76ebf27..7970bcc9f2b67d1854df8fd0dece1ec2277ae774 100644 (file)
 #endif
 
 #include <getopt.h>
-#include "pidfile.h"
 
 #include "conf.h"
+#include "control.h"
+#include "crypto.h"
 #include "device.h"
 #include "logger.h"
 #include "net.h"
 char *program_name = NULL;
 
 /* If nonzero, display usage information and exit. */
-bool show_help = false;
+static bool show_help = false;
 
 /* If nonzero, print the version on standard output and exit.  */
-bool show_version = false;
-
-/* If nonzero, it will attempt to kill a running tincd and exit. */
-int kill_tincd = 0;
-
-/* If nonzero, generate public/private keypair for this host/net. */
-int generate_keys = 0;
+static bool show_version = false;
 
 /* If nonzero, use null ciphers and skip all key exchanges. */
 bool bypass_security = false;
 
 /* If nonzero, disable swapping for this process. */
-bool do_mlock = false;
+static bool do_mlock = false;
 
 /* If nonzero, chroot to netdir after startup. */
 static bool do_chroot = false;
@@ -93,20 +88,18 @@ static const char *switchuser = NULL;
 bool use_logfile = false;
 
 char *identname = NULL;                                /* program name for syslog */
-char *pidfilename = NULL;                      /* pid file location */
 char *logfilename = NULL;                      /* log file location */
+char *pidfilename = NULL;
 char **g_argv;                                 /* a copy of the cmdline arguments */
 
-static int status;
+static int status = 1;
 
 static struct option const long_options[] = {
        {"config", required_argument, NULL, 'c'},
-       {"kill", optional_argument, NULL, 'k'},
        {"net", required_argument, NULL, 'n'},
        {"help", no_argument, NULL, 1},
        {"version", no_argument, NULL, 2},
        {"no-detach", no_argument, NULL, 'D'},
-       {"generate-keys", optional_argument, NULL, 'K'},
        {"debug", optional_argument, NULL, 'd'},
        {"bypass-security", no_argument, NULL, 3},
        {"mlock", no_argument, NULL, 'L'},
@@ -130,20 +123,18 @@ static void usage(bool status) {
                                program_name);
        else {
                printf("Usage: %s [option]...\n\n", program_name);
-               printf("  -c, --config=DIR               Read configuration options from DIR.\n"
-                               "  -D, --no-detach                Don't fork and detach.\n"
-                               "  -d, --debug[=LEVEL]            Increase debug level or set it to LEVEL.\n"
-                               "  -k, --kill[=SIGNAL]            Attempt to kill a running tincd and exit.\n"
-                               "  -n, --net=NETNAME              Connect to net NETNAME.\n"
-                               "  -K, --generate-keys[=BITS]     Generate public/private RSA keypair.\n"
-                               "  -L, --mlock                    Lock tinc into main memory.\n"
-                               "      --logfile[=FILENAME]       Write log entries to a logfile.\n"
-                               "      --pidfile=FILENAME         Write PID to FILENAME.\n"
-                               "  -o, --option=[HOST.]KEY=VALUE  Set global/host configuration value.\n"
-                               "  -R, --chroot                   chroot to NET dir at startup.\n"
-                               "  -U, --user=USER                setuid to given USER at startup.\n"
-                               "      --help                     Display this help and exit.\n"
-                               "      --version                  Output version information and exit.\n\n");
+               printf( "  -c, --config=DIR              Read configuration options from DIR.\n"
+                               "  -D, --no-detach               Don't fork and detach.\n"
+                               "  -d, --debug[=LEVEL]           Increase debug level or set it to LEVEL.\n"
+                               "  -n, --net=NETNAME             Connect to net NETNAME.\n"
+                               "  -L, --mlock                   Lock tinc into main memory.\n"
+                               "      --logfile[=FILENAME]      Write log entries to a logfile.\n"
+                               "      --pidfile=FILENAME        Write PID and control socket cookie to FILENAME.\n"
+                               "      --bypass-security         Disables meta protocol security, for debugging.\n"
+                               "  -o, --option[HOST.]KEY=VALUE  Set global/host configuration value.\n"
+                               "  -R, --chroot                  chroot to NET dir at startup.\n"
+                               "  -U, --user=USER               setuid to given USER at startup.\n"                            "      --help                    Display this help and exit.\n"
+                               "      --version                 Output version information and exit.\n\n");
                printf("Report bugs to tinc@tinc-vpn.org.\n");
        }
 }
@@ -156,7 +147,7 @@ static bool parse_options(int argc, char **argv) {
 
        cmdline_conf = list_alloc((list_action_t)free_config);
 
-       while((r = getopt_long(argc, argv, "c:DLd::k::n:o:K::RU:", long_options, &option_index)) != EOF) {
+       while((r = getopt_long(argc, argv, "c:DLd::n:o:RU:", long_options, &option_index)) != EOF) {
                switch (r) {
                        case 0:                         /* long option */
                                break;
@@ -185,44 +176,6 @@ static bool parse_options(int argc, char **argv) {
                                        debug_level++;
                                break;
 
-                       case 'k':                               /* kill old tincds */
-#ifndef HAVE_MINGW
-                               if(optarg) {
-                                       if(!strcasecmp(optarg, "HUP"))
-                                               kill_tincd = SIGHUP;
-                                       else if(!strcasecmp(optarg, "TERM"))
-                                               kill_tincd = SIGTERM;
-                                       else if(!strcasecmp(optarg, "KILL"))
-                                               kill_tincd = SIGKILL;
-                                       else if(!strcasecmp(optarg, "USR1"))
-                                               kill_tincd = SIGUSR1;
-                                       else if(!strcasecmp(optarg, "USR2"))
-                                               kill_tincd = SIGUSR2;
-                                       else if(!strcasecmp(optarg, "WINCH"))
-                                               kill_tincd = SIGWINCH;
-                                       else if(!strcasecmp(optarg, "INT"))
-                                               kill_tincd = SIGINT;
-                                       else if(!strcasecmp(optarg, "ALRM"))
-                                               kill_tincd = SIGALRM;
-                                       else if(!strcasecmp(optarg, "ABRT"))
-                                               kill_tincd = SIGABRT;
-                                       else {
-                                               kill_tincd = atoi(optarg);
-
-                                               if(!kill_tincd) {
-                                                       fprintf(stderr, "Invalid argument `%s'; SIGNAL must be a number or one of HUP, TERM, KILL, USR1, USR2, WINCH, INT or ALRM.\n",
-                                                                       optarg);
-                                                       usage(true);
-                                                       return false;
-                                               }
-                                       }
-                               } else
-                                       kill_tincd = SIGTERM;
-#else
-                                       kill_tincd = 1;
-#endif
-                               break;
-
                        case 'n':                               /* net name given */
                                /* netname "." is special: a "top-level name" */
                                netname = strcmp(optarg, ".") != 0 ?
@@ -236,22 +189,6 @@ static bool parse_options(int argc, char **argv) {
                                list_insert_tail(cmdline_conf, cfg);
                                break;
 
-                       case 'K':                               /* generate public/private keypair */
-                               if(optarg) {
-                                       generate_keys = atoi(optarg);
-
-                                       if(generate_keys < 512) {
-                                               fprintf(stderr, "Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n",
-                                                               optarg);
-                                               usage(true);
-                                               return false;
-                                       }
-
-                                       generate_keys &= ~7;    /* Round it to bytes */
-                               } else
-                                       generate_keys = 2048;
-                               break;
-
                        case 'R':                               /* chroot to NETNAME dir */
                                do_chroot = true;
                                break;
@@ -278,7 +215,7 @@ static bool parse_options(int argc, char **argv) {
                                        logfilename = xstrdup(optarg);
                                break;
 
-                       case 5:                                 /* write PID to a file */
+                       case 5:                                 /* open control socket here */
                                pidfilename = xstrdup(optarg);
                                break;
 
@@ -294,110 +231,6 @@ static bool parse_options(int argc, char **argv) {
        return true;
 }
 
-/* This function prettyprints the key generation process */
-
-static void indicator(int a, int b, void *p) {
-       switch (a) {
-               case 0:
-                       fprintf(stderr, ".");
-                       break;
-
-               case 1:
-                       fprintf(stderr, "+");
-                       break;
-
-               case 2:
-                       fprintf(stderr, "-");
-                       break;
-
-               case 3:
-                       switch (b) {
-                               case 0:
-                                       fprintf(stderr, " p\n");
-                                       break;
-
-                               case 1:
-                                       fprintf(stderr, " q\n");
-                                       break;
-
-                               default:
-                                       fprintf(stderr, "?");
-                       }
-                       break;
-
-               default:
-                       fprintf(stderr, "?");
-       }
-}
-
-/*
-  Generate a public/private RSA keypair, and ask for a file to store
-  them in.
-*/
-static bool keygen(int bits) {
-       RSA *rsa_key;
-       FILE *f;
-       char *name = NULL;
-       char *filename;
-
-       get_config_string(lookup_config(config_tree, "Name"), &name);
-
-       if(name && !check_id(name)) {
-               fprintf(stderr, "Invalid name for myself!\n");
-               return false;
-       }
-
-       fprintf(stderr, "Generating %d bits keys:\n", bits);
-       rsa_key = RSA_generate_key(bits, 0x10001, indicator, NULL);
-
-       if(!rsa_key) {
-               fprintf(stderr, "Error during key generation!\n");
-               return false;
-       } else
-               fprintf(stderr, "Done.\n");
-
-       xasprintf(&filename, "%s/rsa_key.priv", confbase);
-       f = ask_and_open(filename, "private RSA key");
-
-       if(!f)
-               return false;
-
-       if(disable_old_keys(f))
-               fprintf(stderr, "Warning: old key(s) found and disabled.\n");
-  
-#ifdef HAVE_FCHMOD
-       /* Make it unreadable for others. */
-       fchmod(fileno(f), 0600);
-#endif
-               
-       fputc('\n', f);
-       PEM_write_RSAPrivateKey(f, rsa_key, NULL, NULL, 0, NULL, NULL);
-       fclose(f);
-       free(filename);
-
-       if(name)
-               xasprintf(&filename, "%s/hosts/%s", confbase, name);
-       else
-               xasprintf(&filename, "%s/rsa_key.pub", confbase);
-
-       f = ask_and_open(filename, "public RSA key");
-
-       if(!f)
-               return false;
-
-       if(disable_old_keys(f))
-               fprintf(stderr, "Warning: old key(s) found and disabled.\n");
-
-       fputc('\n', f);
-       PEM_write_RSAPublicKey(f, rsa_key);
-       fclose(f);
-       free(filename);
-       if(name)
-               free(name);
-
-       return true;
-}
-
 /*
   Set all files and paths according to netname
 */
@@ -405,7 +238,7 @@ static void make_names(void) {
 #ifdef HAVE_MINGW
        HKEY key;
        char installdir[1024] = "";
-       long len = sizeof(installdir);
+       long len = sizeof installdir;
 #endif
 
        if(netname)
@@ -424,6 +257,8 @@ static void make_names(void) {
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
+                       if(!pidfilename)
+                               xasprintf(&pidfilename, "%s/pid", confbase);
                }
                RegCloseKey(key);
                if(*installdir)
@@ -431,12 +266,12 @@ static void make_names(void) {
        }
 #endif
 
-       if(!pidfilename)
-               xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
-
        if(!logfilename)
                xasprintf(&logfilename, LOCALSTATEDIR "/log/%s.log", identname);
 
+       if(!pidfilename)
+               xasprintf(&pidfilename, LOCALSTATEDIR "/run/%s.pid", identname);
+
        if(netname) {
                if(!confbase)
                        xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
@@ -448,7 +283,7 @@ static void make_names(void) {
        }
 }
 
-static void free_names() {
+static void free_names(void) {
        if (identname) free(identname);
        if (netname) free(netname);
        if (pidfilename) free(pidfilename);
@@ -456,7 +291,7 @@ static void free_names() {
        if (confbase) free(confbase);
 }
 
-static bool drop_privs() {
+static bool drop_privs(void) {
 #ifdef HAVE_MINGW
        if (switchuser) {
                logger(LOG_ERR, "%s not supported on this platform", "-U");
@@ -522,8 +357,8 @@ int main(int argc, char **argv) {
        make_names();
 
        if(show_version) {
-               printf("%s version %s (built %s %s, protocol %d)\n", PACKAGE,
-                          VERSION, __DATE__, __TIME__, PROT_CURRENT);
+               printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
+                          VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
                printf("Copyright (C) 1998-2011 Ivo Timmermans, Guus Sliepen and others.\n"
                                "See the AUTHORS file for a complete list.\n\n"
                                "tinc comes with ABSOLUTELY NO WARRANTY.  This is free software,\n"
@@ -538,28 +373,28 @@ int main(int argc, char **argv) {
                return 0;
        }
 
-       if(kill_tincd)
-               return !kill_other(kill_tincd);
+#ifdef HAVE_MINGW
+       if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
+               logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
+               return 1;
+       }
+#endif
 
        openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
 
+       if(!event_init()) {
+               logger(LOG_ERR, "Error initializing libevent!");
+               return 1;
+       }
+
        g_argv = argv;
 
        init_configuration(&config_tree);
 
        /* Slllluuuuuuurrrrp! */
 
-       RAND_load_file("/dev/urandom", 1024);
-
-       ENGINE_load_builtin_engines();
-       ENGINE_register_all_complete();
-
-       OpenSSL_add_all_algorithms();
-
-       if(generate_keys) {
-               read_server_config();
-               return !keygen(generate_keys);
-       }
+       srand(time(NULL));
+       crypto_init();
 
        if(!read_server_config())
                return 1;
@@ -572,11 +407,6 @@ int main(int argc, char **argv) {
 #endif
 
 #ifdef HAVE_MINGW
-       if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
-               logger(LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
-               return 1;
-       }
-
        if(!do_detach || !init_service())
                return main2(argc, argv);
        else
@@ -606,7 +436,10 @@ int main2(int argc, char **argv) {
        /* Setup sockets and open device. */
 
        if(!setup_network())
-               goto end;
+               goto end_nonet;
+
+       if(!init_control())
+               goto end_nonet;
 
        /* Initiate all outgoing connections. */
 
@@ -655,22 +488,17 @@ int main2(int argc, char **argv) {
        close_network_connections();
 
 end:
-       logger(LOG_NOTICE, "Terminating");
+       exit_control();
 
-#ifndef HAVE_MINGW
-       remove_pid(pidfilename);
-#endif
+end_nonet:
+       logger(LOG_NOTICE, "Terminating");
 
        free(priority);
 
-       EVP_cleanup();
-       ENGINE_cleanup();
-       CRYPTO_cleanup_all_ex_data();
-       ERR_remove_state(0);
-       ERR_free_strings();
+       crypto_exit();
 
        exit_configuration(&config_tree);
-       list_free(cmdline_conf);
+       free(cmdline_conf);
        free_names();
 
        return status;
diff --git a/src/top.c b/src/top.c
new file mode 100644 (file)
index 0000000..f14395e
--- /dev/null
+++ b/src/top.c
@@ -0,0 +1,319 @@
+/*
+    top.c -- Show real-time statistics from a running tincd
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#ifdef HAVE_CURSES
+
+#include <curses.h>
+
+#include "control_common.h"
+#include "list.h"
+#include "tincctl.h"
+#include "top.h"
+#include "xalloc.h"
+
+typedef struct nodestats_t {
+       char *name;
+       int i;
+       uint64_t in_packets;
+       uint64_t in_bytes;
+       uint64_t out_packets;
+       uint64_t out_bytes;
+       float in_packets_rate;
+       float in_bytes_rate;
+       float out_packets_rate;
+       float out_bytes_rate;
+       bool known;
+} nodestats_t;
+
+static const char *const sortname[] = {
+       "name",
+       "in pkts",
+       "in bytes",
+       "out pkts",
+       "out bytes",
+       "tot pkts",
+       "tot bytes",
+};
+
+static int sortmode = 0;
+static bool cumulative = false;
+
+static list_t node_list;
+static struct timeval now, prev, diff;
+static int delay = 1000;
+static bool running = true;
+static bool changed = true;
+static const char *unit = "bytes";
+static float scale = 1;
+
+#ifndef timersub
+#define timersub(a, b, c) do {(c)->tv_sec = (a)->tv_sec - (b)->tv_sec; (c)->tv_usec = (a)->tv_usec = (b)->tv_usec;} while(0)
+#endif
+
+static void update(int fd) {
+       sendline(fd, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
+       gettimeofday(&now, NULL);
+
+       timersub(&now, &prev, &diff);
+       prev = now;
+       float interval = diff.tv_sec + diff.tv_usec * 1e-6;
+
+       char line[4096];
+       char name[4096];
+       int code;
+       int req;
+       uint64_t in_packets;
+       uint64_t in_bytes;
+       uint64_t out_packets;
+       uint64_t out_bytes;
+
+       for(list_node_t *i = node_list.head; i; i = i->next) {
+               nodestats_t *node = i->data;
+               node->known = false;
+       }
+
+       while(recvline(fd, line, sizeof line)) {
+               int n = sscanf(line, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, &code, &req, name, &in_packets, &in_bytes, &out_packets, &out_bytes);
+
+               if(n == 2)
+                       break;
+
+               if(n != 7) {
+                       endwin();
+                       fprintf(stderr, "Error receiving traffic information\n");
+                       exit(1);
+               }
+
+               nodestats_t *found = NULL;
+
+               for(list_node_t *i = node_list.head; i; i = i->next) {
+                       nodestats_t *node = i->data;
+                       int result = strcmp(name, node->name);
+                       if(result > 0) {
+                               continue;
+                       } if(result == 0) {
+                               found = node;
+                               break;
+                       } else {
+                               found = xmalloc_and_zero(sizeof *found);
+                               found->name = xstrdup(name);
+                               list_insert_before(&node_list, i, found);
+                               changed = true;
+                               break;
+                       }
+               }
+
+               if(!found) {
+                       found = xmalloc_and_zero(sizeof *found);
+                       found->name = xstrdup(name);
+                       list_insert_tail(&node_list, found);
+                       changed = true;
+               }
+
+               found->known = true;
+               found->in_packets_rate = (in_packets - found->in_packets) / interval;
+               found->in_bytes_rate = (in_bytes - found->in_bytes) / interval;
+               found->out_packets_rate = (out_packets - found->out_packets) / interval;
+               found->out_bytes_rate = (out_bytes - found->out_bytes) / interval;
+               found->in_packets = in_packets;
+               found->in_bytes = in_bytes;
+               found->out_packets = out_packets;
+               found->out_bytes = out_bytes;
+       }
+}
+
+static void redraw(void) {
+       erase();
+
+       mvprintw(0, 0, "Tinc %-16s  Nodes: %4d  Sort: %-10s  %s", netname ?: "", node_list.count, sortname[sortmode], cumulative ? "Cumulative" : "Current");
+       attrset(A_REVERSE);
+       mvprintw(2, 0, "Node                IN pkts   IN %s   OUT pkts  OUT %s", unit, unit);
+       chgat(-1, A_REVERSE, 0, NULL);
+
+       static nodestats_t **sorted = 0;
+       static int n = 0;
+       if(changed) {
+               n = 0;
+               sorted = xrealloc(sorted, node_list.count * sizeof *sorted);
+               for(list_node_t *i = node_list.head; i; i = i->next)
+                       sorted[n++] = i->data;
+               changed = false;
+       }
+
+       for(int i = 0; i < n; i++)
+               sorted[i]->i = i;
+       
+       int cmpfloat(float a, float b) {
+               if(a < b)
+                       return -1;
+               else if(a > b)
+                       return 1;
+               else
+                       return 0;
+       }
+
+       int cmpu64(uint64_t a, uint64_t b) {
+               if(a < b)
+                       return -1;
+               else if(a > b)
+                       return 1;
+               else
+                       return 0;
+       }
+
+       int sortfunc(const void *a, const void *b) {
+               const nodestats_t *na = *(const nodestats_t **)a;
+               const nodestats_t *nb = *(const nodestats_t **)b;
+               switch(sortmode) {
+                       case 1:
+                               if(cumulative)
+                                       return -cmpu64(na->in_packets, nb->in_packets) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->in_packets_rate, nb->in_packets_rate) ?: na->i - nb->i;
+                       case 2:
+                               if(cumulative)
+                                       return -cmpu64(na->in_bytes, nb->in_bytes) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->in_bytes_rate, nb->in_bytes_rate) ?: na->i - nb->i;
+                       case 3:
+                               if(cumulative)
+                                       return -cmpu64(na->out_packets, nb->out_packets) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->out_packets_rate, nb->out_packets_rate) ?: na->i - nb->i;
+                       case 4:
+                               if(cumulative)
+                                       return -cmpu64(na->out_bytes, nb->out_bytes) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->out_bytes_rate, nb->out_bytes_rate) ?: na->i - nb->i;
+                       case 5:
+                               if(cumulative)
+                                       return -cmpu64(na->in_packets + na->out_packets, nb->in_packets + nb->out_packets) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->in_packets_rate + na->out_packets_rate, nb->in_packets_rate + nb->out_packets_rate) ?: na->i - nb->i;
+                       case 6:
+                               if(cumulative)
+                                       return -cmpu64(na->in_bytes + na->out_bytes, nb->in_bytes + nb->out_bytes) ?: na->i - nb->i;
+                               else
+                                       return -cmpfloat(na->in_bytes_rate + na->out_bytes_rate, nb->in_bytes_rate + nb->out_bytes_rate) ?: na->i - nb->i;
+                       default:
+                               return strcmp(na->name, nb->name) ?: na->i - nb->i;
+               }
+       }
+
+       qsort(sorted, n, sizeof *sorted, sortfunc);
+
+       for(int i = 0, row = 3; i < n; i++, row++) {
+               nodestats_t *node = sorted[i];
+               if(node->known)
+                       if(node->in_packets_rate || node->out_packets_rate)
+                               attrset(A_BOLD);
+                       else
+                               attrset(A_NORMAL);
+               else
+                       attrset(A_DIM);
+
+               if(cumulative)
+                       mvprintw(row, 0, "%-16s %10"PRIu64" %10.0f %10"PRIu64" %10.0f",
+                                       node->name, node->in_packets, node->in_bytes * scale, node->out_packets, node->out_bytes * scale);
+               else
+                       mvprintw(row, 0, "%-16s %10.0f %10.0f %10.0f %10.0f",
+                                       node->name, node->in_packets_rate, node->in_bytes_rate * scale, node->out_packets_rate, node->out_bytes_rate * scale);
+       }
+
+       attrset(A_NORMAL);
+       move(1, 0);
+
+       refresh();
+}
+
+void top(int fd) {
+       initscr();
+       timeout(delay);
+
+       while(running) {
+               update(fd);
+               redraw();
+
+               switch(getch()) {
+                       case 's': {
+                               timeout(-1);
+                               float input = delay * 1e-3;
+                               mvprintw(1, 0, "Change delay from %.1fs to: ", input);
+                               scanw("%f", &input);
+                               if(input < 0.1)
+                                       input = 0.1;
+                               delay = input * 1e3;
+                               timeout(delay);
+                               break;
+                       }
+                       case 'c':
+                                 cumulative = !cumulative;
+                                 break;
+                       case 'n':
+                                 sortmode = 0;
+                                 break;
+                       case 'i':
+                                 sortmode = 2;
+                                 break;
+                       case 'I':
+                                 sortmode = 1;
+                                 break;
+                       case 'o':
+                                 sortmode = 4;
+                                 break;
+                       case 'O':
+                                 sortmode = 3;
+                                 break;
+                       case 't':
+                                 sortmode = 6;
+                                 break;
+                       case 'T':
+                                 sortmode = 5;
+                                 break;
+                       case 'b':
+                                 unit = "bytes";
+                                 scale = 1;
+                                 break;
+                       case 'k':
+                                 unit = "kbyte";
+                                 scale = 1e-3;
+                                 break;
+                       case 'M':
+                                 unit = "Mbyte";
+                                 scale = 1e-6;
+                                 break;
+                       case 'G':
+                                 unit = "Gbyte";
+                                 scale = 1e-9;
+                                 break;
+                       case 'q':
+                       case KEY_BREAK:
+                               running = false;
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       endwin();
+}
+
+#endif
diff --git a/src/top.h b/src/top.h
new file mode 100644 (file)
index 0000000..b3a264d
--- /dev/null
+++ b/src/top.h
@@ -0,0 +1,26 @@
+/*
+    top.h -- header for top.c.
+    Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_TOP_H__
+#define __TINC_TOP_H__
+
+extern void top(int fd);
+
+#endif
+
index a0b87f9320a10ab36cd9e7651dd54c8935251c5e..f0a9869b888667c2b785e552a43fde33c793db6d 100644 (file)
@@ -180,7 +180,7 @@ void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin;
+       int inlen;
 
        switch(state) {
                case 0: {
@@ -212,7 +212,7 @@ static bool read_packet(vpn_packet_t *packet) {
                }
 
                case 1: {
-                       if((lenin = read(request_fd, &request, sizeof request)) != sizeof request) {
+                       if((inlen = read(request_fd, &request, sizeof request)) != sizeof request) {
                                logger(LOG_ERR, "Error while reading request from %s %s: %s", device_info,
                                           device, strerror(errno));
                                running = false;
@@ -242,14 +242,14 @@ static bool read_packet(vpn_packet_t *packet) {
                }
 
                case 2: {
-                       if((lenin = read(data_fd, packet->data, MTU)) <= 0) {
+                       if((inlen = read(data_fd, packet->data, MTU)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                running = false;
                                return false;
                        }
 
-                       packet->len = lenin;
+                       packet->len = inlen;
 
                        device_total_in += packet->len;
 
diff --git a/src/utils.c b/src/utils.c
new file mode 100644 (file)
index 0000000..cf46221
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+    utils.c -- gathering of some stupid small functions
+    Copyright (C) 1999-2005 Ivo Timmermans
+                  2000-2009 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "system.h"
+
+#include "../src/logger.h"
+#include "utils.h"
+
+static const char hexadecimals[] = "0123456789ABCDEF";
+static const char base64imals[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int charhex2bin(char c) {
+       if(isdigit(c))
+               return c - '0';
+       else
+               return toupper(c) - 'A' + 10;
+}
+
+static int charb64decode(char c) {
+       if(c >= 'a')
+               return c - 'a' + 26;
+       else if(c >= 'A')
+               return c - 'A';
+       else if(c >= '0') 
+               return c - '0' + 52;
+       else if(c == '+')
+               return 62;
+       else
+               return 63;
+}
+
+int hex2bin(const char *src, char *dst, int length) {
+       int i;
+       for(i = 0; i < length && src[i * 2] && src[i * 2 + 1]; i++)
+               dst[i] = charhex2bin(src[i * 2]) * 16 + charhex2bin(src[i * 2 + 1]);
+       return i;
+}
+
+int bin2hex(const char *src, char *dst, int length) {
+       int i;
+       for(i = length - 1; i >= 0; i--) {
+               dst[i * 2 + 1] = hexadecimals[(unsigned char) src[i] & 15];
+               dst[i * 2] = hexadecimals[(unsigned char) src[i] >> 4];
+       }
+       dst[length * 2] = 0;
+       return length * 2;
+}
+
+int b64decode(const char *src, char *dst, int length) {
+       int i;
+       uint32_t triplet = 0;
+       unsigned char *udst = (unsigned char *)dst;
+
+       for(i = 0; i < length / 3 * 4 && src[i]; i++) {
+               triplet |= charb64decode(src[i]) << (6 * (i & 3));
+               if((i & 3) == 3) {
+                       udst[0] = triplet & 0xff; triplet >>= 8;
+                       udst[1] = triplet & 0xff; triplet >>= 8;
+                       udst[2] = triplet;
+                       triplet = 0;
+                       udst += 3;
+               }
+       }
+       if((i & 3) == 3) {
+               udst[0] = triplet & 0xff; triplet >>= 8;
+               udst[1] = triplet & 0xff;
+               return i / 4 * 3 + 2;
+       } else if((i & 3) == 2) {
+               udst[0] = triplet & 0xff;
+               return i / 4 * 3 + 1;
+       } else {
+               return i / 4 * 3;
+       }
+}
+
+int b64encode(const char *src, char *dst, int length) {
+       uint32_t triplet;
+       const unsigned char *usrc = (unsigned char *)src;
+       int si = length / 3 * 3;
+       int di = length / 3 * 4;
+
+       switch(length % 3) {
+               case 2: 
+                       triplet = usrc[si] | usrc[si + 1] << 8;
+                       dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+                       dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
+                       dst[di + 2] = base64imals[triplet];
+                       dst[di + 3] = 0;
+                       length = di + 2;
+                       break;
+               case 1:
+                       triplet = usrc[si];
+                       dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+                       dst[di + 1] = base64imals[triplet];
+                       dst[di + 2] = 0;
+                       length = di + 1;
+                       break;
+               default:
+                       dst[di] = 0;
+                       length = di;
+                       break;
+       }
+
+       while(si > 0) {
+               di -= 4;
+               si -= 3;
+               triplet = usrc[si] | usrc[si + 1] << 8 | usrc[si + 2] << 16;
+               dst[di] = base64imals[triplet & 63]; triplet >>= 6;
+               dst[di + 1] = base64imals[triplet & 63]; triplet >>= 6;
+               dst[di + 2] = base64imals[triplet & 63]; triplet >>= 6;
+               dst[di + 3] = base64imals[triplet];
+       }
+
+       return length;
+}
+
+#if defined(HAVE_MINGW) || defined(HAVE_CYGWIN)
+#ifdef HAVE_CYGWIN
+#include <w32api/windows.h>
+#endif
+
+const char *winerror(int err) {
+       static char buf[1024], *newline;
+
+       if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+               NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL)) {
+               strncpy(buf, "(unable to format errormessage)", sizeof(buf));
+       };
+
+       if((newline = strchr(buf, '\r')))
+               *newline = '\0';
+
+       return buf;
+}
+#endif
+
+unsigned int bitfield_to_int(const void *bitfield, size_t size) {
+       unsigned int value = 0;
+       if(size > sizeof value)
+               size = sizeof value;
+       memcpy(&value, bitfield, size);
+       return value;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644 (file)
index 0000000..67c94f3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    utils.h -- header file for utils.c
+    Copyright (C) 1999-2005 Ivo Timmermans
+                  2000-2009 Guus Sliepen <guus@tinc-vpn.org>
+
+    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 of the License, 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __TINC_UTILS_H__
+#define __TINC_UTILS_H__
+
+extern int hex2bin(const char *src, char *dst, int length);
+extern int bin2hex(const char *src, char *dst, int length);
+
+extern int b64encode(const char *src, char *dst, int length);
+extern int b64decode(const char *src, char *dst, int length);
+
+#ifdef HAVE_MINGW
+extern const char *winerror(int);
+#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
+#define sockerrno WSAGetLastError()
+#define sockstrerror(x) winerror(x)
+#define sockwouldblock(x) ((x) == WSAEWOULDBLOCK || (x) == WSAEINTR)
+#define sockmsgsize(x) ((x) == WSAEMSGSIZE)
+#define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
+#define sockinuse(x) ((x) == WSAEADDRINUSE)
+#else
+#define sockerrno errno
+#define sockstrerror(x) strerror(x)
+#define sockwouldblock(x) ((x) == EWOULDBLOCK || (x) == EINTR)
+#define sockmsgsize(x) ((x) == EMSGSIZE)
+#define sockinprogress(x) ((x) == EINPROGRESS)
+#define sockinuse(x) ((x) == EADDRINUSE)
+#endif
+
+extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
+
+#endif                                                 /* __TINC_UTILS_H__ */
diff --git a/src/xalloc.h b/src/xalloc.h
new file mode 100644 (file)
index 0000000..42d0d95
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+   xalloc.h -- malloc and related fuctions with out of memory checking
+   Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+   Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
+
+   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., Foundation,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  */
+
+#ifndef __TINC_XALLOC_H__
+#define __TINC_XALLOC_H__
+
+static inline void *xmalloc(size_t n) {
+       void *p = malloc(n);
+       if(!p)
+               abort();
+       return p;
+}
+
+static inline void *xmalloc_and_zero(size_t n) {
+       void *p = calloc(1, n);
+       if(!p)
+               abort();
+       return p;
+}
+
+static inline void *xrealloc(void *p, size_t n) {
+       p = realloc(p, n);
+       if(!p)
+               abort();
+       return p;
+}
+
+static inline char *xstrdup(const char *s) {
+       char *p = strdup(s);
+       if(!p)
+               abort();
+       return p;
+}
+
+static inline int xvasprintf(char **strp, const char *fmt, va_list ap) {
+       int result = vasprintf(strp, fmt, ap);
+       if(result < 0)
+               abort();
+       return result;
+}
+
+static inline int xasprintf(char **strp, const char *fmt, ...) {
+       va_list ap;
+       va_start(ap, fmt);
+       int result = xvasprintf(strp, fmt, ap);
+       va_end(ap);
+       return result;
+}
+
+#endif
index dfb4c6d7c69ad49fd83765caecc4880c8a142ae0..5dc1dafcf006502ef3a5d926785803d056559be6 100644 (file)
--- a/system.h
+++ b/system.h
@@ -37,7 +37,7 @@ typedef int bool;
 
 /* Other functions */
 
-#include "dropin.h"
+#include "src/dropin.h"
 
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;