Merge remote-tracking branch 'seehuhn/1.1' into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Sat, 14 Mar 2015 11:45:55 +0000 (11:45 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Sat, 14 Mar 2015 11:45:55 +0000 (11:45 +0000)
88 files changed:
NEWS
README
THANKS
bash_completion.d/tinc
configure.ac
doc/tinc.8.in
doc/tinc.conf.5.in
doc/tinc.texi
gui/tinc-gui
m4/openssl.m4
src/Makefile.am
src/bsd/device.c
src/cipher.h
src/connection.c
src/connection.h
src/control.c
src/cygwin/device.c
src/device.h
src/digest.h
src/ed25519/ecdsa.c
src/ed25519/seed.c [deleted file]
src/ed25519/sha512.c
src/ed25519/sha512.h
src/edge.c
src/edge.h
src/event.c
src/event.h
src/fsck.c [new file with mode: 0644]
src/fsck.h [new file with mode: 0644]
src/graph.c
src/hash.c
src/hash.h
src/have.h
src/info.c
src/invitation.c
src/linux/device.c
src/logger.h
src/meta.c
src/meta.h
src/mingw/device.c
src/multicast_device.c
src/names.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/nolegacy/crypto.c [new file with mode: 0644]
src/nolegacy/prf.c [new file with mode: 0644]
src/openssl/cipher.c
src/openssl/crypto.c
src/openssl/ecdh.c [deleted file]
src/openssl/ecdsa.c [deleted file]
src/openssl/ecdsagen.c [deleted file]
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/raw_socket_device.c
src/route.c
src/script.c
src/solaris/device.c
src/sptps.c
src/sptps.h
src/sptps_keypair.c
src/sptps_speed.c
src/sptps_test.c
src/subnet.c
src/subnet_parse.c
src/tincctl.c
src/tincctl.h
src/tincd.c
src/top.c
src/uml_device.c
src/utils.c
src/utils.h
src/vde_device.c
src/version.c [new file with mode: 0644]
src/version.h [new file with mode: 0644]
test/commandline.test
test/variables.test

diff --git a/NEWS b/NEWS
index ea169c3..abd6a6f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,49 @@
+Version 1.1pre11             December 27 2014
+
+ * Added a "network" command to list or switch networks.
+
+ * Switched to Ed25519 keys and the ChaCha-Poly1305 cipher for the new protocol.
+
+ * AutoConnect is now a boolean option, when enabled tinc always tries to keep
+   at least three meta-connections open.
+
+ * The new protocol now uses UDP much more often.
+
+ * Tinc "del" and "get" commands now return a non-zero exit code when they
+   don't find the requested variable.
+
+ * Updated documentation.
+
+ * Added a "DeviceStandby" option to defer running tinc-up until a working
+   connection is made, and which on Windows will also change the network
+   interface link status accordingly.
+
+ * Tinc now tells the resolver to reload /etc/resolv.conf when it receives
+   SIGALRM.
+
+ * Improved error messages and event loop handling on Windows.
+
+ * LocalDiscovery now uses local address learned from other nodes, and is
+   enabled by default.
+
+ * Added a "BroadcastSubnet" option to change the behavior of broadcast packets
+   in router mode.
+
+ * Added support for dotted quad notation in IPv6 (e.g. ::1.2.3.4).
+
+ * Improved format of printed Subnets, MAC and IPv6 addresses.
+
+ * Added a "--batch" option to force the tinc CLI to run in non-interactive
+   mode.
+
+ * Improve default Device selection on *BSD and Mac OS X.
+
+ * Allow running tinc without RSA keys.
+
+Thanks to Etienne Dechamps, Sven-Haegar Koch, William A. Kennington III,
+Baptiste Jonglez, Alexis Hildebrandt, Armin Fisslthaler, Franz Pletz, Alexander
+Ried and Saverio Proto for their contributions to this version of tinc.
+
 Version 1.1pre10             February 7 2014
 
  * Added a benchmark tool (sptps_speed) for the new protocol.
diff --git a/README b/README
index 03b74b1..c88027b 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-This is the README file for tinc version 1.1pre10. Installation
+This is the README file for tinc version 1.1pre11. Installation
 instructions may be found in the INSTALL file.
 
 tinc is Copyright (C) 1998-2014 by:
@@ -36,11 +36,11 @@ at your own risk.
 Compatibility
 -------------
 
-Version 1.1pre10 is compatible with 1.0pre8, 1.0 and later, but not with older
+Version 1.1pre11 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.1pre10 itself, but not with any other 1.1preX version.
+1.0.X and 1.1pre11 itself, but not with any other 1.1preX version.
 
 
 Requirements
diff --git a/THANKS b/THANKS
index 37431e5..d1ee6b4 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -1,14 +1,20 @@
 We would like to thank the following people for their contributions to tinc:
 
 * Alexander Reil and Gemeinde Berg
+* Alexander Ried
+* Alexis Hildebrandt
 * Allesandro Gatti
 * Andreas van Cranenburgh
 * Anthony G. Basile
 * Armijn Hemel
+* Armin Fisslthaler
+* Baptiste Jonglez
+* Borg
 * Brandon Black
 * Cheng LI
 * Cris van Pelt
 * Darius Jahandarie
+* David Pflug
 * Delf Eldkraft
 * Dennis Joachimsthaler
 * dnk
@@ -17,6 +23,7 @@ We would like to thank the following people for their contributions to tinc:
 * Etienne Dechamps
 * Florent Clairambault
 * Flynn Marquardt
+* Franz Pletz
 * Gary Kessler and Claudia Gonzalez
 * Grzegorz Dymarek
 * Hans Bayle
@@ -25,11 +32,14 @@ We would like to thank the following people for their contributions to tinc:
 * James MacLean
 * Jamie Briggs
 * Jason Harper
+* Jason Livesay
 * Jelle de Jong
 * Jeroen Ubbink
 * Jerome Etienne
+* Jochen Voss
 * Julien Muchembled
 * Lavrans Laading
+* Loïc Dachary
 * Loïc Grenié
 * Lubomír Bulej
 * Mads Kiilerich
@@ -52,13 +62,21 @@ We would like to thank the following people for their contributions to tinc:
 * Philipp Babel
 * Robert van der Meulen
 * Rumko
+* Saverio Proto
 * Scott Lamb
+* Steffan Karger
 * Sven-Haegar Koch
 * Teemu Kiviniemi
+* Thomas Tsiakalakis
 * Timothy Redaelli
+* Tomislav ÄŒohar
+* Tommy Arnkværn
 * Tonnerre Lombard
 * Vil Brekin
+* Vittorio Gambaletta
 * Wessel Dankers
+* William A. Kennington III
+* William McArthur
 * Wouter van Heyst
 
 And everyone we forgot (if we did, please let us know). Thank you!
index c79e4ee..01629af 100644 (file)
@@ -4,7 +4,7 @@ _tinc() {
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
        opts="-c -d -D -K -n -o -L -R -U --config --no-detach --debug --net --option --mlock --logfile --pidfile --chroot --user --help --version"
-       confvars="Address AddressFamily BindToAddress BindToInterface Broadcast Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceType Digest DirectOnly Ed25519PrivateKeyFile Ed25519PublicKey Ed25519PublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire ListenAddress LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
+       confvars="Address AddressFamily BindToAddress BindToInterface Broadcast BroadcastSubnet Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceStandby DeviceType Digest DirectOnly Ed25519PrivateKeyFile Ed25519PublicKey Ed25519PublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire ListenAddress LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPDiscovery UDPDiscoveryKeepaliveInterval UDPDiscoveryInterval UDPDiscoveryTimeout UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
        commands="add connect debug del disconnect dump edit export export-all generate-ed25519-keys generate-keys generate-rsa-keys get help import info init invite join log network pcap pid purge reload restart retry set start stop top version"
 
        case ${prev} in
index 5efb412..9a9bf01 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.61)
-AC_INIT([tinc], [1.1pre10])
+AC_INIT([tinc], [1.1pre11])
 AC_CONFIG_SRCDIR([src/tincd.c])
 AC_GNU_SOURCE
 AM_INIT_AUTOMAKE([check-news std-options subdir-objects -Wall])
@@ -137,7 +137,7 @@ dnl No -fstack-protector-all because it doesn't work on all platforms or archite
 
 AC_ARG_ENABLE([hardening], AS_HELP_STRING([--disable-hardening], [disable compiler and linker hardening flags]))
 AS_IF([test "x$enable_hardening" != "xno"],
-  [AX_CHECK_COMPILE_FLAG([-DFORTIFY_SOURCE=2], [CPPFLAGS="$CPPFLAGS -DFORITFY_SOURCE=2"])
+  [AX_CHECK_COMPILE_FLAG([-DFORTIFY_SOURCE=2], [CPPFLAGS="$CPPFLAGS -DFORTIFY_SOURCE=2"])
    AX_CHECK_COMPILE_FLAG([-fno-strict-overflow], [CPPFLAGS="$CPPFLAGS -fno-strict-overflow"])
    AX_CHECK_COMPILE_FLAG([-fwrapv], [CPPFLAGS="$CPPFLAGS -fwrapv"])
    case $host_os in
@@ -204,30 +204,40 @@ AC_CHECK_DECLS([freeaddrinfo, gai_strerror, getaddrinfo, getnameinfo],
   [], [], [#include "src/have.h"]
 )
 
-AC_CHECK_DECLS([res_init], [LIBS="$LIBS -lresolv"], [], [
+AC_CHECK_DECLS([res_init], [AC_CHECK_LIB(resolv, res_init)], [], [
   #include <netinet/in.h>
   #include <resolv.h>
 ])
 
 AC_CACHE_SAVE
 
+AC_ARG_ENABLE(legacy-protocol,
+  AS_HELP_STRING([--disable-legacy-protocol], [disable support for the legacy (tinc 1.0) protocol]),
+  [ AS_IF([test "x$enable_legacy_protocol" = "xno"],
+    [ AC_DEFINE(DISABLE_LEGACY, 1, [Disable support for the legacy (tinc 1.0) protocol]) ])
+  ]
+)
+
 dnl These are defined in files in m4/
 
 dnl AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt], [enable use of libgcrypt instead of OpenSSL])], [])
+dnl AC_ARG_WITH(openssl, AC_HELP_STRING([--without-openssl], [disable support for OpenSSL])], [])
 
 tinc_CURSES
 tinc_READLINE
 tinc_ZLIB
 tinc_LZO
 
-if test -n "$with_libgcrypt"; then
-       gcrypt=true
-       tinc_LIBGCRYPT
-else
-       openssl=true
-       tinc_OPENSSL
+if test "x$enable_legacy_protocol" != "xno"; then
+       if test -n "$with_libgcrypt"; then
+               gcrypt=true
+               tinc_LIBGCRYPT
+       else
+               openssl=true
+               tinc_OPENSSL
+       fi
 fi
-       
+
 AM_CONDITIONAL(OPENSSL, test -n "$openssl")
 AM_CONDITIONAL(GCRYPT, test -n "$gcrypt")
 
index bb56386..b9bd2d4 100644 (file)
@@ -11,6 +11,7 @@
 .Op Fl -config Ns = Ns Ar DIR
 .Op Fl -net Ns = Ns Ar NETNAME
 .Op Fl -pidfile Ns = Ns Ar FILENAME
+.Op Fl -force
 .Op Fl -help
 .Op Fl -version
 .Op Ar COMMAND
@@ -54,6 +55,8 @@ Use the cookie from
 to authenticate with a running tinc daemon.
 If unspecified, the default is
 .Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
+.It Fl -force
+Force some commands to work despite warnings.
 .It Fl -help
 Display short list of options.
 .It Fl -version
@@ -88,6 +91,7 @@ To set a variable for a specific host, use the notation
 .Ar host Ns Li . Ns Ar variable .
 .It add Ar variable Ar value
 As above, but without removing any previously existing configuration variables.
+If the variable already exists with the given value, nothing happens.
 .It del Ar variable Op Ar value
 Remove configuration variables with the same name and
 .Ar value .
@@ -101,16 +105,16 @@ You do not need to specify the full path to the file.
 Export the host configuration file of the local node to standard output.
 .It export-all
 Export all host configuration files to standard output.
-.It import Op Fl -force
+.It import
 Import host configuration data generated by the
 .Nm
 export command from standard input.
 Already existing host configuration files are not overwritten unless the option
 .Fl -force
 is used.
-.It exchange Op Fl -force
+.It exchange
 The same as export followed by import.
-.It exchange-all Op Fl -force
+.It exchange-all
 The same as export-all followed by import.
 .It invite Ar name
 Prepares an invitation for a new node with the given
@@ -215,6 +219,14 @@ If
 .Ar netname
 is given, switch to that network.
 Otherwise, display a list of all networks for which configuration files exist.
+.It fsck
+This will check the configuration files for possible problems,
+such as unsafe file permissions, missing executable bit on script,
+unknown and obsolete configuration variables, wrong public and/or private keys, and so on.
+.Pp
+When problems are found, this will be printed on a line with WARNING or ERROR in front of it.
+Most problems must be corrected by the user itself, however in some cases (like file permissions and missing public keys),
+tinc will ask if it should fix the problem.
 .El
 .Sh EXAMPLES
 Examples of some commands:
index 7e066bb..bc481ea 100644 (file)
@@ -116,11 +116,13 @@ is selected, then depending on the operating system both IPv4 and IPv6 or just
 IPv6 listening sockets will be created.
 .It Va AutoConnect Li = yes | no Po no Pc Bq experimental
 If set to yes,
-.Nm
+.Nm tinc
 will automatically set up meta connections to other nodes,
 without requiring
 .Va ConnectTo
 variables.
+.Pp
+Note: it is not possible to connect to nodes using zero (system-assigned) ports in this way.
 .It Va BindToAddress Li = Ar address Op Ar port
 This is the same as
 .Va ListenAddress ,
@@ -155,6 +157,13 @@ Broadcast packets are sent directly to all nodes that can be reached directly.
 Broadcast packets received from other nodes are never forwarded.
 If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
 .El
+.It Va BroadcastSubnet Li = Ar address Ns Op Li / Ns Ar prefixlength
+Declares a broadcast subnet. Any packet with a destination address falling into such a subnet will be routed as a broadcast (provided all nodes have it declared).
+This is most useful to declare subnet broadcast addresses (e.g. 10.42.255.255), otherwise
+.Nm tinc
+won't know what to do with them.
+.Pp
+Note that global broadcast addresses (MAC ff:ff:ff:ff:ff:ff, IPv4 255.255.255.255), as well as multicast space (IPv4 224.0.0.0/4, IPv6 ff00::/8) are always considered broadcast addresses and don't need to be declared.
 .It Va ConnectTo Li = Ar name
 Specifies which other tinc daemon to connect to on startup.
 Multiple
@@ -333,7 +342,15 @@ To only listen on a specific port but not on a specific address, use
 .Li *
 for the
 .Ar address .
-.It Va LocalDiscovery Li = yes | no Pq no
+.Pp
+If
+.Ar port
+is set to zero, it will be randomly assigned by the system. This is useful to randomize source ports of UDP packets, which can improve UDP hole punching reliability. In this case it is recommended to set
+.Va AddressFamily
+as well, otherwise
+.Nm tinc
+will assign different ports to different address families but other nodes can only know of one.
+.It Va LocalDiscovery Li = yes | no Pq yes
 When enabled,
 .Nm tinc
 will try to detect peers that are on the same local network.
@@ -341,11 +358,7 @@ This will allow direct communication using LAN addresses, even if both peers are
 and they only ConnectTo a third node outside the NAT,
 which normally would prevent the peers from learning each other's LAN address.
 .Pp
-Currently, local discovery is implemented by sending broadcast packets to the LAN during path MTU discovery.
-This feature may not work in all possible situations.
-.It Va LocalDiscoveryAddress Li = Ar address
-If this variable is specified, local discovery packets are sent to the given
-.Ar address .
+Currently, local discovery is implemented by sending some packets to the local address of the node during UDP discovery. This will not work with old nodes that don't transmit their local address.
 .It Va MACExpire Li = Ar seconds Pq 600
 This option controls the amount of time MAC addresses are kept before they are removed.
 This only has effect when
@@ -384,7 +397,8 @@ while no routing table is managed.
 .It Va Name Li = Ar name Bq required
 This is the name which identifies this tinc daemon.
 It must be unique for the virtual private network this daemon will connect to.
-The Name may only consist of alphanumeric and underscore characters (a-z, A-Z, 0-9 and _), and is case sensitive.
+.Va Name
+may only consist of alphanumeric and underscore characters (a-z, A-Z, 0-9 and _), and is case sensitive.
 If 
 .Va Name
 starts with a
@@ -471,6 +485,21 @@ and will only allow connections with nodes for which host config files are prese
 .Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa /hosts/
 directory.
 Setting this options also implicitly sets StrictSubnets.
+.It Va UDPDiscovery Li = yes | no Po yes Pc
+When this option is enabled tinc will try to establish UDP connectivity to nodes,
+using TCP while it determines if a node is reachable over UDP. If it is disabled,
+tinc always assumes a node is reachable over UDP.
+Note that tinc will never use UDP with nodes that have
+.Va TCPOnly
+enabled.
+.It Va UDPDiscoveryKeepaliveInterval Li = Ar seconds Pq 9
+The minimum amount of time between sending UDP ping datagrams to check UDP connectivity once it has been established.
+Note that these pings are large, since they are used to verify link MTU as well.
+.It Va UDPDiscoveryInterval Li = Ar seconds Pq 2
+The minimum amount of time between sending UDP ping datagrams to try to establish UDP connectivity.
+.It Va UDPDiscoveryTimeout Li = Ar seconds Pq 30
+If tinc doesn't receive any UDP ping replies over the specified interval,
+it will assume UDP communication is broken and will fall back to TCP.
 .It Va UDPRcvBuf Li = Ar bytes Pq OS default
 Sets the socket receive buffer size for the UDP socket, in bytes.
 If unset, the default buffer size will be used by the operating system.
@@ -545,6 +574,14 @@ The port number on which this tinc daemon is listening for incoming connections,
 which is used if no port number is specified in an
 .Va Address
 statement.
+.Pp
+If this is set to zero, the port will be randomly assigned by the system. This is useful to randomize source ports of UDP packets, which can improve UDP hole punching reliability. When setting
+.Va Port
+to zero it is recommended to set
+.Va AddressFamily
+as well, otherwise
+.Nm tinc
+will assign different ports to different address families but other nodes can only know of one.
 .It Va PublicKey Li = Ar key Bq obsolete
 The public RSA key of this tinc daemon.
 It will be used to cryptographically verify it's identity and to set up a secure connection.
index 81cc8c5..581da6e 100644 (file)
@@ -884,6 +884,18 @@ Broadcast packets received from other nodes are never forwarded.
 If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
 @end table
 
+@cindex BroadcastSubnet
+@item BroadcastSubnet = @var{address}[/@var{prefixlength}]
+Declares a broadcast subnet.
+Any packet with a destination address falling into such a subnet will be routed as a broadcast
+(provided all nodes have it declared).
+This is most useful to declare subnet broadcast addresses (e.g. 10.42.255.255),
+otherwise tinc won't know what to do with them.
+
+Note that global broadcast addresses (MAC ff:ff:ff:ff:ff:ff, IPv4 255.255.255.255),
+as well as multicast space (IPv4 224.0.0.0/4, IPv6 ff00::/8)
+are always considered broadcast addresses and don't need to be declared.
+
 @cindex ConnectTo
 @item ConnectTo = <@var{name}>
 Specifies which other tinc daemon to connect to on startup.
@@ -914,6 +926,13 @@ Under Windows, use @var{Interface} instead of @var{Device}.
 Note that you can only use one device per daemon.
 See also @ref{Device files}.
 
+@cindex DeviceStandby
+@item DeviceStandby = <yes | no> (no)
+When disabled, tinc calls @file{tinc-up} on startup, and @file{tinc-down} on shutdown.
+When enabled, tinc will only call @file{tinc-up} when at least one node is reachable,
+and will call @file{tinc-down} as soon as no nodes are reachable.
+On Windows, this also determines when the virtual network interface "cable" is "plugged".
+
 @cindex DeviceType
 @item DeviceType = <@var{type}> (platform dependent)
 The type of the virtual network device.
@@ -1062,8 +1081,8 @@ This will allow direct communication using LAN addresses, even if both peers are
 and they only ConnectTo a third node outside the NAT,
 which normally would prevent the peers from learning each other's LAN address.
 
-Currently, local discovery is implemented by sending broadcast packets to the LAN during path MTU discovery.
-This feature may not work in all possible situations.
+Currently, local discovery is implemented by sending some packets to the local address of the node during UDP discovery.
+This will not work with old nodes that don't transmit their local address.
 
 @cindex LocalDiscoveryAddress
 @item LocalDiscoveryAddress <@var{address}>
@@ -1213,6 +1232,27 @@ and will only allow connections with nodes for which host config files are prese
 @file{@value{sysconfdir}/tinc/@var{netname}/hosts/} directory.
 Setting this options also implicitly sets StrictSubnets.
 
+@cindex UDPDiscovey
+@item UDPDiscovery = <yes|no> (yes)
+When this option is enabled tinc will try to establish UDP connectivity to nodes,
+using TCP while it determines if a node is reachable over UDP. If it is disabled,
+tinc always assumes a node is reachable over UDP.
+Note that tinc will never use UDP with nodes that have TCPOnly enabled.
+
+@cindex UDPDiscoveryKeepaliveInterval
+@item UDPDiscoveryKeepaliveInterval = <seconds> (9)
+The minimum amount of time between sending UDP ping datagrams to check UDP connectivity once it has been established.
+Note that these pings are large, since they are used to verify link MTU as well.
+
+@cindex UDPDiscoveryInterval
+@item UDPDiscoveryInterval = <seconds> (2)
+The minimum amount of time between sending UDP ping datagrams to try to establish UDP connectivity.
+
+@cindex UDPDiscoveryTimeout
+@item UDPDiscoveryTimeout = <seconds> (30)
+If tinc doesn't receive any UDP ping replies over the specified interval,
+it will assume UDP communication is broken and will fall back to TCP.
+
 @cindex UDPRcvBuf
 @item UDPRcvBuf = <bytes> (OS default)
 Sets the socket receive buffer size for the UDP socket, in bytes.
@@ -2207,6 +2247,9 @@ 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 --force
+Force some commands to work despite warnings.
+
 @item --help
 Display a short reminder of runtime options and commands, then terminate.
 
@@ -2253,6 +2296,7 @@ To set a variable for a specific host, use the notation @var{host}.@var{variable
 @cindex add
 @item add @var{variable} @var{value}
 As above, but without removing any previously existing configuration variables.
+If the variable already exists with the given value, nothing happens.
 
 @cindex del
 @item del @var{variable} [@var{value}]
@@ -2273,16 +2317,16 @@ Export the host configuration file of the local node to standard output.
 Export all host configuration files to standard output.
 
 @cindex import
-@item import [--force]
+@item import
 Import host configuration file(s) generated by the tinc export command from standard input.
 Already existing host configuration files are not overwritten unless the option --force is used.
 
 @cindex exchange
-@item exchange [--force]
+@item exchange
 The same as export followed by import.
 
 @cindex exchange-all
-@item exchange-all [--force]
+@item exchange-all
 The same as export-all followed by import.
 
 @cindex invite
@@ -2396,11 +2440,21 @@ Dump VPN traffic going through the local tinc node in pcap-savefile format to st
 from where it can be redirected to a file or piped through a program that can parse it directly,
 such as tcpdump.
 
-@cindex network [@var{netname}]
-@item network
+@cindex network
+@item network [@var{netname}]
 If @var{netname} is given, switch to that network.
 Otherwise, display a list of all networks for which configuration files exist.
 
+@cindex fsck
+@item fsck
+This will check the configuration files for possible problems,
+such as unsafe file permissions, missing executable bit on script,
+unknown and obsolete configuration variables, wrong public and/or private keys, and so on.
+
+When problems are found, this will be printed on a line with WARNING or ERROR in front of it.
+Most problems must be corrected by the user itself, however in some cases (like file permissions and missing public keys),
+tinc will ask if it should fix the problem.
+
 @end table
 
 @c ==================================================================
index f1a9bbf..0a6370a 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 
 # tinc-gui -- GUI for controlling a running tincd
 # Copyright (C) 2009-2014 Guus Sliepen <guus@tinc-vpn.org>
@@ -78,8 +78,8 @@ class Edge:
                self.to = args[1]
                self.address = args[2]
                self.port = args[4]
-               self.options = int(args[5], 16)
-               self.weight = int(args[6])
+               self.options = int(args[-2], 16)
+               self.weight = int(args[-1])
 
 class Subnet:
        def parse(self, args):
@@ -192,6 +192,8 @@ class VPN:
                                subnet.parse(resp[2:])
                                subnet.visited = True
                                self.subnets[(resp[2], resp[3])] = subnet
+                               if subnet.owner == "(broadcast)":
+                                       continue
                                self.nodes[subnet.owner].subnets[resp[2]] = subnet
                        elif resp[1] == '6':
                                if len(resp) < 9:
@@ -535,7 +537,7 @@ class SubnetsPage(wx.Panel):
                                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, 1, str(subnet.weight))
                        self.list.SetStringItem(i, 2, subnet.owner)
                        self.list.itemDataMap[i] = (subnet.address + '/' + subnet.prefixlen, subnet.weight, subnet.owner)
                        self.list.SetItemData(i, i)
index 27c16db..738c68c 100644 (file)
@@ -35,7 +35,7 @@ AC_DEFUN([tinc_OPENSSL],
      LDFLAGS="$LDFLAGS -L$withval"]
   )
 
-  AC_CHECK_HEADERS([openssl/evp.h openssl/rsa.h openssl/rand.h openssl/err.h openssl/sha.h openssl/pem.h openssl/engine.h openssl/ecdh.h openssl/ec.h],
+  AC_CHECK_HEADERS([openssl/evp.h openssl/rsa.h openssl/rand.h openssl/err.h openssl/sha.h openssl/pem.h openssl/engine.h],
     [],
     [AC_MSG_ERROR([OpenSSL header files not found.]); break]
   )
@@ -45,11 +45,11 @@ AC_DEFUN([tinc_OPENSSL],
     [AC_MSG_ERROR([OpenSSL libraries not found.])]
   )
 
-  AC_CHECK_FUNCS([RAND_pseudo_bytes EVP_EncryptInit_ex ECDH_compute_key ECDSA_verify], ,
+  AC_CHECK_FUNCS([RAND_status EVP_EncryptInit_ex], ,
     [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break],
   )
 
-  AC_CHECK_DECLS([OpenSSL_add_all_algorithms, EVP_CTRL_GCM_GET_TAG], ,
+  AC_CHECK_DECLS([OpenSSL_add_all_algorithms], ,
     [AC_MSG_ERROR([Missing OpenSSL functionality, make sure you have installed the latest version.]); break],
     [#include <openssl/evp.h>]
   )
index 1a84098..beb71cd 100644 (file)
@@ -2,6 +2,10 @@
 
 sbin_PROGRAMS = tincd tinc sptps_test sptps_keypair
 
+## Make sure version.c is always rebuilt
+.PHONY: version.c
+version.c:
+
 if LINUX
 sbin_PROGRAMS += sptps_speed
 endif
@@ -18,7 +22,6 @@ ed25519_SOURCES = \
        ed25519/keypair.c \
        ed25519/precomp_data.h \
        ed25519/sc.c ed25519/sc.h \
-       ed25519/seed.c \
        ed25519/sha512.c ed25519/sha512.h \
        ed25519/sign.c \
        ed25519/verify.c
@@ -88,6 +91,9 @@ tincd_SOURCES = \
        tincd.c \
        utils.c utils.h \
        xalloc.h \
+       version.c version.h \
+       ed25519/ecdh.c \
+       ed25519/ecdsa.c \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES)
 
@@ -95,6 +101,7 @@ tinc_SOURCES = \
        dropin.c dropin.h \
        getopt.c getopt.h \
        getopt1.c \
+       fsck.c fsck.h \
        info.c info.h \
        invitation.c invitation.h \
        list.c list.h \
@@ -106,6 +113,10 @@ tinc_SOURCES = \
        tincctl.c tincctl.h \
        top.c top.h \
        utils.c utils.h \
+       version.c version.h \
+       ed25519/ecdh.c \
+       ed25519/ecdsa.c \
+       ed25519/ecdsagen.c \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES)
 
@@ -114,12 +125,15 @@ sptps_test_SOURCES = \
        sptps.c sptps.h \
        sptps_test.c \
        utils.c utils.h \
+       ed25519/ecdh.c \
+       ed25519/ecdsa.c \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES)
 
 sptps_keypair_SOURCES = \
        sptps_keypair.c \
        utils.c utils.h \
+       ed25519/ecdsagen.c \
        $(ed25519_SOURCES)
 
 sptps_speed_SOURCES = \
@@ -127,6 +141,9 @@ sptps_speed_SOURCES = \
        sptps.c sptps.h \
        sptps_speed.c \
        utils.c utils.h \
+       ed25519/ecdh.c \
+       ed25519/ecdsa.c \
+       ed25519/ecdsagen.c \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES)
 
@@ -168,54 +185,37 @@ tincd_SOURCES += \
        openssl/cipher.c \
        openssl/crypto.c \
        openssl/digest.c openssl/digest.h \
-       ed25519/ecdh.c \
-       ed25519/ecdsa.c \
        openssl/prf.c \
        openssl/rsa.c
 tinc_SOURCES += \
        openssl/cipher.c \
        openssl/crypto.c \
        openssl/digest.c openssl/digest.h \
-       ed25519/ecdh.c \
-       ed25519/ecdsa.c \
-       ed25519/ecdsagen.c \
        openssl/prf.c \
        openssl/rsa.c \
        openssl/rsagen.c
 sptps_test_SOURCES += \
        openssl/crypto.c \
        openssl/digest.c openssl/digest.h \
-       ed25519/ecdh.c \
-       ed25519/ecdsa.c \
        openssl/prf.c
 sptps_keypair_SOURCES += \
-       openssl/crypto.c \
-       ed25519/ecdsagen.c
+       openssl/crypto.c
 sptps_speed_SOURCES += \
        openssl/crypto.c \
        openssl/digest.c openssl/digest.h \
-       ed25519/ecdh.c \
-       ed25519/ecdsa.c \
-       ed25519/ecdsagen.c \
        openssl/prf.c
-endif
-
+else
 if GCRYPT
 tincd_SOURCES += \
        gcrypt/cipher.c \
        gcrypt/crypto.c \
        gcrypt/digest.c gcrypt/digest.h \
-       gcrypt/ecdh.c \
-       gcrypt/ecdsa.c \
        gcrypt/prf.c \
        gcrypt/rsa.c
 tinc_SOURCES += \
        gcrypt/cipher.c \
        gcrypt/crypto.c \
        gcrypt/digest.c gcrypt/digest.h \
-       gcrypt/ecdh.c \
-       gcrypt/ecdsa.c \
-       gcrypt/ecdsagen.c \
        gcrypt/prf.c \
        gcrypt/rsa.c \
        gcrypt/rsagen.c
@@ -223,15 +223,35 @@ sptps_test_SOURCES += \
        gcrypt/cipher.c \
        gcrypt/crypto.c \
        gcrypt/digest.c gcrypt/digest.h \
-       gcrypt/ecdh.c \
-       gcrypt/ecdsa.c \
        gcrypt/prf.c
+sptps_keypair_SOURCES += \
+       openssl/crypto.c
+sptps_speed_SOURCES += \
+       openssl/crypto.c \
+       openssl/digest.c openssl/digest.h \
+       openssl/prf.c
+else
+tincd_SOURCES += \
+       nolegacy/crypto.c \
+       nolegacy/prf.c
+tinc_SOURCES += \
+       nolegacy/crypto.c \
+       nolegacy/prf.c
+sptps_test_SOURCES += \
+       nolegacy/crypto.c \
+       nolegacy/prf.c
+sptps_keypair_SOURCES += \
+       nolegacy/crypto.c
+sptps_speed_SOURCES += \
+       nolegacy/crypto.c \
+       nolegacy/prf.c
+endif
 endif
 
 tinc_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
 sptps_speed_LDADD = -lrt
 
-LIBS = @LIBS@
+LIBS = @LIBS@ -lm
 
 if TUNEMU
 LIBS += -lpcap
index 49508bc..d1a993b 100644 (file)
@@ -1,7 +1,7 @@
 /*
     device.c -- Interaction BSD tun/tap device
     Copyright (C) 2001-2005 Ivo Timmermans,
-                  2001-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2001-2014 Guus Sliepen <guus@tinc-vpn.org>
                   2009      Grzegorz Dymarek <gregd72002@googlemail.com>
 
     This program is free software; you can redistribute it and/or modify
@@ -35,7 +35,7 @@
 #endif
 
 #define DEFAULT_TUN_DEVICE "/dev/tun0"
-#if defined(HAVE_FREEBSD) || defined(HAVE_NETBSD)
+#if defined(HAVE_DARWIN) || defined(HAVE_FREEBSD) || defined(HAVE_NETBSD)
 #define DEFAULT_TAP_DEVICE "/dev/tap0"
 #else
 #define DEFAULT_TAP_DEVICE "/dev/tun0"
@@ -63,18 +63,9 @@ static device_type_t device_type = DEVICE_TYPE_TUN;
 #endif
 
 static bool setup_device(void) {
-       char *type;
-
-       if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
-               if(routing_mode == RMODE_ROUTER)
-                       device = xstrdup(DEFAULT_TUN_DEVICE);
-               else
-                       device = xstrdup(DEFAULT_TAP_DEVICE);
-       }
-
-       if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
-               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
+       get_config_string(lookup_config(config_tree, "Device"), &device);
 
+       char *type;
        if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
                if(!strcasecmp(type, "tun"))
                        /* use default */;
@@ -93,10 +84,29 @@ static bool setup_device(void) {
                        return false;
                }
        } else {
-               if(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
+               if((device && strstr(device, "tap")) || routing_mode != RMODE_ROUTER)
                        device_type = DEVICE_TYPE_TAP;
        }
 
+       if(!device) {
+               if(device_type == DEVICE_TYPE_TAP)
+                       device = xstrdup(DEFAULT_TAP_DEVICE);
+               else
+                       device = xstrdup(DEFAULT_TUN_DEVICE);
+       }
+
+       if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
+               iface = NULL;
+#ifndef TAPGIFNAME
+       if (iface) {
+               logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring specified interface name '%s' as device rename is not supported on this platform", iface);
+               free(iface);
+               iface = NULL;
+       }
+#endif
+       if (!iface)
+               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
+
        switch(device_type) {
 #ifdef ENABLE_TUNEMU
                case DEVICE_TYPE_TUNEMU: {
@@ -212,10 +222,10 @@ static bool read_packet(vpn_packet_t *packet) {
 #ifdef ENABLE_TUNEMU
                case DEVICE_TYPE_TUNEMU:
                        if(device_type == DEVICE_TYPE_TUNEMU)
-                               inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
+                               inlen = tunemu_read(device_fd, DATA(packet) + 14, MTU - 14);
                        else
 #endif
-                               inlen = read(device_fd, packet->data + 14, MTU - 14);
+                               inlen = read(device_fd, DATA(packet) + 14, MTU - 14);
 
                        if(inlen <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
@@ -223,29 +233,29 @@ static bool read_packet(vpn_packet_t *packet) {
                                return false;
                        }
 
-                       switch(packet->data[14] >> 4) {
+                       switch(DATA(packet)[14] >> 4) {
                                case 4:
-                                       packet->data[12] = 0x08;
-                                       packet->data[13] = 0x00;
+                                       DATA(packet)[12] = 0x08;
+                                       DATA(packet)[13] = 0x00;
                                        break;
                                case 6:
-                                       packet->data[12] = 0x86;
-                                       packet->data[13] = 0xDD;
+                                       DATA(packet)[12] = 0x86;
+                                       DATA(packet)[13] = 0xDD;
                                        break;
                                default:
                                        logger(DEBUG_TRAFFIC, LOG_ERR,
                                                           "Unknown IP version %d while reading packet from %s %s",
-                                                          packet->data[14] >> 4, device_info, device);
+                                                          DATA(packet)[14] >> 4, device_info, device);
                                        return false;
                        }
 
-                       memset(packet->data, 0, 12);
+                       memset(DATA(packet), 0, 12);
                        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}, {DATA(packet) + 14, MTU - 14}};
 
                        if((inlen = readv(device_fd, vector, 2)) <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
@@ -255,13 +265,13 @@ static bool read_packet(vpn_packet_t *packet) {
 
                        switch (ntohl(type)) {
                                case AF_INET:
-                                       packet->data[12] = 0x08;
-                                       packet->data[13] = 0x00;
+                                       DATA(packet)[12] = 0x08;
+                                       DATA(packet)[13] = 0x00;
                                        break;
 
                                case AF_INET6:
-                                       packet->data[12] = 0x86;
-                                       packet->data[13] = 0xDD;
+                                       DATA(packet)[12] = 0x86;
+                                       DATA(packet)[13] = 0xDD;
                                        break;
 
                                default:
@@ -271,13 +281,13 @@ static bool read_packet(vpn_packet_t *packet) {
                                        return false;
                        }
 
-                       memset(packet->data, 0, 12);
+                       memset(DATA(packet), 0, 12);
                        packet->len = inlen + 10;
                        break;
                }
 
                case DEVICE_TYPE_TAP:
-                       if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
+                       if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
@@ -302,7 +312,7 @@ static bool write_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
+                       if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
@@ -311,10 +321,10 @@ 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}, {DATA(packet) + 14, packet->len - 14}};
                        int af;
 
-                       af = (packet->data[12] << 8) + packet->data[13];
+                       af = (DATA(packet)[12] << 8) + DATA(packet)[13];
 
                        switch (af) {
                                case 0x0800:
@@ -339,7 +349,7 @@ static bool write_packet(vpn_packet_t *packet) {
                }
 
                case DEVICE_TYPE_TAP:
-                       if(write(device_fd, packet->data, packet->len) < 0) {
+                       if(write(device_fd, DATA(packet), packet->len) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
@@ -348,7 +358,7 @@ static bool write_packet(vpn_packet_t *packet) {
 
 #ifdef ENABLE_TUNEMU
                case DEVICE_TYPE_TUNEMU:
-                       if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
+                       if(tunemu_write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
index bbba569..3f98c18 100644 (file)
@@ -24,6 +24,8 @@
 #define CIPHER_MAX_IV_SIZE 16
 #define CIPHER_MAX_KEY_SIZE 32
 
+#ifndef DISABLE_LEGACY
+
 typedef struct cipher cipher_t;
 
 extern cipher_t *cipher_open_by_name(const char *) __attribute__ ((__malloc__));
@@ -31,20 +33,15 @@ extern cipher_t *cipher_open_by_nid(int) __attribute__ ((__malloc__));
 extern cipher_t *cipher_open_blowfish_ofb(void) __attribute__ ((__malloc__));
 extern void cipher_close(cipher_t *);
 extern size_t cipher_keylength(const cipher_t *);
+extern size_t cipher_blocksize(const cipher_t *);
 extern void cipher_get_key(const cipher_t *, void *);
 extern bool cipher_set_key(cipher_t *, void *, bool) __attribute__ ((__warn_unused_result__));
 extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool) __attribute__ ((__warn_unused_result__));
-extern bool cipher_set_counter(cipher_t *, const void *, size_t) __attribute__ ((__warn_unused_result__));
-extern bool cipher_set_counter_key(cipher_t *, void *) __attribute__ ((__warn_unused_result__));
 extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__));
 extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_encrypt_start(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_encrypt_finish(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_decrypt_start(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
-extern bool cipher_gcm_decrypt_finish(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__));
 extern int cipher_get_nid(const cipher_t *);
 extern bool cipher_active(const cipher_t *);
 
 #endif
+
+#endif
index 496f674..0e61132 100644 (file)
@@ -55,14 +55,16 @@ void free_connection(connection_t *c) {
        if(!c)
                return;
 
+#ifndef DISABLE_LEGACY
        cipher_close(c->incipher);
        digest_close(c->indigest);
        cipher_close(c->outcipher);
        digest_close(c->outdigest);
+       rsa_free(c->rsa);
+#endif
 
        sptps_stop(&c->sptps);
        ecdsa_free(c->ecdsa);
-       rsa_free(c->rsa);
 
        free(c->hischallenge);
 
index b5d3d18..30c6598 100644 (file)
@@ -36,7 +36,7 @@
 
 typedef struct connection_status_t {
                unsigned int pinged:1;                  /* sent ping */
-               unsigned int active:1;                  /* 1 if active.. */
+               unsigned int unused_active:1;
                unsigned int connecting:1;              /* 1 if we are waiting for a non-blocking connect() to finish */
                unsigned int unused_termreq:1;          /* the termination of this connection was requested */
                unsigned int remove_unused:1;           /* Set to 1 if you want this connection removed */
@@ -49,7 +49,7 @@ typedef struct connection_status_t {
                unsigned int log:1;                     /* 1 if this is a control connection requesting log dump */
                unsigned int invitation:1;              /* 1 if this is an invitation */
                unsigned int invitation_used:1;         /* 1 if the invitation has been consumed */
-               unsigned int unused:19;
+               unsigned int unused:18;
 } connection_status_t;
 
 #include "ecdsa.h"
@@ -75,12 +75,15 @@ typedef struct connection_t {
        struct node_t *node;            /* node associated with the other end */
        struct edge_t *edge;            /* edge associated with this connection */
 
+#ifndef DISABLE_LEGACY
        rsa_t *rsa;                     /* his public RSA key */
-       ecdsa_t *ecdsa;                 /* his public ECDSA key */
        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;
+#endif
+
+       ecdsa_t *ecdsa;                 /* his public ECDSA key */
        sptps_t sptps;
 
        int inmaclength;
index 4562749..98eae80 100644 (file)
@@ -106,7 +106,7 @@ bool control_h(connection_t *c, const char *request) {
                        for list_each(connection_t, other, connection_list) {
                                if(strcmp(other->name, name))
                                        continue;
-                               terminate_connection(other, other->status.active);
+                               terminate_connection(other, other->edge);
                                found = true;
                        }
 
@@ -178,7 +178,7 @@ bool init_control(void) {
 #ifndef HAVE_MINGW
        int unix_fd = socket(AF_UNIX, SOCK_STREAM, 0);
        if(unix_fd < 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create UNIX socket: %s", sockstrerror(errno));
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create UNIX socket: %s", sockstrerror(sockerrno));
                return false;
        }
 
@@ -198,12 +198,12 @@ bool init_control(void) {
        umask(mask);
 
        if(result < 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind UNIX socket to %s: %s", unixsocketname, sockstrerror(errno));
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not bind UNIX socket to %s: %s", unixsocketname, sockstrerror(sockerrno));
                return false;
        }
 
        if(listen(unix_fd, 3) < 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not listen on UNIX socket %s: %s", unixsocketname, sockstrerror(errno));
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not listen on UNIX socket %s: %s", unixsocketname, sockstrerror(sockerrno));
                return false;
        }
 
index d522b4f..4c3a60d 100644 (file)
@@ -1,7 +1,7 @@
 /*
     device.c -- Interaction with Windows tap driver in a Cygwin environment
     Copyright (C) 2002-2005 Ivo Timmermans,
-                  2002-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2002-2014 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
@@ -227,7 +227,7 @@ static void close_device(void) {
 static bool read_packet(vpn_packet_t *packet) {
        int inlen;
 
-       if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
+       if((inlen = read(sp[0], DATA(packet), MTU)) <= 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
@@ -247,7 +247,7 @@ static bool write_packet(vpn_packet_t *packet) {
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
+       if(!WriteFile (device_handle, DATA(packet), packet->len, &outlen, NULL)) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
index ad91a0e..8046a25 100644 (file)
@@ -27,11 +27,6 @@ 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);
index 1e14945..204048a 100644 (file)
@@ -22,6 +22,8 @@
 
 #define DIGEST_MAX_SIZE 64
 
+#ifndef DISABLE_LEGACY
+
 typedef struct digest digest_t;
 
 extern digest_t *digest_open_by_name(const char *name, int maclength) __attribute__ ((__malloc__));
@@ -37,3 +39,5 @@ extern size_t digest_length(const digest_t *);
 extern bool digest_active(const digest_t *);
 
 #endif
+
+#endif
index bfdabc1..f8aafe4 100644 (file)
@@ -84,11 +84,13 @@ static bool read_pem(FILE *fp, const char *type, void *buf, size_t size) {
                size_t len = b64decode(line, line, linelen);
                if(!len) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Invalid base64 data in PEM file\n");
+                       errno = EINVAL;
                        return false;
                }
 
                if(len > size) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Too much base64 data in PEM file\n");
+                       errno = EINVAL;
                        return false;
                }
 
@@ -98,7 +100,12 @@ static bool read_pem(FILE *fp, const char *type, void *buf, size_t size) {
        }
 
        if(size) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Too little base64 data in PEM file\n");
+               if(data) {
+                       errno = EINVAL;
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Too little base64 data in PEM file\n");
+               } else {
+                       errno = ENOENT;
+               }
                return false;
        }
 
diff --git a/src/ed25519/seed.c b/src/ed25519/seed.c
deleted file mode 100644 (file)
index ca4089c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "ed25519.h"
-
-#ifndef ED25519_NO_SEED
-
-#ifdef _WIN32
-#include <windows.h>
-#include <wincrypt.h>
-#else
-#include <stdio.h>
-#endif
-
-int ed25519_create_seed(unsigned char *seed) {
-#ifdef _WIN32
-    HCRYPTPROV prov;
-
-    if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))  {
-        return 1;
-    }
-
-    if (!CryptGenRandom(prov, 32, seed))  {
-        CryptReleaseContext(prov, 0);
-        return 1;
-    }
-
-    CryptReleaseContext(prov, 0);
-#else
-    FILE *f = fopen("/dev/urandom", "rb");
-
-    if (f == NULL) {
-        return 1;
-    }
-
-    fread(seed, 1, 32, f);
-    fclose(f);
-#endif
-
-    return 0;
-}
-
-#endif
\ No newline at end of file
index cb8ae71..e1db1e5 100644 (file)
 
 /* the K array */
 static const uint64_t K[80] = {
-    UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), 
+    UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
     UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
-    UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), 
+    UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
     UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
-    UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), 
+    UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
     UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
-    UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), 
+    UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
     UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
-    UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), 
+    UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
     UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
-    UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), 
+    UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
     UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
-    UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), 
+    UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
     UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
-    UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), 
+    UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
     UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
-    UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), 
+    UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
     UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
-    UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), 
+    UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
     UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
     UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
     UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
-    UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), 
+    UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
     UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
-    UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), 
+    UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
     UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
-    UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), 
+    UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
     UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
-    UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), 
+    UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
     UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
-    UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), 
+    UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
     UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
-    UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), 
+    UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
     UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
-    UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), 
+    UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
     UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
-    UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), 
+    UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
     UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
-    UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), 
+    UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
     UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
 };
 
@@ -76,7 +76,7 @@ static const uint64_t K[80] = {
 
 
 #define Ch(x,y,z)       (z ^ (x & (y ^ z)))
-#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
 #define S(x, n)         ROR64c(x, n)
 #define R(x, n)         (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n))
 #define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
@@ -88,7 +88,7 @@ static const uint64_t K[80] = {
 #endif
 
 /* compress 1024-bits */
-static int sha512_compress(sha512_context *md, unsigned char *buf)
+static int sha512_compress(sha512_context *md, const unsigned char *buf)
 {
     uint64_t S[8], W[80], t0, t1;
     int i;
@@ -106,7 +106,7 @@ static int sha512_compress(sha512_context *md, unsigned char *buf)
     /* fill W[16..79] */
     for (i = 16; i < 80; i++) {
         W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
-    }        
+    }
 
 /* Compress */
     #define RND(a,b,c,d,e,f,g,h,i) \
@@ -168,25 +168,26 @@ int sha512_init(sha512_context * md) {
    @param inlen  The length of the data (octets)
    @return 0 if successful
 */
-int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)               
-{                                                                                           
+int sha512_update(sha512_context *md, const void *vin, size_t inlen)
+{
+    const unsigned char *in = vin;
     size_t n;
-    size_t i;                                                                        
-    int           err;     
-    if (md == NULL) return 1;  
-    if (in == NULL) return 1;                                                              
-    if (md->curlen > sizeof(md->buf)) {                             
-       return 1;                                                            
-    }                                                                                       
-    while (inlen > 0) {                                                                     
-        if (md->curlen == 0 && inlen >= 128) {                           
-           if ((err = sha512_compress (md, (unsigned char *)in)) != 0) {               
-              return err;                                                                   
-           }                                                                                
-           md->length += 128 * 8;                                        
-           in             += 128;                                                    
-           inlen          -= 128;                                                    
-        } else {                                                                            
+    size_t i;
+    int           err;
+    if (md == NULL) return 1;
+    if (in == NULL) return 1;
+    if (md->curlen > sizeof(md->buf)) {
+       return 1;
+    }
+    while (inlen > 0) {
+        if (md->curlen == 0 && inlen >= 128) {
+           if ((err = sha512_compress (md, in)) != 0) {
+              return err;
+           }
+           md->length += 128 * 8;
+           in             += 128;
+           inlen          -= 128;
+        } else {
            n = MIN(inlen, (128 - md->curlen));
 
            for (i = 0; i < n; i++) {
@@ -194,19 +195,19 @@ int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
            }
 
 
-           md->curlen += n;                                                     
-           in             += n;                                                             
-           inlen          -= n;                                                             
-           if (md->curlen == 128) {                                      
-              if ((err = sha512_compress (md, md->buf)) != 0) {            
-                 return err;                                                                
-              }                                                                             
-              md->length += 8*128;                                       
-              md->curlen = 0;                                                   
-           }                                                                                
-       }                                                                                    
-    }                                                                                       
-    return 0;                                                                        
+           md->curlen += n;
+           in             += n;
+           inlen          -= n;
+           if (md->curlen == 128) {
+              if ((err = sha512_compress (md, md->buf)) != 0) {
+                 return err;
+              }
+              md->length += 8*128;
+              md->curlen = 0;
+           }
+       }
+    }
+    return 0;
 }
 
 /**
@@ -215,22 +216,23 @@ int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
    @param out [out] The destination of the hash (64 bytes)
    @return 0 if successful
 */
-   int sha512_final(sha512_context * md, unsigned char *out)
-   {
+int sha512_final(sha512_context * md, void *vout)
+{
     int i;
+    unsigned char *out = vout;
 
     if (md == NULL) return 1;
     if (out == NULL) return 1;
 
     if (md->curlen >= sizeof(md->buf)) {
-     return 1;
- }
+       return 1;
   }
 
     /* increase the length of the message */
- md->length += md->curlen * UINT64_C(8);
   md->length += md->curlen * UINT64_C(8);
 
     /* append the '1' bit */
- md->buf[md->curlen++] = (unsigned char)0x80;
   md->buf[md->curlen++] = (unsigned char)0x80;
 
     /* if the length is currently above 112 bytes we append zeros
      * then compress.  Then we can fall back to padding zeros and length
@@ -244,27 +246,27 @@ int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
         md->curlen = 0;
     }
 
-    /* pad upto 120 bytes of zeroes 
+    /* pad upto 120 bytes of zeroes
      * note: that from 112 to 120 is the 64 MSB of the length.  We assume that you won't hash
      * > 2^64 bits of data... :-)
      */
-while (md->curlen < 120) {
-    md->buf[md->curlen++] = (unsigned char)0;
-}
+   while (md->curlen < 120) {
+      md->buf[md->curlen++] = (unsigned char)0;
+   }
 
     /* store length */
-STORE64H(md->length, md->buf+120);
-sha512_compress(md, md->buf);
+   STORE64H(md->length, md->buf+120);
+   sha512_compress(md, md->buf);
 
     /* copy output */
-for (i = 0; i < 8; i++) {
-    STORE64H(md->state[i], out+(8*i));
-}
+   for (i = 0; i < 8; i++) {
+      STORE64H(md->state[i], out+(8*i));
+   }
 
-return 0;
+   return 0;
 }
 
-int sha512(const unsigned char *message, size_t message_len, unsigned char *out)
+int sha512(const void *message, size_t message_len, void *out)
 {
     sha512_context ctx;
     int ret;
index e56b00e..e23f8db 100644 (file)
@@ -14,8 +14,8 @@ typedef struct sha512_context_ {
 
 
 int sha512_init(sha512_context * md);
-int sha512_final(sha512_context * md, unsigned char *out);
-int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen);
-int sha512(const unsigned char *message, size_t message_len, unsigned char *out);
+int sha512_final(sha512_context * md, void *out);
+int sha512_update(sha512_context * md, const void *in, size_t inlen);
+int sha512(const void *message, size_t message_len, void *out);
 
-#endif
\ No newline at end of file
+#endif
index f185b4f..2eaae5d 100644 (file)
@@ -110,11 +110,13 @@ bool dump_edges(connection_t *c) {
        for splay_each(node_t, n, node_tree) {
                for splay_each(edge_t, e, n->edge_tree) {
                        char *address = sockaddr2hostname(&e->address);
-                       send_request(c, "%d %d %s %s %s %x %d",
+                       char* local_address = sockaddr2hostname(&e->local_address);
+                       send_request(c, "%d %d %s %s %s %s %x %d",
                                        CONTROL, REQ_DUMP_EDGES,
                                        e->from->name, e->to->name, address,
-                                       e->options, e->weight);
+                                       local_address, e->options, e->weight);
                        free(address);
+                       free(local_address);
                }
        }
 
index cbd9e5a..ed46b8a 100644 (file)
@@ -30,6 +30,7 @@ typedef struct edge_t {
        struct node_t *from;
        struct node_t *to;
        sockaddr_t address;
+       sockaddr_t local_address;
 
        uint32_t options;                       /* options turned on for this edge */
        int weight;                             /* weight of this edge */
index 5a1e4f5..60d357d 100644 (file)
 #include "event.h"
 #include "net.h"
 #include "utils.h"
+#include "xalloc.h"
 
 struct timeval now;
 
+#ifndef HAVE_MINGW
 static fd_set readfds;
 static fd_set writefds;
-static volatile bool running;
+#else
+static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
+static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
+static DWORD event_count = 0;
+#endif
+static bool running;
 
 static int io_compare(const io_t *a, const io_t *b) {
+#ifndef HAVE_MINGW
        return a->fd - b->fd;
+#else
+       return a->event - b->event;
+#endif
 }
 
 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
@@ -60,6 +71,14 @@ void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
                return;
 
        io->fd = fd;
+#ifdef HAVE_MINGW
+       if (io->fd != -1) {
+               io->event = WSACreateEvent();
+               if (io->event == WSA_INVALID_EVENT)
+                       abort();
+       }
+       event_count++;
+#endif
        io->cb = cb;
        io->data = data;
        io->node.data = io;
@@ -70,9 +89,21 @@ void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
                abort();
 }
 
+#ifdef HAVE_MINGW
+void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
+       io->event = event;
+       io_add(io, cb, data, -1, 0);
+}
+#endif
+
 void io_set(io_t *io, int flags) {
+       if (flags == io->flags)
+               return;
        io->flags = flags;
+       if (io->fd == -1)
+               return;
 
+#ifndef HAVE_MINGW
        if(flags & IO_READ)
                FD_SET(io->fd, &readfds);
        else
@@ -82,6 +113,15 @@ void io_set(io_t *io, int flags) {
                FD_SET(io->fd, &writefds);
        else
                FD_CLR(io->fd, &writefds);
+#else
+       long events = 0;
+       if (flags & IO_WRITE)
+               events |= WRITE_EVENTS;
+       if (flags & IO_READ)
+               events |= READ_EVENTS;
+       if (WSAEventSelect(io->fd, io->event, events) != 0)
+               abort();
+#endif
 }
 
 void io_del(io_t *io) {
@@ -89,6 +129,11 @@ void io_del(io_t *io) {
                return;
 
        io_set(io, 0);
+#ifdef HAVE_MINGW
+       if (io->fd != -1 && WSACloseEvent(io->event) == FALSE)
+               abort();
+       event_count--;
+#endif
 
        splay_unlink_node(&io_tree, &io->node);
        io->cb = NULL;
@@ -182,30 +227,37 @@ void signal_del(signal_t *sig) {
 }
 #endif
 
+static struct timeval * get_time_remaining(struct timeval *diff) {
+       gettimeofday(&now, NULL);
+       struct timeval *tv = NULL;
+
+       while(timeout_tree.head) {
+               timeout_t *timeout = timeout_tree.head->data;
+               timersub(&timeout->tv, &now, diff);
+
+               if(diff->tv_sec < 0) {
+                       timeout->cb(timeout->data);
+                       if(timercmp(&timeout->tv, &now, <))
+                               timeout_del(timeout);
+               } else {
+                       tv = diff;
+                       break;
+               }
+       }
+
+       return tv;
+}
+
 bool event_loop(void) {
        running = true;
 
+#ifndef HAVE_MINGW
        fd_set readable;
        fd_set writable;
 
        while(running) {
-               gettimeofday(&now, NULL);
-               struct timeval diff, *tv = NULL;
-
-               while(timeout_tree.head) {
-                       timeout_t *timeout = timeout_tree.head->data;
-                       timersub(&timeout->tv, &now, &diff);
-
-                       if(diff.tv_sec < 0) {
-                               timeout->cb(timeout->data);
-                               if(timercmp(&timeout->tv, &now, <))
-                                       timeout_del(timeout);
-                       } else {
-                               tv = &diff;
-                               break;
-                       }
-               }
-
+               struct timeval diff;
+               struct timeval *tv = get_time_remaining(&diff);
                memcpy(&readable, &readfds, sizeof readable);
                memcpy(&writable, &writefds, sizeof writable);
 
@@ -216,16 +268,10 @@ bool event_loop(void) {
                        fds = last->fd + 1;
                }
 
-#ifdef HAVE_MINGW
-               LeaveCriticalSection(&mutex);
-#endif
                int n = select(fds, &readable, &writable, NULL, tv);
-#ifdef HAVE_MINGW
-               EnterCriticalSection(&mutex);
-#endif
 
                if(n < 0) {
-                       if(sockwouldblock(errno))
+                       if(sockwouldblock(sockerrno))
                                continue;
                        else
                                return false;
@@ -241,14 +287,75 @@ bool event_loop(void) {
                                io->cb(io->data, IO_READ);
                }
        }
+#else
+       while (running) {
+               struct timeval diff;
+               struct timeval *tv = get_time_remaining(&diff);
+               DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
+
+               if (!event_count) {
+                       Sleep(timeout_ms);
+                       continue;
+               }
 
-       return true;
-}
+               /*
+                  For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
+                  which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
+                  it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
+                  is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
+                  to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
+
+                  Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
+                  this event being fired again if ignored.
+               */
+               io_t* writeable_io = NULL;
+               for splay_each(io_t, io, &io_tree)
+                       if (io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
+                               writeable_io = io;
+                               break;
+                       }
+               if (writeable_io) {
+                       writeable_io->cb(writeable_io->data, IO_WRITE);
+                       continue;
+               }
 
-void event_flush_output(void) {
-       for splay_each(io_t, io, &io_tree)
-               if(FD_ISSET(io->fd, &writefds))
-                       io->cb(io->data, IO_WRITE);
+               WSAEVENT* events = xmalloc(event_count * sizeof(*events));
+               DWORD event_index = 0;
+               for splay_each(io_t, io, &io_tree) {
+                       events[event_index] = io->event;
+                       event_index++;
+               }
+
+               DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
+
+               WSAEVENT event;
+               if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count)
+                       event = events[result - WSA_WAIT_EVENT_0];
+               free(events);
+               if (result == WSA_WAIT_TIMEOUT)
+                       continue;
+               if (result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count)
+                       return false;
+
+               io_t *io = splay_search(&io_tree, &((io_t){.event = event}));
+               if (!io)
+                       abort();
+
+               if (io->fd == -1) {
+                       io->cb(io->data, 0);
+               } else {
+                       WSANETWORKEVENTS network_events;
+                       if (WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0)
+                               return false;
+                       if (network_events.lNetworkEvents & WRITE_EVENTS)
+                               io->cb(io->data, IO_WRITE);
+                       if (network_events.lNetworkEvents & READ_EVENTS)
+                               io->cb(io->data, IO_READ);
+               }
+       }
+#endif
+
+       return true;
 }
 
 void event_exit(void) {
index c6522c0..0ff8e01 100644 (file)
@@ -32,6 +32,9 @@ typedef void (*signal_cb_t)(void *data);
 typedef struct io_t {
        int fd;
        int flags;
+#ifdef HAVE_MINGW
+       WSAEVENT event;
+#endif
        io_cb_t cb;
        void *data;
        splay_node_t node;
@@ -54,6 +57,9 @@ typedef struct signal_t {
 extern struct timeval now;
 
 extern void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags);
+#ifdef HAVE_MINGW
+extern void io_add_event(io_t *io, io_cb_t cb, void* data, WSAEVENT event);
+#endif
 extern void io_del(io_t *io);
 extern void io_set(io_t *io, int flags);
 
@@ -65,7 +71,6 @@ extern void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum);
 extern void signal_del(signal_t *sig);
 
 extern bool event_loop(void);
-extern void event_flush_output(void);
 extern void event_exit(void);
 
 #endif
diff --git a/src/fsck.c b/src/fsck.c
new file mode 100644 (file)
index 0000000..8df95ec
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+    fsck.c -- Check the configuration files for problems
+    Copyright (C) 2014 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 "ecdsa.h"
+#include "ecdsagen.h"
+#include "fsck.h"
+#include "names.h"
+#ifndef DISABLE_LEGACY
+#include "rsa.h"
+#include "rsagen.h"
+#endif
+#include "tincctl.h"
+#include "utils.h"
+
+static bool ask_fix(void) {
+       if(force)
+               return true;
+       if(!tty)
+               return false;
+again:
+       fprintf(stderr, "Fix y/n? ");
+       char buf[1024];
+       if(!fgets(buf, sizeof buf, stdin)) {
+               tty = false;
+               return false;
+       }
+       if(buf[0] == 'y' || buf[0] == 'Y')
+               return true;
+       if(buf[0] == 'n' || buf[0] == 'N')
+               return false;
+       goto again;
+}
+
+static void print_tinc_cmd(const char *argv0, const char *format, ...) {
+       if(confbasegiven)
+               fprintf(stderr, "%s -c %s ", argv0, confbase);
+       else if(netname)
+               fprintf(stderr, "%s -n %s ", argv0, netname);
+       else
+               fprintf(stderr, "%s ", argv0);
+       va_list va;
+       va_start(va, format);
+       vfprintf(stderr, format, va);
+       va_end(va);
+       fputc('\n', stderr);
+}
+
+static int strtailcmp(const char *str, const char *tail) {
+       size_t slen = strlen(str);
+       size_t tlen = strlen(tail);
+       if(tlen > slen)
+               return -1;
+       return memcmp(str + slen - tlen, tail, tlen);
+}
+
+static void check_conffile(const char *fname, bool server) {
+       FILE *f = fopen(fname, "r");
+       if(!f) {
+               fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
+               return;
+       }
+
+       char line[2048];
+       int lineno = 0;
+       bool skip = false;
+       const int maxvariables = 50;
+       int count[maxvariables];
+       memset(count, 0, sizeof count);
+
+       while(fgets(line, sizeof line, f)) {
+               if(skip) {
+                       if(!strncmp(line, "-----END", 8))
+                               skip = false;
+                       continue;
+               } else {
+                       if(!strncmp(line, "-----BEGIN", 10)) {
+                               skip = true;
+                               continue;
+                       }
+               }
+
+               int len;
+               char *variable, *value, *eol;
+               variable = value = line;
+
+               lineno++;
+
+               eol = line + strlen(line);
+               while(strchr("\t \r\n", *--eol))
+                       *eol = '\0';
+
+               if(!line[0] || line[0] == '#')
+                       continue;
+
+               len = strcspn(value, "\t =");
+               value += len;
+               value += strspn(value, "\t ");
+               if(*value == '=') {
+                       value++;
+                       value += strspn(value, "\t ");
+               }
+               variable[len] = '\0';
+
+               bool found = false;
+
+               for(int i = 0; variables[i].name; i++) {
+                       if(strcasecmp(variables[i].name, variable))
+                               continue;
+
+                       found = true;
+
+                       if(variables[i].type & VAR_OBSOLETE) {
+                               fprintf(stderr, "WARNING: obsolete variable %s in %s line %d\n", variable, fname, lineno);
+                       }
+
+                       if(i < maxvariables)
+                               count[i]++;
+               }
+
+               if(!found)
+                       fprintf(stderr, "WARNING: unknown variable %s in %s line %d\n", variable, fname, lineno);
+
+               if(!*value)
+                       fprintf(stderr, "ERROR: no value for variable %s in %s line %d\n", variable, fname, lineno);
+       }
+
+       for(int i = 0; variables[i].name && i < maxvariables; i++) {
+               if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE))
+                       fprintf(stderr, "WARNING: multiple instances of variable %s in %s\n", variables[i].name, fname);
+       }
+
+       if(ferror(f))
+               fprintf(stderr, "ERROR: while reading %s: %s\n", fname, strerror(errno));
+
+       fclose(f);
+}
+
+int fsck(const char *argv0) {
+       uid_t uid = getuid();
+
+       // Check that tinc.conf is readable.
+
+       if(access(tinc_conf, R_OK)) {
+               fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
+               if(errno == ENOENT) {
+                       fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
+                       print_tinc_cmd(argv0, "init");
+               } else if(errno == EACCES) {
+                       if(uid != 0)
+                               fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
+                       else
+                               fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
+               }
+               return 1;
+       }
+
+       char *name = get_my_name(true);
+       if(!name) {
+               fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
+               return 1;
+       }
+
+       // Check for private keys.
+       // TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present.
+
+       struct stat st;
+       char fname[PATH_MAX];
+       char dname[PATH_MAX];
+
+#ifndef DISABLE_LEGACY
+       rsa_t *rsa_priv = NULL;
+       snprintf(fname, sizeof fname, "%s/rsa_key.priv", confbase);
+
+       if(stat(fname, &st)) {
+               if(errno != ENOENT) {
+                       // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file.
+                       fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
+                       fprintf(stderr, "Please correct this error.\n");
+                       return 1;
+               }
+       } else {
+               FILE *f = fopen(fname, "r");
+               if(!f) {
+                       fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
+                       return 1;
+               }
+               rsa_priv = rsa_read_pem_private_key(f);
+               fclose(f);
+               if(!rsa_priv) {
+                       fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
+                       fprintf(stderr, "You can generate a new RSA key with:\n\n");
+                       print_tinc_cmd(argv0, "generate-rsa-keys");
+                       return 1;
+               }
+
+               if(st.st_mode & 077) {
+                       fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
+                       if(st.st_uid != uid) {
+                               fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
+                       } else if(ask_fix()) {
+                               if(chmod(fname, st.st_mode & ~077))
+                                       fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
+                               else
+                                       fprintf(stderr, "Fixed permissions of %s.\n", fname);
+                       }
+               }
+       }
+#endif
+
+       ecdsa_t *ecdsa_priv = NULL;
+       snprintf(fname, sizeof fname, "%s/ed25519_key.priv", confbase);
+
+       if(stat(fname, &st)) {
+               if(errno != ENOENT) {
+                       // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file.
+                       fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
+                       fprintf(stderr, "Please correct this error.\n");
+                       return 1;
+               }
+       } else {
+               FILE *f = fopen(fname, "r");
+               if(!f) {
+                       fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
+                       return 1;
+               }
+               ecdsa_priv = ecdsa_read_pem_private_key(f);
+               fclose(f);
+               if(!ecdsa_priv) {
+                       fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
+                       fprintf(stderr, "You can generate a new Ed25519 key with:\n\n");
+                       print_tinc_cmd(argv0, "generate-ed25519-keys");
+                       return 1;
+               }
+
+               if(st.st_mode & 077) {
+                       fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
+                       if(st.st_uid != uid) {
+                               fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
+                       } else if(ask_fix()) {
+                               if(chmod(fname, st.st_mode & ~077))
+                                       fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
+                               else
+                                       fprintf(stderr, "Fixed permissions of %s.\n", fname);
+                       }
+               }
+       }
+
+#ifdef DISABLE_LEGACY
+       if(!ecdsa_priv) {
+               fprintf(stderr, "ERROR: No Ed25519 private key found.\n");
+#else
+       if(!rsa_priv && !ecdsa_priv) {
+               fprintf(stderr, "ERROR: Neither RSA or Ed25519 private key found.\n");
+#endif
+               fprintf(stderr, "You can generate new keys with:\n\n");
+               print_tinc_cmd(argv0, "generate-keys");
+               return 1;
+       }
+
+       // Check for public keys.
+       // TODO: use RSAPublicKeyFile and Ed25519PublicKeyFile variables if present.
+
+       snprintf(fname, sizeof fname, "%s/hosts/%s", confbase, name);
+       if(access(fname, R_OK))
+               fprintf(stderr, "WARNING: cannot read %s\n", fname);
+
+       FILE *f;
+
+#ifndef DISABLE_LEGACY
+       rsa_t *rsa_pub = NULL;
+
+       f = fopen(fname, "r");
+       if(f)
+               rsa_pub = rsa_read_pem_public_key(f);
+       fclose(f);
+
+       if(rsa_priv) {
+               if(!rsa_pub) {
+                       fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
+                       if(ask_fix()) {
+                               FILE *f = fopen(fname, "a");
+                               if(f) {
+                                       if(rsa_write_pem_public_key(rsa_priv, f))
+                                               fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
+                                       else
+                                               fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
+                                       fclose(f);
+                               } else {
+                                       fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
+                               }
+                       }
+               } else {
+                       // TODO: suggest remedies
+                       size_t len = rsa_size(rsa_priv);
+                       if(len != rsa_size(rsa_pub)) {
+                               fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
+                               return 1;
+                       }
+                       char buf1[len], buf2[len], buf3[len];
+                       randomize(buf1, sizeof buf1);
+                       buf1[0] &= 0x7f;
+                       memset(buf2, 0, sizeof buf2);
+                       memset(buf3, 0, sizeof buf2);
+                       if(!rsa_public_encrypt(rsa_pub, buf1, sizeof buf1, buf2)) {
+                               fprintf(stderr, "ERROR: public RSA key does not work.\n");
+                               return 1;
+                       }
+                       if(!rsa_private_decrypt(rsa_priv, buf2, sizeof buf2, buf3)) {
+                               fprintf(stderr, "ERROR: private RSA key does not work.\n");
+                               return 1;
+                       }
+                       if(memcmp(buf1, buf3, sizeof buf1)) {
+                               fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
+                               return 1;
+                       }
+               }
+       } else {
+               if(rsa_pub)
+                       fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
+       }
+#endif
+       //
+       // TODO: this should read the Ed25519PublicKey config variable instead.
+       ecdsa_t *ecdsa_pub = NULL;
+
+       f = fopen(fname, "r");
+       if(f)
+               ecdsa_pub = ecdsa_read_pem_public_key(f);
+       fclose(f);
+
+       if(ecdsa_priv) {
+               if(!ecdsa_pub) {
+                       fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
+                       if(ask_fix()) {
+                               FILE *f = fopen(fname, "a");
+                               if(f) {
+                                       if(ecdsa_write_pem_public_key(ecdsa_priv, f))
+                                               fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
+                                       else
+                                               fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
+                                       fclose(f);
+                               } else {
+                                       fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
+                               }
+                       }
+               } else {
+                       // TODO: suggest remedies
+                       char *key1 = ecdsa_get_base64_public_key(ecdsa_pub);
+                       if(!key1) {
+                               fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
+                               return 1;
+                       }
+                       char *key2 = ecdsa_get_base64_public_key(ecdsa_priv);
+                       if(!key2) {
+                               free(key1);
+                               fprintf(stderr, "ERROR: private Ed25519 key does not work.\n");
+                               return 1;
+                       }
+                       int result = strcmp(key1, key2);
+                       free(key1);
+                       free(key2);
+                       if(result) {
+                               fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
+                               return 1;
+                       }
+               }
+       } else {
+               if(ecdsa_pub)
+                       fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n");
+       }
+
+       // Check whether scripts are executable
+
+       struct dirent *ent;
+       DIR *dir = opendir(confbase);
+       if(!dir) {
+               fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
+               return 1;
+       }
+
+       while((ent = readdir(dir))) {
+               if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
+                       continue;
+
+               strncpy(fname, ent->d_name, sizeof fname);
+               char *dash = strrchr(fname, '-');
+               if(!dash)
+                       continue;
+               *dash = 0;
+
+               if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
+                       static bool explained = false;
+                       fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
+                       if(!explained) {
+                               fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
+                               fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
+                               explained = true;
+                       }
+                       continue;
+               }
+
+               snprintf(fname, sizeof fname, "%s" SLASH "%s", confbase, ent->d_name);
+               if(access(fname, R_OK | X_OK)) {
+                       if(errno != EACCES) {
+                               fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
+                               continue;
+                       }
+                       fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
+                       if(ask_fix()) {
+                               if(chmod(fname, 0755))
+                                       fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
+                       }
+               }
+       }
+       closedir(dir);
+
+       snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase);
+       dir = opendir(dname);
+       if(!dir) {
+               fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno));
+               return 1;
+       }
+
+       while((ent = readdir(dir))) {
+               if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
+                       continue;
+
+               strncpy(fname, ent->d_name, sizeof fname);
+               char *dash = strrchr(fname, '-');
+               if(!dash)
+                       continue;
+               *dash = 0;
+
+               snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
+               if(access(fname, R_OK | X_OK)) {
+                       if(errno != EACCES) {
+                               fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
+                               continue;
+                       }
+                       fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
+                       if(ask_fix()) {
+                               if(chmod(fname, 0755))
+                                       fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
+                       }
+               }
+       }
+       closedir(dir);
+       
+       // Check for obsolete / unsafe / unknown configuration variables.
+
+       check_conffile(tinc_conf, true);
+
+       dir = opendir(dname);
+       if(dir) {
+               while((ent = readdir(dir))) {
+                       if(!check_id(ent->d_name))
+                               continue;
+
+                       snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
+                       check_conffile(fname, false);
+               }
+               closedir(dir);
+       }
+
+       return 0;
+}
+
diff --git a/src/fsck.h b/src/fsck.h
new file mode 100644 (file)
index 0000000..51e4f55
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+    fsck.h -- header for fsck.c.
+    Copyright (C) 2012 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_FSCK_H__
+#define __TINC_FSCK_H__
+
+extern int fsck(const char *argv0);
+
+#endif
+
index 8f601c2..49161c1 100644 (file)
@@ -238,10 +238,11 @@ static void check_reachability(void) {
 
                        n->status.udp_confirmed = false;
                        n->maxmtu = MTU;
+                       n->maxrecentlen = 0;
                        n->minmtu = 0;
                        n->mtuprobes = 0;
 
-                       timeout_del(&n->mtutimeout);
+                       timeout_del(&n->udp_ping_timeout);
 
                        char *name;
                        char *address;
@@ -276,12 +277,9 @@ static void check_reachability(void) {
                                memset(&n->status, 0, sizeof n->status);
                                n->options = 0;
                        } else if(n->connection) {
-                               if(n->status.sptps) {
-                                       if(n->connection->outgoing)
-                                               send_req_key(n);
-                               } else {
+                               // Speed up UDP probing by sending our key.
+                               if(!n->status.sptps)
                                        send_ans_key(n);
-                               }
                        }
                }
 
@@ -292,7 +290,7 @@ static void check_reachability(void) {
        if (device_standby) {
                if (reachable_count == 0 && became_unreachable_count > 0)
                        device_disable();
-               else if (reachable_count == became_reachable_count)
+               else if (reachable_count > 0 && reachable_count == became_reachable_count)
                        device_enable();
        }
 }
index 8fb9ca6..91fc3d6 100644 (file)
@@ -91,6 +91,13 @@ void *hash_search_or_insert(hash_t *hash, const void *key, const void *value) {
        return NULL;
 }
 
+/* Deleting */
+
+void hash_delete(hash_t *hash, const void *key) {
+       uint32_t i = modulo(hash_function(key, hash->size), hash->n);
+       hash->values[i] = NULL;
+}
+
 /* Utility functions */
 
 void hash_clear(hash_t *hash) {
index 83ed6af..30a15fb 100644 (file)
@@ -31,6 +31,7 @@ extern hash_t *hash_alloc(size_t n, size_t size) __attribute__ ((__malloc__));
 extern void hash_free(hash_t *);
 
 extern void hash_insert(hash_t *, const void *key, const void *value);
+extern void hash_delete(hash_t *, const void *key);
 
 extern void *hash_search(const hash_t *, const void *key);
 extern void *hash_search_or_insert(hash_t *, const void *key, const void *value);
index 85479f7..9dc3b04 100644 (file)
@@ -40,6 +40,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
+#include <math.h>
 
 #ifdef HAVE_MINGW
 #include <w32api.h>
 #include <netinet/if_ether.h>
 #endif
 
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#ifdef STATUS
+#undef STATUS
+#endif
+
 #ifdef HAVE_MINGW
 #define SLASH "\\"
 #else
index af085bc..b9a6fcf 100644 (file)
@@ -24,6 +24,7 @@
 #include "subnet.h"
 #include "tincctl.h"
 #include "info.h"
+#include "utils.h"
 #include "xalloc.h"
 
 void logger(int level, int priority, const char *format, ...) {
@@ -49,6 +50,7 @@ static int info_node(int fd, const char *item) {
        char line[4096];
 
        char node[4096];
+       char id[4096];
        char from[4096];
        char to[4096];
        char subnet[4096];
@@ -67,12 +69,12 @@ static int info_node(int fd, const char *item) {
        long int last_state_change;
 
        while(recvline(fd, line, sizeof line)) {
-               int n = sscanf(line, "%d %d %s %s port %s %d %d %d %d %x %"PRIx32" %s %s %d %hd %hd %hd %ld", &code, &req, node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
+               int n = sscanf(line, "%d %d %s %s %s port %s %d %d %d %d %x %"PRIx32" %s %s %d %hd %hd %hd %ld", &code, &req, node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_union.raw, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
 
                if(n == 2)
                        break;
 
-               if(n != 18) {
+               if(n != 19) {
                        fprintf(stderr, "Unable to parse node dump from tincd.\n");
                        return 1;
                }
@@ -94,6 +96,7 @@ static int info_node(int fd, const char *item) {
        }
 
        printf("Node:         %s\n", item);
+       printf("Node ID:      %s\n", id);
        printf("Address:      %s port %s\n", host, port);
 
        char timestr[32] = "never";
index a6996cc..3863488 100644 (file)
@@ -33,6 +33,8 @@
 #include "utils.h"
 #include "xalloc.h"
 
+#include "ed25519/sha512.h"
+
 int addressfamily = AF_UNSPEC;
 
 static void scan_for_hostname(const char *filename, char **hostname, char **port) {
@@ -270,8 +272,6 @@ int cmd_invite(int argc, char *argv[]) {
                }
        }
 
-       char hash[25];
-
        xasprintf(&filename, "%s" SLASH "invitations", confbase);
        if(mkdir(filename, 0700) && errno != EEXIST) {
                fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno));
@@ -365,11 +365,9 @@ int cmd_invite(int argc, char *argv[]) {
                return 1;
 
        // Create a hash of the key.
+       char hash[64];
        char *fingerprint = ecdsa_get_base64_public_key(key);
-       digest_t *digest = digest_open_by_name("sha256", 18);
-       if(!digest)
-               abort();
-       digest_create(digest, fingerprint, strlen(fingerprint), hash);
+       sha512(fingerprint, strlen(fingerprint), hash);
        b64encode_urlsafe(hash, hash, 18);
 
        // Create a random cookie for this invitation.
@@ -378,10 +376,10 @@ int cmd_invite(int argc, char *argv[]) {
 
        // Create a filename that doesn't reveal the cookie itself
        char buf[18 + strlen(fingerprint)];
-       char cookiehash[25];
+       char cookiehash[64];
        memcpy(buf, cookie, 18);
        memcpy(buf + 18, fingerprint, sizeof buf - 18);
-       digest_create(digest, buf, sizeof buf, cookiehash);
+       sha512(buf, sizeof buf, cookiehash);
        b64encode_urlsafe(cookiehash, cookiehash, 18);
 
        b64encode_urlsafe(cookie, cookie, 18);
@@ -613,6 +611,7 @@ make_names:
        FILE *fh = fopen(filename, "w");
        if(!fh) {
                fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+               fclose(f);
                return false;
        }
 
@@ -738,8 +737,9 @@ make_names:
 
        sptps_send_record(&sptps, 1, b64key, strlen(b64key));
        free(b64key);
+       ecdsa_free(key);
 
-
+#ifndef DISABLE_LEGACY
        rsa_t *rsa = rsa_generate(2048, 0x1001);
        xasprintf(&filename, "%s" SLASH "rsa_key.priv", confbase);
        f = fopenmask(filename, "w", 0600);
@@ -750,8 +750,8 @@ make_names:
        rsa_write_pem_public_key(rsa, fh);
        fclose(fh);
 
-       ecdsa_free(key);
        rsa_free(rsa);
+#endif
 
        check_port(name);
 
@@ -786,7 +786,7 @@ ask_netname:
 }
 
 
-static bool invitation_send(void *handle, uint8_t type, const char *data, size_t len) {
+static bool invitation_send(void *handle, uint8_t type, const void *data, size_t len) {
        while(len) {
                int result = send(sock, data, len, 0);
                if(result == -1 && errno == EINTR)
@@ -799,7 +799,7 @@ static bool invitation_send(void *handle, uint8_t type, const char *data, size_t
        return true;
 }
 
-static bool invitation_receive(void *handle, uint8_t type, const char *msg, uint16_t len) {
+static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
        switch(type) {
                case SPTPS_HANDSHAKE:
                        return sptps_send_record(&sptps, 0, cookie, sizeof cookie);
@@ -957,11 +957,8 @@ int cmd_join(int argc, char *argv[]) {
 
        // Check if the hash of the key he gave us matches the hash in the URL.
        char *fingerprint = line + 2;
-       digest_t *digest = digest_open_by_name("sha256", 18);
-       if(!digest)
-               abort();
-       char hishash[18];
-       if(!digest_create(digest, fingerprint, strlen(fingerprint), hishash)) {
+       char hishash[64];
+       if(sha512(fingerprint, strlen(fingerprint), hishash)) {
                fprintf(stderr, "Could not create digest\n%s\n", line + 2);
                return 1;
        }
index cfd99ff..5717d92 100644 (file)
@@ -1,7 +1,7 @@
 /*
     device.c -- Interaction with Linux ethertap and tun/tap device
     Copyright (C) 2001-2005 Ivo Timmermans,
-                  2001-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2001-2014 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
@@ -105,6 +105,14 @@ static bool setup_device(void) {
 
        logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
 
+       if(ifr.ifr_flags & IFF_TAP) {
+               struct ifreq ifr_mac = {};
+               if(!ioctl(device_fd, SIOCGIFHWADDR, &ifr_mac))
+                       memcpy(mymac.x, ifr_mac.ifr_hwaddr.sa_data, ETH_ALEN);
+               else
+                       logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get MAC address of %s: %s", device, strerror(errno));
+       }
+
        return true;
 }
 
@@ -123,7 +131,7 @@ static bool read_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       inlen = read(device_fd, packet->data + 10, MTU - 10);
+                       inlen = read(device_fd, DATA(packet) + 10, MTU - 10);
 
                        if(inlen <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
@@ -131,11 +139,11 @@ static bool read_packet(vpn_packet_t *packet) {
                                return false;
                        }
 
-                       memset(packet->data, 0, 12);
+                       memset(DATA(packet), 0, 12);
                        packet->len = inlen + 10;
                        break;
                case DEVICE_TYPE_TAP:
-                       inlen = read(device_fd, packet->data, MTU);
+                       inlen = read(device_fd, DATA(packet), MTU);
 
                        if(inlen <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s",
@@ -161,15 +169,15 @@ static bool write_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       packet->data[10] = packet->data[11] = 0;
-                       if(write(device_fd, packet->data + 10, packet->len - 10) < 0) {
+                       DATA(packet)[10] = DATA(packet)[11] = 0;
+                       if(write(device_fd, DATA(packet) + 10, packet->len - 10) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
                                           strerror(errno));
                                return false;
                        }
                        break;
                case DEVICE_TYPE_TAP:
-                       if(write(device_fd, packet->data, packet->len) < 0) {
+                       if(write(device_fd, DATA(packet), packet->len) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
                                           strerror(errno));
                                return false;
index 637eef8..8f69029 100644 (file)
@@ -65,6 +65,8 @@ enum {
 #endif
 #endif
 
+#include <stdbool.h>
+
 extern debug_t debug_level;
 extern bool logcontrol;
 extern void openlogger(const char *, logmode_t);
index 887da4a..1c29fe9 100644 (file)
@@ -1,6 +1,6 @@
 /*
     meta.c -- handle the meta communication
-    Copyright (C) 2000-2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2000-2014 Guus Sliepen <guus@tinc-vpn.org>,
                   2000-2005 Ivo Timmermans
                   2006      Scott Lamb <slamb@slamb.org>
 
@@ -30,7 +30,7 @@
 #include "utils.h"
 #include "xalloc.h"
 
-bool send_meta_sptps(void *handle, uint8_t type, const char *buffer, size_t length) {
+bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t length) {
        connection_t *c = handle;
 
        if(!c) {
@@ -58,6 +58,9 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
 
        /* Add our data to buffer */
        if(c->status.encryptout) {
+#ifdef DISABLE_LEGACY
+               return false;
+#else
                size_t outlen = length;
 
                if(!cipher_encrypt(c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
@@ -65,6 +68,7 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
                                        c->name, c->hostname);
                        return false;
                }
+#endif
        } else {
                buffer_add(&c->outbuf, buffer, length);
        }
@@ -76,11 +80,12 @@ bool send_meta(connection_t *c, const char *buffer, int length) {
 
 void broadcast_meta(connection_t *from, const char *buffer, int length) {
        for list_each(connection_t, c, connection_list)
-               if(c != from && c->status.active)
+               if(c != from && c->edge)
                        send_meta(c, buffer, length);
 }
 
-bool receive_meta_sptps(void *handle, uint8_t type, const char *data, uint16_t length) {
+bool receive_meta_sptps(void *handle, uint8_t type, const void *vdata, uint16_t length) {
+       const char *data = vdata;
        connection_t *c = handle;
 
        if(!c) {
@@ -142,7 +147,7 @@ bool receive_meta(connection_t *c) {
        inlen = recv(c->socket, inbuf, sizeof inbuf - c->inbuf.len, 0);
 
        if(inlen <= 0) {
-               if(!inlen || !errno) {
+               if(!inlen || !sockerrno) {
                        logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)",
                                           c->name, c->hostname);
                } else if(sockwouldblock(sockerrno))
@@ -169,6 +174,9 @@ bool receive_meta(connection_t *c) {
                        inlen -= endp - bufp;
                        bufp = endp;
                } else {
+#ifdef DISABLE_LEGACY
+                       return false;
+#else
                        size_t outlen = inlen;
 
                        if(!cipher_decrypt(c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
@@ -178,6 +186,7 @@ bool receive_meta(connection_t *c) {
                        }
 
                        inlen = 0;
+#endif
                }
 
                while(c->inbuf.len) {
index 2a71228..ddc5418 100644 (file)
@@ -1,6 +1,6 @@
 /*
     meta.h -- header for meta.c
-    Copyright (C) 2000-2012 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2000-2014 Guus Sliepen <guus@tinc-vpn.org>,
                   2000-2005 Ivo Timmermans
 
     This program is free software; you can redistribute it and/or modify
@@ -24,8 +24,8 @@
 #include "connection.h"
 
 extern bool send_meta(struct connection_t *, const char *, int);
-extern bool send_meta_sptps(void *, uint8_t, const char *, size_t);
-extern bool receive_meta_sptps(void *, uint8_t, const char *, uint16_t);
+extern bool send_meta_sptps(void *, uint8_t, const void *, size_t);
+extern bool receive_meta_sptps(void *, uint8_t, const void *, uint16_t);
 extern void broadcast_meta(struct connection_t *, const char *, int);
 extern bool receive_meta(struct connection_t *);
 
index a765ce3..19719a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
     device.c -- Interaction with Windows tap driver in a MinGW environment
     Copyright (C) 2002-2005 Ivo Timmermans,
-                  2002-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2002-2014 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
 
 int device_fd = -1;
 static HANDLE device_handle = INVALID_HANDLE_VALUE;
+static io_t device_read_io;
+static OVERLAPPED device_read_overlapped;
+static vpn_packet_t device_read_packet;
 char *device = NULL;
 char *iface = NULL;
 static char *device_info = NULL;
 
-static uint64_t device_total_in = 0;
-static uint64_t device_total_out = 0;
-
 extern char *myport;
 
-static DWORD WINAPI tapreader(void *bla) {
-       int status;
-       DWORD len;
-       OVERLAPPED overlapped;
-       vpn_packet_t packet;
-
-       logger(DEBUG_ALWAYS, LOG_DEBUG, "Tap reader running");
-
-       /* Read from tap device and send to parent */
-
-       overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-
-       for(;;) {
-               overlapped.Offset = 0;
-               overlapped.OffsetHigh = 0;
-               ResetEvent(overlapped.hEvent);
+static void device_issue_read() {
+       device_read_overlapped.Offset = 0;
+       device_read_overlapped.OffsetHigh = 0;
 
-               status = ReadFile(device_handle, (void *)packet.data, MTU, &len, &overlapped);
-
-               if(!status) {
-                       if(GetLastError() == ERROR_IO_PENDING) {
-                               WaitForSingleObject(overlapped.hEvent, INFINITE);
-                               if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
-                                       continue;
-                       } else {
+       int status;
+       for (;;) {
+               DWORD len;
+               status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
+               if (!status) {
+                       if (GetLastError() != ERROR_IO_PENDING)
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
-                               return -1;
-                       }
+                       break;
                }
 
-               EnterCriticalSection(&mutex);
-               packet.len = len;
-               packet.priority = 0;
-               route(myself, &packet);
-               event_flush_output();
-               LeaveCriticalSection(&mutex);
+               device_read_packet.len = len;
+               device_read_packet.priority = 0;
+               route(myself, &device_read_packet);
+       }
+}
+
+static void device_handle_read(void *data, int flags) {
+       ResetEvent(device_read_overlapped.hEvent);
+
+       DWORD len;
+       if (!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
+                          device, strerror(errno));
+               return;
        }
+
+       device_read_packet.len = len;
+       device_read_packet.priority = 0;
+       route(myself, &device_read_packet);
+       device_issue_read();
 }
 
 static bool setup_device(void) {
@@ -98,7 +95,6 @@ static bool setup_device(void) {
        bool found = false;
 
        int err;
-       HANDLE thread;
 
        get_config_string(lookup_config(config_tree, "Device"), &device);
        get_config_string(lookup_config(config_tree, "Interface"), &iface);
@@ -190,15 +186,6 @@ static bool setup_device(void) {
                overwrite_mac = 1;
        }
 
-       /* Start the tap reader */
-
-       thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
-
-       if(!thread) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "CreateThread", winerror(GetLastError()));
-               return false;
-       }
-
        device_info = "Windows tap device";
 
        logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
@@ -208,13 +195,23 @@ static bool setup_device(void) {
 
 static void enable_device(void) {
        logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
+
        ULONG status = 1;
        DWORD len;
        DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
+
+       io_add_event(&device_read_io, device_handle_read, NULL, CreateEvent(NULL, TRUE, FALSE, NULL));
+       device_read_overlapped.hEvent = device_read_io.event;
+       device_issue_read();
 }
 
 static void disable_device(void) {
        logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
+
+       io_del(&device_read_io);
+       CancelIo(device_handle);
+       CloseHandle(device_read_overlapped.hEvent);
+
        ULONG status = 0;
        DWORD len;
        DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
@@ -239,13 +236,11 @@ static bool write_packet(vpn_packet_t *packet) {
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
+       if(!WriteFile(device_handle, DATA(packet), packet->len, &outlen, &overlapped)) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
 
-       device_total_out += packet->len;
-
        return true;
 }
 
index ba272eb..931a8d0 100644 (file)
@@ -129,7 +129,7 @@ static bool setup_device(void) {
 #endif
 
                default:
-                       logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %hx unsupported", ai->ai_family);
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %x unsupported", ai->ai_family);
                        goto error;
        }
 
@@ -162,13 +162,13 @@ static void close_device(void) {
 static bool read_packet(vpn_packet_t *packet) {
        int lenin;
 
-       if((lenin = recv(device_fd, (void *)packet->data, MTU, 0)) <= 0) {
+       if((lenin = recv(device_fd, DATA(packet), MTU, 0)) <= 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
-                          device, strerror(errno));
+                          device, sockstrerror(sockerrno));
                return false;
        }
 
-       if(!memcmp(&ignore_src, packet->data + 6, sizeof ignore_src)) {
+       if(!memcmp(&ignore_src, DATA(packet) + 6, sizeof ignore_src)) {
                logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
                return false;
        }
@@ -185,13 +185,13 @@ static bool write_packet(vpn_packet_t *packet) {
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(sendto(device_fd, (void *)packet->data, packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
+       if(sendto(device_fd, DATA(packet), packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
-                          strerror(errno));
+                          sockstrerror(sockerrno));
                return false;
        }
 
-       memcpy(&ignore_src, packet->data + 6, sizeof ignore_src);
+       memcpy(&ignore_src, DATA(packet) + 6, sizeof ignore_src);
 
        return true;
 }
index 37708f8..8218216 100644 (file)
@@ -64,8 +64,6 @@ void make_names(void) {
                                else
                                        xasprintf(&confbase, "%s", installdir);
                        }
-                       if(!pidfilename)
-                               xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
                }
                RegCloseKey(key);
        }
@@ -73,11 +71,26 @@ void make_names(void) {
        if(!confdir)
                confdir = xstrdup(CONFDIR SLASH "tinc");
 
+       if(!confbase) {
+               if(netname)
+                       xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
+               else
+                       xasprintf(&confbase, CONFDIR SLASH "tinc");
+       }
+
+#ifdef HAVE_MINGW
+       if(!logfilename)
+               xasprintf(&logfilename, "%s" SLASH "log", confbase);
+
+       if(!pidfilename)
+               xasprintf(&pidfilename, "%s" SLASH "pid", confbase);
+#else
        if(!logfilename)
                xasprintf(&logfilename, LOCALSTATEDIR SLASH "log" SLASH "%s.log", identname);
 
        if(!pidfilename)
                xasprintf(&pidfilename, LOCALSTATEDIR SLASH "run" SLASH "%s.pid", identname);
+#endif
 
        if(!unixsocketname) {
                int len = strlen(pidfilename);
@@ -88,13 +101,6 @@ void make_names(void) {
                else
                        strcpy(unixsocketname + len, ".socket");
        }
-
-       if(!confbase) {
-               if(netname)
-                       xasprintf(&confbase, CONFDIR SLASH "tinc" SLASH "%s", netname);
-               else
-                       xasprintf(&confbase, CONFDIR SLASH "tinc");
-       }
 }
 
 void free_names(void) {
index 1e6b4c6..1ce8071 100644 (file)
--- a/src/net.c
+++ b/src/net.c
 #include "subnet.h"
 #include "xalloc.h"
 
-#ifdef HAVE_RESOLV_H
-#include <resolv.h>
-#endif
-
 int contradicting_add_edge = 0;
 int contradicting_del_edge = 0;
 static int sleeptime = 10;
@@ -97,8 +93,6 @@ void purge(void) {
 void terminate_connection(connection_t *c, bool report) {
        logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
 
-       c->status.active = false;
-
        if(c->node && c->node->connection == c)
                c->node->connection = NULL;
 
@@ -155,7 +149,8 @@ static void timeout_handler(void *data) {
                        continue;
 
                if(c->last_ping_time + pingtimeout <= now.tv_sec) {
-                       if(c->status.active) {
+                       if(c->edge) {
+                               try_tx(c->node, false);
                                if(c->status.pinged) {
                                        logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
                                } else if(c->last_ping_time + pinginterval <= now.tv_sec) {
@@ -170,11 +165,12 @@ static void timeout_handler(void *data) {
                                else
                                        logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
                        }
-                       terminate_connection(c, c->status.active);
+                       terminate_connection(c, c->edge);
                }
+
        }
 
-       timeout_set(data, &(struct timeval){pingtimeout, rand() % 100000});
+       timeout_set(data, &(struct timeval){1, rand() % 100000});
 }
 
 static void periodic_handler(void *data) {
@@ -204,7 +200,7 @@ static void periodic_handler(void *data) {
                /* Count number of active connections */
                int nc = 0;
                for list_each(connection_t, c, connection_list) {
-                       if(c->status.active && !c->status.control)
+                       if(c->edge)
                                nc++;
                }
 
@@ -251,7 +247,7 @@ static void periodic_handler(void *data) {
                        int i = 0;
 
                        for list_each(connection_t, c, connection_list) {
-                               if(!c->status.active || c->status.control)
+                               if(!c->edge)
                                        continue;
 
                                if(i++ != r)
@@ -263,7 +259,7 @@ static void periodic_handler(void *data) {
                                logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
                                list_delete(outgoing_list, c->outgoing);
                                c->outgoing = NULL;
-                               terminate_connection(c, c->status.active);
+                               terminate_connection(c, c->edge);
                                break;
                        }
                }
@@ -293,7 +289,7 @@ static void periodic_handler(void *data) {
 
 void handle_meta_connection_data(connection_t *c) {
        if (!receive_meta(c)) {
-               terminate_connection(c, c->status.active);
+               terminate_connection(c, c->edge);
                return;
        }
 }
@@ -313,9 +309,6 @@ static void sighup_handler(void *data) {
 
 static void sigalrm_handler(void *data) {
        logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
-#ifdef HAVE_DECL_RES_INIT
-       res_init();
-#endif
        retry();
 }
 #endif
@@ -347,11 +340,14 @@ int reload_configuration(void) {
 
        if(strictsubnets) {
                for splay_each(subnet_t, subnet, subnet_tree)
-                       subnet->expires = 1;
+                       if (subnet->owner)
+                               subnet->expires = 1;
 
                load_all_subnets();
 
                for splay_each(subnet_t, subnet, subnet_tree) {
+                       if (!subnet->owner)
+                               continue;
                        if(subnet->expires == 1) {
                                send_del_subnet(everyone, subnet);
                                if(subnet->owner->status.reachable)
@@ -415,7 +411,7 @@ int reload_configuration(void) {
                struct stat s;
                if(stat(fname, &s) || s.st_mtime > last_config_check) {
                        logger(DEBUG_CONNECTIONS, LOG_INFO, "Host config file of %s has been changed", c->name);
-                       terminate_connection(c, c->status.active);
+                       terminate_connection(c, c->edge);
                }
                free(fname);
        }
@@ -465,7 +461,7 @@ int main_loop(void) {
 #endif
 
        if(!event_loop()) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno));
+               logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
                return 1;
        }
 
index f0ece89..cfc44d2 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -32,8 +32,8 @@
 #define MTU 1518        /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
 #endif
 
-/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + padding + HMAC + compressor overhead */
-#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)
+/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + srcid + dstid + padding + HMAC + compressor overhead */
+#define MAXSIZE (MTU + 4 + sizeof(node_id_t) + sizeof(node_id_t) + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)
 
 /* MAXBUFSIZE is the maximum size of a request: enough for a MAXSIZEd packet or a 8192 bits RSA key */
 #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128)
@@ -52,7 +52,12 @@ typedef struct ipv6_t {
        uint16_t x[8];
 } ipv6_t;
 
+typedef struct node_id_t {
+       uint8_t x[6];
+} node_id_t;
+
 typedef short length_t;
+typedef uint32_t seqno_t;
 
 #define AF_UNKNOWN 255
 
@@ -80,10 +85,16 @@ typedef union sockaddr_t {
 #define SALEN(s) (s.sa_family==AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))
 #endif
 
+#define SEQNO(x) ((x)->data + (x)->offset - 4)
+#define SRCID(x) ((node_id_t *)((x)->data + (x)->offset - 6))
+#define DSTID(x) ((node_id_t *)((x)->data + (x)->offset - 12))
+#define DATA(x) ((x)->data + (x)->offset)
+#define DEFAULT_PACKET_OFFSET 12
+
 typedef struct vpn_packet_t {
-       length_t len;           /* the actual number of bytes in the `data' field */
+       length_t len;           /* The actual number of valid bytes in the `data' field (including seqno or dstid/srcid) */
+       length_t offset;        /* Offset in the buffer where the packet data starts (righter after seqno or dstid/srcid) */
        int priority;           /* priority or TOS */
-       uint32_t seqno;         /* 32 bits sequence number (network byte order of course) */
        uint8_t data[MAXSIZE];
 } vpn_packet_t;
 
@@ -126,7 +137,11 @@ extern int seconds_till_retry;
 extern int addressfamily;
 extern unsigned replaywin;
 extern bool localdiscovery;
-extern sockaddr_t localdiscovery_address;
+
+extern bool udp_discovery;
+extern int udp_discovery_keepalive_interval;
+extern int udp_discovery_interval;
+extern int udp_discovery_timeout;
 
 extern listen_socket_t listen_socket[MAXSOCKETS];
 extern int listen_sockets;
@@ -173,8 +188,8 @@ extern void handle_new_meta_connection(void *, int);
 extern void handle_new_unix_connection(void *, int);
 extern int setup_listen_socket(const sockaddr_t *);
 extern int setup_vpn_in_socket(const sockaddr_t *);
-extern bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len);
-extern bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len);
+extern bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
+extern bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len);
 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 *);
@@ -191,7 +206,6 @@ extern void terminate_connection(struct connection_t *, bool);
 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(void *, int);
 extern void handle_meta_connection_data(struct connection_t *);
 extern void regenerate_key(void);
@@ -200,11 +214,10 @@ extern void retry(void);
 extern int reload_configuration(void);
 extern void load_all_subnets(void);
 extern void load_all_nodes(void);
+extern void try_tx(struct node_t *n, bool);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
-#else
-extern CRITICAL_SECTION mutex;
 #endif
 
 #endif /* __TINC_NET_H__ */
index 70b6106..8dba325 100644 (file)
@@ -1,7 +1,7 @@
 /*
     net_packet.c -- Handles in- and outgoing VPN packets
     Copyright (C) 1998-2005 Ivo Timmermans,
-                  2000-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2014 Guus Sliepen <guus@tinc-vpn.org>
                   2010      Timothy Redaelli <timothy@redaelli.eu>
                   2010      Brandon Black <blblack@gmail.com>
 
@@ -37,6 +37,8 @@
 #include "digest.h"
 #include "device.h"
 #include "ethernet.h"
+#include "ipv4.h"
+#include "ipv6.h"
 #include "graph.h"
 #include "logger.h"
 #include "net.h"
 #include "utils.h"
 #include "xalloc.h"
 
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* The minimum size of a probe is 14 bytes, but since we normally use CBC mode
+   encryption, we can add a few extra random bytes without increasing the
+   resulting packet size. */
+#define MIN_PROBE_SIZE 18
+
 int keylifetime = 0;
 #ifdef HAVE_LZO
 static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
@@ -54,225 +65,117 @@ static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999
 static void send_udppacket(node_t *, vpn_packet_t *);
 
 unsigned replaywin = 16;
-bool localdiscovery = false;
-sockaddr_t localdiscovery_address;
+bool localdiscovery = true;
+bool udp_discovery = true;
+int udp_discovery_keepalive_interval = 10;
+int udp_discovery_interval = 2;
+int udp_discovery_timeout = 30;
 
 #define MAX_SEQNO 1073741824
 
-/* mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
-   mtuprobes ==    31: sleep pinginterval seconds
-   mtuprobes ==    32: send 1 burst, sleep pingtimeout second
-   mtuprobes ==    33: no response from other side, restart PMTU discovery process
-
-   Probes are sent in batches of at least three, with random sizes between the
-   lower and upper boundaries for the MTU thus far discovered.
-
-   After the initial discovery, a fourth packet is added to each batch with a
-   size larger than the currently known PMTU, to test if the PMTU has increased.
-
-   In case local discovery is enabled, another packet is added to each batch,
-   which will be broadcast to the local network.
-
-*/
-
-static void send_mtu_probe_handler(void *data) {
-       node_t *n = data;
-       int timeout = 1;
-
-       n->mtuprobes++;
-
-       if(!n->status.reachable || !n->status.validkey) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
-               n->mtuprobes = 0;
+static void try_fix_mtu(node_t *n) {
+       if(n->mtuprobes < 0)
                return;
-       }
-
-       if(n->mtuprobes > 32) {
-               if(!n->minmtu) {
-                       n->mtuprobes = 31;
-                       timeout = pinginterval;
-                       goto end;
-               }
 
-               logger(DEBUG_TRAFFIC, LOG_INFO, "%s (%s) did not respond to UDP ping, restarting PMTU discovery", n->name, n->hostname);
-               n->status.udp_confirmed = false;
-               n->mtuprobes = 1;
-               n->minmtu = 0;
-               n->maxmtu = MTU;
-       }
-
-       if(n->mtuprobes >= 10 && n->mtuprobes < 32 && !n->minmtu) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "No response to MTU probes from %s (%s)", n->name, n->hostname);
-               n->mtuprobes = 31;
-       }
-
-       if(n->mtuprobes == 30 || (n->mtuprobes < 30 && n->minmtu >= n->maxmtu)) {
+       if(n->mtuprobes == 20 || n->minmtu >= n->maxmtu) {
                if(n->minmtu > n->maxmtu)
                        n->minmtu = n->maxmtu;
                else
                        n->maxmtu = n->minmtu;
                n->mtu = n->minmtu;
                logger(DEBUG_TRAFFIC, LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
-               n->mtuprobes = 31;
-       }
-
-       if(n->mtuprobes == 31) {
-               timeout = pinginterval;
-               goto end;
-       } else if(n->mtuprobes == 32) {
-               timeout = pingtimeout;
+               n->mtuprobes = -1;
        }
+}
 
-       for(int i = 0; i < 4 + localdiscovery; i++) {
-               int len;
-
-               if(i == 0) {
-                       if(n->mtuprobes < 30 || n->maxmtu + 8 >= MTU)
-                               continue;
-                       len = n->maxmtu + 8;
-               } else if(n->maxmtu <= n->minmtu) {
-                       len = n->maxmtu;
-               } else {
-                       len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
-               }
-
-               if(len < 64)
-                       len = 64;
-
-               vpn_packet_t packet;
-               memset(packet.data, 0, 14);
-               randomize(packet.data + 14, len - 14);
-               packet.len = len;
-               packet.priority = 0;
-               n->status.broadcast = i >= 4 && n->mtuprobes <= 10 && n->prevedge;
+static void udp_probe_timeout_handler(void *data) {
+       node_t *n = data;
+       if(!n->status.udp_confirmed)
+               return;
 
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Too much time has elapsed since last UDP ping response from %s (%s), stopping UDP communication", n->name, n->hostname);
+       n->status.udp_confirmed = false;
+       n->maxrecentlen = 0;
+       n->mtuprobes = 0;
+       n->minmtu = 0;
+       n->maxmtu = MTU;
+}
 
-               send_udppacket(n, &packet);
+static void send_udp_probe_reply(node_t *n, vpn_packet_t *packet, length_t len) {
+       if(!n->status.sptps && !n->status.validkey) {
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP probe reply to %s (%s) but we don't have his key yet", n->name, n->hostname);
+               return;
        }
 
-       n->status.broadcast = false;
-       n->probe_counter = 0;
-       gettimeofday(&n->probe_time, NULL);
-
-       /* Calculate the packet loss of incoming traffic by comparing the rate of
-          packets received to the rate with which the sequence number has increased.
-        */
+       /* Type 2 probe replies were introduced in protocol 17.3 */
+       if ((n->options >> 24) >= 3) {
+               DATA(packet)[0] = 2;
+               uint16_t len16 = htons(len);
+               memcpy(DATA(packet) + 1, &len16, 2);
+               packet->len = MIN_PROBE_SIZE;
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending type 2 probe reply length %u to %s (%s)", len, n->name, n->hostname);
 
-       if(n->received > n->prev_received)
-               n->packetloss = 1.0 - (n->received - n->prev_received) / (float)(n->received_seqno - n->prev_received_seqno);
-       else
-               n->packetloss = n->received_seqno <= n->prev_received_seqno;
-
-       n->prev_received_seqno = n->received_seqno;
-       n->prev_received = n->received;
+       } else {
+               /* Legacy protocol: n won't understand type 2 probe replies. */
+               DATA(packet)[0] = 1;
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending type 1 probe reply length %u to %s (%s)", len, n->name, n->hostname);
+       }
 
-end:
-       timeout_set(&n->mtutimeout, &(struct timeval){timeout, rand() % 100000});
-}
+       /* Temporarily set udp_confirmed, so that the reply is sent
+          back exactly the way it came in. */
 
-void send_mtu_probe(node_t *n) {
-       timeout_add(&n->mtutimeout, send_mtu_probe_handler, n, &(struct timeval){1, 0});
-       send_mtu_probe_handler(n);
+       bool udp_confirmed = n->status.udp_confirmed;
+       n->status.udp_confirmed = true;
+       send_udppacket(n, packet);
+       n->status.udp_confirmed = udp_confirmed;
 }
 
-static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
-       if(!packet->data[0]) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Got MTU probe request %d from %s (%s)", packet->len, n->name, n->hostname);
-
-               /* It's a probe request, send back a reply */
-
-               /* Type 2 probe replies were introduced in protocol 17.3 */
-               if ((n->options >> 24) == 3) {
-                       uint8_t* data = packet->data;
-                       *data++ = 2;
-                       uint16_t len16 = htons(len); memcpy(data, &len16, 2); data += 2;
-                       struct timeval now;
-                       gettimeofday(&now, NULL);
-                       uint32_t sec = htonl(now.tv_sec); memcpy(data, &sec, 4); data += 4;
-                       uint32_t usec = htonl(now.tv_usec); memcpy(data, &usec, 4); data += 4;
-                       packet->len = data - packet->data;
-               } else {
-                       /* Legacy protocol: n won't understand type 2 probe replies. */
-                       packet->data[0] = 1;
-               }
+static void udp_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
+       if(!DATA(packet)[0]) {
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Got UDP probe request %d from %s (%s)", packet->len, n->name, n->hostname);
+               return send_udp_probe_reply(n, packet, len);
+       }
 
-               /* Temporarily set udp_confirmed, so that the reply is sent
-                  back exactly the way it came in. */
+       if (DATA(packet)[0] == 2) {
+               // It's a type 2 probe reply, use the length field inside the packet
+               uint16_t len16;
+               memcpy(&len16, DATA(packet) + 1, 2);
+               len = ntohs(len16);
+       }
 
-               bool udp_confirmed = n->status.udp_confirmed;
-               n->status.udp_confirmed = true;
-               send_udppacket(n, packet);
-               n->status.udp_confirmed = udp_confirmed;
-       } else {
-               length_t probelen = len;
-               if (packet->data[0] == 2) {
-                       if (len < 3)
-                               logger(DEBUG_TRAFFIC, LOG_WARNING, "Received invalid (too short) MTU probe reply from %s (%s)", n->name, n->hostname);
-                       else {
-                               uint16_t probelen16; memcpy(&probelen16, packet->data + 1, 2); probelen = ntohs(probelen16);
-                       }
-               }
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Got type %d MTU probe reply %d from %s (%s)", packet->data[0], probelen, n->name, n->hostname);
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Got type %d UDP probe reply %d from %s (%s)", DATA(packet)[0], len, n->name, n->hostname);
 
-               /* It's a valid reply: now we know bidirectional communication
-                  is possible using the address and socket that the reply
-                  packet used. */
+       /* It's a valid reply: now we know bidirectional communication
+          is possible using the address and socket that the reply
+          packet used. */
+       n->status.udp_confirmed = true;
 
-               n->status.udp_confirmed = true;
+       // Reset the UDP ping timer.
+       n->udp_ping_sent = now;
 
-               /* If we haven't established the PMTU yet, restart the discovery process. */
+       if(udp_discovery) {
+               timeout_del(&n->udp_ping_timeout);
+               timeout_add(&n->udp_ping_timeout, &udp_probe_timeout_handler, n, &(struct timeval){udp_discovery_timeout, 0});
+       }
 
-               if(n->mtuprobes > 30) {
-                       if (probelen == n->maxmtu + 8) {
-                               logger(DEBUG_TRAFFIC, LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
-                               n->maxmtu = MTU;
-                               n->mtuprobes = 10;
-                               return;
-                       }
+       if(len > n->maxmtu) {
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+               n->minmtu = len;
+               n->maxmtu = MTU;
+               /* Set mtuprobes to 1 so that try_mtu() doesn't reset maxmtu */
+               n->mtuprobes = 1;
+               return;
+       } else if(n->mtuprobes < 0 && len == n->maxmtu) {
+               /* We got a maxmtu sized packet, confirming the PMTU is still valid. */
+               n->mtuprobes = -1;
+               n->mtu_ping_sent = now;
+       }
 
-                       if(n->minmtu)
-                               n->mtuprobes = 30;
-                       else
-                               n->mtuprobes = 1;
-               }
+       /* If applicable, raise the minimum supported MTU */
 
-               /* If applicable, raise the minimum supported MTU */
-
-               if(probelen > n->maxmtu)
-                       probelen = n->maxmtu;
-               if(n->minmtu < probelen)
-                       n->minmtu = probelen;
-
-               /* Calculate RTT and bandwidth.
-                  The RTT is the time between the MTU probe burst was sent and the first
-                  reply is received. The bandwidth is measured using the time between the
-                  arrival of the first and third probe reply (or type 2 probe requests).
-                */
-
-               struct timeval now, diff;
-               gettimeofday(&now, NULL);
-               timersub(&now, &n->probe_time, &diff);
-
-               struct timeval probe_timestamp = now;
-               if (packet->data[0] == 2 && packet->len >= 11) {
-                       uint32_t sec; memcpy(&sec, packet->data + 3, 4);
-                       uint32_t usec; memcpy(&usec, packet->data + 7, 4);
-                       probe_timestamp.tv_sec = ntohl(sec);
-                       probe_timestamp.tv_usec = ntohl(usec);
-               }
-               
-               n->probe_counter++;
-
-               if(n->probe_counter == 1) {
-                       n->rtt = diff.tv_sec + diff.tv_usec * 1e-6;
-                       n->probe_time = probe_timestamp;
-               } else if(n->probe_counter == 3) {
-                       struct timeval probe_timestamp_diff;
-                       timersub(&probe_timestamp, &n->probe_time, &probe_timestamp_diff);
-                       n->bandwidth = 2.0 * probelen / (probe_timestamp_diff.tv_sec + probe_timestamp_diff.tv_usec * 1e-6);
-                       logger(DEBUG_TRAFFIC, LOG_DEBUG, "%s (%s) RTT %.2f ms, burst bandwidth %.3f Mbit/s, rx packet loss %.2f %%", n->name, n->hostname, n->rtt * 1e3, n->bandwidth * 8e-6, n->packetloss * 1e2);
-               }
+       if(n->minmtu < len) {
+               n->minmtu = len;
+               try_fix_mtu(n);
        }
 }
 
@@ -349,20 +252,25 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
 
 static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
        if(n->status.sptps)
-               return sptps_verify_datagram(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
+               return sptps_verify_datagram(&n->sptps, DATA(inpkt), inpkt->len);
 
-       if(!digest_active(n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest))
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       if(!n->status.validkey_in || !digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
                return false;
 
-       return digest_verify(n->indigest, &inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest));
+       return digest_verify(n->indigest, SEQNO(inpkt), inpkt->len - digest_length(n->indigest), DATA(inpkt) + inpkt->len - digest_length(n->indigest));
+#endif
 }
 
-static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
+static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        vpn_packet_t pkt1, pkt2;
        vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
        int nextpkt = 0;
-       vpn_packet_t *outpkt = pkt[0];
        size_t outlen;
+       pkt1.offset = DEFAULT_PACKET_OFFSET;
+       pkt2.offset = DEFAULT_PACKET_OFFSET;
 
        if(n->status.sptps) {
                if(!n->sptps.state) {
@@ -372,43 +280,58 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                        } else {
                                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
                        }
-                       return;
+                       return false;
                }
-               sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len);
-               return;
+               inpkt->offset += 2 * sizeof(node_id_t);
+               n->status.udppacket = true;
+               bool result = sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len - 2 * sizeof(node_id_t));
+               n->status.udppacket = false;
+
+               if(!result) {
+                       logger(DEBUG_TRAFFIC, LOG_ERR, "Got bad packet from %s (%s)", n->name, n->hostname);
+                       return false;
+               }
+               return true;
        }
 
-       if(!n->status.validkey) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       if(!n->status.validkey_in) {
                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname);
-               return;
+               return false;
        }
 
        /* Check packet length */
 
-       if(inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest)) {
+       if(inpkt->len < sizeof(seqno_t) + digest_length(n->indigest)) {
                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got too short packet from %s (%s)",
                                        n->name, n->hostname);
-               return;
+               return false;
        }
 
+       /* It's a legacy UDP packet, the data starts after the seqno */
+
+       inpkt->offset += sizeof(seqno_t);
+
        /* Check the message authentication code */
 
        if(digest_active(n->indigest)) {
                inpkt->len -= digest_length(n->indigest);
-               if(!digest_verify(n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
+               if(!digest_verify(n->indigest, SEQNO(inpkt), inpkt->len, SEQNO(inpkt) + inpkt->len)) {
                        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
-                       return;
+                       return false;
                }
        }
        /* Decrypt the packet */
 
        if(cipher_active(n->incipher)) {
-               outpkt = pkt[nextpkt++];
+               vpn_packet_t *outpkt = pkt[nextpkt++];
                outlen = MAXSIZE;
 
-               if(!cipher_decrypt(n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+               if(!cipher_decrypt(n->incipher, SEQNO(inpkt), inpkt->len, SEQNO(outpkt), &outlen, true)) {
                        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
-                       return;
+                       return false;
                }
 
                outpkt->len = outlen;
@@ -417,38 +340,40 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
 
        /* Check the sequence number */
 
-       inpkt->len -= sizeof inpkt->seqno;
-       inpkt->seqno = ntohl(inpkt->seqno);
+       seqno_t seqno;
+       memcpy(&seqno, SEQNO(inpkt), sizeof seqno);
+       seqno = ntohl(seqno);
+       inpkt->len -= sizeof seqno;
 
        if(replaywin) {
-               if(inpkt->seqno != n->received_seqno + 1) {
-                       if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
+               if(seqno != n->received_seqno + 1) {
+                       if(seqno >= n->received_seqno + replaywin * 8) {
                                if(n->farfuture++ < replaywin >> 2) {
                                        logger(DEBUG_ALWAYS, LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
-                                               n->name, n->hostname, inpkt->seqno - n->received_seqno - 1, n->farfuture);
-                                       return;
+                                               n->name, n->hostname, seqno - n->received_seqno - 1, n->farfuture);
+                                       return false;
                                }
                                logger(DEBUG_ALWAYS, LOG_WARNING, "Lost %d packets from %s (%s)",
-                                               inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
+                                               seqno - n->received_seqno - 1, n->name, n->hostname);
                                memset(n->late, 0, replaywin);
-                       } else if (inpkt->seqno <= n->received_seqno) {
-                               if((n->received_seqno >= replaywin * 8 && inpkt->seqno <= n->received_seqno - replaywin * 8) || !(n->late[(inpkt->seqno / 8) % replaywin] & (1 << inpkt->seqno % 8))) {
+                       } else if (seqno <= n->received_seqno) {
+                               if((n->received_seqno >= replaywin * 8 && seqno <= n->received_seqno - replaywin * 8) || !(n->late[(seqno / 8) % replaywin] & (1 << seqno % 8))) {
                                        logger(DEBUG_ALWAYS, LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
-                                               n->name, n->hostname, inpkt->seqno, n->received_seqno);
-                                       return;
+                                               n->name, n->hostname, seqno, n->received_seqno);
+                                       return false;
                                }
                        } else {
-                               for(int i = n->received_seqno + 1; i < inpkt->seqno; i++)
+                               for(int i = n->received_seqno + 1; i < seqno; i++)
                                        n->late[(i / 8) % replaywin] |= 1 << i % 8;
                        }
                }
 
                n->farfuture = 0;
-               n->late[(inpkt->seqno / 8) % replaywin] &= ~(1 << inpkt->seqno % 8);
+               n->late[(seqno / 8) % replaywin] &= ~(1 << seqno % 8);
        }
 
-       if(inpkt->seqno > n->received_seqno)
-               n->received_seqno = inpkt->seqno;
+       if(seqno > n->received_seqno)
+               n->received_seqno = seqno;
 
        n->received++;
 
@@ -460,12 +385,12 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        length_t origlen = inpkt->len;
 
        if(n->incompression) {
-               outpkt = pkt[nextpkt++];
+               vpn_packet_t *outpkt = pkt[nextpkt++];
 
-               if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
+               if((outpkt->len = uncompress_packet(DATA(outpkt), DATA(inpkt), inpkt->len, n->incompression)) < 0) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)",
                                                 n->name, n->hostname);
-                       return;
+                       return false;
                }
 
                inpkt = outpkt;
@@ -473,18 +398,24 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
                origlen -= MTU/64 + 20;
        }
 
+       if(inpkt->len > n->maxrecentlen)
+               n->maxrecentlen = inpkt->len;
+
        inpkt->priority = 0;
 
-       if(!inpkt->data[12] && !inpkt->data[13])
-               mtu_probe_h(n, inpkt, origlen);
+       if(!DATA(inpkt)[12] && !DATA(inpkt)[13])
+               udp_probe_h(n, inpkt, origlen);
        else
                receive_packet(n, inpkt);
+       return true;
+#endif
 }
 
 void receive_tcppacket(connection_t *c, const char *buffer, int len) {
        vpn_packet_t outpkt;
+       outpkt.offset = DEFAULT_PACKET_OFFSET;
 
-       if(len > sizeof outpkt.data)
+       if(len > sizeof outpkt.data - outpkt.offset)
                return;
 
        outpkt.len = len;
@@ -492,30 +423,20 @@ void receive_tcppacket(connection_t *c, const char *buffer, int len) {
                outpkt.priority = 0;
        else
                outpkt.priority = -1;
-       memcpy(outpkt.data, buffer, len);
+       memcpy(DATA(&outpkt), buffer, len);
 
        receive_packet(c->node, &outpkt);
 }
 
 static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
-       if(!n->status.validkey) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
-               if(!n->status.waitingforkey)
-                       send_req_key(n);
-               else if(n->last_req_key + 10 < now.tv_sec) {
-                       logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
-                       sptps_stop(&n->sptps);
-                       n->status.waitingforkey = false;
-                       send_req_key(n);
-               }
+       if(!n->status.validkey && !n->connection)
                return;
-       }
 
        uint8_t type = 0;
        int offset = 0;
 
-       if(!(origpkt->data[12] | origpkt->data[13])) {
-               sptps_send_record(&n->sptps, PKT_PROBE, (char *)origpkt->data, origpkt->len);
+       if(!(DATA(origpkt)[12] | DATA(origpkt)[13])) {
+               sptps_send_record(&n->sptps, PKT_PROBE, (char *)DATA(origpkt), origpkt->len);
                return;
        }
 
@@ -530,7 +451,8 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
        vpn_packet_t outpkt;
 
        if(n->outcompression) {
-               int len = compress_packet(outpkt.data + offset, origpkt->data + offset, origpkt->len - offset, n->outcompression);
+               outpkt.offset = 0;
+               int len = compress_packet(DATA(&outpkt) + offset, DATA(origpkt) + offset, origpkt->len - offset, n->outcompression);
                if(len < 0) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)", n->name, n->hostname);
                } else if(len < origpkt->len - offset) {
@@ -540,10 +462,29 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
                }
        }
 
-       sptps_send_record(&n->sptps, type, (char *)origpkt->data + offset, origpkt->len - offset);
+       /* If we have a direct metaconnection to n, and we can't use UDP, then
+          don't bother with SPTPS and just use a "plaintext" PACKET message.
+          We don't really care about end-to-end security since we're not
+          sending the message through any intermediate nodes. */
+       if(n->connection && origpkt->len > n->minmtu)
+               send_tcppacket(n->connection, origpkt);
+       else
+               sptps_send_record(&n->sptps, type, DATA(origpkt) + offset, origpkt->len - offset);
        return;
 }
 
+static void adapt_socket(const sockaddr_t *sa, int *sock) {
+       /* Make sure we have a suitable socket for the chosen address */
+       if(listen_socket[*sock].sa.sa.sa_family != sa->sa.sa_family) {
+               for(int i = 0; i < listen_sockets; i++) {
+                       if(listen_socket[i].sa.sa.sa_family == sa->sa.sa_family) {
+                               *sock = i;
+                               break;
+                       }
+               }
+       }
+}
+
 static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock) {
        /* Latest guess */
        *sa = &n->address;
@@ -582,54 +523,30 @@ static void choose_udp_address(const node_t *n, const sockaddr_t **sa, int *sock
                *sock = rand() % listen_sockets;
        }
 
-       /* Make sure we have a suitable socket for the chosen address */
-       if(listen_socket[*sock].sa.sa.sa_family != (*sa)->sa.sa_family) {
-               for(int i = 0; i < listen_sockets; i++) {
-                       if(listen_socket[i].sa.sa.sa_family == (*sa)->sa.sa_family) {
-                               *sock = i;
-                               break;
-                       }
-               }
-       }
+       adapt_socket(*sa, sock);
 }
 
-static void choose_broadcast_address(const node_t *n, const sockaddr_t **sa, int *sock) {
-       static sockaddr_t broadcast_ipv4 = {
-               .in = {
-                       .sin_family = AF_INET,
-                       .sin_addr.s_addr = -1,
-               }
-       };
-
-       static sockaddr_t broadcast_ipv6 = {
-               .in6 = {
-                       .sin6_family = AF_INET6,
-                       .sin6_addr.s6_addr[0x0] = 0xff,
-                       .sin6_addr.s6_addr[0x1] = 0x02,
-                       .sin6_addr.s6_addr[0xf] = 0x01,
-               }
-       };
+static void choose_local_address(const node_t *n, const sockaddr_t **sa, int *sock) {
+       *sa = NULL;
 
-       *sock = rand() % listen_sockets;
+       /* Pick one of the edges from this node at random, then use its local address. */
 
-       if(listen_socket[*sock].sa.sa.sa_family == AF_INET6) {
-               if(localdiscovery_address.sa.sa_family == AF_INET6) {
-                       localdiscovery_address.in6.sin6_port = n->prevedge->address.in.sin_port;
-                       *sa = &localdiscovery_address;
-               } else {
-                       broadcast_ipv6.in6.sin6_port = n->prevedge->address.in.sin_port;
-                       broadcast_ipv6.in6.sin6_scope_id = listen_socket[*sock].sa.in6.sin6_scope_id;
-                       *sa = &broadcast_ipv6;
-               }
-       } else {
-               if(localdiscovery_address.sa.sa_family == AF_INET) {
-                       localdiscovery_address.in.sin_port = n->prevedge->address.in.sin_port;
-                       *sa = &localdiscovery_address;
-               } else {
-                       broadcast_ipv4.in.sin_port = n->prevedge->address.in.sin_port;
-                       *sa = &broadcast_ipv4;
+       int i = 0;
+       int j = rand() % n->edge_tree->count;
+       edge_t *candidate = NULL;
+
+       for splay_each(edge_t, e, n->edge_tree) {
+               if(i++ == j) {
+                       candidate = e;
+                       break;
                }
        }
+
+       if (candidate && candidate->local_address.sa.sa_family) {
+               *sa = &candidate->local_address;
+               *sock = rand() % listen_sockets;
+               adapt_socket(*sa, sock);
+       }
 }
 
 static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
@@ -642,8 +559,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        size_t outlen;
 #if defined(SOL_IP) && defined(IP_TOS)
        static int priority = 0;
-#endif
        int origpriority = origpkt->priority;
+#endif
+
+       pkt1.offset = DEFAULT_PACKET_OFFSET;
+       pkt2.offset = DEFAULT_PACKET_OFFSET;
 
        if(!n->status.reachable) {
                logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
@@ -653,24 +573,20 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        if(n->status.sptps)
                return send_sptps_packet(n, origpkt);
 
+#ifdef DISABLE_LEGACY
+       return;
+#else
        /* Make sure we have a valid key */
 
        if(!n->status.validkey) {
                logger(DEBUG_TRAFFIC, LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
-
-               if(n->last_req_key + 10 <= now.tv_sec) {
-                       send_req_key(n);
-                       n->last_req_key = now.tv_sec;
-               }
-
                send_tcppacket(n->nexthop->connection, origpkt);
-
                return;
        }
 
-       if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
+       if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (DATA(inpkt)[12] | DATA(inpkt)[13])) {
                logger(DEBUG_TRAFFIC, LOG_INFO,
                                "Packet for %s (%s) larger than minimum MTU, forwarding via %s",
                                n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
@@ -688,7 +604,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        if(n->outcompression) {
                outpkt = pkt[nextpkt++];
 
-               if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
+               if((outpkt->len = compress_packet(DATA(outpkt), DATA(inpkt), inpkt->len, n->outcompression)) < 0) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while compressing packet to %s (%s)",
                                   n->name, n->hostname);
                        return;
@@ -699,8 +615,9 @@ 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;
+       seqno_t seqno = htonl(++(n->sent_seqno));
+       memcpy(SEQNO(inpkt), &seqno, sizeof seqno);
+       inpkt->len += sizeof seqno;
 
        /* Encrypt the packet */
 
@@ -708,7 +625,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                outpkt = pkt[nextpkt++];
                outlen = MAXSIZE;
 
-               if(!cipher_encrypt(n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
+               if(!cipher_encrypt(n->outcipher, SEQNO(inpkt), inpkt->len, SEQNO(outpkt), &outlen, true)) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
@@ -720,7 +637,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
        /* Add the message authentication code */
 
        if(digest_active(n->outdigest)) {
-               if(!digest_create(n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len)) {
+               if(!digest_create(n->outdigest, SEQNO(inpkt), inpkt->len, SEQNO(inpkt) + inpkt->len)) {
                        logger(DEBUG_TRAFFIC, LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
@@ -730,12 +647,12 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
 
        /* Send the packet */
 
-       const sockaddr_t *sa;
+       const sockaddr_t *sa = NULL;
        int sock;
 
-       if(n->status.broadcast)
-               choose_broadcast_address(n, &sa, &sock);
-       else
+       if(n->status.send_locally)
+               choose_local_address(n, &sa, &sock);
+       if(!sa)
                choose_udp_address(n, &sa, &sock);
 
 #if defined(SOL_IP) && defined(IP_TOS)
@@ -744,62 +661,84 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
                priority = origpriority;
                logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
                if(setsockopt(listen_socket[n->sock].udp.fd, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
-                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
+                       logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
        }
 #endif
 
-       if(sendto(listen_socket[sock].udp.fd, (char *) &inpkt->seqno, inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+       if(sendto(listen_socket[sock].udp.fd, SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
                        if(n->maxmtu >= origlen)
                                n->maxmtu = origlen - 1;
                        if(n->mtu >= origlen)
                                n->mtu = origlen - 1;
+                       try_fix_mtu(n);
                } else
                        logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
        }
 
 end:
        origpkt->len = origlen;
+#endif
 }
 
-bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
-       node_t *to = handle;
+static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void *data, size_t len) {
+       node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop;
+       bool direct = from == myself && to == relay;
+       bool relay_supported = (relay->options >> 24) >= 4;
+       bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
 
-       /* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */
+       /* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU.
+          TODO: When relaying, the original sender does not know the end-to-end PMTU (it only knows the PMTU of the first hop).
+                This can lead to scenarios where large packets are sent over UDP to relay, but then relay has no choice but fall back to TCP. */
 
-       if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > to->minmtu)) {
+       if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
                char buf[len * 4 / 3 + 5];
                b64encode(data, buf, len);
                /* If no valid key is known yet, send the packets using ANS_KEY requests,
                   to ensure we get to learn the reflexive UDP address. */
-               if(!to->status.validkey) {
+               if(from == myself && !to->status.validkey) {
                        to->incompression = myself->incompression;
-                       return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, to->incompression);
+                       return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, from->name, to->name, buf, to->incompression);
                } else {
-                       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf);
+                       return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, REQ_SPTPS, buf);
                }
        }
 
-       /* Otherwise, send the packet via UDP */
-
-       const sockaddr_t *sa;
-       int sock;
+       size_t overhead = 0;
+       if(relay_supported) overhead += sizeof to->id + sizeof from->id;
+       char buf[len + overhead]; char* buf_ptr = buf;
+       if(relay_supported) {
+               if(direct) {
+                       /* Inform the recipient that this packet was sent directly. */
+                       node_id_t nullid = {};
+                       memcpy(buf_ptr, &nullid, sizeof nullid); buf_ptr += sizeof nullid;
+               } else {
+                       memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id;
+               }
+               memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id;
 
-       if(to->status.broadcast)
-               choose_broadcast_address(to, &sa, &sock);
-       else
-               choose_udp_address(to, &sa, &sock);
+       }
+       /* TODO: if this copy turns out to be a performance concern, change sptps_send_record() to add some "pre-padding" to the buffer and use that instead */
+       memcpy(buf_ptr, data, len); buf_ptr += len;
 
-       if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+       const sockaddr_t *sa = NULL;
+       int sock;
+       if(relay->status.send_locally)
+               choose_local_address(relay, &sa, &sock);
+       if(!sa)
+               choose_udp_address(relay, &sa, &sock);
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname);
+       if(sendto(listen_socket[sock].udp.fd, buf, buf_ptr - buf, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
                if(sockmsgsize(sockerrno)) {
                        // Compensate for SPTPS overhead
                        len -= SPTPS_DATAGRAM_OVERHEAD;
-                       if(to->maxmtu >= len)
-                               to->maxmtu = len - 1;
-                       if(to->mtu >= len)
-                               to->mtu = len - 1;
+                       if(relay->maxmtu >= len)
+                               relay->maxmtu = len - 1;
+                       if(relay->mtu >= len)
+                               relay->mtu = len - 1;
+                       try_fix_mtu(relay);
                } else {
-                       logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno));
+                       logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", relay->name, relay->hostname, sockstrerror(sockerrno));
                        return false;
                }
        }
@@ -807,7 +746,11 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
        return true;
 }
 
-bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) {
+bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
+       return send_sptps_data_priv(handle, myself, type, data, len);
+}
+
+bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
        node_t *from = handle;
 
        if(type == SPTPS_HANDSHAKE) {
@@ -825,11 +768,18 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
        }
 
        vpn_packet_t inpkt;
+       inpkt.offset = DEFAULT_PACKET_OFFSET;
 
        if(type == PKT_PROBE) {
+               if(!from->status.udppacket) {
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Got SPTPS PROBE packet from %s (%s) via TCP", from->name, from->hostname);
+                       return false;
+               }
                inpkt.len = len;
-               memcpy(inpkt.data, data, len);
-               mtu_probe_h(from, &inpkt, len);
+               memcpy(DATA(&inpkt), data, len);
+               if(inpkt.len > from->maxrecentlen)
+                       from->maxrecentlen = inpkt.len;
+               udp_probe_h(from, &inpkt, len);
                return true;
        }
 
@@ -848,7 +798,7 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
 
        int offset = (type & PKT_MAC) ? 0 : 14;
        if(type & PKT_COMPRESSED) {
-               length_t ulen = uncompress_packet(inpkt.data + offset, (const uint8_t *)data, len, from->incompression);
+               length_t ulen = uncompress_packet(DATA(&inpkt) + offset, (const uint8_t *)data, len, from->incompression);
                if(ulen < 0) {
                        return false;
                } else {
@@ -857,79 +807,431 @@ bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t
                if(inpkt.len > MAXSIZE)
                        abort();
        } else {
-               memcpy(inpkt.data + offset, data, len);
+               memcpy(DATA(&inpkt) + offset, data, len);
                inpkt.len = len + offset;
        }
 
        /* Generate the Ethernet packet type if necessary */
        if(offset) {
-               switch(inpkt.data[14] >> 4) {
+               switch(DATA(&inpkt)[14] >> 4) {
                        case 4:
-                               inpkt.data[12] = 0x08;
-                               inpkt.data[13] = 0x00;
+                               DATA(&inpkt)[12] = 0x08;
+                               DATA(&inpkt)[13] = 0x00;
                                break;
                        case 6:
-                               inpkt.data[12] = 0x86;
-                               inpkt.data[13] = 0xDD;
+                               DATA(&inpkt)[12] = 0x86;
+                               DATA(&inpkt)[13] = 0xDD;
                                break;
                        default:
                                logger(DEBUG_TRAFFIC, LOG_ERR,
                                                   "Unknown IP version %d while reading packet from %s (%s)",
-                                                  inpkt.data[14] >> 4, from->name, from->hostname);
+                                                  DATA(&inpkt)[14] >> 4, from->name, from->hostname);
                                return false;
                }
        }
 
+       if(from->status.udppacket && inpkt.len > from->maxrecentlen)
+               from->maxrecentlen = inpkt.len;
+
        receive_packet(from, &inpkt);
        return true;
 }
 
-/*
-  send a packet to the given vpn ip.
+// This function tries to get SPTPS keys, if they aren't already known.
+// This function makes no guarantees - it is up to the caller to check the node's state to figure out if the keys are available.
+static void try_sptps(node_t *n) {
+       if(n->status.validkey)
+               return;
+
+       logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
+
+       if(!n->status.waitingforkey)
+               send_req_key(n);
+       else if(n->last_req_key + 10 < now.tv_sec) {
+               logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
+               sptps_stop(&n->sptps);
+               n->status.waitingforkey = false;
+               send_req_key(n);
+       }
+
+       return;
+}
+
+static void send_udp_probe_packet(node_t *n, int len) {
+       vpn_packet_t packet;
+       packet.offset = DEFAULT_PACKET_OFFSET;
+       memset(DATA(&packet), 0, 14);
+       randomize(DATA(&packet) + 14, len - 14);
+       packet.len = len;
+       packet.priority = 0;
+
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Sending UDP probe length %d to %s (%s)", len, n->name, n->hostname);
+
+       send_udppacket(n, &packet);
+}
+
+// This function tries to establish a UDP tunnel to a node so that packets can be sent.
+// If a tunnel is already established, it makes sure it stays up.
+// This function makes no guarantees - it is up to the caller to check the node's state to figure out if UDP is usable.
+static void try_udp(node_t* n) {
+       if(!udp_discovery)
+               return;
+
+       /* Send gratuitous probe replies to 1.1 nodes. */
+
+       if((n->options >> 24) >= 3 && n->status.udp_confirmed) {
+               struct timeval ping_tx_elapsed;
+               timersub(&now, &n->udp_reply_sent, &ping_tx_elapsed);
+
+               if(ping_tx_elapsed.tv_sec >= udp_discovery_keepalive_interval - 1) {
+                       n->udp_reply_sent = now;
+                       if(n->maxrecentlen) {
+                               vpn_packet_t pkt;
+                               pkt.len = n->maxrecentlen;
+                               pkt.offset = DEFAULT_PACKET_OFFSET;
+                               memset(DATA(&pkt), 0, 14);
+                               randomize(DATA(&pkt) + 14, MIN_PROBE_SIZE - 14);
+                               send_udp_probe_reply(n, &pkt, pkt.len);
+                               n->maxrecentlen = 0;
+                       }
+               }
+       }
+
+       /* Probe request */
+
+       struct timeval ping_tx_elapsed;
+       timersub(&now, &n->udp_ping_sent, &ping_tx_elapsed);
+
+       int interval = n->status.udp_confirmed ? udp_discovery_keepalive_interval : udp_discovery_interval;
+
+       if(ping_tx_elapsed.tv_sec >= interval) {
+               send_udp_probe_packet(n, MIN_PROBE_SIZE);
+               n->udp_ping_sent = now;
+
+               if(localdiscovery && !n->status.udp_confirmed && n->prevedge) {
+                       n->status.send_locally = true;
+                       send_udp_probe_packet(n, MIN_PROBE_SIZE);
+                       n->status.send_locally = false;
+               }
+       }
+}
+
+static length_t choose_initial_maxmtu(node_t *n) {
+#ifdef IP_MTU
+
+       int sock = -1;
+
+       const sockaddr_t *sa = NULL;
+       int sockindex;
+       choose_udp_address(n, &sa, &sockindex);
+       if(!sa)
+               return MTU;
+
+       sock = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
+       if(sock < 0) {
+               logger(DEBUG_TRAFFIC, LOG_ERR, "Creating MTU assessment socket for %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+               return MTU;
+       }
+
+       if(connect(sock, &sa->sa, SALEN(sa->sa))) {
+               logger(DEBUG_TRAFFIC, LOG_ERR, "Connecting MTU assessment socket for %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+               close(sock);
+               return MTU;
+       }
+
+       int ip_mtu;
+       socklen_t ip_mtu_len = sizeof ip_mtu;
+       if(getsockopt(sock, IPPROTO_IP, IP_MTU, &ip_mtu, &ip_mtu_len)) {
+               logger(DEBUG_TRAFFIC, LOG_ERR, "getsockopt(IP_MTU) on %s (%s) failed: %s", n->name, n->hostname, sockstrerror(sockerrno));
+               close(sock);
+               return MTU;
+       }
+
+       close(sock);
+
+       /* getsockopt(IP_MTU) returns the MTU of the physical interface.
+          We need to remove various overheads to get to the tinc MTU. */
+       length_t mtu = ip_mtu;
+       mtu -= (sa->sa.sa_family == AF_INET6) ? sizeof(struct ip6_hdr) : sizeof(struct ip);
+       mtu -= 8; /* UDP */
+       if(n->status.sptps) {
+               mtu -= SPTPS_DATAGRAM_OVERHEAD;
+               if((n->options >> 24) >= 4)
+                       mtu -= sizeof(node_id_t) + sizeof(node_id_t);
+#ifndef DISABLE_LEGACY
+       } else {
+               mtu -= digest_length(n->outdigest);
+
+               /* Now it's tricky. We use CBC mode, so the length of the
+                  encrypted payload must be a multiple of the blocksize. The
+                  sequence number is also part of the encrypted payload, so we
+                  must account for it after correcting for the blocksize.
+                  Furthermore, the padding in the last block must be at least
+                  1 byte. */
+
+               length_t blocksize = cipher_blocksize(n->outcipher);
+
+               if(blocksize > 1) {
+                       mtu /= blocksize;
+                       mtu *= blocksize;
+                       mtu--;
+               }
+
+               mtu -= 4; // seqno
+#endif
+       }
+
+       if (mtu < 512) {
+               logger(DEBUG_TRAFFIC, LOG_ERR, "getsockopt(IP_MTU) on %s (%s) returned absurdly small value: %d", n->name, n->hostname, ip_mtu);
+               return MTU;
+       }
+       if (mtu > MTU)
+               return MTU;
+
+       logger(DEBUG_TRAFFIC, LOG_INFO, "Using system-provided maximum tinc MTU for %s (%s): %hd", n->name, n->hostname, mtu);
+       return mtu;
+
+#else
+
+       return MTU;
+
+#endif
+}
+
+/* This function tries to determines the MTU of a node.
+   By calling this function repeatedly, n->minmtu will be progressively
+   increased, and at some point, n->mtu will be fixed to n->minmtu.  If the MTU
+   is already fixed, this function checks if it can be increased.
 */
+
+static void try_mtu(node_t *n) {
+       if(!(n->options & OPTION_PMTU_DISCOVERY))
+               return;
+
+       if(udp_discovery && !n->status.udp_confirmed) {
+               n->maxrecentlen = 0;
+               n->mtuprobes = 0;
+               n->minmtu = 0;
+               n->maxmtu = MTU;
+               return;
+       }
+
+       /* mtuprobes == 0..19: initial discovery, send bursts with 1 second interval, mtuprobes++
+          mtuprobes ==    20: fix MTU, and go to -1
+          mtuprobes ==    -1: send one maxmtu and one maxmtu+1 probe every pinginterval
+          mtuprobes ==-2..-3: send one maxmtu probe every second
+          mtuprobes ==    -4: maxmtu no longer valid, reset minmtu and maxmtu and go to 0 */
+
+       struct timeval elapsed;
+       timersub(&now, &n->mtu_ping_sent, &elapsed);
+       if(n->mtuprobes >= 0) {
+               if(n->mtuprobes != 0 && elapsed.tv_sec == 0 && elapsed.tv_usec < 333333)
+                       return;
+       } else {
+               if(n->mtuprobes < -1) {
+                       if(elapsed.tv_sec < 1)
+                               return;
+               } else {
+                       if(elapsed.tv_sec < pinginterval)
+                               return;
+               }
+       }
+
+       n->mtu_ping_sent = now;
+
+       try_fix_mtu(n);
+
+       if(n->mtuprobes < -3) {
+               /* We lost three MTU probes, restart discovery */
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Decrease in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
+               n->mtuprobes = 0;
+               n->minmtu = 0;
+       }
+
+       if(n->mtuprobes < 0) {
+               /* After the initial discovery, we only send one maxmtu and one
+                  maxmtu+1 probe to detect PMTU increases. */
+               send_udp_probe_packet(n, n->maxmtu);
+               if(n->mtuprobes == -1 && n->maxmtu + 1 < MTU)
+                       send_udp_probe_packet(n, n->maxmtu + 1);
+               n->mtuprobes--;
+       } else {
+               /* Before initial discovery begins, set maxmtu to the most likely value.
+                  If it's underestimated, we will correct it after initial discovery. */
+               if(n->mtuprobes == 0)
+                       n->maxmtu = choose_initial_maxmtu(n);
+
+               for (;;) {
+                       /* Decreasing the number of probes per cycle might make the algorithm react faster to lost packets,
+                          but it will typically increase convergence time in the no-loss case. */
+                       const length_t probes_per_cycle = 8;
+
+                       /* This magic value was determined using math simulations.
+                          It will result in a 1329-byte first probe, followed (if there was a reply) by a 1407-byte probe.
+                          Since 1407 is just below the range of tinc MTUs over typical networks,
+                          this fine-tuning allows tinc to cover a lot of ground very quickly.
+                          This fine-tuning is only valid for maxmtu = MTU; if maxmtu is smaller,
+                          then it's better to use a multiplier of 1. Indeed, this leads to an interesting scenario
+                          if choose_initial_maxmtu() returns the actual MTU value - it will get confirmed with one single probe. */
+                       const float multiplier = (n->maxmtu == MTU) ? 0.97 : 1;
+
+                       const float cycle_position = probes_per_cycle - (n->mtuprobes % probes_per_cycle) - 1;
+                       const length_t minmtu = MAX(n->minmtu, 512);
+                       const float interval = n->maxmtu - minmtu;
+
+                       /* The core of the discovery algorithm is this exponential.
+                          It produces very large probes early in the cycle, and then it very quickly decreases the probe size.
+                          This reflects the fact that in the most difficult cases, we don't get any feedback for probes that
+                          are too large, and therefore we need to concentrate on small offsets so that we can quickly converge
+                          on the precise MTU as we are approaching it.
+                          The last probe of the cycle is always 1 byte in size - this is to make sure we'll get at least one
+                          reply per cycle so that we can make progress. */
+                       const length_t offset = powf(interval, multiplier * cycle_position / (probes_per_cycle - 1));
+
+                       length_t maxmtu = n->maxmtu;
+                       send_udp_probe_packet(n, minmtu + offset);
+                       /* If maxmtu changed, it means the probe was rejected by the system because it was too large.
+                          In that case, we recalculate with the new maxmtu and try again. */
+                       if(n->mtuprobes < 0 || maxmtu == n->maxmtu)
+                               break;
+               }
+
+               if(n->mtuprobes >= 0)
+                       n->mtuprobes++;
+       }
+}
+
+/* These functions try to establish a tunnel to a node (or its relay) so that
+   packets can be sent (e.g. exchange keys).
+   If a tunnel is already established, it tries to improve it (e.g. by trying
+   to establish a UDP tunnel instead of TCP).  This function makes no
+   guarantees - it is up to the caller to check the node's state to figure out
+   if TCP and/or UDP is usable.  By calling this function repeatedly, the
+   tunnel is gradually improved until we hit the wall imposed by the underlying
+   network environment.  It is recommended to call this function every time a
+   packet is sent (or intended to be sent) to a node, so that the tunnel keeps
+   improving as packets flow, and then gracefully downgrades itself as it goes
+   idle.
+*/
+
+static void try_tx_sptps(node_t *n, bool mtu) {
+       /* If n is a TCP-only neighbor, we'll only use "cleartext" PACKET
+          messages anyway, so there's no need for SPTPS at all. */
+
+       if(n->connection && ((myself->options | n->options) & OPTION_TCPONLY))
+               return;
+
+       /* Otherwise, try to do SPTPS authentication with n if necessary. */
+
+       try_sptps(n);
+
+       /* Do we need to statically relay packets? */
+
+       node_t *via = (n->via == myself) ? n->nexthop : n->via;
+
+       /* If the static relay doesn't support SPTPS, everything goes via TCP anyway. */
+
+       if((via->options >> 24) < 4)
+               return;
+
+       /* If we do have a static relay, try everything with that one instead. */
+
+       if(via != n)
+               try_tx_sptps(via, mtu);
+
+       /* Otherwise, try to establish UDP connectivity. */
+
+       try_udp(n);
+       if(mtu)
+               try_mtu(n);
+
+       /* If we don't have UDP connectivity (yet), we need to use a dynamic relay (nexthop)
+          while we try to establish direct connectivity. */
+
+       if(!n->status.udp_confirmed && n != n->nexthop && (n->nexthop->options >> 24) >= 4)
+               try_tx_sptps(n->nexthop, mtu);
+}
+
+static void try_tx_legacy(node_t *n, bool mtu) {
+       /* Does he have our key? If not, send one. */
+
+       if(!n->status.validkey_in)
+               send_ans_key(n);
+
+       /* Check if we already have a key, or request one. */
+
+       if(!n->status.validkey) {
+               if(n->last_req_key + 10 <= now.tv_sec) {
+                       send_req_key(n);
+                       n->last_req_key = now.tv_sec;
+               }
+               return;
+       }
+
+       try_udp(n);
+       if(mtu)
+               try_mtu(n);
+}
+
+void try_tx(node_t *n, bool mtu) {
+       if(n->status.sptps)
+               try_tx_sptps(n, mtu);
+       else
+               try_tx_legacy(n, mtu);
+}
+
 void send_packet(node_t *n, vpn_packet_t *packet) {
-       node_t *via;
+       // If it's for myself, write it to the tun/tap device.
 
        if(n == myself) {
                if(overwrite_mac)
-                        memcpy(packet->data, mymac.x, ETH_ALEN);
+                        memcpy(DATA(packet), mymac.x, ETH_ALEN);
                n->out_packets++;
                n->out_bytes += packet->len;
                devops.write(packet);
                return;
        }
 
-       logger(DEBUG_TRAFFIC, LOG_ERR, "Sending packet of %d bytes to %s (%s)",
-                          packet->len, n->name, n->hostname);
+       logger(DEBUG_TRAFFIC, LOG_ERR, "Sending packet of %d bytes to %s (%s)", packet->len, n->name, n->hostname);
+
+       // If the node is not reachable, drop it.
 
        if(!n->status.reachable) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Node %s (%s) is not reachable",
-                                  n->name, n->hostname);
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Node %s (%s) is not reachable", n->name, n->hostname);
                return;
        }
 
+       // Keep track of packet statistics.
+
        n->out_packets++;
        n->out_bytes += packet->len;
 
+       // Check if it should be sent as an SPTPS packet.
+
        if(n->status.sptps) {
                send_sptps_packet(n, packet);
+               try_tx_sptps(n, true);
                return;
        }
 
-       via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
+       // Determine which node to actually send it to.
+
+       node_t *via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
 
        if(via != n)
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet to %s via %s (%s)",
-                          n->name, via->name, n->via->hostname);
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet to %s via %s (%s)", n->name, via->name, n->via->hostname);
+
+       // Try to send via UDP, unless TCP is forced.
 
        if(packet->priority == -1 || ((myself->options | via->options) & OPTION_TCPONLY)) {
                if(!send_tcppacket(via->connection, packet))
                        terminate_connection(via->connection, true);
-       } else
-               send_udppacket(via, packet);
-}
+               return;
+       }
 
-/* Broadcast a packet using the minimum spanning tree */
+       send_udppacket(via, packet);
+       try_tx_legacy(via, true);
+}
 
 void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
        // Always give ourself a copy of the packet.
@@ -950,7 +1252,7 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
                // usually distributes the sending of broadcast packets over all nodes.
                case BMODE_MST:
                        for list_each(connection_t, c, connection_list)
-                               if(c->status.active && c->status.mst && c != from->nexthop->connection)
+                               if(c->edge && c->status.mst && c != from->nexthop->connection)
                                        send_packet(c->node, packet);
                        break;
 
@@ -971,45 +1273,66 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
        }
 }
 
+/* We got a packet from some IP address, but we don't know who sent it.  Try to
+   verify the message authentication code against all active session keys.
+   Since this is actually an expensive operation, we only do a full check once
+   a minute, the rest of the time we only check against nodes for which we know
+   an IP address that matches the one from the packet.  */
+
 static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
-       node_t *n = NULL;
+       node_t *match = NULL;
        bool hard = false;
        static time_t last_hard_try = 0;
 
-       for splay_each(edge_t, e, edge_weight_tree) {
-               if(!e->to->status.reachable || e->to == myself)
+       for splay_each(node_t, n, node_tree) {
+               if(!n->status.reachable || n == myself)
+                       continue;
+
+               if((n->status.sptps && !n->sptps.instate) || !n->status.validkey_in)
                        continue;
 
-               if(sockaddrcmp_noport(from, &e->address)) {
+               bool soft = false;
+
+               for splay_each(edge_t, e, n->edge_tree) {
+                       if(!e->reverse)
+                               continue;
+                       if(!sockaddrcmp_noport(from, &e->reverse->address)) {
+                               soft = true;
+                               break;
+                       }
+               }
+
+               if(!soft) {
                        if(last_hard_try == now.tv_sec)
                                continue;
                        hard = true;
                }
 
-               if(!try_mac(e->to, pkt))
+               if(!try_mac(n, pkt))
                        continue;
 
-               n = e->to;
+               match = n;
                break;
        }
 
        if(hard)
                last_hard_try = now.tv_sec;
 
-       last_hard_try = now.tv_sec;
-       return n;
+       return match;
 }
 
 void handle_incoming_vpn_data(void *data, int flags) {
        listen_socket_t *ls = data;
        vpn_packet_t pkt;
        char *hostname;
-       sockaddr_t from = {{0}};
-       socklen_t fromlen = sizeof from;
-       node_t *n;
-       int len;
+       node_id_t nullid = {};
+       sockaddr_t addr = {};
+       socklen_t addrlen = sizeof addr;
+       node_t *from, *to;
+       bool direct = false;
 
-       len = recvfrom(ls->udp.fd, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
+       pkt.offset = 0;
+       int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
 
        if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
@@ -1019,32 +1342,80 @@ void handle_incoming_vpn_data(void *data, int flags) {
 
        pkt.len = len;
 
-       sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
+       sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
+
+       // Try to figure out who sent this packet.
+
+       node_t *n = lookup_node_udp(&addr);
+
+       if(n && !n->status.udp_confirmed)
+               n = NULL; // Don't believe it if we don't have confirmation yet.
+
+       if(!n) {
+               // It might be from a 1.1 node, which might have a source ID in the packet.
+               pkt.offset = 2 * sizeof(node_id_t);
+               from = lookup_node_id(SRCID(&pkt));
+               if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) {
+                       if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)))
+                               n = from;
+                       else
+                               goto skip_harder;
+               }
+       }
 
-       n = lookup_node_udp(&from);
+       if(!n) {
+               pkt.offset = 0;
+               n = try_harder(&addr, &pkt);
+       }
 
+skip_harder:
        if(!n) {
-               n = try_harder(&from, &pkt);
-               if(n)
-                       update_node_udp(n, &from);
-               else if(debug_level >= DEBUG_PROTOCOL) {
-                       hostname = sockaddr2hostname(&from);
+               if(debug_level >= DEBUG_PROTOCOL) {
+                       hostname = sockaddr2hostname(&addr);
                        logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
                        free(hostname);
+               }
+               return;
+       }
+
+       if(n->status.sptps) {
+               pkt.offset = 2 * sizeof(node_id_t);
+
+               if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) {
+                       direct = true;
+                       from = n;
+                       to = myself;
+               } else {
+                       from = lookup_node_id(SRCID(&pkt));
+                       to = lookup_node_id(DSTID(&pkt));
+               }
+               if(!from || !to) {
+                       logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
                        return;
                }
-               else
+
+               if(to != myself) {
+                       send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t));
+                       try_tx_sptps(n, true);
                        return;
+               }
+       } else {
+               direct = true;
+               from = n;
        }
 
-       n->sock = ls - listen_socket;
+       pkt.offset = 0;
+       if(!receive_udppacket(from, &pkt))
+               return;
 
-       receive_udppacket(n, &pkt);
+       n->sock = ls - listen_socket;
+       if(direct && sockaddrcmp(&addr, &n->address))
+               update_node_udp(n, &addr);
 }
 
 void handle_device_data(void *data, int flags) {
        vpn_packet_t packet;
-
+       packet.offset = DEFAULT_PACKET_OFFSET;
        packet.priority = 0;
 
        if(devops.read(&packet)) {
index eec1711..d1d5c04 100644 (file)
@@ -44,6 +44,7 @@
 #include "xalloc.h"
 
 char *myport;
+static char *myname;
 static io_t device_io;
 devops_t devops;
 bool device_standby = false;
@@ -136,14 +137,16 @@ bool read_ecdsa_public_key(connection_t *c) {
        }
 
        c->ecdsa = ecdsa_read_pem_public_key(fp);
-       fclose(fp);
 
-       if(!c->ecdsa)
+       if(!c->ecdsa && errno != ENOENT)
                logger(DEBUG_ALWAYS, LOG_ERR, "Parsing Ed25519 public key file `%s' failed.", fname);
+
+       fclose(fp);
        free(fname);
        return c->ecdsa;
 }
 
+#ifndef DISABLE_LEGACY
 bool read_rsa_public_key(connection_t *c) {
        if(ecdsa_active(c->ecdsa))
                return true;
@@ -181,6 +184,7 @@ bool read_rsa_public_key(connection_t *c) {
        free(fname);
        return c->rsa;
 }
+#endif
 
 static bool read_ecdsa_private_key(void) {
        FILE *fp;
@@ -247,6 +251,7 @@ static bool read_invitation_key(void) {
        return invitation_key;
 }
 
+#ifndef DISABLE_LEGACY
 static bool read_rsa_private_key(void) {
        FILE *fp;
        char *fname;
@@ -276,6 +281,8 @@ static bool read_rsa_private_key(void) {
        if(!fp) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error reading RSA private key file `%s': %s",
                           fname, strerror(errno));
+               if(errno == ENOENT)
+                       logger(DEBUG_ALWAYS, LOG_INFO, "Create an RSA keypair with `tinc -n %s generate-rsa-keys'.", netname ?: ".");
                free(fname);
                return false;
        }
@@ -301,6 +308,7 @@ static bool read_rsa_private_key(void) {
        free(fname);
        return myself->connection->rsa;
 }
+#endif
 
 static timeout_t keyexpire_timeout;
 
@@ -312,6 +320,8 @@ static void keyexpire_handler(void *data) {
 void regenerate_key(void) {
        logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
        send_key_changed();
+       for splay_each(node_t, n, node_tree)
+               n->status.validkey_in = false;
 }
 
 /*
@@ -402,41 +412,16 @@ void load_all_nodes(void) {
 
 char *get_name(void) {
        char *name = NULL;
+       char *returned_name;
 
        get_config_string(lookup_config(config_tree, "Name"), &name);
 
        if(!name)
                return NULL;
 
-       if(*name == '$') {
-               char *envname = getenv(name + 1);
-               char hostname[32] = "";
-               if(!envname) {
-                       if(strcmp(name + 1, "HOST")) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
-                               return false;
-                       }
-                       if(gethostname(hostname, sizeof hostname) || !*hostname) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", strerror(errno));
-                               return false;
-                       }
-                       hostname[31] = 0;
-                       envname = hostname;
-               }
-               free(name);
-               name = xstrdup(envname);
-               for(char *c = name; *c; c++)
-                       if(!isalnum(*c))
-                               *c = '_';
-       }
-
-       if(!check_id(name)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
-               free(name);
-               return false;
-       }
-
-       return name;
+       returned_name = replace_name(name);
+       free(name);
+       return returned_name;
 }
 
 bool setup_myself_reloadable(void) {
@@ -445,7 +430,6 @@ bool setup_myself_reloadable(void) {
        char *fmode = NULL;
        char *bmode = NULL;
        char *afname = NULL;
-       char *address = NULL;
        char *space;
        bool choice;
 
@@ -529,19 +513,14 @@ bool setup_myself_reloadable(void) {
        if(myself->options & OPTION_TCPONLY)
                myself->options |= OPTION_INDIRECT;
 
+       get_config_bool(lookup_config(config_tree, "UDPDiscovery"), &udp_discovery);
+       get_config_int(lookup_config(config_tree, "UDPDiscoveryKeepaliveInterval"), &udp_discovery_keepalive_interval);
+       get_config_int(lookup_config(config_tree, "UDPDiscoveryInterval"), &udp_discovery_interval);
+       get_config_int(lookup_config(config_tree, "UDPDiscoveryTimeout"), &udp_discovery_timeout);
+
        get_config_bool(lookup_config(config_tree, "DirectOnly"), &directonly);
        get_config_bool(lookup_config(config_tree, "LocalDiscovery"), &localdiscovery);
 
-       memset(&localdiscovery_address, 0, sizeof localdiscovery_address);
-       if(get_config_string(lookup_config(config_tree, "LocalDiscoveryAddress"), &address)) {
-               struct addrinfo *ai = str2addrinfo(address, myport, SOCK_DGRAM);
-               free(address);
-               if(!ai)
-                       return false;
-               memcpy(&localdiscovery_address, ai->ai_addr, ai->ai_addrlen);
-       }
-
-
        if(get_config_string(lookup_config(config_tree, "Mode"), &rmode)) {
                if(!strcasecmp(rmode, "router"))
                        routing_mode = RMODE_ROUTER;
@@ -596,6 +575,20 @@ bool setup_myself_reloadable(void) {
                free(bmode);
        }
 
+       const char* const DEFAULT_BROADCAST_SUBNETS[] = { "ff:ff:ff:ff:ff:ff", "255.255.255.255", "224.0.0.0/4", "ff00::/8" };
+       for (size_t i = 0; i < sizeof(DEFAULT_BROADCAST_SUBNETS) / sizeof(*DEFAULT_BROADCAST_SUBNETS); i++) {
+               subnet_t *s = new_subnet();
+               if (!str2net(s, DEFAULT_BROADCAST_SUBNETS[i]))
+                       abort();
+               subnet_add(NULL, s);
+       }
+       for (config_t* cfg = lookup_config(config_tree, "BroadcastSubnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+               subnet_t *s;
+               if (!get_config_subnet(cfg, &s))
+                       continue;
+               subnet_add(NULL, s);
+       }
+
 #if !defined(SOL_IP) || !defined(IP_TOS)
        if(priorityinheritance)
                logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
@@ -671,6 +664,9 @@ static bool add_listen_address(char *address, bool bindto) {
        hint.ai_protocol = IPPROTO_TCP;
        hint.ai_flags = AI_PASSIVE;
 
+#ifdef HAVE_DECL_RES_INIT
+       res_init();
+#endif
        int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
        free(address);
 
@@ -737,7 +733,7 @@ void device_enable(void) {
        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
        xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
-       xasprintf(&envp[3], "NAME=%s", myself->name);
+       xasprintf(&envp[3], "NAME=%s", myname);
 
        execute_script("tinc-up", envp);
 
@@ -750,7 +746,7 @@ void device_disable(void) {
        xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
        xasprintf(&envp[1], "DEVICE=%s", device ? : "");
        xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
-       xasprintf(&envp[3], "NAME=%s", myself->name);
+       xasprintf(&envp[3], "NAME=%s", myname);
 
        execute_script("tinc-down", envp);
 
@@ -774,6 +770,7 @@ static bool setup_myself(void) {
                return false;
        }
 
+       myname = xstrdup(name);
        myself = new_node();
        myself->connection = new_connection();
        myself->name = name;
@@ -791,6 +788,13 @@ static bool setup_myself(void) {
 
        myself->options |= PROT_MINOR << 24;
 
+#ifdef DISABLE_LEGACY
+       experimental = read_ecdsa_private_key();
+       if(!experimental) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "No private key available, cannot start tinc!");
+               return false;
+       }
+#else
        if(!get_config_bool(lookup_config(config_tree, "ExperimentalProtocol"), &experimental)) {
                experimental = read_ecdsa_private_key();
                if(!experimental)
@@ -800,8 +804,15 @@ static bool setup_myself(void) {
                        return false;
        }
 
-       if(!read_rsa_private_key())
-               return false;
+       if(!read_rsa_private_key()) {
+               if(experimental) {
+                       logger(DEBUG_ALWAYS, LOG_WARNING, "Support for legacy protocol disabled.");
+               } else {
+                       logger(DEBUG_ALWAYS, LOG_ERR, "No private keys available, cannot start tinc!");
+                       return false;
+               }
+       }
+#endif
 
        /* Ensure myport is numeric */
 
@@ -866,6 +877,7 @@ static bool setup_myself(void) {
                sptps_replaywin = replaywin;
        }
 
+#ifndef DISABLE_LEGACY
        /* Generate packet encryption key */
 
        if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
@@ -903,6 +915,7 @@ static bool setup_myself(void) {
        }
 
        free(digest);
+#endif
 
        /* Compression */
 
@@ -980,7 +993,7 @@ static bool setup_myself(void) {
                for(int i = 0; i < listen_sockets; i++) {
                        salen = sizeof sa;
                        if(getsockname(i + 3, &sa.sa, &salen) < 0) {
-                               logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
+                               logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(sockerrno));
                                return false;
                        }
 
@@ -1033,7 +1046,7 @@ static bool setup_myself(void) {
 
        /* If no Port option was specified, set myport to the port used by the first listening socket. */
 
-       if(!port_specified) {
+       if(!port_specified || atoi(myport) == 0) {
                sockaddr_t sa;
                socklen_t salen = sizeof sa;
                if(!getsockname(listen_socket[0].udp.fd, &sa.sa, &salen)) {
@@ -1134,13 +1147,18 @@ void close_network_connections(void) {
        if (!device_standby)
                device_disable();
 
-       if(myport) free(myport);
+       free(myport);
 
        if (device_fd >= 0)
                io_del(&device_io);
-       devops.close();
+       if (devops.close)
+               devops.close();
 
        exit_control();
 
+       free(myname);
+       free(scriptextension);
+       free(scriptinterpreter);
+
        return;
 }
index cc91521..85bc4df 100644 (file)
@@ -103,7 +103,7 @@ static bool bind_to_interface(int sd) {
        status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
        if(status) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
-                               strerror(errno));
+                               sockstrerror(sockerrno));
                return false;
        }
 #else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
@@ -134,7 +134,7 @@ static bool bind_to_address(connection_t *c) {
                sa.in6.sin6_port = 0;
 
        if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
-               logger(DEBUG_CONNECTIONS, LOG_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
+               logger(DEBUG_CONNECTIONS, LOG_WARNING, "Can't bind outgoing socket: %s", sockstrerror(sockerrno));
                return false;
        }
 
@@ -179,7 +179,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
                if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
                        closesocket(nfd);
                        logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to interface %s: %s", iface,
-                                  strerror(sockerrno));
+                                  sockstrerror(sockerrno));
                        return -1;
                }
 #else
@@ -247,10 +247,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
        setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof option);
 
        if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
-               logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
+               logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, sockstrerror(sockerrno));
 
        if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
-               logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
+               logger(DEBUG_ALWAYS, LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, sockstrerror(sockerrno));
 
 #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
        if(sa->sa.sa_family == AF_INET6)
@@ -330,7 +330,7 @@ static void do_outgoing_pipe(connection_t *c, char *command) {
        int fd[2];
 
        if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", strerror(errno));
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", sockstrerror(sockerrno));
                return;
        }
 
@@ -379,16 +379,16 @@ static void handle_meta_write(connection_t *c) {
 
        ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
        if(outlen <= 0) {
-               if(!errno || errno == EPIPE) {
+               if(!sockerrno || sockerrno == EPIPE) {
                        logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)", c->name, c->hostname);
                } else if(sockwouldblock(sockerrno)) {
                        logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Sending %d bytes to %s (%s) would block", c->outbuf.len - c->outbuf.offset, c->name, c->hostname);
                        return;
                } else {
-                       logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, strerror(errno));
+                       logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, sockstrerror(sockerrno));
                }
 
-               terminate_connection(c, c->status.active);
+               terminate_connection(c, c->edge);
                return;
        }
 
@@ -401,19 +401,38 @@ static void handle_meta_io(void *data, int flags) {
        connection_t *c = data;
 
        if(c->status.connecting) {
-               c->status.connecting = false;
-
-               int result;
-               socklen_t len = sizeof result;
-               getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
-
-               if(!result)
-                       finish_connecting(c);
-               else {
-                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(result));
-                       terminate_connection(c, false);
+               /*
+                  The event loop does not protect against spurious events. Verify that we are actually connected
+                  by issuing an empty send() call.
+
+                  Note that the behavior of send() on potentially unconnected sockets differ between platforms:
+                  +------------+-----------+-------------+-----------+
+                  |   Event    |   POSIX   |    Linux    |  Windows  |
+                  +------------+-----------+-------------+-----------+
+                  | Spurious   | ENOTCONN  | EWOULDBLOCK | ENOTCONN  |
+                  | Failed     | ENOTCONN  | (cause)     | ENOTCONN  |
+                  | Successful | (success) | (success)   | (success) |
+                  +------------+-----------+-------------+-----------+
+               */
+               if (send(c->socket, NULL, 0, 0) != 0) {
+                       if (sockwouldblock(sockerrno))
+                               return;
+                       int socket_error;
+                       if (!socknotconn(sockerrno))
+                               socket_error = sockerrno;
+                       else {
+                               socklen_t len = sizeof socket_error;
+                               getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&socket_error, &len);
+                       }
+                       if (socket_error) {
+                               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Error while connecting to %s (%s): %s", c->name, c->hostname, sockstrerror(socket_error));
+                               terminate_connection(c, false);
+                       }
                        return;
                }
+
+               c->status.connecting = false;
+               finish_connecting(c);
        }
 
        if(flags & IO_WRITE)
@@ -530,8 +549,10 @@ begin:
 
        c->status.connecting = true;
        c->name = xstrdup(outgoing->name);
+#ifndef DISABLE_LEGACY
        c->outcipher = myself->connection->outcipher;
        c->outdigest = myself->connection->outdigest;
+#endif
        c->outmaclength = myself->connection->outmaclength;
        c->outcompression = myself->connection->outcompression;
        c->last_ping_time = now.tv_sec;
@@ -677,8 +698,10 @@ void handle_new_meta_connection(void *data, int flags) {
 
        c = new_connection();
        c->name = xstrdup("<unknown>");
+#ifndef DISABLE_LEGACY
        c->outcipher = myself->connection->outcipher;
        c->outdigest = myself->connection->outdigest;
+#endif
        c->outmaclength = myself->connection->outmaclength;
        c->outcompression = myself->connection->outcompression;
 
@@ -801,7 +824,7 @@ void try_outgoing_connections(void) {
                if(c->outgoing && c->outgoing->timeout == -1) {
                        c->outgoing = NULL;
                        logger(DEBUG_CONNECTIONS, LOG_INFO, "No more outgoing connection to %s", c->name);
-                       terminate_connection(c, c->status.active);
+                       terminate_connection(c, c->edge);
                }
        }
 
index 989a152..3ea5d4a 100644 (file)
@@ -39,6 +39,9 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
        hint.ai_family = addressfamily;
        hint.ai_socktype = socktype;
 
+#ifdef HAVE_DECL_RES_INIT
+       res_init();
+#endif
        err = getaddrinfo(address, service, &hint, &ai);
 
        if(err) {
index aab83ca..fb4b7eb 100644 (file)
 #include "utils.h"
 #include "xalloc.h"
 
+#include "ed25519/sha512.h"
+
 splay_tree_t *node_tree;
+static splay_tree_t *node_id_tree;
 static hash_t *node_udp_cache;
+static hash_t *node_id_cache;
 
 node_t *myself;
 
@@ -39,13 +43,21 @@ static int node_compare(const node_t *a, const node_t *b) {
        return strcmp(a->name, b->name);
 }
 
+static int node_id_compare(const node_t *a, const node_t *b) {
+       return memcmp(&a->id, &b->id, sizeof(node_id_t));
+}
+
 void init_nodes(void) {
        node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
+       node_id_tree = splay_alloc_tree((splay_compare_t) node_id_compare, NULL);
        node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t));
+       node_id_cache = hash_alloc(0x100, sizeof(node_id_t));
 }
 
 void exit_nodes(void) {
+       hash_free(node_id_cache);
        hash_free(node_udp_cache);
+       splay_delete_tree(node_id_tree);
        splay_delete_tree(node_tree);
 }
 
@@ -70,15 +82,17 @@ void free_node(node_t *n) {
 
        sockaddrfree(&n->address);
 
+#ifndef DISABLE_LEGACY
        cipher_close(n->incipher);
        digest_close(n->indigest);
        cipher_close(n->outcipher);
        digest_close(n->outdigest);
+#endif
 
        ecdsa_free(n->ecdsa);
        sptps_stop(&n->sptps);
 
-       timeout_del(&n->mtutimeout);
+       timeout_del(&n->udp_ping_timeout);
 
        if(n->hostname)
                free(n->hostname);
@@ -93,16 +107,25 @@ void free_node(node_t *n) {
 }
 
 void node_add(node_t *n) {
+       unsigned char buf[64];
+       sha512(n->name, strlen(n->name),buf);
+       memcpy(&n->id, buf, sizeof n->id);
+
        splay_insert(node_tree, n);
+       splay_insert(node_id_tree, n);
 }
 
 void node_del(node_t *n) {
+       hash_delete(node_udp_cache, &n->address);
+       hash_delete(node_id_cache, &n->id);
+
        for splay_each(subnet_t, s, n->subnet_tree)
                subnet_del(n, s);
 
        for splay_each(edge_t, e, n->edge_tree)
                edge_del(e);
 
+       splay_delete(node_id_tree, n);
        splay_delete(node_tree, n);
 }
 
@@ -114,6 +137,18 @@ node_t *lookup_node(char *name) {
        return splay_search(node_tree, &n);
 }
 
+node_t *lookup_node_id(const node_id_t *id) {
+       node_t *n = hash_search(node_id_cache, id);
+       if(!n) {
+               node_t tmp = {.id = *id};
+               n = splay_search(node_id_tree, &tmp);
+               if(n)
+                       hash_insert(node_id_cache, id, n);
+       }
+
+       return n;
+}
+
 node_t *lookup_node_udp(const sockaddr_t *sa) {
        return hash_search(node_udp_cache, sa);
 }
@@ -124,7 +159,7 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
                return;
        }
 
-       hash_insert(node_udp_cache, &n->address, NULL);
+       hash_delete(node_udp_cache, &n->address);
 
        if(sa) {
                n->address = *sa;
@@ -140,15 +175,33 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
                n->hostname = sockaddr2hostname(&n->address);
                logger(DEBUG_PROTOCOL, LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
        }
+
+       /* invalidate UDP information - note that this is a security feature as well to make sure
+          we can't be tricked into flooding any random address with UDP packets */
+       n->status.udp_confirmed = false;
+       n->maxrecentlen = 0;
+       n->mtuprobes = 0;
+       n->minmtu = 0;
+       n->maxmtu = MTU;
 }
 
 bool dump_nodes(connection_t *c) {
-       for splay_each(node_t, n, node_tree)
-               send_request(c, "%d %d %s %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", CONTROL, REQ_DUMP_NODES,
-                          n->name, n->hostname ?: "unknown port unknown", 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->distance, n->mtu, n->minmtu, n->maxmtu, (long)n->last_state_change);
+       for splay_each(node_t, n, node_tree) {
+               char id[2 * sizeof n->id + 1];
+               for (size_t c = 0; c < sizeof n->id; ++c)
+                       sprintf(id + 2 * c, "%02hhx", n->id.x[c]);
+               id[sizeof id - 1] = 0;
+               send_request(c, "%d %d %s %s %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", CONTROL, REQ_DUMP_NODES,
+                          n->name, id, n->hostname ?: "unknown port unknown",
+#ifdef DISABLE_LEGACY
+                          0, 0, 0,
+#else
+                          cipher_get_nid(n->outcipher), digest_get_nid(n->outdigest), (int)digest_length(n->outdigest),
+#endif
+                          n->outcompression, n->options, bitfield_to_int(&n->status, sizeof n->status),
+                          n->nexthop ? n->nexthop->name : "-", n->via ? n->via->name ?: "-" : "-", n->distance,
+                          n->mtu, n->minmtu, n->maxmtu, (long)n->last_state_change);
+       }
 
        return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
 }
index 1c9f230..5fe6dfa 100644 (file)
@@ -37,12 +37,15 @@ typedef struct node_status_t {
        unsigned int indirect:1;                /* 1 if this node is not directly reachable by us */
        unsigned int sptps:1;                   /* 1 if this node supports SPTPS */
        unsigned int udp_confirmed:1;           /* 1 if the address is one that we received UDP traffic on */
-       unsigned int broadcast:1;               /* 1 if the next UDP packet should be broadcast to the local network */
-       unsigned int unused:23;
+       unsigned int send_locally:1;            /* 1 if the next UDP packet should be sent on the local network */
+       unsigned int udppacket:1;               /* 1 if the most recently received packet was UDP */
+       unsigned int validkey_in;               /* 1 if we have sent a valid key to him */
+       unsigned int unused:22;
 } node_status_t;
 
 typedef struct node_t {
        char *name;                             /* name of this node */
+       node_id_t id;                           /* unique node ID (name hash) */
        uint32_t options;                       /* options turned on for this node */
 
        int sock;                               /* Socket to use for outgoing UDP packets */
@@ -56,11 +59,13 @@ typedef struct node_t {
        ecdsa_t *ecdsa;                         /* His public ECDSA key */
        sptps_t sptps;
 
+#ifndef DISABLE_LEGACY
        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 */
+#endif
 
        int incompression;                      /* Compressionlevel, 0 = no compression */
        int outcompression;                     /* Compressionlevel, 0 = no compression */
@@ -84,16 +89,18 @@ typedef struct node_t {
        uint32_t farfuture;                     /* Packets in a row that have arrived from the far future */
        unsigned char* late;                    /* Bitfield marking late packets */
 
+       struct timeval udp_reply_sent;          /* Last time a (gratuitous) UDP probe reply was sent */
+       struct timeval udp_ping_sent;           /* Last time a UDP probe was sent */
+       timeout_t udp_ping_timeout;             /* Ping timeout event */
+
+       struct timeval mtu_ping_sent;           /* Last time a MTU probe was sent */
+
+       length_t maxrecentlen;                  /* Maximum size of recently received packets */
+
        length_t mtu;                           /* Maximum size of packets to send to this node */
        length_t minmtu;                        /* Probed minimum MTU */
        length_t maxmtu;                        /* Probed maximum MTU */
        int mtuprobes;                          /* Number of probes */
-       timeout_t mtutimeout;                   /* Probe event */
-       struct timeval probe_time;              /* Time the last probe was sent or received */
-       int probe_counter;                      /* Number of probes received since last burst was sent */
-       float rtt;                              /* Last measured round trip time */
-       float bandwidth;                        /* Last measured bandwidth */
-       float packetloss;                       /* Last measured packet loss rate */
 
        uint64_t in_packets;
        uint64_t in_bytes;
@@ -111,6 +118,7 @@ extern void free_node(node_t *);
 extern void node_add(node_t *);
 extern void node_del(node_t *);
 extern node_t *lookup_node(char *);
+extern node_t *lookup_node_id(const node_id_t *);
 extern node_t *lookup_node_udp(const sockaddr_t *);
 extern bool dump_nodes(struct connection_t *);
 extern bool dump_traffic(struct connection_t *);
diff --git a/src/nolegacy/crypto.c b/src/nolegacy/crypto.c
new file mode 100644 (file)
index 0000000..96e4dda
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+    crypto.c -- Cryptographic miscellaneous functions and initialisation
+    Copyright (C) 2007-2014 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"
+
+#ifndef HAVE_MINGW
+
+static int random_fd = -1;
+
+static void random_init(void) {
+       random_fd = open("/dev/urandom", O_RDONLY);
+       if(random_fd < 0)
+               random_fd = open("/dev/random", O_RDONLY);
+       if(random_fd < 0) {
+               fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno));
+               abort();
+       }
+}
+
+static void random_exit(void) {
+       close(random_fd);
+}
+
+void randomize(void *out, size_t outlen) {
+       while(outlen) {
+               size_t len = read(random_fd, out, outlen);
+               if(len <= 0) {
+                       if(errno == EAGAIN || errno == EINTR)
+                               continue;
+                       fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno));
+                       abort();
+               }
+               out += len;
+               outlen -= len;
+       }
+}
+
+#else
+
+#include <wincrypt.h>
+HCRYPTPROV prov;
+
+void random_init(void) {
+       if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+               fprintf(stderr, "CryptAcquireContext() failed!\n");
+               abort();
+       }
+}
+
+void random_exit(void) {
+       CryptReleaseContext(prov, 0);
+}
+
+void randomize(void *out, size_t outlen) {
+       if(!CryptGenRandom(prov, outlen, out)) {
+               fprintf(stderr, "CryptGenRandom() failed\n");
+               abort();
+       }
+}
+
+#endif
+
+void crypto_init(void) {
+       random_init();
+}
+
+void crypto_exit(void) {
+       random_exit();
+}
diff --git a/src/nolegacy/prf.c b/src/nolegacy/prf.c
new file mode 100644 (file)
index 0000000..3c4c27e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    prf.c -- Pseudo-Random Function for key material generation
+    Copyright (C) 2011-2013 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 "../prf.h"
+#include "../ed25519/sha512.h"
+
+static void memxor(char *buf, char c, size_t len) {
+       for(size_t i = 0; i < len; i++)
+               buf[i] ^= c;
+}
+
+static const size_t mdlen = 64;
+
+static bool hmac_sha512(const char *key, size_t keylen, const char *msg, size_t msglen, char *out) {
+       char tmp[2 * mdlen];
+       sha512_context md;
+
+       if(keylen <= mdlen) {
+               memcpy(tmp, key, keylen);
+               memset(tmp + keylen, 0, mdlen - keylen);
+       } else {
+               if(sha512(key, keylen, tmp) != 0)
+                       return false;
+       }
+
+       if(sha512_init(&md) != 0)
+               return false;
+
+       // ipad
+       memxor(tmp, 0x36, mdlen);
+       if(sha512_update(&md, tmp, mdlen) != 0)
+               return false;
+
+       // message
+       if(sha512_update(&md, msg, msglen) != 0)
+               return false;
+
+       if(sha512_final(&md, tmp + mdlen) != 0)
+               return false;
+
+       // opad
+       memxor(tmp, 0x36 ^ 0x5c, mdlen);
+       if(sha512(tmp, sizeof tmp, out) != 0)
+               return false;
+
+       return true;
+}
+
+
+/* Generate key material from a master secret and a seed, based on RFC 4346 section 5.
+   We use SHA512 instead of MD5 and SHA1.
+ */
+
+bool prf(const char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
+       /* Data is what the "inner" HMAC function processes.
+          It consists of the previous HMAC result plus the seed.
+        */
+
+       char data[mdlen + seedlen];
+       memset(data, 0, mdlen);
+       memcpy(data + mdlen, seed, seedlen);
+
+       char hash[mdlen];
+
+       while(outlen > 0) {
+               /* Inner HMAC */
+               if(!hmac_sha512(data, sizeof data, secret, secretlen, data))
+                       return false;
+
+               /* Outer HMAC */
+               if(outlen >= mdlen) {
+                       if(!hmac_sha512(data, sizeof data, secret, secretlen, out))
+                               return false;
+                       out += mdlen;
+                       outlen -= mdlen;
+               } else {
+                       if(!hmac_sha512(data, sizeof data, secret, secretlen, hash))
+                               return false;
+                       memcpy(out, hash, outlen);
+                       out += outlen;
+                       outlen = 0;
+               }
+       }
+
+       return true;
+}
index 5172d82..04aee27 100644 (file)
 struct cipher {
        EVP_CIPHER_CTX ctx;
        const EVP_CIPHER *cipher;
-       struct cipher_counter *counter;
 };
 
-typedef struct cipher_counter {
-       unsigned char counter[CIPHER_MAX_IV_SIZE];
-       unsigned char block[CIPHER_MAX_IV_SIZE];
-       int n;
-} cipher_counter_t;
-
 static cipher_t *cipher_open(const EVP_CIPHER *evp_cipher) {
        cipher_t *cipher = xzalloc(sizeof *cipher);
        cipher->cipher = evp_cipher;
@@ -76,7 +69,6 @@ void cipher_close(cipher_t *cipher) {
                return;
 
        EVP_CIPHER_CTX_cleanup(&cipher->ctx);
-       free(cipher->counter);
        free(cipher);
 }
 
@@ -87,6 +79,13 @@ size_t cipher_keylength(const cipher_t *cipher) {
        return cipher->cipher->key_len + cipher->cipher->iv_len;
 }
 
+size_t cipher_blocksize(const cipher_t *cipher) {
+       if(!cipher || !cipher->cipher)
+               return 1;
+
+       return cipher->cipher->block_size;
+}
+
 bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
        bool result;
 
@@ -117,124 +116,6 @@ bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encry
        return false;
 }
 
-bool cipher_set_counter(cipher_t *cipher, const void *counter, size_t len) {
-       if(len > cipher->cipher->iv_len - 4) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Counter too long");
-               return false;
-       }
-
-       memcpy(cipher->counter->counter, counter, len);
-       cipher->counter->n = 0;
-
-       return true;
-}
-
-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(DEBUG_ALWAYS, LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-
-       if(!cipher->counter)
-               cipher->counter = xzalloc(sizeof *cipher->counter);
-       else
-               cipher->counter->n = 0;
-
-       memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->iv_len);
-
-       return true;
-}
-
-bool cipher_gcm_encrypt_start(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       int len = 0;
-       if(!EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter)
-                       || (inlen && !EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen))) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       if(outlen)
-               *outlen = len;
-       return true;
-}
-
-bool cipher_gcm_encrypt_finish(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       int len = 0, pad = 0;
-       if((inlen && !EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen))
-                       || !EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)outdata + len + pad);
-       if(outlen)
-               *outlen = len + pad + 16;
-       return true;
-}
-
-bool cipher_gcm_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       int len = 0, pad = 0;
-       if(!EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter) ||
-                       !EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen) ||
-                       !EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)outdata + len + pad);
-       if(outlen)
-               *outlen = len + pad + 16;
-       return true;
-}
-
-bool cipher_gcm_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       if(inlen < 16)
-               return false;
-
-       int len = 0, pad = 0;
-       if(!EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-
-       EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)indata + inlen - 16);
-
-       if(!EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen - 16) ||
-                       !EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       if(outlen)
-               *outlen = len;
-       return true;
-}
-
-bool cipher_gcm_decrypt_start(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       int len = 0;
-       if(!EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter)
-                       || (inlen && !EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen))) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       if(outlen)
-               *outlen = len;
-       return true;
-}
-
-bool cipher_gcm_decrypt_finish(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) {
-       if(inlen < 16)
-               return false;
-
-       EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)indata + inlen - 16);
-
-       int len = 0, pad = 0;
-       if((inlen > 16 && !EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen - 16))
-                       || !EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-       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;
index 6c5cbc8..5b866b0 100644 (file)
@@ -1,6 +1,6 @@
 /*
     crypto.c -- Cryptographic miscellaneous functions and initialisation
-    Copyright (C) 2007-2013 Guus Sliepen <guus@tinc-vpn.org>
+    Copyright (C) 2007-2014 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
 
 #include "../crypto.h"
 
+#ifndef HAVE_MINGW
+
+static int random_fd = -1;
+
+static void random_init(void) {
+       random_fd = open("/dev/urandom", O_RDONLY);
+       if(random_fd < 0)
+               random_fd = open("/dev/random", O_RDONLY);
+       if(random_fd < 0) {
+               fprintf(stderr, "Could not open source of random numbers: %s\n", strerror(errno));
+               abort();
+       }
+}
+
+static void random_exit(void) {
+       close(random_fd);
+}
+
+void randomize(void *out, size_t outlen) {
+       while(outlen) {
+               size_t len = read(random_fd, out, outlen);
+               if(len <= 0) {
+                       if(errno == EAGAIN || errno == EINTR)
+                               continue;
+                       fprintf(stderr, "Could not read random numbers: %s\n", strerror(errno));
+                       abort();
+               }
+               out += len;
+               outlen -= len;
+       }
+}
+
+#else
+
+#include <wincrypt.h>
+HCRYPTPROV prov;
+
+void random_init(void) {
+       if(!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+               fprintf(stderr, "CryptAcquireContext() failed!\n");
+               abort();
+       }
+}
+
+void random_exit(void) {
+       CryptReleaseContext(prov, 0);
+}
+
+void randomize(void *out, size_t outlen) {
+       if(!CryptGenRandom(prov, outlen, out)) {
+               fprintf(stderr, "CryptGenRandom() failed\n");
+               abort();
+       }
+}
+
+#endif
+
 void crypto_init(void) {
-       RAND_load_file("/dev/urandom", 1024);
+       random_init();
 
        ENGINE_load_builtin_engines();
        ENGINE_register_all_complete();
@@ -42,8 +99,7 @@ void crypto_init(void) {
 
 void crypto_exit(void) {
        EVP_cleanup();
-}
-
-void randomize(void *out, size_t outlen) {
-       RAND_pseudo_bytes(out, outlen);
+       ERR_free_strings();
+       ENGINE_cleanup();
+       random_exit();
 }
diff --git a/src/openssl/ecdh.c b/src/openssl/ecdh.c
deleted file mode 100644 (file)
index d997007..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-    ecdh.c -- Diffie-Hellman key exchange handling
-    Copyright (C) 2011-2013 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/err.h>
-#include <openssl/ec.h>
-#include <openssl/ecdh.h>
-#include <openssl/obj_mac.h>
-
-#define __TINC_ECDH_INTERNAL__
-typedef EC_KEY ecdh_t;
-
-#include "../ecdh.h"
-#include "../logger.h"
-#include "../utils.h"
-#include "../xalloc.h"
-
-ecdh_t *ecdh_generate_public(void *pubkey) {
-       ecdh_t *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
-       if(!ecdh) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               return false;
-       }
-
-       if(!EC_KEY_generate_key(ecdh)) {
-               EC_KEY_free(ecdh);
-               logger(DEBUG_ALWAYS, LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               return NULL;
-       }
-
-       const EC_POINT *point = EC_KEY_get0_public_key(ecdh);
-       if(!point) {
-               EC_KEY_free(ecdh);
-               logger(DEBUG_ALWAYS, LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               return NULL;
-       }
-
-       size_t result = EC_POINT_point2oct(EC_KEY_get0_group(ecdh), point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL);
-       if(!result) {
-               EC_KEY_free(ecdh);
-               logger(DEBUG_ALWAYS, LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               return NULL;
-       }
-
-       return ecdh;
-}
-
-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(DEBUG_ALWAYS, LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               EC_KEY_free(ecdh);
-               return false;
-       }
-
-       int result = EC_POINT_oct2point(EC_KEY_get0_group(ecdh), point, pubkey, ECDH_SIZE, NULL);
-       if(!result) {
-               EC_POINT_free(point);
-               EC_KEY_free(ecdh);
-               logger(DEBUG_ALWAYS, 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);
-
-       if(!result) {
-               logger(DEBUG_ALWAYS, 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);
-}
diff --git a/src/openssl/ecdsa.c b/src/openssl/ecdsa.c
deleted file mode 100644 (file)
index bca89fc..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
-    ecdsa.c -- ECDSA key handling
-    Copyright (C) 2011-2013 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>
-
-#define __TINC_ECDSA_INTERNAL__
-typedef EC_KEY ecdsa_t;
-
-#include "../logger.h"
-#include "../ecdsa.h"
-#include "../utils.h"
-#include "../xalloc.h"
-
-// Get and set ECDSA keys
-//
-ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
-       ecdsa_t *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
-       if(!ecdsa) {
-               logger(DEBUG_ALWAYS, LOG_DEBUG, "EC_KEY_new_by_curve_name failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               return NULL;
-       }
-
-       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(DEBUG_ALWAYS, LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               EC_KEY_free(ecdsa);
-               return NULL;
-       }
-
-       return ecdsa;
-}
-
-char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
-       unsigned char *pubkey = NULL;
-       int len = i2o_ECPublicKey(ecdsa, &pubkey);
-
-       char *base64 = xmalloc(len * 4 / 3 + 5);
-       b64encode((char *)pubkey, base64, len);
-
-       free(pubkey);
-
-       return base64;
-}
-
-// Read PEM ECDSA keys
-
-ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
-       ecdsa_t *ecdsa = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL);
-
-       if(!ecdsa)
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
-
-       return ecdsa;
-}
-
-ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) {
-       ecdsa_t *ecdsa = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);
-
-       if(!ecdsa)
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
-
-       return ecdsa;
-}
-
-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(DEBUG_ALWAYS, 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(DEBUG_ALWAYS, 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);
-}
diff --git a/src/openssl/ecdsagen.c b/src/openssl/ecdsagen.c
deleted file mode 100644 (file)
index 1affce0..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-    ecdsagen.c -- ECDSA key generation and export
-    Copyright (C) 2011-2013 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>
-
-#define __TINC_ECDSA_INTERNAL__
-typedef EC_KEY ecdsa_t;
-
-#include "../ecdsagen.h"
-#include "../utils.h"
-#include "../xalloc.h"
-
-// Generate ECDSA key
-
-ecdsa_t *ecdsa_generate(void) {
-       ecdsa_t *ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
-
-       if(!ecdsa || !EC_KEY_generate_key(ecdsa)) {
-               fprintf(stderr, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
-               ecdsa_free(ecdsa);
-               return false;
-       }
-
-       EC_KEY_set_asn1_flag(ecdsa, OPENSSL_EC_NAMED_CURVE);
-       EC_KEY_set_conv_form(ecdsa, POINT_CONVERSION_COMPRESSED);
-
-       return ecdsa;
-}
-
-// Write PEM ECDSA keys
-
-bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
-       return PEM_write_EC_PUBKEY(fp, ecdsa);
-}
-
-bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
-       return PEM_write_ECPrivateKey(fp, ecdsa, NULL, NULL, 0, NULL, NULL);
-}
index 86953ad..2dab2b5 100644 (file)
@@ -34,6 +34,7 @@
 #include "subnet.h"
 #include "utils.h"
 #include "xalloc.h"
+#include "version.h"
 
 /* If zero, don't detach from the terminal. */
 bool do_detach = true;
@@ -109,6 +110,8 @@ static bool install_service(void) {
        return true;
 }
 
+io_t stop_io;
+
 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
        switch(request) {
                case SERVICE_CONTROL_INTERROGATE:
@@ -125,10 +128,11 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
                        return ERROR_CALL_NOT_IMPLEMENTED;
        }
 
-       event_exit();
-       status.dwWaitHint = 30000;
+       status.dwWaitHint = 1000;
        status.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(statushandle, &status);
+       if (WSASetEvent(stop_io.event) == FALSE)
+               abort();
        return NO_ERROR;
 }
 
@@ -215,7 +219,7 @@ bool detach(void) {
        openlogger(identname, logmode);
 
        logger(DEBUG_ALWAYS, LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
-                          VERSION, __DATE__, __TIME__, debug_level);
+                          VERSION, BUILD_DATE, BUILD_TIME, debug_level);
 
        return true;
 }
index 4cdf711..ce2daed 100644 (file)
@@ -29,6 +29,7 @@ extern bool detach(void);
 extern bool kill_other(int);
 
 #ifdef HAVE_MINGW
+extern io_t stop_io;
 extern bool init_service(void);
 #endif
 
index 374c522..1ec169a 100644 (file)
@@ -55,17 +55,6 @@ static char (*request_name[]) = {
 
 static splay_tree_t *past_request_tree;
 
-bool check_id(const char *id) {
-       if(!id || !*id)
-               return false;
-
-       for(; *id; id++)
-               if(!isalnum(*id) && *id != '_')
-                       return false;
-
-       return true;
-}
-
 /* Generic request routines - takes care of logging and error
    detection as well */
 
index e771c54..080d50c 100644 (file)
@@ -26,7 +26,7 @@
 /* Protocol version. Different major versions are incompatible. */
 
 #define PROT_MAJOR 17
-#define PROT_MINOR 3 /* Should not exceed 255! */
+#define PROT_MINOR 4 /* Should not exceed 255! */
 
 /* Silly Windows */
 
@@ -81,7 +81,6 @@ extern ecdsa_t *invitation_key;
 extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
 extern void forward_request(struct connection_t *, const char *);
 extern bool receive_request(struct connection_t *, const char *);
-extern bool check_id(const char *);
 
 extern void init_requests(void);
 extern void exit_requests(void);
index b8d4ee8..0882ddf 100644 (file)
@@ -45,6 +45,8 @@
 #include "utils.h"
 #include "xalloc.h"
 
+#include "ed25519/sha512.h"
+
 ecdsa_t *invitation_key = NULL;
 
 static bool send_proxyrequest(connection_t *c) {
@@ -198,7 +200,7 @@ static bool finalize_invitation(connection_t *c, const char *data, uint16_t len)
        return true;
 }
 
-static bool receive_invitation_sptps(void *handle, uint8_t type, const char *data, uint16_t len) {
+static bool receive_invitation_sptps(void *handle, uint8_t type, const void *data, uint16_t len) {
        connection_t *c = handle;
 
        if(type == 128)
@@ -211,17 +213,13 @@ static bool receive_invitation_sptps(void *handle, uint8_t type, const char *dat
                return false;
 
        // Recover the filename from the cookie and the key
-       digest_t *digest = digest_open_by_name("sha256", 18);
-       if(!digest)
-               abort();
        char *fingerprint = ecdsa_get_base64_public_key(invitation_key);
        char hashbuf[18 + strlen(fingerprint)];
-       char cookie[25];
+       char cookie[64];
        memcpy(hashbuf, data, 18);
        memcpy(hashbuf + 18, fingerprint, sizeof hashbuf - 18);
-       digest_create(digest, hashbuf, sizeof hashbuf, cookie);
+       sha512(hashbuf, sizeof hashbuf, cookie);
        b64encode_urlsafe(cookie, cookie, 18);
-       digest_close(digest);
        free(fingerprint);
 
        char filename[PATH_MAX], usedname[PATH_MAX];
@@ -379,13 +377,13 @@ bool id_h(connection_t *c, const char *request) {
                }
 
                if(experimental)
-                       if(!read_ecdsa_public_key(c))
-                               return false;
-       } else {
-               if(c->protocol_minor && !ecdsa_active(c->ecdsa))
-                       c->protocol_minor = 1;
+                       read_ecdsa_public_key(c);
+                       /* Ignore failures if no key known yet */
        }
 
+       if(c->protocol_minor && !ecdsa_active(c->ecdsa))
+               c->protocol_minor = 1;
+
        /* Forbid version rollback for nodes whose Ed25519 key we know */
 
        if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) {
@@ -412,6 +410,14 @@ bool id_h(connection_t *c, const char *request) {
 }
 
 bool send_metakey(connection_t *c) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       if(!myself->connection->rsa) {
+               logger(DEBUG_CONNECTIONS, LOG_ERR, "Peer %s (%s) uses legacy protocol which we don't support", c->name, c->hostname);
+               return false;
+       }
+
        if(!read_rsa_public_key(c))
                return false;
 
@@ -421,7 +427,7 @@ bool send_metakey(connection_t *c) {
        if(!(c->outdigest = digest_open_sha1(-1)))
                return false;
 
-       size_t len = rsa_size(c->rsa);
+       const size_t len = rsa_size(c->rsa);
        char key[len];
        char enckey[len];
        char hexkey[2 * len + 1];
@@ -475,12 +481,19 @@ bool send_metakey(connection_t *c) {
 
        c->status.encryptout = true;
        return result;
+#endif
 }
 
 bool metakey_h(connection_t *c, const char *request) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       if(!myself->connection->rsa)
+               return false;
+
        char hexkey[MAX_STRING_SIZE];
        int cipher, digest, maclength, compression;
-       size_t len = rsa_size(myself->connection->rsa);
+       const size_t len = rsa_size(myself->connection->rsa);
        char enckey[len];
        char key[len];
 
@@ -537,10 +550,14 @@ bool metakey_h(connection_t *c, const char *request) {
        c->allow_request = CHALLENGE;
 
        return send_challenge(c);
+#endif
 }
 
 bool send_challenge(connection_t *c) {
-       size_t len = rsa_size(c->rsa);
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       const size_t len = rsa_size(c->rsa);
        char buffer[len * 2 + 1];
 
        if(!c->hischallenge)
@@ -557,11 +574,18 @@ bool send_challenge(connection_t *c) {
        /* Send the challenge */
 
        return send_request(c, "%d %s", CHALLENGE, buffer);
+#endif
 }
 
 bool challenge_h(connection_t *c, const char *request) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
+       if(!myself->connection->rsa)
+               return false;
+
        char buffer[MAX_STRING_SIZE];
-       size_t len = rsa_size(myself->connection->rsa);
+       const size_t len = rsa_size(myself->connection->rsa);
        size_t digestlen = digest_length(c->indigest);
        char digest[digestlen];
 
@@ -595,9 +619,13 @@ bool challenge_h(connection_t *c, const char *request) {
        c->allow_request = CHAL_REPLY;
 
        return send_request(c, "%d %s", CHAL_REPLY, buffer);
+#endif
 }
 
 bool chal_reply_h(connection_t *c, const char *request) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
        char hishash[MAX_STRING_SIZE];
 
        if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
@@ -634,9 +662,13 @@ bool chal_reply_h(connection_t *c, const char *request) {
        c->allow_request = ACK;
 
        return send_ack(c);
+#endif
 }
 
 static bool send_upgrade(connection_t *c) {
+#ifdef DISABLE_LEGACY
+       return false;
+#else
        /* Special case when protocol_minor is 1: the other end is Ed25519 capable,
         * but doesn't know our key yet. So send it now. */
 
@@ -648,6 +680,7 @@ static bool send_upgrade(connection_t *c) {
        bool result = send_request(c, "%d %s", ACK, pubkey);
        free(pubkey);
        return result;
+#endif
 }
 
 bool send_ack(connection_t *c) {
@@ -726,7 +759,21 @@ static bool upgrade_h(connection_t *c, const char *request) {
        }
 
        if(ecdsa_active(c->ecdsa) || read_ecdsa_public_key(c)) {
-               logger(DEBUG_ALWAYS, LOG_INFO, "Already have Ed25519 public key from %s (%s), not upgrading.", c->name, c->hostname);
+               char *knownkey = ecdsa_get_base64_public_key(c->ecdsa);
+               bool different = strcmp(knownkey, pubkey);
+               free(knownkey);
+               if(different) {
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Already have an Ed25519 public key from %s (%s) which is different from the one presented now!", c->name, c->hostname);
+                       return false;
+               }
+               logger(DEBUG_ALWAYS, LOG_INFO, "Already have Ed25519 public key from %s (%s), ignoring.", c->name, c->hostname);
+               c->allow_request = TERMREQ;
+               return send_termreq(c);
+       }
+
+       c->ecdsa = ecdsa_set_base64_public_key(pubkey);
+       if(!c->ecdsa) {
+               logger(DEBUG_ALWAYS, LOG_INFO, "Got bad Ed25519 public key from %s (%s), not upgrading.", c->name, c->hostname);
                return false;
        }
 
@@ -805,7 +852,6 @@ bool ack_h(connection_t *c, const char *request) {
        /* Activate this connection */
 
        c->allow_request = ALL;
-       c->status.active = true;
 
        logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
                           c->hostname);
@@ -822,6 +868,16 @@ bool ack_h(connection_t *c, const char *request) {
        sockaddr2str(&c->address, &hisaddress, NULL);
        c->edge->address = str2sockaddr(hisaddress, hisport);
        free(hisaddress);
+       sockaddr_t local_sa;
+       socklen_t local_salen = sizeof local_sa;
+       if (getsockname(c->socket, &local_sa.sa, &local_salen) < 0)
+               logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get local socket address for connection with %s", c->name);
+       else {
+               char *local_address;
+               sockaddr2str(&local_sa, &local_address, NULL);
+               c->edge->local_address = str2sockaddr(local_address, myport);
+               free(local_address);
+       }
        c->edge->weight = (weight + c->estimated_weight) / 2;
        c->edge->connection = c;
        c->edge->options = c->options;
index e285a6d..4760162 100644 (file)
 bool send_add_edge(connection_t *c, const edge_t *e) {
        bool x;
        char *address, *port;
+       char *local_address, *local_port;
 
        sockaddr2str(&e->address, &address, &port);
+       sockaddr2str(&e->local_address, &local_address, &local_port);
 
-       x = send_request(c, "%d %x %s %s %s %s %x %d", ADD_EDGE, rand(),
+       x = send_request(c, "%d %x %s %s %s %s %x %d %s %s", ADD_EDGE, rand(),
                                         e->from->name, e->to->name, address, port,
-                                        e->options, e->weight);
+                                        e->options, e->weight, local_address, local_port);
+
        free(address);
        free(port);
+       free(local_address);
+       free(local_port);
 
        return x;
 }
@@ -56,12 +61,15 @@ bool add_edge_h(connection_t *c, const char *request) {
        char to_name[MAX_STRING_SIZE];
        char to_address[MAX_STRING_SIZE];
        char to_port[MAX_STRING_SIZE];
-       sockaddr_t address;
+       char address_local[MAX_STRING_SIZE] = "unknown";
+       char port_local[MAX_STRING_SIZE] = "unknown";
+       sockaddr_t address, local_address;
        uint32_t options;
        int weight;
 
-       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) {
+       int parameter_count = sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d "MAX_STRING" "MAX_STRING,
+                                             from_name, to_name, to_address, to_port, &options, &weight, address_local, port_local);
+       if (parameter_count != 6 && parameter_count != 8) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
                           c->hostname);
                return false;
@@ -109,13 +117,14 @@ bool add_edge_h(connection_t *c, const char *request) {
        /* Convert addresses */
 
        address = str2sockaddr(to_address, to_port);
+       local_address = str2sockaddr(address_local, port_local);
 
        /* Check if edge already exists */
 
        e = lookup_edge(from, to);
 
        if(e) {
-               if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
+               if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address) || sockaddrcmp(&e->local_address, &local_address)) {
                        if(from == myself) {
                                logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
                                                   "ADD_EDGE", c->name, c->hostname);
@@ -145,6 +154,7 @@ bool add_edge_h(connection_t *c, const char *request) {
        e->from = from;
        e->to = to;
        e->address = address;
+       e->local_address = local_address;
        e->options = options;
        e->weight = weight;
        edge_add(e);
index e838f61..8cbec1b 100644 (file)
@@ -1,7 +1,7 @@
 /*
     protocol_key.c -- handle the meta-protocol, key exchange
     Copyright (C) 1999-2005 Ivo Timmermans,
-                  2000-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2000-2014 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
@@ -41,7 +41,7 @@ void send_key_changed(void) {
        /* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
 
        for list_each(connection_t, c, connection_list)
-               if(c->status.active && c->node && c->node->status.reachable && !c->node->status.sptps)
+               if(c->edge && c->node && c->node->status.reachable && !c->node->status.sptps)
                        send_ans_key(c->node);
 
        /* Force key exchange for connections using SPTPS */
@@ -87,7 +87,7 @@ bool key_changed_h(connection_t *c, const char *request) {
        return true;
 }
 
-static bool send_initial_sptps_data(void *handle, uint8_t type, const char *data, size_t len) {
+static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
        node_t *to = handle;
        to->sptps.send_data = send_sptps_data;
        char buf[len * 4 / 3 + 5];
@@ -124,6 +124,11 @@ bool send_req_key(node_t *to) {
 static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, int reqno) {
        switch(reqno) {
                case REQ_PUBKEY: {
+                       if(!node_read_ecdsa_public_key(from)) {
+                               /* Request their key *before* we send our key back. Otherwise the first SPTPS packet from them will get dropped. */
+                               logger(DEBUG_PROTOCOL, LOG_DEBUG, "Preemptively requesting Ed25519 key for %s (%s)", from->name, from->hostname);
+                               send_request(from->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, from->name, REQ_PUBKEY);
+                       }
                        char *pubkey = ecdsa_get_base64_public_key(myself->connection->ecdsa);
                        send_request(from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, from->name, ANS_PUBKEY, pubkey);
                        free(pubkey);
@@ -250,6 +255,7 @@ bool req_key_h(connection_t *c, const char *request) {
                        return true;
                }
 
+               /* TODO: forwarding SPTPS packets in this way is inefficient because we send them over TCP without checking for UDP connectivity */
                send_request(to->nexthop->connection, "%s", request);
        }
 
@@ -260,6 +266,9 @@ bool send_ans_key(node_t *to) {
        if(to->status.sptps)
                abort();
 
+#ifdef DISABLE_LEGACY
+       return false;
+#else
        size_t keylen = myself->incipher ? cipher_keylength(myself->incipher) : 1;
        char key[keylen * 2 + 1];
 
@@ -294,12 +303,15 @@ bool send_ans_key(node_t *to) {
        to->received = 0;
        if(replaywin) memset(to->late, 0, replaywin);
 
+       to->status.validkey_in = true;
+
        return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
                                                myself->name, to->name, key,
                                                cipher_get_nid(to->incipher),
                                                digest_get_nid(to->indigest),
                                                (int)digest_length(to->indigest),
                                                to->incompression);
+#endif
 }
 
 bool ans_key_h(connection_t *c, const char *request) {
@@ -365,9 +377,11 @@ bool ans_key_h(connection_t *c, const char *request) {
                return send_request(to->nexthop->connection, "%s", request);
        }
 
+#ifndef DISABLE_LEGACY
        /* Don't use key material until every check has passed. */
        cipher_close(from->outcipher);
        digest_close(from->outdigest);
+#endif
        from->status.validkey = false;
 
        if(compression < 0 || compression > 11) {
@@ -392,14 +406,15 @@ bool ans_key_h(connection_t *c, const char *request) {
                                sockaddr_t sa = str2sockaddr(address, port);
                                update_node_udp(from, &sa);
                        }
-
-                       if(from->options & OPTION_PMTU_DISCOVERY && !(from->options & OPTION_TCPONLY))
-                               send_mtu_probe(from);
                }
 
                return true;
        }
 
+#ifdef DISABLE_LEGACY
+       logger(DEBUG_ALWAYS, LOG_ERR, "Node %s (%s) uses legacy protocol!", from->name, from->hostname);
+       return false;
+#else
        /* Check and lookup cipher and digest algorithms */
 
        if(cipher) {
@@ -450,8 +465,6 @@ bool ans_key_h(connection_t *c, const char *request) {
                update_node_udp(from, &sa);
        }
 
-       if(from->options & OPTION_PMTU_DISCOVERY && !(from->options & OPTION_TCPONLY))
-               send_mtu_probe(from);
-
        return true;
+#endif
 }
index 022438e..713dacf 100644 (file)
@@ -131,7 +131,7 @@ bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
        if(!send_request(c, "%d %hd", PACKET, packet->len))
                return false;
 
-       return send_meta(c, (char *)packet->data, packet->len);
+       return send_meta(c, (char *)DATA(packet), packet->len);
 }
 
 bool tcppacket_h(connection_t *c, const char *request) {
index a8c2c86..eb38be2 100644 (file)
@@ -93,7 +93,7 @@ static void close_device(void) {
 static bool read_packet(vpn_packet_t *packet) {
        int inlen;
 
-       if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
+       if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
@@ -111,7 +111,7 @@ static bool write_packet(vpn_packet_t *packet) {
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(write(device_fd, packet->data, packet->len) < 0) {
+       if(write(device_fd, DATA(packet), packet->len) < 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
                           strerror(errno));
                return false;
index 00ba4c0..2785146 100644 (file)
@@ -115,16 +115,16 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
 
        /* Find TCP header */
        int start = ether_size;
-       uint16_t type = packet->data[12] << 8 | packet->data[13];
+       uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
 
        if(type == ETH_P_8021Q) {
                start += 4;
-               type = packet->data[16] << 8 | packet->data[17];
+               type = DATA(packet)[16] << 8 | DATA(packet)[17];
        }
 
-       if(type == ETH_P_IP && packet->data[start + 9] == 6)
-               start += (packet->data[start] & 0xf) * 4;
-       else if(type == ETH_P_IPV6 && packet->data[start + 6] == 6)
+       if(type == ETH_P_IP && DATA(packet)[start + 9] == 6)
+               start += (DATA(packet)[start] & 0xf) * 4;
+       else if(type == ETH_P_IPV6 && DATA(packet)[start + 6] == 6)
                start += 40;
        else
                return;
@@ -133,38 +133,38 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
                return;
 
        /* Use data offset field to calculate length of options field */
-       int len = ((packet->data[start + 12] >> 4) - 5) * 4;
+       int len = ((DATA(packet)[start + 12] >> 4) - 5) * 4;
 
        if(packet->len < start + 20 + len)
                return;
 
        /* Search for MSS option header */
        for(int i = 0; i < len;) {
-               if(packet->data[start + 20 + i] == 0)
+               if(DATA(packet)[start + 20 + i] == 0)
                        break;
 
-               if(packet->data[start + 20 + i] == 1) {
+               if(DATA(packet)[start + 20 + i] == 1) {
                        i++;
                        continue;
                }
 
-               if(i > len - 2 || i > len - packet->data[start + 21 + i])
+               if(i > len - 2 || i > len - DATA(packet)[start + 21 + i])
                        break;
 
-               if(packet->data[start + 20 + i] != 2) {
-                       if(packet->data[start + 21 + i] < 2)
+               if(DATA(packet)[start + 20 + i] != 2) {
+                       if(DATA(packet)[start + 21 + i] < 2)
                                break;
-                       i += packet->data[start + 21 + i];
+                       i += DATA(packet)[start + 21 + i];
                        continue;
                }
 
-               if(packet->data[start + 21] != 4)
+               if(DATA(packet)[start + 21] != 4)
                        break;
 
                /* Found it */
-               uint16_t oldmss = packet->data[start + 22 + i] << 8 | packet->data[start + 23 + i];
+               uint16_t oldmss = DATA(packet)[start + 22 + i] << 8 | DATA(packet)[start + 23 + i];
                uint16_t newmss = mtu - start - 20;
-               uint16_t csum = packet->data[start + 16] << 8 | packet->data[start + 17];
+               uint16_t csum = DATA(packet)[start + 16] << 8 | DATA(packet)[start + 17];
 
                if(oldmss <= newmss)
                        break;
@@ -172,23 +172,23 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
                logger(DEBUG_TRAFFIC, LOG_INFO, "Clamping MSS of packet from %s to %s to %d", source->name, via->name, newmss);
 
                /* Update the MSS value and the checksum */
-               packet->data[start + 22 + i] = newmss >> 8;
-               packet->data[start + 23 + i] = newmss & 0xff;
+               DATA(packet)[start + 22 + i] = newmss >> 8;
+               DATA(packet)[start + 23 + i] = newmss & 0xff;
                csum ^= 0xffff;
                csum -= oldmss;
                csum += newmss;
                csum ^= 0xffff;
-               packet->data[start + 16] = csum >> 8;
-               packet->data[start + 17] = csum & 0xff;
+               DATA(packet)[start + 16] = csum >> 8;
+               DATA(packet)[start + 17] = csum & 0xff;
                break;
        }
 }
 
 static void swap_mac_addresses(vpn_packet_t *packet) {
        mac_t tmp;
-       memcpy(&tmp, &packet->data[0], sizeof tmp);
-       memcpy(&packet->data[0], &packet->data[6], sizeof tmp);
-       memcpy(&packet->data[6], &tmp, sizeof tmp);
+       memcpy(&tmp, &DATA(packet)[0], sizeof tmp);
+       memcpy(&DATA(packet)[0], &DATA(packet)[6], sizeof tmp);
+       memcpy(&DATA(packet)[6], &tmp, sizeof tmp);
 }
 
 static void age_subnets(void *data) {
@@ -203,7 +203,7 @@ static void age_subnets(void *data) {
                        }
 
                        for list_each(connection_t, c, connection_list)
-                               if(c->status.active)
+                               if(c->edge)
                                        send_del_subnet(c, s);
 
                        subnet_del(myself, s);
@@ -223,7 +223,7 @@ static void learn_mac(mac_t *address) {
        /* If we don't know this MAC address yet, store it */
 
        if(!subnet) {
-               logger(DEBUG_TRAFFIC, LOG_INFO, "Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx",
+               logger(DEBUG_TRAFFIC, LOG_INFO, "Learned new MAC address %x:%x:%x:%x:%x:%x",
                                   address->x[0], address->x[1], address->x[2], address->x[3],
                                   address->x[4], address->x[5]);
 
@@ -238,7 +238,7 @@ static void learn_mac(mac_t *address) {
                /* And tell all other tinc daemons it's our MAC */
 
                for list_each(connection_t, c, connection_list)
-                       if(c->status.active)
+                       if(c->edge)
                                send_add_subnet(c, subnet);
 
                timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
@@ -267,7 +267,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* Copy headers from packet into properly aligned structs on the stack */
 
-       memcpy(&ip, packet->data + ether_size, ip_size);
+       memcpy(&ip, DATA(packet) + ether_size, ip_size);
 
        /* Remember original source and destination */
 
@@ -284,7 +284,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* Copy first part of original contents to ICMP message */
 
-       memmove(packet->data + ether_size + ip_size + icmp_size, packet->data + ether_size, oldlen);
+       memmove(DATA(packet) + ether_size + ip_size + icmp_size, DATA(packet) + ether_size, oldlen);
 
        /* Fill in IPv4 header */
 
@@ -309,12 +309,12 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
        icmp.icmp_cksum = 0;
 
        icmp.icmp_cksum = inet_checksum(&icmp, icmp_size, ~0);
-       icmp.icmp_cksum = inet_checksum(packet->data + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
+       icmp.icmp_cksum = inet_checksum(DATA(packet) + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
 
        /* Copy structs on stack back to packet */
 
-       memcpy(packet->data + ether_size, &ip, ip_size);
-       memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size);
+       memcpy(DATA(packet) + ether_size, &ip, ip_size);
+       memcpy(DATA(packet) + ether_size + ip_size, &icmp, icmp_size);
 
        packet->len = ether_size + ip_size + icmp_size + oldlen;
 
@@ -330,8 +330,9 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
        uint8_t *offset;
        uint16_t ip_off, origf;
 
-       memcpy(&ip, packet->data + ether_size, ip_size);
+       memcpy(&ip, DATA(packet) + ether_size, ip_size);
        fragment.priority = packet->priority;
+       fragment.offset = DEFAULT_PACKET_OFFSET;
 
        if(ip.ip_hl != ip_size / 4)
                return;
@@ -345,7 +346,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
 
        logger(DEBUG_TRAFFIC, LOG_INFO, "Fragmenting packet of %d bytes to %s (%s)", packet->len, dest->name, dest->hostname);
 
-       offset = packet->data + ether_size + ip_size;
+       offset = DATA(packet) + ether_size + ip_size;
        maxlen = (dest->mtu - ether_size - ip_size) & ~0x7;
        ip_off = ntohs(ip.ip_off);
        origf = ip_off & ~IP_OFFMASK;
@@ -353,7 +354,7 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
 
        while(todo) {
                len = todo > maxlen ? maxlen : todo;
-               memcpy(fragment.data + ether_size + ip_size, offset, len);
+               memcpy(DATA(&fragment) + ether_size + ip_size, offset, len);
                todo -= len;
                offset += len;
 
@@ -361,8 +362,8 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
                ip.ip_off = htons(ip_off | origf | (todo ? IP_MF : 0));
                ip.ip_sum = 0;
                ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
-               memcpy(fragment.data, packet->data, ether_size);
-               memcpy(fragment.data + ether_size, &ip, ip_size);
+               memcpy(DATA(&fragment), DATA(packet), ether_size);
+               memcpy(DATA(&fragment) + ether_size, &ip, ip_size);
                fragment.len = ether_size + ip_size + len;
 
                send_packet(dest, &fragment);
@@ -371,12 +372,15 @@ static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t et
        }
 }
 
-static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
+static void route_ipv4(node_t *source, vpn_packet_t *packet) {
+       if(!checklength(source, packet, ether_size + ip_size))
+               return;
+
        subnet_t *subnet;
        node_t *via;
        ipv4_t dest;
 
-       memcpy(&dest, &packet->data[30], sizeof dest);
+       memcpy(&dest, &DATA(packet)[30], sizeof dest);
        subnet = lookup_subnet_ipv4(&dest);
 
        if(!subnet) {
@@ -391,6 +395,11 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
                return;
        }
 
+       if (!subnet->owner) {
+               broadcast_packet(source, packet);
+               return;
+       }
+
        if(subnet->owner == source) {
                logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
                return;
@@ -403,7 +412,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
                return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
 
        if(priorityinheritance)
-               packet->priority = packet->data[15];
+               packet->priority = DATA(packet)[15];
 
        via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
 
@@ -417,7 +426,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
 
        if(via && packet->len > MAX(via->mtu, 590) && via != myself) {
                logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
-               if(packet->data[20] & 0x40) {
+               if(DATA(packet)[20] & 0x40) {
                        packet->len = MAX(via->mtu, 590);
                        route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
                } else {
@@ -432,20 +441,6 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
        send_packet(subnet->owner, packet);
 }
 
-static void route_ipv4(node_t *source, vpn_packet_t *packet) {
-       if(!checklength(source, packet, ether_size + ip_size))
-               return;
-
-       if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
-                       packet->data[30] == 255 &&
-                       packet->data[31] == 255 &&
-                       packet->data[32] == 255 &&
-                       packet->data[33] == 255)))
-               broadcast_packet(source, packet);
-       else
-               route_ipv4_unicast(source, packet);
-}
-
 /* RFC 2463 */
 
 static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
@@ -469,7 +464,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* Copy headers from packet to structs on the stack */
 
-       memcpy(&ip6, packet->data + ether_size, ip6_size);
+       memcpy(&ip6, DATA(packet) + ether_size, ip6_size);
 
        /* Remember original source and destination */
 
@@ -486,7 +481,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* Copy first part of original contents to ICMP message */
 
-       memmove(packet->data + ether_size + ip6_size + icmp6_size, packet->data + ether_size, pseudo.length);
+       memmove(DATA(packet) + ether_size + ip6_size + icmp6_size, DATA(packet) + ether_size, pseudo.length);
 
        /* Fill in IPv6 header */
 
@@ -512,26 +507,36 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        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);
+       checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
 
        icmp6.icmp6_cksum = checksum;
 
        /* Copy structs on stack back to packet */
 
-       memcpy(packet->data + ether_size, &ip6, ip6_size);
-       memcpy(packet->data + ether_size + ip6_size, &icmp6, icmp6_size);
+       memcpy(DATA(packet) + ether_size, &ip6, ip6_size);
+       memcpy(DATA(packet) + ether_size + ip6_size, &icmp6, icmp6_size);
 
        packet->len = ether_size + ip6_size + ntohl(pseudo.length);
 
        send_packet(source, packet);
 }
 
-static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
+static void route_neighborsol(node_t *source, vpn_packet_t *packet);
+
+static void route_ipv6(node_t *source, vpn_packet_t *packet) {
+       if(!checklength(source, packet, ether_size + ip6_size))
+               return;
+
+       if(DATA(packet)[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && DATA(packet)[54] == ND_NEIGHBOR_SOLICIT) {
+               route_neighborsol(source, packet);
+               return;
+       }
+
        subnet_t *subnet;
        node_t *via;
        ipv6_t dest;
 
-       memcpy(&dest, &packet->data[38], sizeof dest);
+       memcpy(&dest, &DATA(packet)[38], sizeof dest);
        subnet = lookup_subnet_ipv6(&dest);
 
        if(!subnet) {
@@ -550,6 +555,11 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
                return;
        }
 
+       if (!subnet->owner) {
+               broadcast_packet(source, packet);
+               return;
+       }
+
        if(subnet->owner == source) {
                logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
                return;
@@ -612,15 +622,15 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
 
        /* Copy headers from packet to structs on the stack */
 
-       memcpy(&ip6, packet->data + ether_size, ip6_size);
-       memcpy(&ns, packet->data + ether_size + ip6_size, ns_size);
+       memcpy(&ip6, DATA(packet) + ether_size, ip6_size);
+       memcpy(&ns, DATA(packet) + ether_size + ip6_size, ns_size);
        if(has_opt)
-               memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size);
+               memcpy(&opt, DATA(packet) + ether_size + ip6_size + ns_size, opt_size);
 
        /* First, snatch the source address from the neighbor solicitation packet */
 
        if(overwrite_mac)
-               memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
+               memcpy(mymac.x, DATA(packet) + ETH_ALEN, ETH_ALEN);
 
        /* Check if this is a valid neighbor solicitation request */
 
@@ -646,7 +656,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
        checksum = inet_checksum(&ns, ns_size, checksum);
        if(has_opt) {
                checksum = inet_checksum(&opt, opt_size, checksum);
-               checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
+               checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
        }
 
        if(checksum) {
@@ -679,14 +689,14 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
 
        /* Create neighbor advertation reply */
 
-       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(DATA(packet), DATA(packet) + ETH_ALEN, ETH_ALEN); /* copy destination address */
+       DATA(packet)[ETH_ALEN * 2 - 1] ^= 0xFF;                  /* mangle source address so it looks like it's not from us */
 
        ip6.ip6_dst = ip6.ip6_src;                               /* swap destination and source protocoll address */
        ip6.ip6_src = ns.nd_ns_target;
 
        if(has_opt)
-               memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN);   /* add fake source hard addr */
+               memcpy(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, DATA(packet) + ETH_ALEN, ETH_ALEN);   /* add fake source hard addr */
 
        ns.nd_ns_cksum = 0;
        ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
@@ -709,36 +719,21 @@ static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
        checksum = inet_checksum(&ns, ns_size, checksum);
        if(has_opt) {
                checksum = inet_checksum(&opt, opt_size, checksum);
-               checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
+               checksum = inet_checksum(DATA(packet) + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
        }
 
        ns.nd_ns_hdr.icmp6_cksum = checksum;
 
        /* Copy structs on stack back to packet */
 
-       memcpy(packet->data + ether_size, &ip6, ip6_size);
-       memcpy(packet->data + ether_size + ip6_size, &ns, ns_size);
+       memcpy(DATA(packet) + ether_size, &ip6, ip6_size);
+       memcpy(DATA(packet) + ether_size + ip6_size, &ns, ns_size);
        if(has_opt)
-               memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size);
+               memcpy(DATA(packet) + ether_size + ip6_size + ns_size, &opt, opt_size);
 
        send_packet(source, packet);
 }
 
-static void route_ipv6(node_t *source, vpn_packet_t *packet) {
-       if(!checklength(source, packet, ether_size + ip6_size))
-               return;
-
-       if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
-               route_neighborsol(source, packet);
-               return;
-       }
-
-       if(broadcast_mode && packet->data[38] == 255)
-               broadcast_packet(source, packet);
-       else
-               route_ipv6_unicast(source, packet);
-}
-
 /* RFC 826 */
 
 static void route_arp(node_t *source, vpn_packet_t *packet) {
@@ -757,11 +752,11 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
        /* First, snatch the source address from the ARP packet */
 
        if(overwrite_mac)
-               memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
+               memcpy(mymac.x, DATA(packet) + ETH_ALEN, ETH_ALEN);
 
        /* Copy headers from packet to structs on the stack */
 
-       memcpy(&arp, packet->data + ether_size, arp_size);
+       memcpy(&arp, DATA(packet) + ether_size, arp_size);
 
        /* Check if this is a valid ARP request */
 
@@ -787,20 +782,20 @@ static void route_arp(node_t *source, vpn_packet_t *packet) {
        if(subnet->owner == myself)
                return;                                          /* silently ignore */
 
-       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(DATA(packet), DATA(packet) + ETH_ALEN, ETH_ALEN); /* copy destination address */
+       DATA(packet)[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(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 */
+       memcpy(arp.arp_sha, DATA(packet) + ETH_ALEN, ETH_ALEN);  /* add fake source hard addr */
        arp.arp_op = htons(ARPOP_REPLY);
 
        /* Copy structs on stack back to packet */
 
-       memcpy(packet->data + ether_size, &arp, arp_size);
+       memcpy(DATA(packet) + ether_size, &arp, arp_size);
 
        send_packet(source, packet);
 }
@@ -813,16 +808,16 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
 
        if(source == myself) {
                mac_t src;
-               memcpy(&src, &packet->data[6], sizeof src);
+               memcpy(&src, &DATA(packet)[6], sizeof src);
                learn_mac(&src);
        }
 
        /* Lookup destination address */
 
-       memcpy(&dest, &packet->data[0], sizeof dest);
+       memcpy(&dest, &DATA(packet)[0], sizeof dest);
        subnet = lookup_subnet_mac(NULL, &dest);
 
-       if(!subnet) {
+       if(!subnet || !subnet->owner) {
                broadcast_packet(source, packet);
                return;
        }
@@ -835,10 +830,10 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
        if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
                return;
 
-       uint16_t type = packet->data[12] << 8 | packet->data[13];
+       uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
 
        if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size)
-               packet->priority = packet->data[15];
+               packet->priority = DATA(packet)[15];
 
        // Handle packets larger than PMTU
 
@@ -852,12 +847,12 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
                length_t ethlen = 14;
 
                if(type == ETH_P_8021Q) {
-                       type = packet->data[16] << 8 | packet->data[17];
+                       type = DATA(packet)[16] << 8 | DATA(packet)[17];
                        ethlen += 4;
                }
 
                if(type == ETH_P_IP && packet->len > 576 + ethlen) {
-                       if(packet->data[6 + ethlen] & 0x40) {
+                       if(DATA(packet)[6 + ethlen] & 0x40) {
                                packet->len = via->mtu;
                                route_ipv4_unreachable(source, packet, ethlen, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
                        } else {
@@ -889,16 +884,16 @@ static void send_pcap(vpn_packet_t *packet) {
                        len = c->outmaclength;
 
                if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, len))
-                       send_meta(c, (char *)packet->data, len);
+                       send_meta(c, (char *)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];
+       uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
        length_t ethlen = ether_size;
 
        if(type == ETH_P_8021Q) {
-               type = packet->data[16] << 8 | packet->data[17];
+               type = DATA(packet)[16] << 8 | DATA(packet)[17];
                ethlen += 4;
        }
 
@@ -907,22 +902,22 @@ static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
                        if(!checklength(source, packet, ethlen + ip_size))
                                return false;
 
-                       if(packet->data[ethlen + 8] < 1) {
-                               if(packet->data[ethlen + 11] != IPPROTO_ICMP || packet->data[ethlen + 32] != ICMP_TIME_EXCEEDED)
+                       if(DATA(packet)[ethlen + 8] < 1) {
+                               if(DATA(packet)[ethlen + 11] != IPPROTO_ICMP || DATA(packet)[ethlen + 32] != ICMP_TIME_EXCEEDED)
                                        route_ipv4_unreachable(source, packet, ethlen, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
                                return false;
                        }
 
-                       uint16_t old = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
-                       packet->data[ethlen + 8]--;
-                       uint16_t new = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
+                       uint16_t old = DATA(packet)[ethlen + 8] << 8 | DATA(packet)[ethlen + 9];
+                       DATA(packet)[ethlen + 8]--;
+                       uint16_t new = DATA(packet)[ethlen + 8] << 8 | DATA(packet)[ethlen + 9];
 
-                       uint32_t checksum = packet->data[ethlen + 10] << 8 | packet->data[ethlen + 11];
+                       uint32_t checksum = DATA(packet)[ethlen + 10] << 8 | DATA(packet)[ethlen + 11];
                        checksum += old + (~new & 0xFFFF);
                        while(checksum >> 16)
                                checksum = (checksum & 0xFFFF) + (checksum >> 16);
-                       packet->data[ethlen + 10] = checksum >> 8;
-                       packet->data[ethlen + 11] = checksum & 0xff;
+                       DATA(packet)[ethlen + 10] = checksum >> 8;
+                       DATA(packet)[ethlen + 11] = checksum & 0xff;
 
                        return true;
 
@@ -930,13 +925,13 @@ static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
                        if(!checklength(source, packet, ethlen + ip6_size))
                                return false;
 
-                       if(packet->data[ethlen + 7] < 1) {
-                               if(packet->data[ethlen + 6] != IPPROTO_ICMPV6 || packet->data[ethlen + 40] != ICMP6_TIME_EXCEEDED)
+                       if(DATA(packet)[ethlen + 7] < 1) {
+                               if(DATA(packet)[ethlen + 6] != IPPROTO_ICMPV6 || DATA(packet)[ethlen + 40] != ICMP6_TIME_EXCEEDED)
                                        route_ipv6_unreachable(source, packet, ethlen, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
                                return false;
                        }
 
-                       packet->data[ethlen + 7]--;
+                       DATA(packet)[ethlen + 7]--;
 
                        return true;
 
@@ -961,7 +956,7 @@ void route(node_t *source, vpn_packet_t *packet) {
                if(!do_decrement_ttl(source, packet))
                        return;
 
-       uint16_t type = packet->data[12] << 8 | packet->data[13];
+       uint16_t type = DATA(packet)[12] << 8 | DATA(packet)[13];
 
        switch (routing_mode) {
                case RMODE_ROUTER:
index 9a43d53..6389cb4 100644 (file)
@@ -49,7 +49,7 @@ bool execute_script(const char *name, char **envp) {
                        if(q) {
                                memcpy(ext, p, q - p);
                                ext[q - p] = 0;
-                               *q++;
+                               q++;
                        } else {
                                strcpy(ext, p);
                        }
index a4c0d27..fadae57 100644 (file)
@@ -2,7 +2,7 @@
     device.c -- Interaction with Solaris tun device
     Copyright (C) 2001-2005 Ivo Timmermans,
                   2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
-                  2001-2013 Guus Sliepen <guus@tinc-vpn.org>
+                  2001-2014 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
@@ -299,31 +299,31 @@ static bool read_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
+                       if((inlen = read(device_fd, DATA(packet) + 14, MTU - 14)) <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
                                return false;
                        }
 
-                       switch(packet->data[14] >> 4) {
+                       switch(DATA(packet)[14] >> 4) {
                                case 4:
-                                       packet->data[12] = 0x08;
-                                       packet->data[13] = 0x00;
+                                       DATA(packet)[12] = 0x08;
+                                       DATA(packet)[13] = 0x00;
                                        break;
                                case 6:
-                                       packet->data[12] = 0x86;
-                                       packet->data[13] = 0xDD;
+                                       DATA(packet)[12] = 0x86;
+                                       DATA(packet)[13] = 0xDD;
                                        break;
                                default:
-                                       logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", packet->data[14] >> 4, device_info, device);
+                                       logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", DATA(packet)[14] >> 4, device_info, device);
                                        return false;
                        }
 
-                       memset(packet->data, 0, 12);
+                       memset(DATA(packet), 0, 12);
                        packet->len = inlen + 14;
                        break;
 
                case DEVICE_TYPE_TAP:
-                       if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
+                       if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
                                return false;
                        }
@@ -345,14 +345,14 @@ static bool write_packet(vpn_packet_t *packet) {
 
        switch(device_type) {
                case DEVICE_TYPE_TUN:
-                       if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
+                       if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
                                return false;
                        }
                        break;
 
                case DEVICE_TYPE_TAP:
-                       if(write(device_fd, packet->data, packet->len) < 0) {
+                       if(write(device_fd, DATA(packet), packet->len) < 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
                                return false;
                        }
index 7d6293c..4a9683f 100644 (file)
@@ -1,6 +1,6 @@
 /*
     sptps.c -- Simple Peer-to-Peer Security
-    Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2011-2014 Guus Sliepen <guus@tinc-vpn.org>,
                   2010      Brandon L. Black <blblack@gmail.com>
 
     This program is free software; you can redistribute it and/or modify
@@ -81,7 +81,7 @@ static void warning(sptps_t *s, const char *format, ...) {
 }
 
 // Send a record (datagram version, accepts all record types, handles encryption and authentication).
-static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
        char buffer[len + 21UL];
 
        // Create header with sequence number, length and record type
@@ -102,7 +102,7 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data
        }
 }
 // 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) {
+static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
        if(s->datagram)
                return send_record_priv_datagram(s, type, data, len);
 
@@ -127,7 +127,7 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_
 }
 
 // Send an application record.
-bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+bool sptps_send_record(sptps_t *s, uint8_t type, const void *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)
@@ -370,14 +370,73 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
        }
 }
 
+static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) {
+       // Replay protection using a sliding window of configurable size.
+       // s->inseqno is expected sequence number
+       // seqno is received sequence number
+       // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
+       // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
+       if(s->replaywin) {
+               if(seqno != s->inseqno) {
+                       if(seqno >= s->inseqno + s->replaywin * 8) {
+                               // Prevent packets that jump far ahead of the queue from causing many others to be dropped.
+                               bool farfuture = s->farfuture < s->replaywin >> 2;
+                               if (update_state)
+                                       s->farfuture++;
+                               if(farfuture)
+                                       return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture);
+
+                               // Unless we have seen lots of them, in which case we consider the others lost.
+                               warning(s, "Lost %d packets\n", seqno - s->inseqno);
+                               if (update_state) {
+                                       // Mark all packets in the replay window as being late.
+                                       memset(s->late, 255, s->replaywin);
+                               }
+                       } else if (seqno < s->inseqno) {
+                               // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
+                               if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
+                                       return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
+                       } else if (update_state) {
+                               // We missed some packets. Mark them in the bitmap as being late.
+                               for(int i = s->inseqno; i < seqno; i++)
+                                       s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
+                       }
+               }
+
+               if (update_state) {
+                       // Mark the current packet as not being late.
+                       s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
+                       s->farfuture = 0;
+               }
+       }
+
+       if (update_state) {
+               if(seqno >= s->inseqno)
+                       s->inseqno = seqno + 1;
+
+               if(!s->inseqno)
+                       s->received = 0;
+               else
+                       s->received++;
+       }
+
+       return true;
+}
+
 // Check datagram for valid HMAC
-bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) {
+bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) {
        if(!s->instate || len < 21)
                return error(s, EIO, "Received short packet");
 
-       // TODO: just decrypt without updating the replay window
+       uint32_t seqno;
+       memcpy(&seqno, data, 4);
+       seqno = ntohl(seqno);
+       if (!sptps_check_seqno(s, seqno, false))
+               return false;
 
-       return true;
+       char buffer[len];
+       size_t outlen;
+       return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
 }
 
 // Receive incoming data, datagram version.
@@ -412,45 +471,8 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
        if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
                return error(s, EIO, "Failed to decrypt and verify packet");
 
-       // Replay protection using a sliding window of configurable size.
-       // s->inseqno is expected sequence number
-       // seqno is received sequence number
-       // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
-       // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
-       if(s->replaywin) {
-               if(seqno != s->inseqno) {
-                       if(seqno >= s->inseqno + s->replaywin * 8) {
-                               // Prevent packets that jump far ahead of the queue from causing many others to be dropped.
-                               if(s->farfuture++ < s->replaywin >> 2)
-                                       return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture);
-
-                               // Unless we have seen lots of them, in which case we consider the others lost.
-                               warning(s, "Lost %d packets\n", seqno - s->inseqno);
-                               // Mark all packets in the replay window as being late.
-                               memset(s->late, 255, s->replaywin);
-                       } else if (seqno < s->inseqno) {
-                               // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
-                               if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
-                                       return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
-                       } else {
-                               // We missed some packets. Mark them in the bitmap as being late.
-                               for(int i = s->inseqno; i < seqno; i++)
-                                       s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
-                       }
-               }
-
-               // Mark the current packet as not being late.
-               s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
-               s->farfuture = 0;
-       }
-
-       if(seqno >= s->inseqno)
-               s->inseqno = seqno + 1;
-
-       if(!s->inseqno)
-               s->received = 0;
-       else
-               s->received++;
+       if(!sptps_check_seqno(s, seqno, true))
+               return false;
 
        // Append a NULL byte for safety.
        buffer[len - 20] = 0;
@@ -461,10 +483,10 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
                if(!s->instate)
                        return error(s, EIO, "Application record received before handshake finished");
                if(!s->receive_record(s->handle, type, buffer + 1, len - 21))
-                       abort();
+                       return false;
        } else if(type == SPTPS_HANDSHAKE) {
                if(!receive_handshake(s, buffer + 1, len - 21))
-                       abort();
+                       return false;
        } else {
                return error(s, EIO, "Invalid record type %d", type);
        }
@@ -473,7 +495,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
 }
 
 // Receive incoming data. Check if it contains a complete record, if so, handle it.
-bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
+bool sptps_receive_data(sptps_t *s, const void *data, size_t len) {
        if(!s->state)
                return error(s, EIO, "Invalid session state zero");
 
@@ -560,7 +582,7 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
 }
 
 // Start a SPTPS session.
-bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
+bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
        // Initialise struct sptps
        memset(s, 0, sizeof *s);
 
index efca2b3..a2633bd 100644 (file)
@@ -1,6 +1,6 @@
 /*
     sptps.h -- Simple Peer-to-Peer Security
-    Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2011-2014 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
@@ -42,8 +42,8 @@
 // Overhead for datagrams
 #define SPTPS_DATAGRAM_OVERHEAD 21
 
-typedef bool (*send_data_t)(void *handle, uint8_t type, const char *data, size_t len);
-typedef bool (*receive_record_t)(void *handle, uint8_t type, const char *data, uint16_t len);
+typedef bool (*send_data_t)(void *handle, uint8_t type, const void *data, size_t len);
+typedef bool (*receive_record_t)(void *handle, uint8_t type, const void *data, uint16_t len);
 
 typedef struct sptps {
        bool initiator;
@@ -85,11 +85,11 @@ extern unsigned int sptps_replaywin;
 extern void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap);
 extern void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap);
 extern void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap);
-extern bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record);
+extern bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record);
 extern bool sptps_stop(sptps_t *s);
-extern bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len);
-extern bool sptps_receive_data(sptps_t *s, const char *data, size_t len);
+extern bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len);
+extern bool sptps_receive_data(sptps_t *s, const void *data, size_t len);
 extern bool sptps_force_kex(sptps_t *s);
-extern bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len);
+extern bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len);
 
 #endif
index f66a771..399404e 100644 (file)
 
 static char *program_name;
 
+void logger(int level, int priority, const char *format, ...) {
+       va_list ap;
+       va_start(ap, format);
+       vfprintf(stderr, format, ap);
+       va_end(ap);
+       fputc('\n', stderr);
+}
+
 static void usage() {
        fprintf(stderr, "Usage: %s [options] private_key_file public_key_file\n\n", program_name);
        fprintf(stderr, "Valid options are:\n"
index 953d7f5..ab41e8d 100644 (file)
@@ -1,6 +1,6 @@
 /*
     sptps_speed.c -- SPTPS benchmark
-    Copyright (C) 2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2013-2014 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
@@ -18,6 +18,7 @@
 */
 
 #include "system.h"
+#include "utils.h"
 
 #include <poll.h>
 
@@ -34,13 +35,13 @@ bool send_meta(void *c, const char *msg , int len) { return false; }
 char *logfilename = NULL;
 struct timeval now;
 
-static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
+static bool send_data(void *handle, uint8_t type, const void *data, size_t len) {
        int fd = *(int *)handle;
        send(fd, data, len, 0);
        return true;
 }
 
-static bool receive_record(void *handle, uint8_t type, const char *data, uint16_t len) {
+static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
        return true;
 }
 
@@ -121,7 +122,7 @@ int main(int argc, char *argv[]) {
 
        int fd[2];
        if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
-               fprintf(stderr, "Could not create a UNIX socket pair: %s\n", strerror(errno));
+               fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
                return 1;
        }
 
@@ -174,7 +175,7 @@ int main(int argc, char *argv[]) {
        close(fd[1]);
 
        if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
-               fprintf(stderr, "Could not create a UNIX socket pair: %s\n", strerror(errno));
+               fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
                return 1;
        }
 
index 3ec9f98..38c2c08 100644 (file)
@@ -1,6 +1,6 @@
 /*
     sptps_test.c -- Simple Peer-to-Peer Security test program
-    Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2011-2014 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
@@ -43,7 +43,7 @@ static bool writeonly;
 static int in = 0;
 static int out = 1;
 
-static bool send_data(void *handle, uint8_t type, const char *data, size_t len) {
+static bool send_data(void *handle, uint8_t type, const void *data, size_t len) {
        char hex[len * 2 + 1];
        bin2hex(data, hex, len);
        if(verbose)
@@ -54,7 +54,7 @@ static bool send_data(void *handle, uint8_t type, const char *data, size_t len)
        return true;
 }
 
-static bool receive_record(void *handle, uint8_t type, const char *data, uint16_t len) {
+static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
        if(verbose)
                fprintf(stderr, "Received type %d record of %hu bytes:\n", type, len);
        if(!writeonly)
@@ -210,13 +210,13 @@ int main(int argc, char *argv[]) {
        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));
+               fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
                return 1;
        }
 
        int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if(sock < 0) {
-               fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
+               fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
                return 1;
        }
 
@@ -225,26 +225,26 @@ int main(int argc, char *argv[]) {
 
        if(initiator) {
                if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
-                       fprintf(stderr, "Could not connect to peer: %s\n", strerror(errno));
+                       fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
                        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));
+                       fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
                        return 1;
                }
 
                if(!datagram) {
                        if(listen(sock, 1)) {
-                               fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
+                               fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
                                return 1;
                        }
                        fprintf(stderr, "Listening...\n");
 
                        sock = accept(sock, NULL, NULL);
                        if(sock < 0) {
-                               fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
+                               fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
                                return 1;
                        }
                } else {
@@ -255,12 +255,12 @@ int main(int argc, char *argv[]) {
                        socklen_t addrlen = sizeof addr;
 
                        if(recvfrom(sock, buf, sizeof buf, MSG_PEEK, &addr, &addrlen) <= 0) {
-                               fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
+                               fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
                                return 1;
                        }
 
                        if(connect(sock, &addr, addrlen)) {
-                               fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
+                               fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
                                return 1;
                        }
                }
@@ -271,11 +271,19 @@ int main(int argc, char *argv[]) {
        crypto_init();
 
        FILE *fp = fopen(argv[1], "r");
+       if(!fp) {
+               fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
+               return 1;
+       }
        if(!(mykey = ecdsa_read_pem_private_key(fp)))
                return 1;
        fclose(fp);
 
        fp = fopen(argv[2], "r");
+       if(!fp) {
+               fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
+               return 1;
+       }
        if(!(hiskey = ecdsa_read_pem_public_key(fp)))
                return 1;
        fclose(fp);
@@ -331,7 +339,7 @@ int main(int argc, char *argv[]) {
                if(FD_ISSET(sock, &fds)) {
                        ssize_t len = recv(sock, buf, sizeof buf, 0);
                        if(len < 0) {
-                               fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
+                               fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
                                return 1;
                        }
                        if(len == 0) {
index 7ff8f7a..534e5b5 100644 (file)
@@ -92,13 +92,15 @@ void subnet_add(node_t *n, subnet_t *subnet) {
        subnet->owner = n;
 
        splay_insert(subnet_tree, subnet);
-       splay_insert(n->subnet_tree, subnet);
+       if (n)
+               splay_insert(n->subnet_tree, subnet);
 
        subnet_cache_flush();
 }
 
 void subnet_del(node_t *n, subnet_t *subnet) {
-       splay_delete(n->subnet_tree, subnet);
+       if (n)
+               splay_delete(n->subnet_tree, subnet);
        splay_delete(subnet_tree, subnet);
 
        subnet_cache_flush();
@@ -126,7 +128,7 @@ subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
 
                if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
                        r = p;
-                       if(p->owner->status.reachable)
+                       if(!p->owner || p->owner->status.reachable)
                                break;
                }
        }
@@ -155,7 +157,7 @@ subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
 
                if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
                        r = p;
-                       if(p->owner->status.reachable)
+                       if(!p->owner || p->owner->status.reachable)
                                break;
                }
        }
@@ -184,7 +186,7 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
 
                if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
                        r = p;
-                       if(p->owner->status.reachable)
+                       if(!p->owner || p->owner->status.reachable)
                                break;
                }
        }
@@ -205,21 +207,21 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
        // Prepare environment variables to be passed to the script
 
        char *envp[10] = {NULL};
-       xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
-       xasprintf(&envp[1], "DEVICE=%s", device ? : "");
-       xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
-       xasprintf(&envp[3], "NODE=%s", owner->name);
+       int n = 0;
+       xasprintf(&envp[n++], "NETNAME=%s", netname ? : "");
+       xasprintf(&envp[n++], "DEVICE=%s", device ? : "");
+       xasprintf(&envp[n++], "INTERFACE=%s", iface ? : "");
+       xasprintf(&envp[n++], "NODE=%s", owner->name);
 
        if(owner != myself) {
                sockaddr2str(&owner->address, &address, &port);
-               // 4 and 5 are reserved for SUBNET and WEIGHT
-               xasprintf(&envp[6], "REMOTEADDRESS=%s", address);
-               xasprintf(&envp[7], "REMOTEPORT=%s", port);
+               xasprintf(&envp[n++], "REMOTEADDRESS=%s", address);
+               xasprintf(&envp[n++], "REMOTEPORT=%s", port);
                free(port);
                free(address);
        }
 
-       xasprintf(&envp[8], "NAME=%s", myself->name);
+       xasprintf(&envp[n++], "NAME=%s", myself->name);
 
        name = up ? "subnet-up" : "subnet-down";
 
@@ -236,12 +238,10 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
                                weight = empty;
 
                        // Prepare the SUBNET and WEIGHT variables
-                       if(envp[4])
-                               free(envp[4]);
-                       if(envp[5])
-                               free(envp[5]);
-                       xasprintf(&envp[4], "SUBNET=%s", netstr);
-                       xasprintf(&envp[5], "WEIGHT=%s", weight);
+                       free(envp[n]);
+                       free(envp[n + 1]);
+                       xasprintf(&envp[n], "SUBNET=%s", netstr);
+                       xasprintf(&envp[n + 1], "WEIGHT=%s", weight);
 
                        execute_script(name, envp);
                }
@@ -255,8 +255,8 @@ void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
                                weight = empty;
 
                        // Prepare the SUBNET and WEIGHT variables
-                       xasprintf(&envp[4], "SUBNET=%s", netstr);
-                       xasprintf(&envp[5], "WEIGHT=%s", weight);
+                       xasprintf(&envp[n], "SUBNET=%s", netstr);
+                       xasprintf(&envp[n + 1], "WEIGHT=%s", weight);
 
                        execute_script(name, envp);
                }
@@ -275,7 +275,7 @@ bool dump_subnets(connection_t *c) {
 
                send_request(c, "%d %d %s %s",
                                CONTROL, REQ_DUMP_SUBNETS,
-                               netstr, subnet->owner->name);
+                               netstr, subnet->owner ? subnet->owner->name : "(broadcast)");
        }
 
        return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
index f980180..c919b59 100644 (file)
@@ -27,6 +27,9 @@
 #include "utils.h"
 #include "xalloc.h"
 
+/* Changing this default will affect ADD_SUBNET messages - beware of inconsistencies between versions */
+static const int DEFAULT_WEIGHT = 10;
+
 /* Subnet mask handling */
 
 int maskcmp(const void *va, const void *vb, int masklen) {
@@ -181,150 +184,121 @@ int subnet_compare(const subnet_t *a, const subnet_t *b) {
 /* Ascii representation of subnets */
 
 bool str2net(subnet_t *subnet, const char *subnetstr) {
-       int i, l;
-       uint16_t x[8];
-       int weight = 10;
-
-       if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d",
-                         &x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) {
-               if(l < 0 || l > 32)
+       char str[1024];
+       strncpy(str, subnetstr, sizeof(str));
+       str[sizeof str - 1] = 0;
+       int consumed;
+
+       int weight = DEFAULT_WEIGHT;
+       char *weight_separator = strchr(str, '#');
+       if (weight_separator) {
+               char *weight_str = weight_separator + 1;
+               if (sscanf(weight_str, "%d%n", &weight, &consumed) < 1)
                        return false;
+               if (weight_str[consumed])
+                       return false;
+               *weight_separator = 0;
+       }
 
-               subnet->type = SUBNET_IPV4;
-               subnet->net.ipv4.prefixlength = l;
-               subnet->weight = weight;
-
-               for(int i = 0; i < 4; i++) {
-                       if(x[i] > 255)
-                               return false;
-                       subnet->net.ipv4.address.x[i] = x[i];
-               }
+       int prefixlength = -1;
+       char *prefixlength_separator = strchr(str, '/');
+       if (prefixlength_separator) {
+               char* prefixlength_str = prefixlength_separator + 1;
+               if (sscanf(prefixlength_str, "%d%n", &prefixlength, &consumed) < 1)
+                       return false;
+               if (prefixlength_str[consumed])
+                       return false;
+               *prefixlength_separator = 0;
 
-               return true;
+               if (prefixlength < 0)
+                       return false;
        }
 
-       if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
-                         &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
-                         &l, &weight) >= 9) {
-               if(l < 0 || l > 128)
+       uint16_t x[8];
+       if (sscanf(str, "%hx:%hx:%hx:%hx:%hx:%hx%n", &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &consumed) >= 6 && !str[consumed]) {
+               /*
+                  Normally we should check that each part has two digits to prevent ambiguities.
+                  However, in old tinc versions net2str() will agressively return MAC addresses with one-digit parts,
+                  so we have to accept them otherwise we would be unable to parse ADD_SUBNET messages.
+               */
+               if (prefixlength >= 0)
                        return false;
 
-               subnet->type = SUBNET_IPV6;
-               subnet->net.ipv6.prefixlength = l;
+               subnet->type = SUBNET_MAC;
                subnet->weight = weight;
-
-               for(i = 0; i < 8; i++)
-                       subnet->net.ipv6.address.x[i] = htons(x[i]);
-
+               for(int i = 0; i < 6; i++)
+                       subnet->net.mac.address.x[i] = x[i];
                return true;
        }
 
-       if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) {
+       if (sscanf(str, "%hu.%hu.%hu.%hu%n", &x[0], &x[1], &x[2], &x[3], &consumed) >= 4 && !str[consumed]) {
+               if (prefixlength == -1)
+                       prefixlength = 32;
+               if (prefixlength > 32)
+                       return false;
+
                subnet->type = SUBNET_IPV4;
-               subnet->net.ipv4.prefixlength = 32;
+               subnet->net.ipv4.prefixlength = prefixlength;
                subnet->weight = weight;
-
-               for(i = 0; i < 4; i++) {
-                       if(x[i] > 255)
+               for(int i = 0; i < 4; i++) {
+                       if (x[i] > 255)
                                return false;
                        subnet->net.ipv4.address.x[i] = x[i];
                }
-
                return true;
        }
 
-       if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx#%d",
-                         &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &weight) >= 8) {
-               subnet->type = SUBNET_IPV6;
-               subnet->net.ipv6.prefixlength = 128;
-               subnet->weight = weight;
-
-               for(i = 0; i < 8; i++)
-                       subnet->net.ipv6.address.x[i] = htons(x[i]);
-
-               return true;
-       }
+       /* IPv6 */
 
-       if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
-                         &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) {
-               subnet->type = SUBNET_MAC;
-               subnet->weight = weight;
-
-               for(i = 0; i < 6; i++)
-                       subnet->net.mac.address.x[i] = x[i];
-
-               return true;
+       char* last_colon = strrchr(str, ':');
+       if (last_colon && sscanf(last_colon, ":%hu.%hu.%hu.%hu%n", &x[0], &x[1], &x[2], &x[3], &consumed) >= 4 && !last_colon[consumed]) {
+               /* Dotted quad suffix notation, convert to standard IPv6 notation */
+               for (int i = 0; i < 4; i++)
+                       if (x[i] > 255)
+                               return false;
+               snprintf(last_colon, sizeof str - (last_colon - str), ":%02x%02x:%02x%02x", x[0], x[1], x[2], x[3]);
        }
 
-       // IPv6 short form
-       if(strstr(subnetstr, "::")) {
-               const char *p;
-               char *q;
-               int colons = 0;
-
-               // Count number of colons
-               for(p = subnetstr; *p; p++)
-                       if(*p == ':')
-                               colons++;
-
-               if(colons > 7)
+       char* double_colon = strstr(str, "::");
+       if (double_colon) {
+               /* Figure out how many zero groups we need to expand */
+               int zero_group_count = 8;
+               for (const char* cur = str; *cur; cur++)
+                       if (*cur != ':') {
+                               zero_group_count--;
+                               while(cur[1] && cur[1] != ':')
+                                       cur++;
+                       }
+               if (zero_group_count < 1)
                        return false;
 
-               // Scan numbers before the double colon
-               p = subnetstr;
-               for(i = 0; i < colons; i++) {
-                       if(*p == ':')
-                               break;
-                       x[i] = strtoul(p, &q, 0x10);
-                       if(!q || p == q || *q != ':')
-                               return false;
-                       p = ++q;
-               }
-
-               p++;
-               colons -= i;
-               if(!i) {
-                       p++;
-                       colons--;
-               }
-
-               if(!*p || *p == '/' || *p == '#')
-                       colons--;
+               /* Split the double colon in the middle to make room for zero groups */
+               double_colon++;
+               memmove(double_colon + (zero_group_count * 2 - 1), double_colon, strlen(double_colon) + 1);
 
-               // Fill in the blanks
-               for(; i < 8 - colons; i++)
-                       x[i] = 0;
-
-               // Scan the remaining numbers
-               for(; i < 8; i++) {
-                       x[i] = strtoul(p, &q, 0x10);
-                       if(!q || p == q)
-                               return false;
-                       if(i == 7) {
-                               p = q;
-                               break;
-                       }
-                       if(*q != ':')
-                               return false;
-                       p = ++q;
-               }
+               /* Write zero groups in the resulting gap, overwriting the second colon */
+               for (int i = 0; i < zero_group_count; i++)
+                       memcpy(&double_colon[i * 2], "0:", 2);
 
-               l = 128;
-               if(*p == '/')
-                       sscanf(p, "/%d#%d", &l, &weight);
-               else if(*p == '#')
-                       sscanf(p, "#%d", &weight);
+               /* Remove any leading or trailing colons */
+               if (str[0] == ':')
+                       memmove(&str[0], &str[1], strlen(&str[1]) + 1);
+               if (str[strlen(str) - 1] == ':')
+                       str[strlen(str) - 1] = 0;
+       }
 
-               if(l < 0 || l > 128)
+       if (sscanf(str, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx%n",
+               &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &consumed) >= 8 && !str[consumed]) {
+               if (prefixlength == -1)
+                       prefixlength = 128;
+               if (prefixlength > 128)
                        return false;
 
                subnet->type = SUBNET_IPV6;
-               subnet->net.ipv6.prefixlength = l;
+               subnet->net.ipv6.prefixlength = prefixlength;
                subnet->weight = weight;
-
-               for(i = 0; i < 8; i++)
+               for(int i = 0; i < 8; i++)
                        subnet->net.ipv6.address.x[i] = htons(x[i]);
-
                return true;
        }
 
@@ -337,46 +311,101 @@ bool net2str(char *netstr, int len, const subnet_t *subnet) {
                return false;
        }
 
+       int result;
+       int prefixlength = -1;
        switch (subnet->type) {
                case SUBNET_MAC:
-                       snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
+                       result = snprintf(netstr, len, "%02x:%02x:%02x:%02x:%02x:%02x",
                                         subnet->net.mac.address.x[0],
                                         subnet->net.mac.address.x[1],
                                         subnet->net.mac.address.x[2],
                                         subnet->net.mac.address.x[3],
                                         subnet->net.mac.address.x[4],
-                                        subnet->net.mac.address.x[5],
-                                        subnet->weight);
+                                        subnet->net.mac.address.x[5]);
+                       netstr += result;
+                       len -= result;
                        break;
 
                case SUBNET_IPV4:
-                       snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%d",
+                       result = snprintf(netstr, len, "%u.%u.%u.%u",
                                         subnet->net.ipv4.address.x[0],
                                         subnet->net.ipv4.address.x[1],
                                         subnet->net.ipv4.address.x[2],
-                                        subnet->net.ipv4.address.x[3],
-                                        subnet->net.ipv4.prefixlength,
-                                        subnet->weight);
+                                        subnet->net.ipv4.address.x[3]);
+                       netstr += result;
+                       len -= result;
+                       prefixlength = subnet->net.ipv4.prefixlength;
+                       if (prefixlength == 32)
+                               prefixlength = -1;
                        break;
 
-               case SUBNET_IPV6:
-                       snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
-                                        ntohs(subnet->net.ipv6.address.x[0]),
-                                        ntohs(subnet->net.ipv6.address.x[1]),
-                                        ntohs(subnet->net.ipv6.address.x[2]),
-                                        ntohs(subnet->net.ipv6.address.x[3]),
-                                        ntohs(subnet->net.ipv6.address.x[4]),
-                                        ntohs(subnet->net.ipv6.address.x[5]),
-                                        ntohs(subnet->net.ipv6.address.x[6]),
-                                        ntohs(subnet->net.ipv6.address.x[7]),
-                                        subnet->net.ipv6.prefixlength,
-                                        subnet->weight);
+               case SUBNET_IPV6: {
+                       /* Find the longest sequence of consecutive zeroes */
+                       int max_zero_length = 0;
+                       int max_zero_length_index = 0;
+                       int current_zero_length = 0;
+                       int current_zero_length_index = 0;
+                       for (int i = 0; i < 8; i++) {
+                               if (subnet->net.ipv6.address.x[i] != 0)
+                                       current_zero_length = 0;
+                               else {
+                                       if (current_zero_length == 0)
+                                               current_zero_length_index = i;
+                                       current_zero_length++;
+                                       if (current_zero_length > max_zero_length) {
+                                               max_zero_length = current_zero_length;
+                                               max_zero_length_index = current_zero_length_index;
+                                       }
+                               }
+                       }
+
+                       /* Print the address */
+                       for (int i = 0; i < 8;) {
+                               if (max_zero_length > 1 && max_zero_length_index == i) {
+                                       /* Shorten the representation as per RFC 5952 */
+                                       const char* const FORMATS[] = { "%.1s", "%.2s", "%.3s" };
+                                       const char* const* format = &FORMATS[0];
+                                       if (i == 0)
+                                               format++;
+                                       if (i + max_zero_length == 8)
+                                               format++;
+                                       result = snprintf(netstr, len, *format, ":::");
+                                       i += max_zero_length;
+                               } else {
+                                       result = snprintf(netstr, len, "%hx:", ntohs(subnet->net.ipv6.address.x[i]));
+                                       i++;
+                               }
+                               netstr += result;
+                               len -= result;
+                       }
+
+                       /* Remove the trailing colon */
+                       netstr--;
+                       len++;
+                       *netstr = 0;
+
+                       prefixlength = subnet->net.ipv6.prefixlength;
+                       if (prefixlength == 128)
+                               prefixlength = -1;
                        break;
+               }
 
                default:
                        logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with unknown subnet type %d, exiting!", subnet->type);
                        exit(1);
        }
 
+       if (prefixlength >= 0) {
+               result = snprintf(netstr, len, "/%d", prefixlength);
+               netstr += result;
+               len -= result;
+       }
+
+       if (subnet->weight != DEFAULT_WEIGHT) {
+               snprintf(netstr, len, "#%d", subnet->weight);
+               netstr += result;
+               len -= result;
+       }
+
        return true;
 }
index 9fc749b..b36de76 100644 (file)
@@ -31,6 +31,7 @@
 #include "control_common.h"
 #include "crypto.h"
 #include "ecdsagen.h"
+#include "fsck.h"
 #include "info.h"
 #include "invitation.h"
 #include "names.h"
@@ -38,6 +39,7 @@
 #include "utils.h"
 #include "tincctl.h"
 #include "top.h"
+#include "version.h"
 
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
@@ -65,7 +67,7 @@ char line[4096];
 static int code;
 static int req;
 static int result;
-static bool force = false;
+bool force = false;
 bool tty = true;
 bool confbasegiven = false;
 bool netnamegiven = false;
@@ -74,6 +76,7 @@ char *scriptextension = "";
 static char *prompt;
 
 static struct option const long_options[] = {
+       {"batch", no_argument, NULL, 'b'},
        {"config", required_argument, NULL, 'c'},
        {"net", required_argument, NULL, 'n'},
        {"help", no_argument, NULL, 1},
@@ -85,8 +88,8 @@ static struct option const long_options[] = {
 
 static void version(void) {
        printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
-                  VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
-       printf("Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others.\n"
+                  VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
+       printf("Copyright (C) 1998-2014 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"
@@ -99,9 +102,11 @@ static void usage(bool status) {
        } else {
                printf("Usage: %s [options] command\n\n", program_name);
                printf("Valid options are:\n"
+                               "  -b, --batch             Don't ask for anything (non-interactive mode).\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"
+                               "      --force             Force some commands to work despite warnings.\n"
                                "      --help              Display this help and exit.\n"
                                "      --version           Output version information and exit.\n"
                                "\n"
@@ -116,8 +121,12 @@ static void usage(bool status) {
                                "  restart [tincd options]    Restart tincd.\n"
                                "  reload                     Partially reload configuration of running tincd.\n"
                                "  pid                        Show PID of currently running tincd.\n"
+#ifdef DISABLE_LEGACY
+                               "  generate-keys              Generate a new Ed25519 public/private keypair.\n"
+#else
                                "  generate-keys [bits]       Generate new RSA and Ed25519 public/private keypairs.\n"
                                "  generate-rsa-keys [bits]   Generate a new RSA public/private keypair.\n"
+#endif
                                "  generate-ed25519-keys      Generate a new Ed25519 public/private keypair.\n"
                                "  dump                       Dump a list of one of the following things:\n"
                                "    [reachable] nodes        - all known nodes in the VPN\n"
@@ -137,12 +146,13 @@ static void usage(bool status) {
                                "  log [level]                Dump log output [up to the specified level]\n"
                                "  export                     Export host configuration of local node to standard output\n"
                                "  export-all                 Export all host configuration files to standard output\n"
-                               "  import [--force]           Import host configuration file(s) from standard input\n"
-                               "  exchange [--force]         Same as export followed by import\n"
-                               "  exchange-all [--force]     Same as export-all followed by import\n"
+                               "  import                     Import host configuration file(s) from standard input\n"
+                               "  exchange                   Same as export followed by import\n"
+                               "  exchange-all               Same as export-all followed by import\n"
                                "  invite NODE [...]          Generate an invitation for NODE\n"
                                "  join INVITATION            Join a VPN using an INVITIATION\n"
                                "  network [NETNAME]          List all known networks, or switch to the one named NETNAME.\n"
+                               "  fsck                       Check the configuration files for problems.\n"
                                "\n");
                printf("Report bugs to tinc@tinc-vpn.org.\n");
        }
@@ -157,6 +167,10 @@ static bool parse_options(int argc, char **argv) {
                        case 0:   /* long option */
                                break;
 
+                       case 'b':
+                               tty = false;
+                               break;
+
                        case 'c': /* config file */
                                confbase = xstrdup(optarg);
                                confbasegiven = true;
@@ -408,6 +422,7 @@ static bool ed25519_keygen(bool ask) {
        return true;
 }
 
+#ifndef DISABLE_LEGACY
 /*
   Generate a public/private RSA keypair, and ask for a file to store
   them in.
@@ -473,6 +488,7 @@ static bool rsa_keygen(int bits, bool ask) {
 
        return true;
 }
+#endif
 
 char buffer[4096];
 size_t blen = 0;
@@ -485,7 +501,7 @@ bool recvline(int fd, char *line, size_t len) {
 
        while(!(newline = memchr(buffer, '\n', blen))) {
                int result = recv(fd, buffer + blen, sizeof buffer - blen, 0);
-               if(result == -1 && errno == EINTR)
+               if(result == -1 && sockerrno == EINTR)
                        continue;
                else if(result <= 0)
                        return false;
@@ -511,7 +527,7 @@ 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)
+               if(result == -1 && sockerrno == EINTR)
                        continue;
                else if(result <= 0)
                        return false;
@@ -543,7 +559,7 @@ bool sendline(int fd, char *format, ...) {
 
        while(blen) {
                int result = send(fd, p, blen, MSG_NOSIGNAL);
-               if(result == -1 && errno == EINTR)
+               if(result == -1 && sockerrno == EINTR)
                        continue;
                else if(result <= 0)
                        return false;
@@ -723,7 +739,7 @@ bool connect_tincd(bool verbose) {
 
        if(getaddrinfo(host, port, &hints, &res) || !res) {
                if(verbose)
-                       fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, strerror(errno));
+                       fprintf(stderr, "Cannot resolve %s port %s: %s", host, port, sockstrerror(sockerrno));
                return false;
        }
 
@@ -756,7 +772,7 @@ bool connect_tincd(bool verbose) {
 
 #ifdef SO_NOSIGPIPE
        static const int one = 1;
-       setsockopt(c, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
+       setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&one, sizeof one);
 #endif
 
        char data[4096];
@@ -809,16 +825,31 @@ static int cmd_start(int argc, char *argv[]) {
        int nargc = 0;
        char **nargv = xzalloc((optind + argc) * sizeof *nargv);
 
-       nargv[nargc++] = c;
+       char *arg0 = c;
+#ifdef HAVE_MINGW
+       /*
+          Windows has no real concept of an "argv array". A command line is just one string.
+          The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
+          it uses quotes to handle spaces in arguments.
+          Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
+          If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
+          into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
+       */
+       xasprintf(&arg0, "\"%s\"", arg0);
+#endif
+       nargv[nargc++] = arg0;
        for(int i = 1; i < optind; i++)
                nargv[nargc++] = orig_argv[i];
        for(int i = 1; i < argc; i++)
                nargv[nargc++] = argv[i];
 
 #ifdef HAVE_MINGW
-       execvp(c, nargv);
-       fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
-       return 1;
+       int status = spawnvp(_P_WAIT, c, nargv);
+       if (status == -1) {
+               fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
+               return 1;
+       }
+       return status;
 #else
        pid_t pid = fork();
        if(pid == -1) {
@@ -980,11 +1011,14 @@ static int cmd_dump(int argc, char *argv[]) {
                        break;
 
                char node[4096];
+               char id[4096];
                char from[4096];
                char to[4096];
                char subnet[4096];
                char host[4096];
                char port[4096];
+               char local_host[4096];
+               char local_port[4096];
                char via[4096];
                char nexthop[4096];
                int cipher, digest, maclength, compression, distance, socket, weight;
@@ -995,8 +1029,8 @@ static int cmd_dump(int argc, char *argv[]) {
 
                switch(req) {
                        case REQ_DUMP_NODES: {
-                               int n = sscanf(line, "%*d %*d %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
-                               if(n != 16) {
+                               int n = sscanf(line, "%*d %*d %s %s %s port %s %d %d %d %d %x %x %s %s %d %hd %hd %hd %ld", node, id, host, port, &cipher, &digest, &maclength, &compression, &options, &status_int, nexthop, via, &distance, &pmtu, &minmtu, &maxmtu, &last_state_change);
+                               if(n != 17) {
                                        fprintf(stderr, "Unable to parse node dump from tincd: %s\n", line);
                                        return 1;
                                }
@@ -1019,14 +1053,14 @@ static int cmd_dump(int argc, char *argv[]) {
                                } else {
                                        if(only_reachable && !status.reachable)
                                                continue;
-                                       printf("%s at %s port %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)\n",
-                                                       node, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
+                                       printf("%s id %s at %s port %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)\n",
+                                                       node, id, host, port, cipher, digest, maclength, compression, options, status_int, nexthop, via, distance, pmtu, minmtu, maxmtu);
                                }
                        } break;
 
                        case REQ_DUMP_EDGES: {
-                               int n = sscanf(line, "%*d %*d %s %s %s port %s %x %d", from, to, host, port, &options, &weight);
-                               if(n != 6) {
+                               int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
+                               if(n != 8) {
                                        fprintf(stderr, "Unable to parse edge dump from tincd.\n");
                                        return 1;
                                }
@@ -1038,7 +1072,7 @@ static int cmd_dump(int argc, char *argv[]) {
                                        else if(do_graph == 2)
                                                printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
                                } else {
-                                       printf("%s to %s at %s port %s options %x weight %d\n", from, to, host, port, options, weight);
+                                       printf("%s to %s at %s port %s local %s port %s options %x weight %d\n", from, to, host, port, local_host, local_port, options, weight);
                                }
                        } break;
 
@@ -1281,7 +1315,7 @@ char *get_my_name(bool verbose) {
                        continue;
                if(*value) {
                        fclose(f);
-                       return strdup(value);
+                       return replace_name(value);
                }
        }
 
@@ -1298,9 +1332,11 @@ const var_t variables[] = {
        {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
        {"BindToInterface", VAR_SERVER},
        {"Broadcast", VAR_SERVER | VAR_SAFE},
+       {"BroadcastSubnet", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
        {"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
        {"DecrementTTL", VAR_SERVER},
        {"Device", VAR_SERVER},
+       {"DeviceStandby", VAR_SERVER},
        {"DeviceType", VAR_SERVER},
        {"DirectOnly", VAR_SERVER},
        {"Ed25519PrivateKeyFile", VAR_SERVER},
@@ -1331,6 +1367,10 @@ const var_t variables[] = {
        {"ScriptsInterpreter", VAR_SERVER},
        {"StrictSubnets", VAR_SERVER},
        {"TunnelServer", VAR_SERVER},
+       {"UDPDiscovery", VAR_SERVER},
+       {"UDPDiscoveryKeepaliveInterval", VAR_SERVER},
+       {"UDPDiscoveryInterval", VAR_SERVER},
+       {"UDPDiscoveryTimeout", VAR_SERVER},
        {"UDPRcvBuf", VAR_SERVER},
        {"UDPSndBuf", VAR_SERVER},
        {"VDEGroup", VAR_SERVER},
@@ -1571,6 +1611,11 @@ static int cmd_config(int argc, char *argv[]) {
                                }
                                set = true;
                                continue;
+                       // Add
+                       } else if(action > 0) {
+                               // Check if we've already seen this variable with the same value
+                               if(!strcasecmp(bvalue, value))
+                                       found = true;
                        }
                }
 
@@ -1603,7 +1648,7 @@ static int cmd_config(int argc, char *argv[]) {
        }
 
        // Add new variable if necessary.
-       if(action > 0 || (action == 0 && !set)) {
+       if((action > 0 && !found)|| (action == 0 && !set)) {
                if(fprintf(tf, "%s = %s\n", variable, value) < 0) {
                        fprintf(stderr, "Error writing to temporary file %s: %s\n", tmpfile, strerror(errno));
                        return 1;
@@ -1611,9 +1656,12 @@ static int cmd_config(int argc, char *argv[]) {
        }
 
        if(action < -1) {
-               if(!found)
+               if(found) {
+                       return 0;
+               } else {
                        fprintf(stderr, "No matching configuration variables found.\n");
-               return 1;
+                       return 1;
+               }
        }
 
        // Make sure we wrote everything...
@@ -1648,18 +1696,6 @@ static int cmd_config(int argc, char *argv[]) {
        return 0;
 }
 
-bool check_id(const char *name) {
-       if(!name || !*name)
-               return false;
-
-       for(int i = 0; i < strlen(name); i++) {
-               if(!isalnum(name[i]) && name[i] != '_')
-                       return false;
-       }
-
-       return true;
-}
-
 static bool try_bind(int port) {
        struct addrinfo *ai = NULL;
        struct addrinfo hint = {
@@ -1782,7 +1818,12 @@ static int cmd_init(int argc, char *argv[]) {
        fprintf(f, "Name = %s\n", name);
        fclose(f);
 
-       if(!rsa_keygen(2048, false) || !ed25519_keygen(false))
+#ifndef DISABLE_LEGACY
+       if(!rsa_keygen(2048, false))
+               return 1;
+#endif
+
+       if(!ed25519_keygen(false))
                return 1;
 
        check_port(name);
@@ -1806,7 +1847,11 @@ static int cmd_init(int argc, char *argv[]) {
 }
 
 static int cmd_generate_keys(int argc, char *argv[]) {
+#ifdef DISABLE_LEGACY
+       if(argc > 1) {
+#else
        if(argc > 2) {
+#endif
                fprintf(stderr, "Too many arguments!\n");
                return 1;
        }
@@ -1814,9 +1859,18 @@ static int cmd_generate_keys(int argc, char *argv[]) {
        if(!name)
                name = get_my_name(false);
 
-       return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ed25519_keygen(true));
+#ifndef DISABLE_LEGACY
+       if(!rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true))
+               return 1;
+#endif
+
+       if(!ed25519_keygen(true))
+               return 1;
+
+       return 0;
 }
 
+#ifndef DISABLE_LEGACY
 static int cmd_generate_rsa_keys(int argc, char *argv[]) {
        if(argc > 2) {
                fprintf(stderr, "Too many arguments!\n");
@@ -1828,6 +1882,7 @@ static int cmd_generate_rsa_keys(int argc, char *argv[]) {
 
        return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
 }
+#endif
 
 static int cmd_generate_ed25519_keys(int argc, char *argv[]) {
        if(argc > 1) {
@@ -2107,7 +2162,6 @@ static int switch_network(char *name) {
        free(netname);
        netname = strcmp(name, ".") ? xstrdup(name) : NULL;
 
-       make_names();
         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
        xasprintf(&prompt, "%s> ", identname);
@@ -2152,6 +2206,15 @@ static int cmd_network(int argc, char *argv[]) {
        return 0;
 }
 
+static int cmd_fsck(int argc, char *argv[]) {
+       if(argc > 1) {
+               fprintf(stderr, "Too many arguments!\n");
+               return 1;
+       }
+
+       return fsck(orig_argv[0]);
+}
+
 static const struct {
        const char *command;
        int (*function)(int argc, char *argv[]);
@@ -2178,7 +2241,9 @@ static const struct {
        {"set", cmd_config},
        {"init", cmd_init},
        {"generate-keys", cmd_generate_keys},
+#ifndef DISABLE_LEGACY
        {"generate-rsa-keys", cmd_generate_rsa_keys},
+#endif
        {"generate-ed25519-keys", cmd_generate_ed25519_keys},
        {"help", cmd_help},
        {"version", cmd_version},
@@ -2192,6 +2257,7 @@ static const struct {
        {"invite", cmd_invite},
        {"join", cmd_join},
        {"network", cmd_network},
+       {"fsck", cmd_fsck},
        {NULL, NULL},
 };
 
@@ -2421,6 +2487,7 @@ int main(int argc, char *argv[]) {
        program_name = argv[0];
        orig_argv = argv;
        orig_argc = argc;
+       tty = isatty(0) && isatty(1);
 
        if(!parse_options(argc, argv))
                return 1;
@@ -2451,8 +2518,6 @@ int main(int argc, char *argv[]) {
        srand(time(NULL));
        crypto_init();
 
-       tty = isatty(0) && isatty(1);
-
        if(optind >= argc)
                return cmd_shell(argc, argv);
 
index e636887..4a917af 100644 (file)
@@ -21,6 +21,7 @@
 #define __TINC_TINCCTL_H__
 
 extern bool tty;
+extern bool force;
 extern char line[4096];
 extern int fd;
 extern char buffer[4096];
index c707f54..c4806ba 100644 (file)
@@ -49,6 +49,7 @@
 #include "control.h"
 #include "crypto.h"
 #include "device.h"
+#include "event.h"
 #include "logger.h"
 #include "names.h"
 #include "net.h"
@@ -57,6 +58,7 @@
 #include "protocol.h"
 #include "utils.h"
 #include "xalloc.h"
+#include "version.h"
 
 /* If nonzero, display usage information and exit. */
 static bool show_help = false;
@@ -110,7 +112,6 @@ static struct option const long_options[] = {
 
 #ifdef HAVE_MINGW
 static struct WSAData wsa_state;
-CRITICAL_SECTION mutex;
 int main2(int argc, char **argv);
 #endif
 
@@ -312,6 +313,17 @@ static bool drop_privs(void) {
 
 #ifdef HAVE_MINGW
 # define setpriority(level) !SetPriorityClass(GetCurrentProcess(), (level))
+
+static void stop_handler(void *data, int flags) {
+       event_exit();
+}
+
+static BOOL WINAPI console_ctrl_handler(DWORD type) {
+       logger(DEBUG_ALWAYS, LOG_NOTICE, "Got console shutdown request");
+       if (WSASetEvent(stop_io.event) == FALSE)
+               abort();
+       return TRUE;
+}
 #else
 # define NORMAL_PRIORITY_CLASS 0
 # define BELOW_NORMAL_PRIORITY_CLASS 10
@@ -329,7 +341,7 @@ int main(int argc, char **argv) {
 
        if(show_version) {
                printf("%s version %s (built %s %s, protocol %d.%d)\n", PACKAGE,
-                          VERSION, __DATE__, __TIME__, PROT_MAJOR, PROT_MINOR);
+                          VERSION, BUILD_DATE, BUILD_TIME, PROT_MAJOR, PROT_MINOR);
                printf("Copyright (C) 1998-2014 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"
@@ -380,15 +392,24 @@ int main(int argc, char **argv) {
 #endif
 
 #ifdef HAVE_MINGW
-       if(!do_detach || !init_service())
-               return main2(argc, argv);
-       else
-               return 1;
+       io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
+       if (stop_io.event == FALSE)
+               abort();
+
+       int result;
+       if(!do_detach || !init_service()) {
+               SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
+               result = main2(argc, argv);
+       } else
+               result = 1;
+
+       if (WSACloseEvent(stop_io.event) == FALSE)
+               abort();
+       io_del(&stop_io);
+       return result;
 }
 
 int main2(int argc, char **argv) {
-       InitializeCriticalSection(&mutex);
-       EnterCriticalSection(&mutex);
 #endif
        char *priority = NULL;
 
index 2824261..40b8047 100644 (file)
--- a/src/top.c
+++ b/src/top.c
@@ -21,6 +21,7 @@
 
 #ifdef HAVE_CURSES
 
+#undef KEY_EVENT  /* There are conflicting declarations for KEY_EVENT in Windows wincon.h and curses.h. */
 #include <curses.h>
 
 #include "control_common.h"
index f2b52cb..57d4843 100644 (file)
@@ -244,7 +244,7 @@ static bool read_packet(vpn_packet_t *packet) {
                }
 
                case 2: {
-                       if((inlen = read(data_fd, packet->data, MTU)) <= 0) {
+                       if((inlen = read(data_fd, DATA(packet), MTU)) <= 0) {
                                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                event_exit();
@@ -275,7 +275,7 @@ static bool write_packet(vpn_packet_t *packet) {
        logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
 
-       if(write(write_fd, packet->data, packet->len) < 0) {
+       if(write(write_fd, DATA(packet), packet->len) < 0) {
                if(errno != EINTR && errno != EAGAIN) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
                        event_exit();
index 0d7bc02..65ba4b9 100644 (file)
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "logger.h"
 #include "system.h"
-
-#include "../src/logger.h"
 #include "utils.h"
+#include "xalloc.h"
 
 static const char hexadecimals[] = "0123456789ABCDEF";
 static const char base64_original[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -179,3 +179,54 @@ unsigned int bitfield_to_int(const void *bitfield, size_t size) {
        memcpy(&value, bitfield, size);
        return value;
 }
+
+bool check_id(const char *id) {
+       if(!id || !*id)
+               return false;
+
+       for(; *id; id++)
+               if(!isalnum(*id) && *id != '_')
+                       return false;
+
+       return true;
+}
+
+/* Windows doesn't define HOST_NAME_MAX. */
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+char *replace_name(const char *name) {
+       char *ret_name;
+
+       if (name[0] == '$') {
+               char *envname = getenv(name + 1);
+               char hostname[HOST_NAME_MAX+1];
+               if (!envname) {
+                       if (strcmp(name + 1, "HOST")) {
+                               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid Name: environment variable %s does not exist\n", name + 1);
+                               return NULL;
+                       }
+                       if (gethostname(hostname, sizeof hostname) || !*hostname) {
+                               logger(DEBUG_ALWAYS, LOG_ERR, "Could not get hostname: %s\n", sockstrerror(sockerrno));
+                               return NULL;
+                       }
+                       hostname[HOST_NAME_MAX] = 0;
+                       envname = hostname;
+               }
+               ret_name = xstrdup(envname);
+               for (char *c = ret_name; *c; c++)
+                       if (!isalnum(*c))
+                               *c = '_';
+       } else {
+               ret_name = xstrdup(name);
+       }
+
+       if (!check_id(ret_name)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid name for myself!");
+               free(ret_name);
+               return NULL;
+       }
+
+       return ret_name;
+}
index d89d077..c3364ce 100644 (file)
@@ -37,6 +37,7 @@ extern const char *winerror(int);
 #define sockmsgsize(x) ((x) == WSAEMSGSIZE)
 #define sockinprogress(x) ((x) == WSAEINPROGRESS || (x) == WSAEWOULDBLOCK)
 #define sockinuse(x) ((x) == WSAEADDRINUSE)
+#define socknotconn(x) ((x) == WSAENOTCONN)
 #else
 #define sockerrno errno
 #define sockstrerror(x) strerror(x)
@@ -44,8 +45,12 @@ extern const char *winerror(int);
 #define sockmsgsize(x) ((x) == EMSGSIZE)
 #define sockinprogress(x) ((x) == EINPROGRESS)
 #define sockinuse(x) ((x) == EADDRINUSE)
+#define socknotconn(x) ((x) == ENOTCONN)
 #endif
 
 extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
 
+extern bool check_id(const char *);
+char *replace_name(const char *name);
+
 #endif /* __TINC_UTILS_H__ */
index 55d66b4..73ad713 100644 (file)
@@ -97,7 +97,7 @@ static void close_device(void) {
 }
 
 static bool read_packet(vpn_packet_t *packet) {
-       int lenin = (ssize_t)plug.vde_recv(conn, packet->data, MTU, 0);
+       int lenin = (ssize_t)plug.vde_recv(conn, DATA(packet), MTU, 0);
        if(lenin <= 0) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno));
                event_exit();
@@ -112,7 +112,7 @@ static bool read_packet(vpn_packet_t *packet) {
 }
 
 static bool write_packet(vpn_packet_t *packet) {
-       if((ssize_t)plug.vde_send(conn, packet->data, packet->len, 0) < 0) {
+       if((ssize_t)plug.vde_send(conn, DATA(packet), packet->len, 0) < 0) {
                if(errno != EINTR && errno != EAGAIN) {
                        logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno));
                        event_exit();
diff --git a/src/version.c b/src/version.c
new file mode 100644 (file)
index 0000000..fc62c8a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+    version.c -- version information 
+    Copyright (C) 2014      Etienne Dechamps <etienne@edechamps.fr>
+
+    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 "version.h"
+
+/* This file is always rebuilt (even if there are no changes) so that the following is updated */
+const char* const BUILD_DATE = __DATE__;
+const char* const BUILD_TIME = __TIME__;
diff --git a/src/version.h b/src/version.h
new file mode 100644 (file)
index 0000000..d3e4a1f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+    version.h -- header for version.c
+    Copyright (C) 2014      Etienne Dechamps <etienne@edechamps.fr>
+
+    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_VERSION_H__
+#define __TINC_VERSION_H__
+
+extern const char* const BUILD_DATE;
+extern const char* const BUILD_TIME;
+
+#endif /* __TINC_VERSION_H__ */
index e95c953..157eb54 100755 (executable)
@@ -44,6 +44,7 @@ $tinc $c1 --net foo get name
 
 # Test tinc command line options that should not work
 
+$tinc $c1 -n foo get somethingreallyunknown && exit 1 || true
 $tinc $c1 --net && exit 1 || true
 $tinc $c1 --net get name && exit 1 || true
 $tinc $c1 foo && exit 1 || true
index 4cf9d5e..5fe6046 100755 (executable)
@@ -38,7 +38,6 @@ $tinc $c1 add Subnet 2
 $tinc $c1 add Subnet 3
 test "`$tinc $c1 get Subnet`" = "1
 2
-2
 3"
 $tinc $c1 del Subnet 2
 test "`$tinc $c1 get Subnet`" = "1
@@ -65,7 +64,6 @@ $tinc $c1 add bar.Subnet 2
 $tinc $c1 add bar.Subnet 3
 test "`$tinc $c1 get bar.Subnet`" = "1
 2
-2
 3"
 $tinc $c1 del bar.Subnet 2
 test "`$tinc $c1 get bar.Subnet`" = "1