Import Upstream version 1.1~pre2

This commit is contained in:
Guus Sliepen 2019-08-26 13:44:49 +02:00
parent a04a7bcd67
commit 02de1cd2f1
172 changed files with 32291 additions and 25994 deletions

View file

@ -1,89 +1,50 @@
## Produce this file with automake to get Makefile.in
sbin_PROGRAMS = tincd
sbin_PROGRAMS = tincd tincctl
EXTRA_DIST = linux bsd solaris cygwin mingw raw_socket uml_socket openssl gcrypt
tincd_SOURCES = \
have.h \
system.h \
avl_tree.c avl_tree.h \
conf.c conf.h \
connection.c connection.h \
device.h \
dropin.c dropin.h \
dummy_device.c \
edge.c edge.h \
ethernet.h \
event.c event.h \
fake-getaddrinfo.c fake-getaddrinfo.h \
fake-getnameinfo.c fake-getnameinfo.h \
graph.c graph.h \
ipv4.h \
ipv6.h \
list.c list.h \
logger.c logger.h \
meta.c meta.h \
multicast_device.c \
net.c net.h \
net_packet.c \
net_setup.c \
net_socket.c \
netutl.c netutl.h \
node.c node.h \
pidfile.c pidfile.h \
process.c process.h \
protocol.c protocol.h \
protocol_auth.c \
protocol_edge.c \
protocol_misc.c \
protocol_key.c \
protocol_subnet.c \
proxy.c proxy.h \
raw_socket_device.c \
route.c route.h \
subnet.c subnet.h \
tincd.c \
utils.c utils.h \
xalloc.h
utils.c getopt.c getopt1.c list.c splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
buffer.c conf.c connection.c control.c edge.c graph.c logger.c meta.c net.c net_packet.c net_setup.c \
net_socket.c netutl.c node.c process.c protocol.c protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c route.c subnet.c tincd.c
if !GETOPT
tincd_SOURCES += \
getopt.c getopt.h \
getopt1.c
endif
nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
if LINUX
tincd_SOURCES += linux/device.c
endif
tincctl_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \
list.c tincctl.c top.c
nodist_tincctl_SOURCES = \
ecdsagen.c rsagen.c
if BSD
tincd_SOURCES += bsd/device.c
if TUNEMU
tincd_SOURCES += bsd/tunemu.c bsd/tunemu.h
endif
tincd_SOURCES += bsd/tunemu.c
endif
if SOLARIS
tincd_SOURCES += solaris/device.c
endif
tincctl_LDADD = $(CURSES_LIBS)
if MINGW
tincd_SOURCES += mingw/device.c mingw/common.h
endif
DEFAULT_INCLUDES =
if CYGWIN
tincd_SOURCES += cygwin/device.c
endif
INCLUDES = @INCLUDES@ -I$(top_builddir)
if UML
tincd_SOURCES += uml_device.c
endif
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h tincctl.h top.h bsd/tunemu.h
if VDE
tincd_SOURCES += vde_device.c
endif
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h
LIBS = @LIBS@ @LIBGCRYPT_LIBS@
if TUNEMU
LIBS += -lpcap
endif
AM_CPPFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DRUNSTATEDIR=\"$(runstatedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -I $(abs_top_builddir)/
AM_CFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
dist-hook:
rm -f `find . -type l`

View file

@ -1,8 +1,9 @@
# Makefile.in generated by automake 1.16.1 from Makefile.am.
# Makefile.in generated by automake 1.11.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
# Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -14,62 +15,8 @@
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@ -88,163 +35,74 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
sbin_PROGRAMS = tincd$(EXEEXT)
@GETOPT_FALSE@am__append_1 = \
@GETOPT_FALSE@ getopt.c getopt.h \
@GETOPT_FALSE@ getopt1.c
@LINUX_TRUE@am__append_2 = linux/device.c
@BSD_TRUE@am__append_3 = bsd/device.c
@BSD_TRUE@@TUNEMU_TRUE@am__append_4 = bsd/tunemu.c bsd/tunemu.h
@SOLARIS_TRUE@am__append_5 = solaris/device.c
@MINGW_TRUE@am__append_6 = mingw/device.c mingw/common.h
@CYGWIN_TRUE@am__append_7 = cygwin/device.c
@UML_TRUE@am__append_8 = uml_device.c
@VDE_TRUE@am__append_9 = vde_device.c
@TUNEMU_TRUE@am__append_10 = -lpcap
sbin_PROGRAMS = tincd$(EXEEXT) tincctl$(EXEEXT)
@TUNEMU_TRUE@am__append_1 = bsd/tunemu.c
@TUNEMU_TRUE@am__append_2 = -lpcap
subdir = src
DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/attribute.m4 \
$(top_srcdir)/m4/ax_append_flag.m4 \
$(top_srcdir)/m4/ax_cflags_warn_all.m4 \
$(top_srcdir)/m4/ax_check_compile_flag.m4 \
$(top_srcdir)/m4/ax_check_link_flag.m4 \
$(top_srcdir)/m4/ax_require_defined.m4 $(top_srcdir)/m4/lzo.m4 \
$(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/zlib.m4 \
$(top_srcdir)/configure.ac
$(top_srcdir)/m4/curses.m4 $(top_srcdir)/m4/libevent.m4 \
$(top_srcdir)/m4/lzo.m4 $(top_srcdir)/m4/openssl.m4 \
$(top_srcdir)/m4/zlib.m4 $(top_srcdir)/configure.in
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(sbindir)"
PROGRAMS = $(sbin_PROGRAMS)
am__tincd_SOURCES_DIST = have.h system.h avl_tree.c avl_tree.h conf.c \
conf.h connection.c connection.h device.h dropin.c dropin.h \
dummy_device.c edge.c edge.h ethernet.h event.c event.h \
fake-getaddrinfo.c fake-getaddrinfo.h fake-getnameinfo.c \
fake-getnameinfo.h graph.c graph.h ipv4.h ipv6.h list.c list.h \
logger.c logger.h meta.c meta.h multicast_device.c net.c net.h \
net_packet.c net_setup.c net_socket.c netutl.c netutl.h node.c \
node.h pidfile.c pidfile.h process.c process.h protocol.c \
protocol.h protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c proxy.c proxy.h \
raw_socket_device.c route.c route.h subnet.c subnet.h tincd.c \
utils.c utils.h xalloc.h getopt.c getopt.h getopt1.c \
linux/device.c bsd/device.c bsd/tunemu.c bsd/tunemu.h \
solaris/device.c mingw/device.c mingw/common.h cygwin/device.c \
uml_device.c vde_device.c
@GETOPT_FALSE@am__objects_1 = getopt.$(OBJEXT) getopt1.$(OBJEXT)
am__dirstamp = $(am__leading_dot)dirstamp
@LINUX_TRUE@am__objects_2 = linux/device.$(OBJEXT)
@BSD_TRUE@am__objects_3 = bsd/device.$(OBJEXT)
@BSD_TRUE@@TUNEMU_TRUE@am__objects_4 = bsd/tunemu.$(OBJEXT)
@SOLARIS_TRUE@am__objects_5 = solaris/device.$(OBJEXT)
@MINGW_TRUE@am__objects_6 = mingw/device.$(OBJEXT)
@CYGWIN_TRUE@am__objects_7 = cygwin/device.$(OBJEXT)
@UML_TRUE@am__objects_8 = uml_device.$(OBJEXT)
@VDE_TRUE@am__objects_9 = vde_device.$(OBJEXT)
am_tincd_OBJECTS = avl_tree.$(OBJEXT) conf.$(OBJEXT) \
connection.$(OBJEXT) dropin.$(OBJEXT) dummy_device.$(OBJEXT) \
edge.$(OBJEXT) event.$(OBJEXT) fake-getaddrinfo.$(OBJEXT) \
fake-getnameinfo.$(OBJEXT) graph.$(OBJEXT) list.$(OBJEXT) \
logger.$(OBJEXT) meta.$(OBJEXT) multicast_device.$(OBJEXT) \
net.$(OBJEXT) net_packet.$(OBJEXT) net_setup.$(OBJEXT) \
net_socket.$(OBJEXT) netutl.$(OBJEXT) node.$(OBJEXT) \
pidfile.$(OBJEXT) process.$(OBJEXT) protocol.$(OBJEXT) \
protocol_auth.$(OBJEXT) protocol_edge.$(OBJEXT) \
protocol_misc.$(OBJEXT) protocol_key.$(OBJEXT) \
protocol_subnet.$(OBJEXT) proxy.$(OBJEXT) \
raw_socket_device.$(OBJEXT) route.$(OBJEXT) subnet.$(OBJEXT) \
tincd.$(OBJEXT) utils.$(OBJEXT) $(am__objects_1) \
$(am__objects_2) $(am__objects_3) $(am__objects_4) \
$(am__objects_5) $(am__objects_6) $(am__objects_7) \
$(am__objects_8) $(am__objects_9)
tincd_OBJECTS = $(am_tincd_OBJECTS)
am_tincctl_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) \
getopt1.$(OBJEXT) dropin.$(OBJEXT) list.$(OBJEXT) \
tincctl.$(OBJEXT) top.$(OBJEXT)
nodist_tincctl_OBJECTS = ecdsagen.$(OBJEXT) rsagen.$(OBJEXT)
tincctl_OBJECTS = $(am_tincctl_OBJECTS) $(nodist_tincctl_OBJECTS)
am__DEPENDENCIES_1 =
tincctl_DEPENDENCIES = $(am__DEPENDENCIES_1)
am__tincd_SOURCES_DIST = utils.c getopt.c getopt1.c list.c \
splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
buffer.c conf.c connection.c control.c edge.c graph.c logger.c \
meta.c net.c net_packet.c net_setup.c net_socket.c netutl.c \
node.c process.c protocol.c protocol_auth.c protocol_edge.c \
protocol_misc.c protocol_key.c protocol_subnet.c route.c \
subnet.c tincd.c bsd/tunemu.c
@TUNEMU_TRUE@am__objects_1 = tunemu.$(OBJEXT)
am_tincd_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
list.$(OBJEXT) splay_tree.$(OBJEXT) dropin.$(OBJEXT) \
fake-getaddrinfo.$(OBJEXT) fake-getnameinfo.$(OBJEXT) \
buffer.$(OBJEXT) conf.$(OBJEXT) connection.$(OBJEXT) \
control.$(OBJEXT) edge.$(OBJEXT) graph.$(OBJEXT) \
logger.$(OBJEXT) meta.$(OBJEXT) net.$(OBJEXT) \
net_packet.$(OBJEXT) net_setup.$(OBJEXT) net_socket.$(OBJEXT) \
netutl.$(OBJEXT) node.$(OBJEXT) process.$(OBJEXT) \
protocol.$(OBJEXT) protocol_auth.$(OBJEXT) \
protocol_edge.$(OBJEXT) protocol_misc.$(OBJEXT) \
protocol_key.$(OBJEXT) protocol_subnet.$(OBJEXT) \
route.$(OBJEXT) subnet.$(OBJEXT) tincd.$(OBJEXT) \
$(am__objects_1)
nodist_tincd_OBJECTS = device.$(OBJEXT) cipher.$(OBJEXT) \
crypto.$(OBJEXT) ecdh.$(OBJEXT) ecdsa.$(OBJEXT) \
digest.$(OBJEXT) prf.$(OBJEXT) rsa.$(OBJEXT)
tincd_OBJECTS = $(am_tincd_OBJECTS) $(nodist_tincd_OBJECTS)
tincd_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/avl_tree.Po ./$(DEPDIR)/conf.Po \
./$(DEPDIR)/connection.Po ./$(DEPDIR)/dropin.Po \
./$(DEPDIR)/dummy_device.Po ./$(DEPDIR)/edge.Po \
./$(DEPDIR)/event.Po ./$(DEPDIR)/fake-getaddrinfo.Po \
./$(DEPDIR)/fake-getnameinfo.Po ./$(DEPDIR)/getopt.Po \
./$(DEPDIR)/getopt1.Po ./$(DEPDIR)/graph.Po \
./$(DEPDIR)/list.Po ./$(DEPDIR)/logger.Po ./$(DEPDIR)/meta.Po \
./$(DEPDIR)/multicast_device.Po ./$(DEPDIR)/net.Po \
./$(DEPDIR)/net_packet.Po ./$(DEPDIR)/net_setup.Po \
./$(DEPDIR)/net_socket.Po ./$(DEPDIR)/netutl.Po \
./$(DEPDIR)/node.Po ./$(DEPDIR)/pidfile.Po \
./$(DEPDIR)/process.Po ./$(DEPDIR)/protocol.Po \
./$(DEPDIR)/protocol_auth.Po ./$(DEPDIR)/protocol_edge.Po \
./$(DEPDIR)/protocol_key.Po ./$(DEPDIR)/protocol_misc.Po \
./$(DEPDIR)/protocol_subnet.Po ./$(DEPDIR)/proxy.Po \
./$(DEPDIR)/raw_socket_device.Po ./$(DEPDIR)/route.Po \
./$(DEPDIR)/subnet.Po ./$(DEPDIR)/tincd.Po \
./$(DEPDIR)/uml_device.Po ./$(DEPDIR)/utils.Po \
./$(DEPDIR)/vde_device.Po bsd/$(DEPDIR)/device.Po \
bsd/$(DEPDIR)/tunemu.Po cygwin/$(DEPDIR)/device.Po \
linux/$(DEPDIR)/device.Po mingw/$(DEPDIR)/device.Po \
solaris/$(DEPDIR)/device.Po
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(tincd_SOURCES)
DIST_SOURCES = $(am__tincd_SOURCES_DIST)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
SOURCES = $(tincctl_SOURCES) $(nodist_tincctl_SOURCES) \
$(tincd_SOURCES) $(nodist_tincd_SOURCES)
DIST_SOURCES = $(tincctl_SOURCES) $(am__tincd_SOURCES_DIST)
HEADERS = $(nodist_noinst_HEADERS) $(noinst_HEADERS)
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
@ -254,6 +112,7 @@ CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CURSES_LIBS = @CURSES_LIBS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
@ -263,15 +122,21 @@ ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
GREP = @GREP@
INCLUDES = @INCLUDES@ -I$(top_builddir)
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@ $(am__append_10)
LIBS = @LIBS@ @LIBGCRYPT_LIBS@ $(am__append_2)
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
@ -283,6 +148,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
@ -329,37 +195,48 @@ pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
systemd_path = @systemd_path@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
tincd_SOURCES = have.h system.h avl_tree.c avl_tree.h conf.c conf.h \
connection.c connection.h device.h dropin.c dropin.h \
dummy_device.c edge.c edge.h ethernet.h event.c event.h \
fake-getaddrinfo.c fake-getaddrinfo.h fake-getnameinfo.c \
fake-getnameinfo.h graph.c graph.h ipv4.h ipv6.h list.c list.h \
logger.c logger.h meta.c meta.h multicast_device.c net.c net.h \
net_packet.c net_setup.c net_socket.c netutl.c netutl.h node.c \
node.h pidfile.c pidfile.h process.c process.h protocol.c \
protocol.h protocol_auth.c protocol_edge.c protocol_misc.c \
protocol_key.c protocol_subnet.c proxy.c proxy.h \
raw_socket_device.c route.c route.h subnet.c subnet.h tincd.c \
utils.c utils.h xalloc.h $(am__append_1) $(am__append_2) \
$(am__append_3) $(am__append_4) $(am__append_5) \
$(am__append_6) $(am__append_7) $(am__append_8) \
$(am__append_9)
AM_CPPFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DRUNSTATEDIR=\"$(runstatedir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -I $(abs_top_builddir)/
EXTRA_DIST = linux bsd solaris cygwin mingw raw_socket uml_socket openssl gcrypt
tincd_SOURCES = utils.c getopt.c getopt1.c list.c splay_tree.c \
dropin.c fake-getaddrinfo.c fake-getnameinfo.c buffer.c conf.c \
connection.c control.c edge.c graph.c logger.c meta.c net.c \
net_packet.c net_setup.c net_socket.c netutl.c node.c \
process.c protocol.c protocol_auth.c protocol_edge.c \
protocol_misc.c protocol_key.c protocol_subnet.c route.c \
subnet.c tincd.c $(am__append_1)
nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
tincctl_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \
list.c tincctl.c top.c
nodist_tincctl_SOURCES = \
ecdsagen.c rsagen.c
tincctl_LDADD = $(CURSES_LIBS)
DEFAULT_INCLUDES =
noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
buffer.h conf.h connection.h control.h control_common.h device.h edge.h graph.h logger.h meta.h net.h netutl.h node.h process.h \
protocol.h route.h subnet.h tincctl.h top.h bsd/tunemu.h
nodist_noinst_HEADERS = \
cipher.h crypto.h ecdh.h ecdsa.h digest.h prf.h rsa.h ecdsagen.h rsagen.h
AM_CFLAGS = -DCONFDIR=\"$(sysconfdir)\" -DLOCALSTATEDIR=\"$(localstatedir)\" -DSBINDIR=\"$(sbindir)\"
all: all-am
.SUFFIXES:
.SUFFIXES: .c .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
@ -371,37 +248,34 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-sbinPROGRAMS: $(sbin_PROGRAMS)
@$(NORMAL_INSTALL)
test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
while read p p1; do if test -f $$p; \
then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@ -422,164 +296,120 @@ uninstall-sbinPROGRAMS:
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
-e 's/$$/$(EXEEXT)/' `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(sbindir)" && rm -f $$files
clean-sbinPROGRAMS:
-test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
installcheck-sbinPROGRAMS: $(sbin_PROGRAMS)
bad=0; pid=$$$$; list="$(sbin_PROGRAMS)"; for p in $$list; do \
case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
*" $$p "* | *" $(srcdir)/$$p "*) continue;; \
esac; \
f=`echo "$$p" | \
sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
for opt in --help --version; do \
if "$(DESTDIR)$(sbindir)/$$f" $$opt >c$${pid}_.out \
2>c$${pid}_.err </dev/null \
&& test -n "`cat c$${pid}_.out`" \
&& test -z "`cat c$${pid}_.err`"; then :; \
else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
done; \
done; rm -f c$${pid}_.???; exit $$bad
linux/$(am__dirstamp):
@$(MKDIR_P) linux
@: > linux/$(am__dirstamp)
linux/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) linux/$(DEPDIR)
@: > linux/$(DEPDIR)/$(am__dirstamp)
linux/device.$(OBJEXT): linux/$(am__dirstamp) \
linux/$(DEPDIR)/$(am__dirstamp)
bsd/$(am__dirstamp):
@$(MKDIR_P) bsd
@: > bsd/$(am__dirstamp)
bsd/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) bsd/$(DEPDIR)
@: > bsd/$(DEPDIR)/$(am__dirstamp)
bsd/device.$(OBJEXT): bsd/$(am__dirstamp) \
bsd/$(DEPDIR)/$(am__dirstamp)
bsd/tunemu.$(OBJEXT): bsd/$(am__dirstamp) \
bsd/$(DEPDIR)/$(am__dirstamp)
solaris/$(am__dirstamp):
@$(MKDIR_P) solaris
@: > solaris/$(am__dirstamp)
solaris/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) solaris/$(DEPDIR)
@: > solaris/$(DEPDIR)/$(am__dirstamp)
solaris/device.$(OBJEXT): solaris/$(am__dirstamp) \
solaris/$(DEPDIR)/$(am__dirstamp)
mingw/$(am__dirstamp):
@$(MKDIR_P) mingw
@: > mingw/$(am__dirstamp)
mingw/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) mingw/$(DEPDIR)
@: > mingw/$(DEPDIR)/$(am__dirstamp)
mingw/device.$(OBJEXT): mingw/$(am__dirstamp) \
mingw/$(DEPDIR)/$(am__dirstamp)
cygwin/$(am__dirstamp):
@$(MKDIR_P) cygwin
@: > cygwin/$(am__dirstamp)
cygwin/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) cygwin/$(DEPDIR)
@: > cygwin/$(DEPDIR)/$(am__dirstamp)
cygwin/device.$(OBJEXT): cygwin/$(am__dirstamp) \
cygwin/$(DEPDIR)/$(am__dirstamp)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES) $(EXTRA_tincd_DEPENDENCIES)
tincctl$(EXEEXT): $(tincctl_OBJECTS) $(tincctl_DEPENDENCIES)
@rm -f tincctl$(EXEEXT)
$(LINK) $(tincctl_OBJECTS) $(tincctl_LDADD) $(LIBS)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES)
@rm -f tincd$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS)
$(LINK) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f bsd/*.$(OBJEXT)
-rm -f cygwin/*.$(OBJEXT)
-rm -f linux/*.$(OBJEXT)
-rm -f mingw/*.$(OBJEXT)
-rm -f solaris/*.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/avl_tree.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dropin.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getaddrinfo.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getnameinfo.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/graph.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meta.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multicast_device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_packet.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_setup.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_socket.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netutl.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pidfile.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_auth.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_edge.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_key.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_misc.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_subnet.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw_socket_device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnet.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tincd.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uml_device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vde_device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@bsd/$(DEPDIR)/device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@bsd/$(DEPDIR)/tunemu.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@cygwin/$(DEPDIR)/device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@linux/$(DEPDIR)/device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@mingw/$(DEPDIR)/device.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@solaris/$(DEPDIR)/device.Po@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@echo '# dummy' >$@-t && $(am__mv) $@-t $@
am--depfiles: $(am__depfiles_remade)
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cipher.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/digest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dropin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdh.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecdsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getaddrinfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fake-getnameinfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/graph.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meta.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_packet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_setup.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net_socket.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netutl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/process.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_auth.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_edge.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_key.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_misc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol_subnet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsagen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splay_tree.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subnet.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tincctl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tincd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/top.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tunemu.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
@am__fastdepCC_FALSE@ $(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tunemu.o: bsd/tunemu.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tunemu.o -MD -MP -MF $(DEPDIR)/tunemu.Tpo -c -o tunemu.o `test -f 'bsd/tunemu.c' || echo '$(srcdir)/'`bsd/tunemu.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tunemu.Tpo $(DEPDIR)/tunemu.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bsd/tunemu.c' object='tunemu.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tunemu.o `test -f 'bsd/tunemu.c' || echo '$(srcdir)/'`bsd/tunemu.c
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
tunemu.obj: bsd/tunemu.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tunemu.obj -MD -MP -MF $(DEPDIR)/tunemu.Tpo -c -o tunemu.obj `if test -f 'bsd/tunemu.c'; then $(CYGPATH_W) 'bsd/tunemu.c'; else $(CYGPATH_W) '$(srcdir)/bsd/tunemu.c'; fi`
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tunemu.Tpo $(DEPDIR)/tunemu.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bsd/tunemu.c' object='tunemu.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tunemu.obj `if test -f 'bsd/tunemu.c'; then $(CYGPATH_W) 'bsd/tunemu.c'; else $(CYGPATH_W) '$(srcdir)/bsd/tunemu.c'; fi`
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
mkid -fID $$unique
tags: TAGS
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@ -591,11 +421,15 @@ tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in files) print i; }; }'`; \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@ -604,29 +438,11 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@ -656,9 +472,12 @@ distdir-am: $(DISTFILES)
|| exit 1; \
fi; \
done
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$(top_distdir)" distdir="$(distdir)" \
dist-hook
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
all-am: Makefile $(PROGRAMS) $(HEADERS)
installdirs:
for dir in "$(DESTDIR)$(sbindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
@ -673,15 +492,10 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
@ -689,16 +503,6 @@ clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-rm -f bsd/$(DEPDIR)/$(am__dirstamp)
-rm -f bsd/$(am__dirstamp)
-rm -f cygwin/$(DEPDIR)/$(am__dirstamp)
-rm -f cygwin/$(am__dirstamp)
-rm -f linux/$(DEPDIR)/$(am__dirstamp)
-rm -f linux/$(am__dirstamp)
-rm -f mingw/$(DEPDIR)/$(am__dirstamp)
-rm -f mingw/$(am__dirstamp)
-rm -f solaris/$(DEPDIR)/$(am__dirstamp)
-rm -f solaris/$(am__dirstamp)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@ -708,50 +512,7 @@ clean: clean-am
clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
distclean: distclean-am
-rm -f ./$(DEPDIR)/avl_tree.Po
-rm -f ./$(DEPDIR)/conf.Po
-rm -f ./$(DEPDIR)/connection.Po
-rm -f ./$(DEPDIR)/dropin.Po
-rm -f ./$(DEPDIR)/dummy_device.Po
-rm -f ./$(DEPDIR)/edge.Po
-rm -f ./$(DEPDIR)/event.Po
-rm -f ./$(DEPDIR)/fake-getaddrinfo.Po
-rm -f ./$(DEPDIR)/fake-getnameinfo.Po
-rm -f ./$(DEPDIR)/getopt.Po
-rm -f ./$(DEPDIR)/getopt1.Po
-rm -f ./$(DEPDIR)/graph.Po
-rm -f ./$(DEPDIR)/list.Po
-rm -f ./$(DEPDIR)/logger.Po
-rm -f ./$(DEPDIR)/meta.Po
-rm -f ./$(DEPDIR)/multicast_device.Po
-rm -f ./$(DEPDIR)/net.Po
-rm -f ./$(DEPDIR)/net_packet.Po
-rm -f ./$(DEPDIR)/net_setup.Po
-rm -f ./$(DEPDIR)/net_socket.Po
-rm -f ./$(DEPDIR)/netutl.Po
-rm -f ./$(DEPDIR)/node.Po
-rm -f ./$(DEPDIR)/pidfile.Po
-rm -f ./$(DEPDIR)/process.Po
-rm -f ./$(DEPDIR)/protocol.Po
-rm -f ./$(DEPDIR)/protocol_auth.Po
-rm -f ./$(DEPDIR)/protocol_edge.Po
-rm -f ./$(DEPDIR)/protocol_key.Po
-rm -f ./$(DEPDIR)/protocol_misc.Po
-rm -f ./$(DEPDIR)/protocol_subnet.Po
-rm -f ./$(DEPDIR)/proxy.Po
-rm -f ./$(DEPDIR)/raw_socket_device.Po
-rm -f ./$(DEPDIR)/route.Po
-rm -f ./$(DEPDIR)/subnet.Po
-rm -f ./$(DEPDIR)/tincd.Po
-rm -f ./$(DEPDIR)/uml_device.Po
-rm -f ./$(DEPDIR)/utils.Po
-rm -f ./$(DEPDIR)/vde_device.Po
-rm -f bsd/$(DEPDIR)/device.Po
-rm -f bsd/$(DEPDIR)/tunemu.Po
-rm -f cygwin/$(DEPDIR)/device.Po
-rm -f linux/$(DEPDIR)/device.Po
-rm -f mingw/$(DEPDIR)/device.Po
-rm -f solaris/$(DEPDIR)/device.Po
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@ -794,53 +555,10 @@ install-ps: install-ps-am
install-ps-am:
installcheck-am: installcheck-sbinPROGRAMS
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/avl_tree.Po
-rm -f ./$(DEPDIR)/conf.Po
-rm -f ./$(DEPDIR)/connection.Po
-rm -f ./$(DEPDIR)/dropin.Po
-rm -f ./$(DEPDIR)/dummy_device.Po
-rm -f ./$(DEPDIR)/edge.Po
-rm -f ./$(DEPDIR)/event.Po
-rm -f ./$(DEPDIR)/fake-getaddrinfo.Po
-rm -f ./$(DEPDIR)/fake-getnameinfo.Po
-rm -f ./$(DEPDIR)/getopt.Po
-rm -f ./$(DEPDIR)/getopt1.Po
-rm -f ./$(DEPDIR)/graph.Po
-rm -f ./$(DEPDIR)/list.Po
-rm -f ./$(DEPDIR)/logger.Po
-rm -f ./$(DEPDIR)/meta.Po
-rm -f ./$(DEPDIR)/multicast_device.Po
-rm -f ./$(DEPDIR)/net.Po
-rm -f ./$(DEPDIR)/net_packet.Po
-rm -f ./$(DEPDIR)/net_setup.Po
-rm -f ./$(DEPDIR)/net_socket.Po
-rm -f ./$(DEPDIR)/netutl.Po
-rm -f ./$(DEPDIR)/node.Po
-rm -f ./$(DEPDIR)/pidfile.Po
-rm -f ./$(DEPDIR)/process.Po
-rm -f ./$(DEPDIR)/protocol.Po
-rm -f ./$(DEPDIR)/protocol_auth.Po
-rm -f ./$(DEPDIR)/protocol_edge.Po
-rm -f ./$(DEPDIR)/protocol_key.Po
-rm -f ./$(DEPDIR)/protocol_misc.Po
-rm -f ./$(DEPDIR)/protocol_subnet.Po
-rm -f ./$(DEPDIR)/proxy.Po
-rm -f ./$(DEPDIR)/raw_socket_device.Po
-rm -f ./$(DEPDIR)/route.Po
-rm -f ./$(DEPDIR)/subnet.Po
-rm -f ./$(DEPDIR)/tincd.Po
-rm -f ./$(DEPDIR)/uml_device.Po
-rm -f ./$(DEPDIR)/utils.Po
-rm -f ./$(DEPDIR)/vde_device.Po
-rm -f bsd/$(DEPDIR)/device.Po
-rm -f bsd/$(DEPDIR)/tunemu.Po
-rm -f cygwin/$(DEPDIR)/device.Po
-rm -f linux/$(DEPDIR)/device.Po
-rm -f mingw/$(DEPDIR)/device.Po
-rm -f solaris/$(DEPDIR)/device.Po
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@ -860,22 +578,22 @@ uninstall-am: uninstall-sbinPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-generic clean-sbinPROGRAMS cscopelist-am ctags ctags-am \
distclean distclean-compile distclean-generic distclean-tags \
distdir dvi dvi-am html html-am info info-am install \
install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-sbinPROGRAMS install-strip installcheck \
installcheck-am installcheck-sbinPROGRAMS installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
clean-sbinPROGRAMS ctags dist-hook distclean distclean-compile \
distclean-generic distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-sbinPROGRAMS install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
uninstall-am uninstall-sbinPROGRAMS
.PRECIOUS: Makefile
dist-hook:
rm -f `find . -type l`
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.

View file

@ -1,757 +0,0 @@
/*
avl_tree.c -- avl_ tree and linked list convenience
Copyright (C) 1998 Michael H. Buselli
2000-2005 Ivo Timmermans,
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
2000-2005 Wessel Dankers <wsl@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> to use counts
instead of depths, to add the ->next and ->prev and to generally obfuscate
the code. Mail me if you found a bug.
Cleaned up and incorporated some of the ideas from the red-black tree
library for inclusion into tinc (https://www.tinc-vpn.org/) by
Guus Sliepen <guus@tinc-vpn.org>.
*/
#include "system.h"
#include "avl_tree.h"
#include "xalloc.h"
#ifdef AVL_COUNT
#define AVL_NODE_COUNT(n) ((n) ? (n)->count : 0)
#define AVL_L_COUNT(n) (AVL_NODE_COUNT((n)->left))
#define AVL_R_COUNT(n) (AVL_NODE_COUNT((n)->right))
#define AVL_CALC_COUNT(n) (AVL_L_COUNT(n) + AVL_R_COUNT(n) + 1)
#endif
#ifdef AVL_DEPTH
#define AVL_NODE_DEPTH(n) ((n) ? (n)->depth : 0)
#define L_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->left))
#define R_AVL_DEPTH(n) (AVL_NODE_DEPTH((n)->right))
#define AVL_CALC_DEPTH(n) ((L_AVL_DEPTH(n)>R_AVL_DEPTH(n)?L_AVL_DEPTH(n):R_AVL_DEPTH(n)) + 1)
#endif
#ifndef AVL_DEPTH
static int lg(unsigned int u) __attribute__((__const__));
static int lg(unsigned int u) {
int r = 1;
if(!u) {
return 0;
}
if(u & 0xffff0000) {
u >>= 16;
r += 16;
}
if(u & 0x0000ff00) {
u >>= 8;
r += 8;
}
if(u & 0x000000f0) {
u >>= 4;
r += 4;
}
if(u & 0x0000000c) {
u >>= 2;
r += 2;
}
if(u & 0x00000002) {
r++;
}
return r;
}
#endif
/* Internal helper functions */
static int avl_check_balance(const avl_node_t *node) {
#ifdef AVL_DEPTH
int d;
d = R_AVL_DEPTH(node) - L_AVL_DEPTH(node);
return d < -1 ? -1 : d > 1 ? 1 : 0;
#else
/* int d;
* d = lg(AVL_R_COUNT(node)) - lg(AVL_L_COUNT(node));
* d = d<-1?-1:d>1?1:0;
*/
int pl, r;
pl = lg(AVL_L_COUNT(node));
r = AVL_R_COUNT(node);
if(r >> pl + 1) {
return 1;
}
if(pl < 2 || r >> pl - 2) {
return 0;
}
return -1;
#endif
}
static void avl_rebalance(avl_tree_t *tree, avl_node_t *node) {
avl_node_t *child;
avl_node_t *gchild;
avl_node_t *parent;
avl_node_t **superparent;
while(node) {
parent = node->parent;
superparent =
parent ? node ==
parent->left ? &parent->left : &parent->right : &tree->root;
switch(avl_check_balance(node)) {
case -1:
child = node->left;
#ifdef AVL_DEPTH
if(L_AVL_DEPTH(child) >= R_AVL_DEPTH(child)) {
#else
if(AVL_L_COUNT(child) >= AVL_R_COUNT(child)) {
#endif
node->left = child->right;
if(node->left) {
node->left->parent = node;
}
child->right = node;
node->parent = child;
*superparent = child;
child->parent = parent;
#ifdef AVL_COUNT
node->count = AVL_CALC_COUNT(node);
child->count = AVL_CALC_COUNT(child);
#endif
#ifdef AVL_DEPTH
node->depth = AVL_CALC_DEPTH(node);
child->depth = AVL_CALC_DEPTH(child);
#endif
} else {
gchild = child->right;
node->left = gchild->right;
if(node->left) {
node->left->parent = node;
}
child->right = gchild->left;
if(child->right) {
child->right->parent = child;
}
gchild->right = node;
gchild->right->parent = gchild;
gchild->left = child;
gchild->left->parent = gchild;
*superparent = gchild;
gchild->parent = parent;
#ifdef AVL_COUNT
node->count = AVL_CALC_COUNT(node);
child->count = AVL_CALC_COUNT(child);
gchild->count = AVL_CALC_COUNT(gchild);
#endif
#ifdef AVL_DEPTH
node->depth = AVL_CALC_DEPTH(node);
child->depth = AVL_CALC_DEPTH(child);
gchild->depth = AVL_CALC_DEPTH(gchild);
#endif
}
break;
case 1:
child = node->right;
#ifdef AVL_DEPTH
if(R_AVL_DEPTH(child) >= L_AVL_DEPTH(child)) {
#else
if(AVL_R_COUNT(child) >= AVL_L_COUNT(child)) {
#endif
node->right = child->left;
if(node->right) {
node->right->parent = node;
}
child->left = node;
node->parent = child;
*superparent = child;
child->parent = parent;
#ifdef AVL_COUNT
node->count = AVL_CALC_COUNT(node);
child->count = AVL_CALC_COUNT(child);
#endif
#ifdef AVL_DEPTH
node->depth = AVL_CALC_DEPTH(node);
child->depth = AVL_CALC_DEPTH(child);
#endif
} else {
gchild = child->left;
node->right = gchild->left;
if(node->right) {
node->right->parent = node;
}
child->left = gchild->right;
if(child->left) {
child->left->parent = child;
}
gchild->left = node;
gchild->left->parent = gchild;
gchild->right = child;
gchild->right->parent = gchild;
*superparent = gchild;
gchild->parent = parent;
#ifdef AVL_COUNT
node->count = AVL_CALC_COUNT(node);
child->count = AVL_CALC_COUNT(child);
gchild->count = AVL_CALC_COUNT(gchild);
#endif
#ifdef AVL_DEPTH
node->depth = AVL_CALC_DEPTH(node);
child->depth = AVL_CALC_DEPTH(child);
gchild->depth = AVL_CALC_DEPTH(gchild);
#endif
}
break;
default:
#ifdef AVL_COUNT
node->count = AVL_CALC_COUNT(node);
#endif
#ifdef AVL_DEPTH
node->depth = AVL_CALC_DEPTH(node);
#endif
}
node = parent;
}
}
/* (De)constructors */
avl_tree_t *avl_alloc_tree(avl_compare_t compare, avl_action_t delete) {
avl_tree_t *tree;
tree = xmalloc_and_zero(sizeof(avl_tree_t));
tree->compare = compare;
tree->delete = delete;
return tree;
}
void avl_free_tree(avl_tree_t *tree) {
free(tree);
}
avl_node_t *avl_alloc_node(void) {
return xmalloc_and_zero(sizeof(avl_node_t));
}
void avl_free_node(avl_tree_t *tree, avl_node_t *node) {
if(node->data && tree->delete) {
tree->delete(node->data);
}
free(node);
}
/* Searching */
void *avl_search(const avl_tree_t *tree, const void *data) {
avl_node_t *node;
node = avl_search_node(tree, data);
return node ? node->data : NULL;
}
void *avl_search_closest(const avl_tree_t *tree, const void *data, int *result) {
avl_node_t *node;
node = avl_search_closest_node(tree, data, result);
return node ? node->data : NULL;
}
void *avl_search_closest_smaller(const avl_tree_t *tree, const void *data) {
avl_node_t *node;
node = avl_search_closest_smaller_node(tree, data);
return node ? node->data : NULL;
}
void *avl_search_closest_greater(const avl_tree_t *tree, const void *data) {
avl_node_t *node;
node = avl_search_closest_greater_node(tree, data);
return node ? node->data : NULL;
}
avl_node_t *avl_search_node(const avl_tree_t *tree, const void *data) {
avl_node_t *node;
int result;
node = avl_search_closest_node(tree, data, &result);
return result ? NULL : node;
}
avl_node_t *avl_search_closest_node(const avl_tree_t *tree, const void *data,
int *result) {
avl_node_t *node;
int c;
node = tree->root;
if(!node) {
if(result) {
*result = 0;
}
return NULL;
}
for(;;) {
c = tree->compare(data, node->data);
if(c < 0) {
if(node->left) {
node = node->left;
} else {
if(result) {
*result = -1;
}
break;
}
} else if(c > 0) {
if(node->right) {
node = node->right;
} else {
if(result) {
*result = 1;
}
break;
}
} else {
if(result) {
*result = 0;
}
break;
}
}
return node;
}
avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *tree,
const void *data) {
avl_node_t *node;
int result;
node = avl_search_closest_node(tree, data, &result);
if(result < 0) {
node = node->prev;
}
return node;
}
avl_node_t *avl_search_closest_greater_node(const avl_tree_t *tree,
const void *data) {
avl_node_t *node;
int result;
node = avl_search_closest_node(tree, data, &result);
if(result > 0) {
node = node->next;
}
return node;
}
/* Insertion and deletion */
avl_node_t *avl_insert(avl_tree_t *tree, void *data) {
avl_node_t *closest, *new;
int result;
if(!tree->root) {
new = avl_alloc_node();
new->data = data;
avl_insert_top(tree, new);
} else {
closest = avl_search_closest_node(tree, data, &result);
switch(result) {
case -1:
new = avl_alloc_node();
new->data = data;
avl_insert_before(tree, closest, new);
break;
case 1:
new = avl_alloc_node();
new->data = data;
avl_insert_after(tree, closest, new);
break;
default:
return NULL;
}
}
#ifdef AVL_COUNT
new->count = 1;
#endif
#ifdef AVL_DEPTH
new->depth = 1;
#endif
return new;
}
avl_node_t *avl_insert_node(avl_tree_t *tree, avl_node_t *node) {
avl_node_t *closest;
int result;
if(!tree->root) {
avl_insert_top(tree, node);
} else {
closest = avl_search_closest_node(tree, node->data, &result);
switch(result) {
case -1:
avl_insert_before(tree, closest, node);
break;
case 1:
avl_insert_after(tree, closest, node);
break;
case 0:
return NULL;
}
}
#ifdef AVL_COUNT
node->count = 1;
#endif
#ifdef AVL_DEPTH
node->depth = 1;
#endif
return node;
}
void avl_insert_top(avl_tree_t *tree, avl_node_t *node) {
node->prev = node->next = node->parent = NULL;
tree->head = tree->tail = tree->root = node;
}
void avl_insert_before(avl_tree_t *tree, avl_node_t *before,
avl_node_t *node) {
if(!before) {
if(tree->tail) {
avl_insert_after(tree, tree->tail, node);
} else {
avl_insert_top(tree, node);
}
return;
}
node->next = before;
node->parent = before;
node->prev = before->prev;
if(before->left) {
avl_insert_after(tree, before->prev, node);
return;
}
if(before->prev) {
before->prev->next = node;
} else {
tree->head = node;
}
before->prev = node;
before->left = node;
avl_rebalance(tree, before);
}
void avl_insert_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node) {
if(!after) {
if(tree->head) {
avl_insert_before(tree, tree->head, node);
} else {
avl_insert_top(tree, node);
}
return;
}
if(after->right) {
avl_insert_before(tree, after->next, node);
return;
}
node->prev = after;
node->parent = after;
node->next = after->next;
if(after->next) {
after->next->prev = node;
} else {
tree->tail = node;
}
after->next = node;
after->right = node;
avl_rebalance(tree, after);
}
avl_node_t *avl_unlink(avl_tree_t *tree, void *data) {
avl_node_t *node;
node = avl_search_node(tree, data);
if(node) {
avl_unlink_node(tree, node);
}
return node;
}
void avl_unlink_node(avl_tree_t *tree, avl_node_t *node) {
avl_node_t *parent;
avl_node_t **superparent;
avl_node_t *subst, *left, *right;
avl_node_t *balnode;
if(node->prev) {
node->prev->next = node->next;
} else {
tree->head = node->next;
}
if(node->next) {
node->next->prev = node->prev;
} else {
tree->tail = node->prev;
}
parent = node->parent;
superparent =
parent ? node ==
parent->left ? &parent->left : &parent->right : &tree->root;
left = node->left;
right = node->right;
if(!left) {
*superparent = right;
if(right) {
right->parent = parent;
}
balnode = parent;
} else if(!right) {
*superparent = left;
left->parent = parent;
balnode = parent;
} else {
subst = node->prev;
if(!subst) { // This only happens if node is not actually in a tree at all.
abort();
}
if(subst == left) {
balnode = subst;
} else {
balnode = subst->parent;
balnode->right = subst->left;
if(balnode->right) {
balnode->right->parent = balnode;
}
subst->left = left;
left->parent = subst;
}
subst->right = right;
subst->parent = parent;
right->parent = subst;
*superparent = subst;
}
avl_rebalance(tree, balnode);
node->next = node->prev = node->parent = node->left = node->right = NULL;
#ifdef AVL_COUNT
node->count = 0;
#endif
#ifdef AVL_DEPTH
node->depth = 0;
#endif
}
void avl_delete_node(avl_tree_t *tree, avl_node_t *node) {
avl_unlink_node(tree, node);
avl_free_node(tree, node);
}
void avl_delete(avl_tree_t *tree, void *data) {
avl_node_t *node;
node = avl_search_node(tree, data);
if(node) {
avl_delete_node(tree, node);
}
}
/* Fast tree cleanup */
void avl_delete_tree(avl_tree_t *tree) {
avl_node_t *node, *next;
for(node = tree->head; node; node = next) {
next = node->next;
avl_free_node(tree, node);
}
avl_free_tree(tree);
}
/* Tree walking */
void avl_foreach(const avl_tree_t *tree, avl_action_t action) {
avl_node_t *node, *next;
for(node = tree->head; node; node = next) {
next = node->next;
action(node->data);
}
}
void avl_foreach_node(const avl_tree_t *tree, avl_action_t action) {
avl_node_t *node, *next;
for(node = tree->head; node; node = next) {
next = node->next;
action(node);
}
}
/* Indexing */
#ifdef AVL_COUNT
unsigned int avl_count(const avl_tree_t *tree) {
return AVL_NODE_COUNT(tree->root);
}
avl_node_t *avl_get_node(const avl_tree_t *tree, unsigned int index) {
avl_node_t *node;
unsigned int c;
node = tree->root;
while(node) {
c = AVL_L_COUNT(node);
if(index < c) {
node = node->left;
} else if(index > c) {
node = node->right;
index -= c + 1;
} else {
return node;
}
}
return NULL;
}
unsigned int avl_index(const avl_node_t *node) {
avl_node_t *next;
unsigned int index;
index = AVL_L_COUNT(node);
while((next = node->parent)) {
if(node == next->right) {
index += AVL_L_COUNT(next) + 1;
}
node = next;
}
return index;
}
#endif
#ifdef AVL_DEPTH
unsigned int avl_depth(const avl_tree_t *tree) {
return AVL_NODE_DEPTH(tree->root);
}
#endif

View file

@ -1,142 +0,0 @@
#ifndef TINC_AVL_TREE_H
#define TINC_AVL_TREE_H
/*
avl_tree.h -- header file for avl_tree.c
Copyright (C) 1998 Michael H. Buselli
2000-2005 Ivo Timmermans,
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
2000-2005 Wessel Dankers <wsl@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Original AVL tree library by Michael H. Buselli <cosine@cosine.org>.
Modified 2000-11-28 by Wessel Dankers <wsl@tinc-vpn.org> to use counts
instead of depths, to add the ->next and ->prev and to generally obfuscate
the code. Mail me if you found a bug.
Cleaned up and incorporated some of the ideas from the red-black tree
library for inclusion into tinc (https://www.tinc-vpn.org/) by
Guus Sliepen <guus@tinc-vpn.org>.
*/
#ifndef AVL_DEPTH
#ifndef AVL_COUNT
#define AVL_DEPTH
#endif
#endif
typedef struct avl_node_t {
/* Linked list part */
struct avl_node_t *next;
struct avl_node_t *prev;
/* Tree part */
struct avl_node_t *parent;
struct avl_node_t *left;
struct avl_node_t *right;
#ifdef AVL_COUNT
unsigned int count;
#endif
#ifdef AVL_DEPTH
unsigned char depth;
#endif
/* Payload */
void *data;
} avl_node_t;
typedef int (*avl_compare_t)(const void *data1, const void *data2);
typedef void (*avl_action_t)(const void *data);
typedef void (*avl_action_node_t)(const avl_node_t *node);
typedef struct avl_tree_t {
/* Linked list part */
avl_node_t *head;
avl_node_t *tail;
/* Tree part */
avl_node_t *root;
avl_compare_t compare;
avl_action_t delete;
} avl_tree_t;
/* (De)constructors */
extern avl_tree_t *avl_alloc_tree(avl_compare_t compare, avl_action_t delete);
extern void avl_free_tree(avl_tree_t *tree);
extern avl_node_t *avl_alloc_node(void);
extern void avl_free_node(avl_tree_t *tree, avl_node_t *node);
/* Insertion and deletion */
extern avl_node_t *avl_insert(avl_tree_t *tree, void *data);
extern avl_node_t *avl_insert_node(avl_tree_t *tree, avl_node_t *node);
extern void avl_insert_top(avl_tree_t *tree, avl_node_t *node);
extern void avl_insert_before(avl_tree_t *tree, avl_node_t *before, avl_node_t *node);
extern void avl_insert_after(avl_tree_t *tree, avl_node_t *after, avl_node_t *node);
extern avl_node_t *avl_unlink(avl_tree_t *tree, void *data);
extern void avl_unlink_node(avl_tree_t *tree, avl_node_t *node);
extern void avl_delete(avl_tree_t *tree, void *data);
extern void avl_delete_node(avl_tree_t *tree, avl_node_t *node);
/* Fast tree cleanup */
extern void avl_delete_tree(avl_tree_t *tree);
/* Searching */
extern void *avl_search(const avl_tree_t *tree, const void *data);
extern void *avl_search_closest(const avl_tree_t *tree, const void *data, int *result);
extern void *avl_search_closest_smaller(const avl_tree_t *tree, const void *data);
extern void *avl_search_closest_greater(const avl_tree_t *tree, const void *data);
extern avl_node_t *avl_search_node(const avl_tree_t *tree, const void *data);
extern avl_node_t *avl_search_closest_node(const avl_tree_t *tree, const void *data, int *result);
extern avl_node_t *avl_search_closest_smaller_node(const avl_tree_t *tree, const void *data);
extern avl_node_t *avl_search_closest_greater_node(const avl_tree_t *tree, const void *data);
/* Tree walking */
extern void avl_foreach(const avl_tree_t *tree, avl_action_t action);
extern void avl_foreach_node(const avl_tree_t *tree, avl_action_t action);
/* Indexing */
#ifdef AVL_COUNT
extern unsigned int avl_count(const avl_tree_t *tree);
extern avl_node_t *avl_get_node(const avl_tree_t *tree, unsigned int index);
extern unsigned int avl_index(const avl_node_t *node);
#endif
#ifdef AVL_DEPTH
extern unsigned int avl_depth(const avl_tree_t *tree);
#endif
#endif

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction BSD tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2016 Guus Sliepen <guus@tinc-vpn.org>
2001-2011 Guus Sliepen <guus@tinc-vpn.org>
2009 Grzegorz Dymarek <gregd72002@googlemail.com>
This program is free software; you can redistribute it and/or modify
@ -19,46 +19,38 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../system.h"
#include "system.h"
#include "../conf.h"
#include "../device.h"
#include "../logger.h"
#include "../net.h"
#include "../route.h"
#include "../utils.h"
#include "../xalloc.h"
#include "conf.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#ifdef ENABLE_TUNEMU
#include "tunemu.h"
#ifdef HAVE_TUNEMU
#include "bsd/tunemu.h"
#endif
#ifdef HAVE_NET_IF_UTUN_H
#include <sys/sys_domain.h>
#include <sys/kern_control.h>
#include <net/if_utun.h>
#endif
#define DEFAULT_TUN_DEVICE "/dev/tun0"
#define DEFAULT_TAP_DEVICE "/dev/tap0"
#define DEFAULT_DEVICE "/dev/tun0"
typedef enum device_type {
DEVICE_TYPE_TUN,
DEVICE_TYPE_TUNIFHEAD,
DEVICE_TYPE_TAP,
#ifdef ENABLE_TUNEMU
#ifdef HAVE_TUNEMU
DEVICE_TYPE_TUNEMU,
#endif
DEVICE_TYPE_UTUN,
} device_type_t;
int device_fd = -1;
char *device = NULL;
char *iface = NULL;
static const char *device_info = "OS X utun device";
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
#if defined(ENABLE_TUNEMU)
#if defined(TUNEMU)
static device_type_t device_type = DEVICE_TYPE_TUNEMU;
#elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) || defined(HAVE_DRAGONFLY)
static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD;
@ -66,140 +58,47 @@ static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD;
static device_type_t device_type = DEVICE_TYPE_TUN;
#endif
#ifdef HAVE_NET_IF_UTUN_H
static bool setup_utun(void) {
device_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if(device_fd == -1) {
logger(LOG_ERR, "Could not open PF_SYSTEM socket: %s\n", strerror(errno));
return false;
}
struct ctl_info info = {};
strlcpy(info.ctl_name, UTUN_CONTROL_NAME, sizeof(info.ctl_name));
if(ioctl(device_fd, CTLIOCGINFO, &info) == -1) {
logger(LOG_ERR, "ioctl(CTLIOCGINFO) failed: %s", strerror(errno));
return false;
}
int unit = -1;
char *p = strstr(device, "utun"), *e = NULL;
if(p) {
unit = strtol(p + 4, &e, 10);
if(!e) {
unit = -1;
}
}
struct sockaddr_ctl sc = {
.sc_id = info.ctl_id,
.sc_len = sizeof(sc),
.sc_family = AF_SYSTEM,
.ss_sysaddr = AF_SYS_CONTROL,
.sc_unit = unit + 1,
};
if(connect(device_fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
logger(LOG_ERR, "Could not connect utun socket: %s\n", strerror(errno));
return false;
}
char name[64] = "";
socklen_t len = sizeof(name);
if(getsockopt(device_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &len)) {
iface = xstrdup(device);
} else {
iface = xstrdup(name);
}
logger(LOG_INFO, "%s is a %s", device, device_info);
return true;
}
#endif
static bool setup_device(void) {
// Find out which device file to open
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);
}
}
// Find out if it's supposed to be a tun or a tap device
bool setup_device(void) {
char *type;
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
if(!strcasecmp(type, "tun"))
/* use default */;
#ifdef ENABLE_TUNEMU
else if(!strcasecmp(type, "tunemu")) {
/* use default */;
#ifdef HAVE_TUNEMU
else if(!strcasecmp(type, "tunemu"))
device_type = DEVICE_TYPE_TUNEMU;
}
#endif
#ifdef HAVE_NET_IF_UTUN_H
else if(!strcasecmp(type, "utun")) {
device_type = DEVICE_TYPE_UTUN;
}
#endif
else if(!strcasecmp(type, "tunnohead")) {
else if(!strcasecmp(type, "tunnohead"))
device_type = DEVICE_TYPE_TUN;
} else if(!strcasecmp(type, "tunifhead")) {
else if(!strcasecmp(type, "tunifhead"))
device_type = DEVICE_TYPE_TUNIFHEAD;
} else if(!strcasecmp(type, "tap")) {
else if(!strcasecmp(type, "tap"))
device_type = DEVICE_TYPE_TAP;
} else {
else {
logger(LOG_ERR, "Unknown device type %s!", type);
return false;
}
} else {
#ifdef HAVE_NET_IF_UTUN_H
if(strncmp(device, "utun", 4) == 0 || strncmp(device, "/dev/utun", 9) == 0) {
device_type = DEVICE_TYPE_UTUN;
} else
#endif
if(strstr(device, "tap") || routing_mode != RMODE_ROUTER) {
device_type = DEVICE_TYPE_TAP;
}
if(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
device_type = DEVICE_TYPE_TAP;
}
if(routing_mode == RMODE_SWITCH && device_type != DEVICE_TYPE_TAP) {
logger(LOG_ERR, "Only tap devices support switch mode!");
return false;
}
// Open the device
switch(device_type) {
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU: {
char dynamic_name[256] = "";
device_fd = tunemu_open(dynamic_name);
}
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU: {
char dynamic_name[256] = "";
device_fd = tunemu_open(dynamic_name);
}
break;
#endif
#ifdef HAVE_NET_IF_UTUN_H
case DEVICE_TYPE_UTUN:
return setup_utun();
#endif
default:
device_fd = open(device, O_RDWR | O_NONBLOCK);
default:
device_fd = open(device, O_RDWR | O_NONBLOCK);
}
if(device_fd < 0) {
@ -207,307 +106,244 @@ static bool setup_device(void) {
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
// Guess what the corresponding interface is called
char *realname = NULL;
#if defined(HAVE_FDEVNAME)
realname = fdevname(device_fd);
#elif defined(HAVE_DEVNAME)
struct stat buf;
if(!fstat(device_fd, &buf)) {
realname = devname(buf.st_rdev, S_IFCHR);
}
#endif
if(!realname) {
realname = device;
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) {
iface = xstrdup(strrchr(realname, '/') ? strrchr(realname, '/') + 1 : realname);
} else if(strcmp(iface, strrchr(realname, '/') ? strrchr(realname, '/') + 1 : realname)) {
logger(LOG_WARNING, "Warning: Interface does not match Device. $INTERFACE might be set incorrectly.");
}
// Configure the device as best as we can
switch(device_type) {
default:
device_type = DEVICE_TYPE_TUN;
case DEVICE_TYPE_TUN:
default:
device_type = DEVICE_TYPE_TUN;
case DEVICE_TYPE_TUN:
#ifdef TUNSIFHEAD
{
{
const int zero = 0;
if(ioctl(device_fd, TUNSIFHEAD, &zero, sizeof(zero)) == -1) {
if(ioctl(device_fd, TUNSIFHEAD, &zero, sizeof zero) == -1) {
logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
return false;
}
}
#endif
#if defined(TUNSIFMODE) && defined(IFF_BROADCAST) && defined(IFF_MULTICAST)
{
const int mode = IFF_BROADCAST | IFF_MULTICAST;
ioctl(device_fd, TUNSIFMODE, &mode, sizeof(mode));
ioctl(device_fd, TUNSIFMODE, &mode, sizeof mode);
}
#endif
device_info = "Generic BSD tun device";
break;
case DEVICE_TYPE_TUNIFHEAD:
device_info = "Generic BSD tun device";
break;
case DEVICE_TYPE_TUNIFHEAD:
#ifdef TUNSIFHEAD
{
const int one = 1;
if(ioctl(device_fd, TUNSIFHEAD, &one, sizeof(one)) == -1) {
if(ioctl(device_fd, TUNSIFHEAD, &one, sizeof one) == -1) {
logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno));
return false;
}
}
#endif
#if defined(TUNSIFMODE) && defined(IFF_BROADCAST) && defined(IFF_MULTICAST)
{
const int mode = IFF_BROADCAST | IFF_MULTICAST;
ioctl(device_fd, TUNSIFMODE, &mode, sizeof(mode));
const int mode = IFF_BROADCAST | IFF_MULTICAST;
ioctl(device_fd, TUNSIFMODE, &mode, sizeof mode);
}
#endif
device_info = "Generic BSD tun device";
break;
case DEVICE_TYPE_TAP:
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = true;
}
device_info = "Generic BSD tap device";
device_info = "Generic BSD tun device";
break;
case DEVICE_TYPE_TAP:
if(routing_mode == RMODE_ROUTER)
overwrite_mac = true;
device_info = "Generic BSD tap device";
#ifdef TAPGIFNAME
{
struct ifreq ifr;
if(ioctl(device_fd, TAPGIFNAME, (void *)&ifr) == 0) {
if(iface) {
free(iface);
{
struct ifreq ifr;
if(ioctl(device_fd, TAPGIFNAME, (void*)&ifr) == 0) {
if(iface)
free(iface);
iface = xstrdup(ifr.ifr_name);
}
iface = xstrdup(ifr.ifr_name);
}
}
#endif
break;
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
device_info = "BSD tunemu device";
break;
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
device_info = "BSD tunemu device";
break;
#endif
}
#ifdef SIOCGIFADDR
if(overwrite_mac) {
ioctl(device_fd, SIOCGIFADDR, mymac.x);
}
#endif
logger(LOG_INFO, "%s is a %s", device, device_info);
return true;
}
static void close_device(void) {
void close_device(void) {
switch(device_type) {
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
tunemu_close(device_fd);
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
tunemu_close(device_fd);
break;
#endif
default:
close(device_fd);
default:
close(device_fd);
}
free(device);
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
bool read_packet(vpn_packet_t *packet) {
int inlen;
switch(device_type) {
case DEVICE_TYPE_TUN:
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(device_type == DEVICE_TYPE_TUNEMU) {
lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14);
} else
case DEVICE_TYPE_TUN:
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(device_type == DEVICE_TYPE_TUNEMU)
inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
else
#endif
lenin = read(device_fd, packet->data + 14, MTU - 14);
inlen = read(device_fd, packet->data + 14, MTU - 14);
if(lenin <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
switch(packet->data[14] >> 4) {
case 4:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
switch(packet->data[14] >> 4) {
case 4:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
break;
case 6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
packet->len = inlen + 14;
break;
case 6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
case DEVICE_TYPE_TUNIFHEAD: {
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}};
if((inlen = readv(device_fd, vector, 2)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
switch (ntohl(type)) {
case AF_INET:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
break;
case AF_INET6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown address family %x while reading packet from %s %s",
ntohl(type), device_info, device);
return false;
}
packet->len = inlen + 10;
break;
}
case DEVICE_TYPE_TAP:
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
packet->len = inlen;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
memset(packet->data, 0, 12);
packet->len = lenin + 14;
break;
case DEVICE_TYPE_UTUN:
case DEVICE_TYPE_TUNIFHEAD: {
if((lenin = read(device_fd, packet->data + 10, MTU - 10)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
switch(packet->data[14] >> 4) {
case 4:
packet->data[12] = 0x08;
packet->data[13] = 0x00;
break;
case 6:
packet->data[12] = 0x86;
packet->data[13] = 0xDD;
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown IP version %d while reading packet from %s %s",
packet->data[14] >> 4, device_info, device);
return false;
}
memset(packet->data, 0, 12);
packet->len = lenin + 10;
break;
}
case DEVICE_TYPE_TAP:
if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
packet->len = lenin;
break;
default:
return false;
}
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s",
packet->len, device_info);
packet->len, device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
packet->len, device_info);
switch(device_type) {
case DEVICE_TYPE_TUN:
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
case DEVICE_TYPE_UTUN:
case DEVICE_TYPE_TUNIFHEAD: {
int af = (packet->data[12] << 8) + packet->data[13];
uint32_t type;
switch(af) {
case 0x0800:
type = htonl(AF_INET);
case DEVICE_TYPE_TUN:
if(write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
case 0x86DD:
type = htonl(AF_INET6);
case DEVICE_TYPE_TUNIFHEAD: {
u_int32_t type;
struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}};
int af;
af = (packet->data[12] << 8) + packet->data[13];
switch (af) {
case 0x0800:
type = htonl(AF_INET);
break;
case 0x86DD:
type = htonl(AF_INET6);
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown address family %x while writing packet to %s %s",
af, device_info, device);
return false;
}
if(writev(device_fd, vector, 2) < 0) {
logger(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) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
default:
ifdebug(TRAFFIC) logger(LOG_ERR,
"Unknown address family %x while writing packet to %s %s",
af, device_info, device);
return false;
}
memcpy(packet->data + 10, &type, sizeof(type));
if(write(device_fd, packet->data + 10, packet->len - 10) < 0) {
logger(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) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
#ifdef ENABLE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
#ifdef HAVE_TUNEMU
case DEVICE_TYPE_TUNEMU:
if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info,
device, strerror(errno));
return false;
}
break;
#endif
default:
return false;
default:
return false;
}
device_total_out += packet->len;
@ -515,16 +351,8 @@ static bool write_packet(vpn_packet_t *packet) {
return true;
}
static void dump_device_stats(void) {
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,20 +1,20 @@
/*
* tunemu - Tun device emulation for Darwin
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
*
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "tunemu.h"
@ -36,34 +36,37 @@
#define PPPPROTO_CTL 1
#define PPP_IP 0x21
#define PPP_IPV6 0x57
#define PPP_IP 0x21
#define PPP_IPV6 0x57
#define SC_LOOP_TRAFFIC 0x00000200
#define PPPIOCNEWUNIT _IOWR('t', 62, int)
#define PPPIOCSFLAGS _IOW('t', 89, int)
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
#define PPPIOCATTCHAN _IOW('t', 56, int)
#define PPPIOCGCHAN _IOR('t', 55, int)
#define PPPIOCCONNECT _IOW('t', 58, int)
#define PPPIOCGUNIT _IOR('t', 86, int)
#define PPPIOCNEWUNIT _IOWR('t', 62, int)
#define PPPIOCSFLAGS _IOW('t', 89, int)
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
#define PPPIOCATTCHAN _IOW('t', 56, int)
#define PPPIOCGCHAN _IOR('t', 55, int)
#define PPPIOCCONNECT _IOW('t', 58, int)
#define PPPIOCGUNIT _IOR('t', 86, int)
struct sockaddr_ppp {
struct sockaddr_ppp
{
u_int8_t ppp_len;
u_int8_t ppp_family;
u_int16_t ppp_proto;
u_int32_t ppp_cookie;
};
enum NPmode {
enum NPmode
{
NPMODE_PASS,
NPMODE_DROP,
NPMODE_ERROR,
NPMODE_QUEUE
NPMODE_DROP,
NPMODE_ERROR,
NPMODE_QUEUE
};
struct npioctl {
struct npioctl
{
int protocol;
enum NPmode mode;
};
@ -80,55 +83,58 @@ static pcap_t *pcap = NULL;
static int data_buffer_length = 0;
static char *data_buffer = NULL;
static void tun_error(char *format, ...) {
static void tun_error(char *format, ...)
{
va_list vl;
va_start(vl, format);
vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
va_end(vl);
}
static void tun_noerror() {
static void tun_noerror()
{
*tunemu_error = 0;
}
static void closeall() {
int fd = getdtablesize();
while(fd--) {
static void closeall()
{
int fd = getdtablesize();
while (fd--)
close(fd);
}
open("/dev/null", O_RDWR, 0);
dup(0);
dup(0);
open("/dev/null", O_RDWR, 0);
dup(0);
dup(0);
}
static int ppp_load_kext() {
static int ppp_load_kext()
{
int pid = fork();
if(pid < 0) {
if (pid < 0)
{
tun_error("fork for ppp kext: %s", strerror(errno));
return -1;
}
if(pid == 0) {
if (pid == 0)
{
closeall();
execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
exit(1);
}
int status;
while(waitpid(pid, &status, 0) < 0) {
if(errno == EINTR) {
while (waitpid(pid, &status, 0) < 0)
{
if (errno == EINTR)
continue;
}
tun_error("waitpid for ppp kext: %s", strerror(errno));
return -1;
}
if(WEXITSTATUS(status) != 0) {
if (WEXITSTATUS(status) != 0)
{
tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
return -1;
}
@ -137,73 +143,74 @@ static int ppp_load_kext() {
return 0;
}
static int ppp_new_instance() {
static int ppp_new_instance()
{
// create ppp socket
int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
if(ppp_sockfd < 0) {
if(ppp_load_kext() < 0) {
int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
if (ppp_sockfd < 0)
{
if (ppp_load_kext() < 0)
return -1;
}
ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
if(ppp_sockfd < 0) {
if (ppp_sockfd < 0)
{
tun_error("creating ppp socket: %s", strerror(errno));
return -1;
}
}
// connect to ppp procotol
struct sockaddr_ppp pppaddr;
pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
pppaddr.ppp_family = AF_PPP;
pppaddr.ppp_proto = PPPPROTO_CTL;
pppaddr.ppp_cookie = 0;
if(connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0) {
struct sockaddr_ppp pppaddr;
pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
pppaddr.ppp_family = AF_PPP;
pppaddr.ppp_proto = PPPPROTO_CTL;
pppaddr.ppp_cookie = 0;
if (connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0)
{
tun_error("connecting ppp socket: %s", strerror(errno));
close(ppp_sockfd);
return -1;
}
}
tun_noerror();
return ppp_sockfd;
}
static int ppp_new_unit(int *unit_number) {
static int ppp_new_unit(int *unit_number)
{
int fd = ppp_new_instance();
if(fd < 0) {
if (fd < 0)
return -1;
}
// create ppp unit
if(ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0) {
if (ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0)
{
tun_error("creating ppp unit: %s", strerror(errno));
close(fd);
return -1;
}
}
tun_noerror();
return fd;
}
static int ppp_setup_unit(int unit_fd) {
static int ppp_setup_unit(int unit_fd)
{
// send traffic to program
int flags = SC_LOOP_TRAFFIC;
if(ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0) {
if (ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0)
{
tun_error("setting ppp loopback mode: %s", strerror(errno));
return -1;
}
}
// allow packets
struct npioctl npi;
npi.protocol = PPP_IP;
npi.mode = NPMODE_PASS;
if(ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0) {
if (ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0)
{
tun_error("starting ppp unit: %s", strerror(errno));
return -1;
}
@ -212,8 +219,10 @@ static int ppp_setup_unit(int unit_fd) {
return 0;
}
static int open_pcap() {
if(pcap != NULL) {
static int open_pcap()
{
if (pcap != NULL)
{
pcap_use_count++;
return 0;
}
@ -222,7 +231,8 @@ static int open_pcap() {
pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
pcap_use_count = 1;
if(pcap == NULL) {
if (pcap == NULL)
{
tun_error("opening pcap: %s", errbuf);
return -1;
}
@ -231,57 +241,59 @@ static int open_pcap() {
return 0;
}
static void close_pcap() {
if(pcap == NULL) {
static void close_pcap()
{
if (pcap == NULL)
return;
}
pcap_use_count--;
if(pcap_use_count == 0) {
if (pcap_use_count == 0)
{
pcap_close(pcap);
pcap = NULL;
}
}
static void allocate_data_buffer(int size) {
if(data_buffer_length < size) {
static void allocate_data_buffer(int size)
{
if (data_buffer_length < size)
{
free(data_buffer);
data_buffer_length = size;
data_buffer = malloc(data_buffer_length);
}
}
static void make_device_name(tunemu_device device, int unit_number) {
static void make_device_name(tunemu_device device, int unit_number)
{
snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
}
static int check_device_name(tunemu_device device) {
if(strlen(device) < 4) {
static int check_device_name(tunemu_device device)
{
if (strlen(device) < 4)
return -1;
}
int unit_number = atoi(device + 3);
if(unit_number < 0 || unit_number > 999) {
if (unit_number < 0 || unit_number > 999)
return -1;
}
tunemu_device compare;
make_device_name(compare, unit_number);
if(strcmp(device, compare) != 0) {
if (strcmp(device, compare) != 0)
return -1;
}
return 0;
}
int tunemu_open(tunemu_device device) {
int tunemu_open(tunemu_device device)
{
int ppp_unit_number = -1;
if(device[0] != 0) {
if(check_device_name(device) < 0) {
if (device[0] != 0)
{
if (check_device_name(device) < 0)
{
tun_error("invalid device name \"%s\"", device);
return -1;
}
@ -290,17 +302,17 @@ int tunemu_open(tunemu_device device) {
}
int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
if(ppp_unit_fd < 0) {
if (ppp_unit_fd < 0)
return -1;
}
if(ppp_setup_unit(ppp_unit_fd) < 0) {
if (ppp_setup_unit(ppp_unit_fd) < 0)
{
close(ppp_unit_fd);
return -1;
}
if(open_pcap() < 0) {
if (open_pcap() < 0)
{
close(ppp_unit_fd);
return -1;
}
@ -310,40 +322,39 @@ int tunemu_open(tunemu_device device) {
return ppp_unit_fd;
}
int tunemu_close(int ppp_sockfd) {
int tunemu_close(int ppp_sockfd)
{
int ret = close(ppp_sockfd);
if(ret == 0) {
if (ret == 0)
close_pcap();
}
return ret;
}
int tunemu_read(int ppp_sockfd, char *buffer, int length) {
int tunemu_read(int ppp_sockfd, char *buffer, int length)
{
allocate_data_buffer(length + 2);
length = read(ppp_sockfd, data_buffer, length + 2);
if(length < 0) {
if (length < 0)
{
tun_error("reading packet: %s", strerror(errno));
return length;
}
tun_noerror();
length -= 2;
if(length < 0) {
if (length < 0)
return 0;
}
memcpy(buffer, data_buffer + 2, length);
return length;
}
int tunemu_write(int ppp_sockfd, char *buffer, int length) {
int tunemu_write(int ppp_sockfd, char *buffer, int length)
{
allocate_data_buffer(length + 4);
data_buffer[0] = 0x02;
@ -353,25 +364,23 @@ int tunemu_write(int ppp_sockfd, char *buffer, int length) {
memcpy(data_buffer + 4, buffer, length);
if(pcap == NULL) {
if (pcap == NULL)
{
tun_error("pcap not open");
return -1;
}
length = pcap_inject(pcap, data_buffer, length + 4);
if(length < 0) {
if (length < 0)
{
tun_error("injecting packet: %s", pcap_geterr(pcap));
return length;
}
tun_noerror();
length -= 4;
if(length < 0) {
if (length < 0)
return 0;
}
return length;
}

View file

@ -1,20 +1,20 @@
/*
* tunemu - Tun device emulation for Darwin
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
*
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef TUNEMU_H

108
src/buffer.c Normal file
View file

@ -0,0 +1,108 @@
/*
buffer.c -- buffer management
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "buffer.h"
#include "xalloc.h"
void buffer_compact(buffer_t *buffer, int maxsize) {
if(buffer->len >= maxsize || buffer->offset / 7 > buffer->len / 8) {
memmove(buffer->data, buffer->data + buffer->offset, buffer->len - buffer->offset);
buffer->len -= buffer->offset;
buffer->offset = 0;
}
}
// Make sure we can add size bytes to the buffer, and return a pointer to the start of those bytes.
char *buffer_prepare(buffer_t *buffer, int size) {
if(!buffer->data) {
buffer->maxlen = size;
buffer->data = xmalloc(size);
} else {
if(buffer->offset && buffer->len + size > buffer->maxlen) {
memmove(buffer->data, buffer->data + buffer->offset, buffer->len - buffer->offset);
buffer->len -= buffer->offset;
buffer->offset = 0;
}
if(buffer->len + size > buffer->maxlen) {
buffer->maxlen = buffer->len + size;
buffer->data = xrealloc(buffer->data, buffer->maxlen);
}
}
char *start = buffer->data + buffer->len;
buffer->len += size;
return start;
}
// Copy data into the buffer.
void buffer_add(buffer_t *buffer, const char *data, int size) {
memcpy(buffer_prepare(buffer, size), data, size);
}
// Remove given number of bytes from the buffer, return a pointer to the start of them.
static char *buffer_consume(buffer_t *buffer, int size) {
char *start = buffer->data + buffer->offset;
buffer->offset += size;
if(buffer->offset >= buffer->len) {
buffer->offset = 0;
buffer->len = 0;
}
return start;
}
// Check if there is a complete line in the buffer, and if so, return it NULL-terminated.
char *buffer_readline(buffer_t *buffer) {
char *newline = memchr(buffer->data + buffer->offset, '\n', buffer->len - buffer->offset);
if(!newline)
return NULL;
int len = newline + 1 - (buffer->data + buffer->offset);
*newline = 0;
return buffer_consume(buffer, len);
}
// Check if we have enough bytes in the buffer, and if so, return a pointer to the start of them.
char *buffer_read(buffer_t *buffer, int size) {
if(buffer->len - buffer->offset < size)
return NULL;
return buffer_consume(buffer, size);
}
void buffer_clear(buffer_t *buffer) {
free(buffer->data);
buffer->data = NULL;
buffer->maxlen = 0;
buffer->len = 0;
buffer->offset = 0;
}

18
src/buffer.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef __TINC_BUFFER_H__
#define __TINC_BUFFER_H__
typedef struct buffer_t {
char *data;
int maxlen;
int len;
int offset;
} buffer_t;
extern void buffer_compact(buffer_t *buffer, int maxsize);
extern char *buffer_prepare(buffer_t *buffer, int size);
extern void buffer_add(buffer_t *buffer, const char *data, int size);
extern char *buffer_readline(buffer_t *buffer);
extern char *buffer_read(buffer_t *buffer, int size);
extern void buffer_clear(buffer_t *buffer);
#endif

View file

@ -2,9 +2,9 @@
conf.c -- configuration code
Copyright (C) 1998 Robert van der Meulen
1998-2005 Ivo Timmermans
2000-2014 Guus Sliepen <guus@tinc-vpn.org>
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2010-2011 Julien Muchembled <jm@jmuchemb.eu>
2000 Cris van Pelt
2000 Cris van Pelt
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
@ -23,23 +23,23 @@
#include "system.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "connection.h"
#include "conf.h"
#include "list.h"
#include "logger.h"
#include "netutl.h" /* for str2address */
#include "netutl.h" /* for str2address */
#include "protocol.h"
#include "utils.h" /* for cp */
#include "utils.h" /* for cp */
#include "xalloc.h"
avl_tree_t *config_tree;
splay_tree_t *config_tree;
int pinginterval = 0; /* seconds between pings */
int pingtimeout = 0; /* seconds to wait for response */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
list_t *cmdline_conf = NULL; /* global/host configuration values given at the command line */
int pinginterval = 0; /* seconds between pings */
int pingtimeout = 0; /* seconds to wait for response */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */
list_t *cmdline_conf = NULL; /* global/host configuration values given at the command line */
static int config_compare(const config_t *a, const config_t *b) {
@ -47,32 +47,28 @@ static int config_compare(const config_t *a, const config_t *b) {
result = strcasecmp(a->variable, b->variable);
if(result) {
if(result)
return result;
}
/* give priority to command line options */
result = !b->file - !a->file;
if(result) {
if (result)
return result;
}
result = a->line - b->line;
if(result) {
if(result)
return result;
} else {
else
return a->file ? strcmp(a->file, b->file) : 0;
}
}
void init_configuration(avl_tree_t **config_tree) {
*config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
void init_configuration(splay_tree_t ** config_tree) {
*config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
}
void exit_configuration(avl_tree_t **config_tree) {
avl_delete_tree(*config_tree);
void exit_configuration(splay_tree_t ** config_tree) {
splay_delete_tree(*config_tree);
*config_tree = NULL;
}
@ -81,49 +77,52 @@ config_t *new_config(void) {
}
void free_config(config_t *cfg) {
free(cfg->variable);
free(cfg->value);
free(cfg->file);
if(cfg->variable)
free(cfg->variable);
if(cfg->value)
free(cfg->value);
if(cfg->file)
free(cfg->file);
free(cfg);
}
void config_add(avl_tree_t *config_tree, config_t *cfg) {
avl_insert(config_tree, cfg);
void config_add(splay_tree_t *config_tree, config_t *cfg) {
splay_insert(config_tree, cfg);
}
config_t *lookup_config(const avl_tree_t *config_tree, char *variable) {
config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
config_t cfg, *found;
cfg.variable = variable;
cfg.file = NULL;
cfg.line = 0;
found = avl_search_closest_greater(config_tree, &cfg);
found = splay_search_closest_greater(config_tree, &cfg);
if(!found) {
if(!found)
return NULL;
}
if(strcasecmp(found->variable, variable)) {
if(strcasecmp(found->variable, variable))
return NULL;
}
return found;
}
config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg) {
avl_node_t *node;
config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
splay_node_t *node;
config_t *found;
node = avl_search_node(config_tree, cfg);
node = splay_search_node(config_tree, cfg);
if(node) {
if(node->next) {
found = node->next->data;
if(!strcasecmp(found->variable, cfg->variable)) {
if(!strcasecmp(found->variable, cfg->variable))
return found;
}
}
}
@ -131,9 +130,8 @@ config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg)
}
bool get_config_bool(const config_t *cfg, bool *result) {
if(!cfg) {
if(!cfg)
return false;
}
if(!strcasecmp(cfg->value, "yes")) {
*result = true;
@ -144,30 +142,27 @@ bool get_config_bool(const config_t *cfg, bool *result) {
}
logger(LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_int(const config_t *cfg, int *result) {
if(!cfg) {
if(!cfg)
return false;
}
if(sscanf(cfg->value, "%d", result) == 1) {
if(sscanf(cfg->value, "%d", result) == 1)
return true;
}
logger(LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_string(const config_t *cfg, char **result) {
if(!cfg) {
if(!cfg)
return false;
}
*result = xstrdup(cfg->value);
@ -177,9 +172,8 @@ bool get_config_string(const config_t *cfg, char **result) {
bool get_config_address(const config_t *cfg, struct addrinfo **result) {
struct addrinfo *ai;
if(!cfg) {
if(!cfg)
return false;
}
ai = str2addrinfo(cfg->value, NULL, 0);
@ -189,32 +183,31 @@ bool get_config_address(const config_t *cfg, struct addrinfo **result) {
}
logger(LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
cfg->variable, cfg->file, cfg->line);
return false;
}
bool get_config_subnet(const config_t *cfg, subnet_t **result) {
subnet_t subnet = {0};
bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
subnet_t subnet = {NULL};
if(!cfg) {
if(!cfg)
return false;
}
if(!str2net(&subnet, cfg->value)) {
logger(LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
cfg->variable, cfg->file, cfg->line);
return false;
}
/* Teach newbies what subnets are... */
if(((subnet.type == SUBNET_IPV4)
&& !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
|| ((subnet.type == SUBNET_IPV6)
&& !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
&& !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
|| ((subnet.type == SUBNET_IPV6)
&& !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
cfg->variable, cfg->file, cfg->line);
cfg->variable, cfg->file, cfg->line);
return false;
}
@ -226,31 +219,26 @@ bool get_config_subnet(const config_t *cfg, subnet_t **result) {
/*
Read exactly one line and strip the trailing newline if any.
*/
static char *readline(FILE *fp, char *buf, size_t buflen) {
static char *readline(FILE * fp, char *buf, size_t buflen) {
char *newline = NULL;
char *p;
if(feof(fp)) {
if(feof(fp))
return NULL;
}
p = fgets(buf, buflen, fp);
if(!p) {
if(!p)
return NULL;
}
newline = strchr(p, '\n');
if(!newline) {
if(!newline)
return buf;
}
*newline = '\0'; /* kill newline */
if(newline > p && newline[-1] == '\r') { /* and carriage return if necessary */
*newline = '\0'; /* kill newline */
if(newline > p && newline[-1] == '\r') /* and carriage return if necessary */
newline[-1] = '\0';
}
return buf;
}
@ -262,32 +250,26 @@ config_t *parse_config_line(char *line, const char *fname, int lineno) {
variable = value = line;
eol = line + strlen(line);
while(strchr("\t ", *--eol)) {
while(strchr("\t ", *--eol))
*eol = '\0';
}
len = strcspn(value, "\t =");
value += len;
value += strspn(value, "\t ");
if(*value == '=') {
value++;
value += strspn(value, "\t ");
}
variable[len] = '\0';
if(!*value) {
const char err[] = "No value for variable";
if(fname)
if (fname)
logger(LOG_ERR, "%s `%s' on line %d while reading config file %s",
err, variable, lineno, fname);
err, variable, lineno, fname);
else
logger(LOG_ERR, "%s `%s' in command line option %d",
err, variable, lineno);
err, variable, lineno);
return NULL;
}
@ -304,7 +286,7 @@ config_t *parse_config_line(char *line, const char *fname, int lineno) {
Parse a configuration file and put the results in the configuration tree
starting at *base.
*/
bool read_config_file(avl_tree_t *config_tree, const char *fname) {
bool read_config_file(splay_tree_t *config_tree, const char *fname) {
FILE *fp;
char buffer[MAX_STRING_SIZE];
char *line;
@ -321,41 +303,33 @@ bool read_config_file(avl_tree_t *config_tree, const char *fname) {
}
for(;;) {
line = readline(fp, buffer, sizeof(buffer));
line = readline(fp, buffer, sizeof buffer);
if(!line) {
if(feof(fp)) {
if(feof(fp))
result = true;
}
break;
}
lineno++;
if(!*line || *line == '#') {
if(!*line || *line == '#')
continue;
}
if(ignore) {
if(!strncmp(line, "-----END", 8)) {
if(!strncmp(line, "-----END", 8))
ignore = false;
}
continue;
}
if(!strncmp(line, "-----BEGIN", 10)) {
ignore = true;
continue;
}
cfg = parse_config_line(line, fname, lineno);
if(!cfg) {
if (!cfg)
break;
}
config_add(config_tree, cfg);
}
@ -364,234 +338,122 @@ bool read_config_file(avl_tree_t *config_tree, const char *fname) {
return result;
}
void read_config_options(avl_tree_t *config_tree, const char *prefix) {
void read_config_options(splay_tree_t *config_tree, const char *prefix) {
list_node_t *node, *next;
size_t prefix_len = prefix ? strlen(prefix) : 0;
for(const list_node_t *node = cmdline_conf->tail; node; node = node->prev) {
const config_t *cfg = node->data;
for(node = cmdline_conf->tail; node; node = next) {
config_t *orig_cfg, *cfg = (config_t *)node->data;
next = node->prev;
if(!prefix) {
if(strchr(cfg->variable, '.')) {
if(strchr(cfg->variable, '.'))
continue;
}
node->data = NULL;
list_unlink_node(cmdline_conf, node);
} else {
if(strncmp(prefix, cfg->variable, prefix_len) ||
cfg->variable[prefix_len] != '.') {
cfg->variable[prefix_len] != '.')
continue;
}
/* Because host configuration is parsed again when
reconnecting, nodes must not be freed when a prefix
is given. */
orig_cfg = cfg;
cfg = new_config();
cfg->variable = xstrdup(orig_cfg->variable + prefix_len + 1);
cfg->value = xstrdup(orig_cfg->value);
cfg->file = NULL;
cfg->line = orig_cfg->line;
}
config_t *new = new_config();
if(prefix) {
new->variable = xstrdup(cfg->variable + prefix_len + 1);
} else {
new->variable = xstrdup(cfg->variable);
}
new->value = xstrdup(cfg->value);
new->file = NULL;
new->line = cfg->line;
config_add(config_tree, new);
config_add(config_tree, cfg);
}
}
bool read_server_config(void) {
char fname[PATH_MAX];
char *fname;
bool x;
read_config_options(config_tree, NULL);
snprintf(fname, sizeof(fname), "%s/tinc.conf", confbase);
errno = 0;
xasprintf(&fname, "%s/tinc.conf", confbase);
x = read_config_file(config_tree, fname);
// We will try to read the conf files in the "conf.d" dir
if(x) {
char dname[PATH_MAX];
snprintf(dname, sizeof(dname), "%s/conf.d", confbase);
DIR *dir = opendir(dname);
// If we can find this dir
if(dir) {
struct dirent *ep;
// We list all the files in it
while(x && (ep = readdir(dir))) {
size_t l = strlen(ep->d_name);
// And we try to read the ones that end with ".conf"
if(l > 5 && !strcmp(".conf", & ep->d_name[ l - 5 ])) {
if((size_t)snprintf(fname, sizeof(fname), "%s/%s", dname, ep->d_name) >= sizeof(fname)) {
logger(LOG_ERR, "Pathname too long: %s/%s", dname, ep->d_name);
return false;
}
x = read_config_file(config_tree, fname);
}
}
closedir(dir);
}
}
if(!x && errno) {
if(!x) { /* System error: complain */
logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
}
free(fname);
return x;
}
bool read_connection_config(connection_t *c) {
char fname[PATH_MAX];
char *fname;
bool x;
read_config_options(c->config_tree, c->name);
snprintf(fname, sizeof(fname), "%s/hosts/%s", confbase, c->name);
xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
x = read_config_file(c->config_tree, fname);
free(fname);
return x;
}
static void disable_old_keys(const char *filename) {
char tmpfile[PATH_MAX] = "";
char buf[1024];
bool disabled = false;
FILE *r, *w;
bool append_config_file(const char *name, const char *key, const char *value) {
char *fname;
xasprintf(&fname, "%s/hosts/%s", confbase, name);
r = fopen(filename, "r");
FILE *fp = fopen(fname, "a");
if(!r) {
return;
if(!fp) {
logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
} else {
fprintf(fp, "\n# The following line was automatically added by tinc\n%s = %s\n", key, value);
fclose(fp);
}
snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
free(fname);
w = fopen(tmpfile, "w");
return fp;
}
while(fgets(buf, sizeof(buf), r)) {
if(!strncmp(buf, "-----BEGIN RSA", 14)) {
bool disable_old_keys(FILE *f) {
char buf[100];
long pos;
bool disabled = false;
rewind(f);
pos = ftell(f);
if(pos < 0)
return false;
while(fgets(buf, sizeof buf, f)) {
if(!strncmp(buf, "-----BEGIN RSA", 14)) {
buf[11] = 'O';
buf[12] = 'L';
buf[13] = 'D';
if(fseek(f, pos, SEEK_SET))
break;
if(fputs(buf, f) <= 0)
break;
disabled = true;
} else if(!strncmp(buf, "-----END RSA", 12)) {
}
else if(!strncmp(buf, "-----END RSA", 12)) {
buf[ 9] = 'O';
buf[10] = 'L';
buf[11] = 'D';
if(fseek(f, pos, SEEK_SET))
break;
if(fputs(buf, f) <= 0)
break;
disabled = true;
}
if(w && fputs(buf, w) < 0) {
disabled = false;
pos = ftell(f);
if(pos < 0)
break;
}
}
if(w) {
fclose(w);
}
fclose(r);
if(!w && disabled) {
fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
return;
}
if(disabled) {
#ifdef HAVE_MINGW
// We cannot atomically replace files on Windows.
char bakfile[PATH_MAX] = "";
snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
if(rename(filename, bakfile) || rename(tmpfile, filename)) {
rename(bakfile, filename);
#else
if(rename(tmpfile, filename)) {
#endif
fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
} else {
#ifdef HAVE_MINGW
unlink(bakfile);
#endif
fprintf(stderr, "Warning: old key(s) found and disabled.\n");
}
}
unlink(tmpfile);
return disabled;
}
FILE *ask_and_open(const char *filename, const char *what) {
FILE *r;
char directory[PATH_MAX];
char line[PATH_MAX];
char abspath[PATH_MAX];
const char *fn;
/* Check stdin and stdout */
if(!isatty(0) || !isatty(1)) {
/* Argh, they are running us from a script or something. Write
the files to the current directory and let them burn in hell
for ever. */
fn = filename;
} else {
/* Ask for a file and/or directory name. */
fprintf(stdout, "Please enter a file to save %s to [%s]: ",
what, filename);
fflush(stdout);
fn = readline(stdin, line, sizeof(line));
if(!fn) {
fprintf(stderr, "Error while reading stdin: %s\n",
strerror(errno));
return NULL;
}
if(!strlen(fn))
/* User just pressed enter. */
{
fn = filename;
}
}
#ifdef HAVE_MINGW
if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
#else
if(fn[0] != '/') {
#endif
/* The directory is a relative path or a filename. */
getcwd(directory, sizeof(directory));
if((size_t)snprintf(abspath, sizeof(abspath), "%s/%s", directory, fn) >= sizeof(abspath)) {
fprintf(stderr, "Pathname too long: %s/%s\n", directory, fn);
return NULL;
}
fn = abspath;
}
umask(0077); /* Disallow everything for group and other */
disable_old_keys(fn);
/* Open it first to keep the inode busy */
r = fopen(fn, "a");
if(!r) {
fprintf(stderr, "Error opening file `%s': %s\n",
fn, strerror(errno));
return NULL;
}
return r;
}

View file

@ -1,10 +1,7 @@
#ifndef TINC_CONF_H
#define TINC_CONF_H
/*
conf.h -- header for conf.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,7 +18,10 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "avl_tree.h"
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
#include "splay_tree.h"
#include "list.h"
typedef struct config_t {
@ -33,35 +33,37 @@ typedef struct config_t {
#include "subnet.h"
extern avl_tree_t *config_tree;
extern splay_tree_t *config_tree;
extern int pinginterval;
extern int pingtimeout;
extern int maxtimeout;
extern int mintimeout;
extern bool bypass_security;
extern char *confbase;
extern char *netname;
extern list_t *cmdline_conf;
extern void init_configuration(avl_tree_t **config_tree);
extern void exit_configuration(avl_tree_t **config_tree);
extern config_t *new_config(void) __attribute__((__malloc__));
extern void free_config(config_t *cfg);
extern void config_add(avl_tree_t *config_tree, config_t *cfg);
extern config_t *lookup_config(const avl_tree_t *config_tree, char *variable);
extern config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg);
extern bool get_config_bool(const config_t *cfg, bool *result);
extern bool get_config_int(const config_t *cfg, int *result);
extern bool get_config_string(const config_t *cfg, char **result);
extern bool get_config_address(const config_t *cfg, struct addrinfo **result);
extern bool get_config_subnet(const config_t *cfg, struct subnet_t **result);
extern void init_configuration(splay_tree_t **);
extern void exit_configuration(splay_tree_t **);
extern config_t *new_config(void) __attribute__ ((__malloc__));
extern void free_config(config_t *);
extern void config_add(splay_tree_t *, config_t *);
extern config_t *lookup_config(splay_tree_t *, char *);
extern config_t *lookup_config_next(splay_tree_t *, const config_t *);
extern bool get_config_bool(const config_t *, bool *);
extern bool get_config_int(const config_t *, int *);
extern bool get_config_string(const config_t *, char **);
extern bool get_config_address(const config_t *, struct addrinfo **);
extern bool get_config_subnet(const config_t *, struct subnet_t **);
extern config_t *parse_config_line(char *line, const char *fname, int lineno);
extern bool read_config_file(avl_tree_t *config_tree, const char *fname);
extern void read_config_options(avl_tree_t *config_tree, const char *prefix);
extern config_t *parse_config_line(char *, const char *, int);
extern bool read_config_file(splay_tree_t *, const char *);
extern void read_config_options(splay_tree_t *, const char *);
extern bool read_server_config(void);
extern bool read_connection_config(struct connection_t *c);
extern FILE *ask_and_open(const char *fname, const char *what);
extern bool read_connection_config(struct connection_t *);
extern bool append_config_file(const char *, const char *, const char *);
extern FILE *ask_and_open(const char *, const char *, const char *);
extern bool is_safe_path(const char *);
extern bool disable_old_keys(FILE *);
#endif
#endif /* __TINC_CONF_H__ */

View file

@ -1,6 +1,6 @@
/*
connection.c -- connection list management
Copyright (C) 2000-2016 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
2008 Max Rijevski <maksuf@gmail.com>
@ -21,131 +21,98 @@
#include "system.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "cipher.h"
#include "conf.h"
#include "control_common.h"
#include "list.h"
#include "logger.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *connection_tree; /* Meta connections */
connection_t *everyone;
splay_tree_t *connection_tree; /* Meta connections */
connection_t *broadcast;
static int connection_compare(const connection_t *a, const connection_t *b) {
return a < b ? -1 : a == b ? 0 : 1;
}
void init_connections(void) {
connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection);
everyone = new_connection();
everyone->name = xstrdup("everyone");
everyone->hostname = xstrdup("BROADCAST");
connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection);
broadcast = new_connection();
broadcast->name = xstrdup("everyone");
broadcast->hostname = xstrdup("BROADCAST");
}
void exit_connections(void) {
avl_delete_tree(connection_tree);
free_connection(everyone);
splay_delete_tree(connection_tree);
free_connection(broadcast);
}
connection_t *new_connection(void) {
connection_t *c;
c = xmalloc_and_zero(sizeof(connection_t));
if(!c) {
return NULL;
}
gettimeofday(&c->start, NULL);
return c;
}
void free_connection_partially(connection_t *c) {
free(c->inkey);
free(c->outkey);
free(c->mychallenge);
free(c->hischallenge);
free(c->outbuf);
c->inkey = NULL;
c->outkey = NULL;
c->mychallenge = NULL;
c->hischallenge = NULL;
c->outbuf = NULL;
c->status.pinged = false;
c->status.active = false;
c->status.connecting = false;
c->status.timeout = false;
c->status.encryptout = false;
c->status.decryptin = false;
c->status.mst = false;
c->options = 0;
c->buflen = 0;
c->reqlen = 0;
c->tcplen = 0;
c->allow_request = 0;
c->outbuflen = 0;
c->outbufsize = 0;
c->outbufstart = 0;
c->last_ping_time = 0;
c->last_flushed_time = 0;
c->inbudget = 0;
c->outbudget = 0;
if(c->inctx) {
EVP_CIPHER_CTX_cleanup(c->inctx);
free(c->inctx);
c->inctx = NULL;
}
if(c->outctx) {
EVP_CIPHER_CTX_cleanup(c->outctx);
free(c->outctx);
c->outctx = NULL;
}
if(c->rsa_key) {
RSA_free(c->rsa_key);
c->rsa_key = NULL;
}
return xmalloc_and_zero(sizeof(connection_t));
}
void free_connection(connection_t *c) {
free_connection_partially(c);
if(!c)
return;
free(c->name);
free(c->hostname);
if(c->name)
free(c->name);
if(c->config_tree) {
if(c->hostname)
free(c->hostname);
cipher_close(&c->incipher);
digest_close(&c->indigest);
cipher_close(&c->outcipher);
digest_close(&c->outdigest);
ecdh_free(&c->ecdh);
ecdsa_free(&c->ecdsa);
rsa_free(&c->rsa);
if(c->hischallenge)
free(c->hischallenge);
if(c->config_tree)
exit_configuration(&c->config_tree);
}
buffer_clear(&c->inbuf);
buffer_clear(&c->outbuf);
if(event_initialized(&c->inevent))
event_del(&c->inevent);
if(event_initialized(&c->outevent))
event_del(&c->outevent);
if(c->socket > 0)
closesocket(c->socket);
free(c);
}
void connection_add(connection_t *c) {
avl_insert(connection_tree, c);
splay_insert(connection_tree, c);
}
void connection_del(connection_t *c) {
avl_delete(connection_tree, c);
splay_delete(connection_tree, c);
}
void dump_connections(void) {
avl_node_t *node;
bool dump_connections(connection_t *cdump) {
splay_node_t *node;
connection_t *c;
logger(LOG_DEBUG, "Connections:");
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d",
c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof(c->status)),
c->outbufsize, c->outbufstart, c->outbuflen);
send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
CONTROL, REQ_DUMP_CONNECTIONS,
c->name, c->hostname, c->options, c->socket,
bitfield_to_int(&c->status, sizeof c->status));
}
logger(LOG_DEBUG, "End of connections.");
return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
}

View file

@ -1,9 +1,6 @@
#ifndef TINC_CONNECTION_H
#define TINC_CONNECTION_H
/*
connection.h -- header for connection.c
Copyright (C) 2000-2016 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2010 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -21,99 +18,95 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <openssl/rsa.h>
#include <openssl/evp.h>
#ifndef __TINC_CONNECTION_H__
#define __TINC_CONNECTION_H__
#include "avl_tree.h"
#include "buffer.h"
#include "cipher.h"
#include "digest.h"
#include "rsa.h"
#include "splay_tree.h"
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
#define OPTION_CLAMP_MSS 0x0008
#define OPTION_INDIRECT 0x0001
#define OPTION_TCPONLY 0x0002
#define OPTION_PMTU_DISCOVERY 0x0004
#define OPTION_CLAMP_MSS 0x0008
typedef struct connection_status_t {
unsigned int pinged: 1; /* sent ping */
unsigned int active: 1; /* 1 if active.. */
unsigned int connecting: 1; /* 1 if we are waiting for a non-blocking connect() to finish */
unsigned int unused_termreq: 1; /* the termination of this connection was requested */
unsigned int remove: 1; /* Set to 1 if you want this connection removed */
unsigned int timeout: 1; /* 1 if gotten timeout */
unsigned int encryptout: 1; /* 1 if we can encrypt outgoing traffic */
unsigned int decryptin: 1; /* 1 if we have to decrypt incoming traffic */
unsigned int mst: 1; /* 1 if this connection is part of a minimum spanning tree */
unsigned int proxy_passed: 1; /* 1 if we are connecting via a proxy and we have finished talking with it */
unsigned int tarpit: 1; /* 1 if the connection should be added to the tarpit */
unsigned int unused: 21;
unsigned int pinged:1; /* sent ping */
unsigned int active:1; /* 1 if active.. */
unsigned int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
unsigned int termreq:1; /* the termination of this connection was requested */
unsigned int remove_unused:1; /* Set to 1 if you want this connection removed */
unsigned int timeout_unused:1; /* 1 if gotten timeout */
unsigned int encryptout:1; /* 1 if we can encrypt outgoing traffic */
unsigned int decryptin:1; /* 1 if we have to decrypt incoming traffic */
unsigned int mst:1; /* 1 if this connection is part of a minimum spanning tree */
unsigned int control:1;
unsigned int pcap:1;
unsigned int unused:21;
} connection_status_t;
#include "ecdh.h"
#include "ecdsa.h"
#include "edge.h"
#include "net.h"
#include "node.h"
typedef struct connection_t {
char *name; /* name he claims to have */
char *name; /* name he claims to have */
union sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_version; /* used protocol */
union sockaddr_t address; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
int protocol_major; /* used protocol */
int protocol_minor; /* used protocol */
int socket; /* socket used for this connection */
uint32_t options; /* options for this connection */
connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
int socket; /* socket used for this connection */
uint32_t options; /* options for this connection */
connection_status_t status; /* status info */
int estimated_weight; /* estimation for the weight of the edge for this connection */
struct timeval start; /* time this connection was started, used for above estimation */
struct outgoing_t *outgoing; /* used to keep track of outgoing connections */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
struct node_t *node; /* node associated with the other end */
struct edge_t *edge; /* edge associated with this connection */
rsa_t rsa; /* his public RSA key */
ecdsa_t ecdsa; /* his public ECDSA key */
ecdsa_t ecdh; /* state for ECDH key exchange */
cipher_t incipher; /* Cipher he will use to send data to us */
cipher_t outcipher; /* Cipher we will use to send data to him */
digest_t indigest;
digest_t outdigest;
RSA *rsa_key; /* his public/private key */
const EVP_CIPHER *incipher; /* Cipher he will use to send data to us */
const EVP_CIPHER *outcipher; /* Cipher we will use to send data to him */
EVP_CIPHER_CTX *inctx; /* Context of encrypted meta data that will come from him to us */
EVP_CIPHER_CTX *outctx; /* Context of encrypted meta data that will be sent from us to him */
uint64_t inbudget; /* Encrypted bytes send budget */
uint64_t outbudget; /* Encrypted bytes receive budget */
char *inkey; /* His symmetric meta key + iv */
char *outkey; /* Our symmetric meta key + iv */
int inkeylength; /* Length of his key + iv */
int outkeylength; /* Length of our key + iv */
const EVP_MD *indigest;
const EVP_MD *outdigest;
int inmaclength;
int outmaclength;
int incompression;
int outcompression;
char *mychallenge; /* challenge we received from him */
char *hischallenge; /* challenge we sent to him */
char buffer[MAXBUFSIZE]; /* metadata input buffer */
int buflen; /* bytes read into buffer */
int reqlen; /* length of incoming request */
length_t tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
char *hischallenge; /* The challenge we sent to him */
char *outbuf; /* metadata output buffer */
int outbufstart; /* index of first meaningful byte in output buffer */
int outbuflen; /* number of meaningful bytes in output buffer */
int outbufsize; /* number of bytes allocated to output buffer */
struct buffer_t inbuf;
struct buffer_t outbuf;
struct event inevent; /* input event on this metadata connection */
struct event outevent; /* output event on this metadata connection */
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */
time_t last_flushed_time; /* last time buffer was empty. Only meaningful if outbuflen > 0 */
time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */
avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;
extern avl_tree_t *connection_tree;
extern connection_t *everyone;
extern splay_tree_t *connection_tree;
extern connection_t *broadcast;
extern void init_connections(void);
extern void exit_connections(void);
extern connection_t *new_connection(void) __attribute__((__malloc__));
extern void free_connection(connection_t *c);
extern void free_connection_partially(connection_t *c);
extern void connection_add(connection_t *c);
extern void connection_del(connection_t *c);
extern void dump_connections(void);
extern connection_t *new_connection(void) __attribute__ ((__malloc__));
extern void free_connection(connection_t *);
extern void connection_add(connection_t *);
extern void connection_del(connection_t *);
extern bool dump_connections(struct connection_t *);
#endif
#endif /* __TINC_CONNECTION_H__ */

182
src/control.c Normal file
View file

@ -0,0 +1,182 @@
/*
control.c -- Control socket handling.
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "crypto.h"
#include "conf.h"
#include "control.h"
#include "control_common.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "route.h"
#include "splay_tree.h"
#include "utils.h"
#include "xalloc.h"
char controlcookie[65];
extern char *pidfilename;
static bool control_return(connection_t *c, int type, int error) {
return send_request(c, "%d %d %d", CONTROL, type, error);
}
static bool control_ok(connection_t *c, int type) {
return control_return(c, type, 0);
}
bool control_h(connection_t *c, char *request) {
int type;
if(!c->status.control || c->allow_request != CONTROL) {
logger(LOG_ERR, "Unauthorized control request from %s (%s)", c->name, c->hostname);
return false;
}
if(sscanf(request, "%*d %d", &type) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CONTROL", c->name, c->hostname);
return false;
}
switch (type) {
case REQ_STOP:
event_loopexit(NULL);
return control_ok(c, REQ_STOP);
case REQ_DUMP_NODES:
return dump_nodes(c);
case REQ_DUMP_EDGES:
return dump_edges(c);
case REQ_DUMP_SUBNETS:
return dump_subnets(c);
case REQ_DUMP_CONNECTIONS:
return dump_connections(c);
case REQ_PURGE:
purge();
return control_ok(c, REQ_PURGE);
case REQ_SET_DEBUG: {
int new_level;
if(sscanf(request, "%*d %*d %d", &new_level) != 1)
return false;
send_request(c, "%d %d %d", CONTROL, REQ_SET_DEBUG, debug_level);
if(new_level >= 0)
debug_level = new_level;
return true;
}
case REQ_RETRY:
retry();
return control_ok(c, REQ_RETRY);
case REQ_RELOAD:
logger(LOG_NOTICE, "Got '%s' command", "reload");
int result = reload_configuration();
return control_return(c, REQ_RELOAD, result);
case REQ_DISCONNECT: {
char name[MAX_STRING_SIZE];
connection_t *other;
splay_node_t *node, *next;
bool found = false;
if(sscanf(request, "%*d %*d " MAX_STRING, name) != 1)
return control_return(c, REQ_DISCONNECT, -1);
for(node = connection_tree->head; node; node = next) {
next = node->next;
other = node->data;
if(strcmp(other->name, name))
continue;
terminate_connection(other, other->status.active);
found = true;
}
return control_return(c, REQ_DISCONNECT, found ? 0 : -2);
}
case REQ_DUMP_TRAFFIC:
return dump_traffic(c);
case REQ_PCAP:
c->status.pcap = true;
pcap = true;
return true;
default:
return send_request(c, "%d %d", CONTROL, REQ_INVALID);
}
}
bool init_control(void) {
randomize(controlcookie, sizeof controlcookie / 2);
bin2hex(controlcookie, controlcookie, sizeof controlcookie / 2);
FILE *f = fopen(pidfilename, "w");
if(!f) {
logger(LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
return false;
}
#ifdef HAVE_FCHMOD
fchmod(fileno(f), 0600);
#else
chmod(pidfilename, 0600);
#endif
// Get the address and port of the first listening socket
char *localhost = NULL;
sockaddr_t sa;
socklen_t len = sizeof sa;
// Make sure we have a valid address, and map 0.0.0.0 and :: to 127.0.0.1 and ::1.
if(getsockname(listen_socket[0].tcp, (struct sockaddr *)&sa, &len)) {
xasprintf(&localhost, "127.0.0.1 port %d", myport);
} else {
if(sa.sa.sa_family == AF_INET) {
if(sa.in.sin_addr.s_addr == 0)
sa.in.sin_addr.s_addr = htonl(0x7f000001);
} else if(sa.sa.sa_family == AF_INET6) {
static const uint8_t zero[16] = {0};
if(!memcmp(sa.in6.sin6_addr.s6_addr, zero, sizeof zero))
sa.in6.sin6_addr.s6_addr[15] = 1;
}
localhost = sockaddr2hostname(&sa);
}
fprintf(f, "%d %s %s\n", (int)getpid(), controlcookie, localhost);
free(localhost);
fclose(f);
return true;
}
void exit_control(void) {
unlink(pidfilename);
}

27
src/control.h Normal file
View file

@ -0,0 +1,27 @@
/*
control.h -- header for control.c.
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CONTROL_H__
#define __TINC_CONTROL_H__
extern bool init_control();
extern void exit_control();
extern char controlcookie[];
#endif

46
src/control_common.h Normal file
View file

@ -0,0 +1,46 @@
/*
control_protocol.h -- control socket protocol.
Copyright (C) 2007 Scott Lamb <slamb@slamb.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CONTROL_PROTOCOL_H__
#define __TINC_CONTROL_PROTOCOL_H__
#include "protocol.h"
enum request_type {
REQ_INVALID = -1,
REQ_STOP = 0,
REQ_RELOAD,
REQ_RESTART,
REQ_DUMP_NODES,
REQ_DUMP_EDGES,
REQ_DUMP_SUBNETS,
REQ_DUMP_CONNECTIONS,
REQ_DUMP_GRAPH,
REQ_PURGE,
REQ_SET_DEBUG,
REQ_RETRY,
REQ_CONNECT,
REQ_DISCONNECT,
REQ_DUMP_TRAFFIC,
REQ_PCAP,
};
#define TINC_CTL_VERSION_CURRENT 0
#endif

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a Cygwin environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2016 Guus Sliepen <guus@tinc-vpn.org>
2002-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,26 +18,26 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../system.h"
#include "../net.h"
#include "system.h"
#include <w32api/windows.h>
#include <w32api/winioctl.h>
#include "../conf.h"
#include "../device.h"
#include "../logger.h"
#include "../route.h"
#include "../utils.h"
#include "../xalloc.h"
#include "conf.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#include "../mingw/common.h"
#include "mingw/common.h"
int device_fd = -1;
static HANDLE device_handle = INVALID_HANDLE_VALUE;
char *device = NULL;
char *iface = NULL;
static const char *device_info = "Windows tap device";
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
@ -45,7 +45,7 @@ static uint64_t device_total_out = 0;
static pid_t reader_pid;
static int sp[2];
static bool setup_device(void) {
bool setup_device(void) {
HKEY key, key2;
int i, err;
@ -61,10 +61,6 @@ static bool setup_device(void) {
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
if(device && iface) {
logger(LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
}
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
@ -72,51 +68,44 @@ static bool setup_device(void) {
return false;
}
for(i = 0; ; i++) {
len = sizeof(adapterid);
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
for (i = 0; ; i++) {
len = sizeof adapterid;
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break;
}
/* Find out more about this adapter */
snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
}
len = sizeof(adaptername);
len = sizeof adaptername;
err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
RegCloseKey(key2);
if(err) {
if(err)
continue;
}
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else {
} else
continue;
}
}
if(iface) {
if(!strcmp(iface, adaptername)) {
found = true;
break;
} else {
} else
continue;
}
}
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if(device_handle != INVALID_HANDLE_VALUE) {
CloseHandle(device_handle);
found = true;
@ -131,16 +120,14 @@ static bool setup_device(void) {
return false;
}
if(!device) {
if(!device)
device = xstrdup(adapterid);
}
if(!iface) {
if(!iface)
iface = xstrdup(adaptername);
}
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
/* Now we are going to open this device twice: once for reading and once for writing.
We do this because apparently it isn't possible to check for activity in the select() loop.
Furthermore I don't really know how to do it the "Windows" way. */
@ -151,9 +138,9 @@ static bool setup_device(void) {
}
/* The parent opens the tap device for writing. */
device_handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
device_handle = CreateFile(tapname, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM , 0);
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, "Could not open Windows tap device %s (%s) for writing: %s", device, iface, winerror(GetLastError()));
return false;
@ -163,7 +150,7 @@ static bool setup_device(void) {
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
return false;
}
@ -184,9 +171,9 @@ static bool setup_device(void) {
if(!reader_pid) {
/* The child opens the tap device for reading, blocking.
It passes everything it reads to the socket. */
char buf[MTU];
long lenin;
long inlen;
CloseHandle(device_handle);
@ -209,24 +196,25 @@ static bool setup_device(void) {
/* Pass packets */
for(;;) {
ReadFile(device_handle, buf, MTU, &lenin, NULL);
write(sp[1], buf, lenin);
ReadFile(device_handle, buf, MTU, &inlen, NULL);
write(sp[1], buf, inlen);
}
}
read(device_fd, &gelukt, 1);
if(gelukt != 1) {
logger(LOG_DEBUG, "Tap reader failed!");
return false;
}
device_info = "Windows tap device";
logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
static void close_device(void) {
void close_device(void) {
close(sp[0]);
close(sp[1]);
CloseHandle(device_handle);
@ -237,32 +225,32 @@ static void close_device(void) {
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
bool read_packet(vpn_packet_t *packet) {
int inlen;
if((lenin = read(sp[0], packet->data, MTU)) <= 0) {
if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
device, strerror(errno));
return false;
}
packet->len = lenin;
packet->len = inlen;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
long lenout;
bool write_packet(vpn_packet_t *packet) {
long outlen;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
packet->len, device_info);
if(!WriteFile(device_handle, packet->data, packet->len, &lenout, NULL)) {
if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
@ -272,16 +260,8 @@ static bool write_packet(vpn_packet_t *packet) {
return true;
}
static void dump_device_stats(void) {
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,10 +1,7 @@
#ifndef TINC_DEVICE_H
#define TINC_DEVICE_H
/*
device.h -- generic header for device.c
net.h -- generic header for device.c
Copyright (C) 2001-2005 Ivo Timmermans
2001-2012 Guus Sliepen <guus@tinc-vpn.org>
2001-2006 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,27 +18,24 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_DEVICE_H__
#define __TINC_DEVICE_H__
#include "net.h"
extern int device_fd;
extern char *device;
extern char *iface;
typedef struct devops_t {
bool (*setup)(void);
void (*close)(void);
bool (*read)(struct vpn_packet_t *packet);
bool (*write)(struct vpn_packet_t *packet);
void (*dump_stats)(void);
} devops_t;
extern uint64_t device_in_packets;
extern uint64_t device_in_bytes;
extern uint64_t device_out_packets;
extern uint64_t device_out_bytes;
extern const devops_t os_devops;
extern const devops_t dummy_devops;
extern const devops_t raw_socket_devops;
extern const devops_t multicast_devops;
extern const devops_t uml_devops;
extern const devops_t vde_devops;
extern devops_t devops;
extern bool setup_device(void);
extern void close_device(void);
extern bool read_packet(struct vpn_packet_t *);
extern bool write_packet(struct vpn_packet_t *);
extern void dump_device_stats(void);
#endif
#endif /* __TINC_DEVICE_H__ */

View file

@ -1,7 +1,7 @@
/*
dropin.c -- a set of drop-in replacements for libc functions
Copyright (C) 2000-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,7 +25,7 @@
#ifndef HAVE_DAEMON
/*
Replacement for the daemon() function.
The daemon() function is for programs wishing to detach themselves
from the controlling terminal and run in the background as system
daemons.
@ -50,9 +50,8 @@ int daemon(int nochdir, int noclose) {
}
/* If we are the parent, terminate */
if(pid) {
if(pid)
exit(0);
}
/* Detach by becoming the new process group leader */
if(setsid() < 0) {
@ -87,6 +86,40 @@ int daemon(int nochdir, int noclose) {
}
#endif
#ifndef HAVE_GET_CURRENT_DIR_NAME
/*
Replacement for the GNU get_current_dir_name function:
get_current_dir_name will malloc(3) an array big enough to hold the
current directory name. If the environment variable PWD is set, and
its value is correct, then that value will be returned.
*/
char *get_current_dir_name(void) {
size_t size;
char *buf;
char *r;
/* Start with 100 bytes. If this turns out to be insufficient to
contain the working directory, double the size. */
size = 100;
buf = xmalloc(size);
errno = 0; /* Success */
r = getcwd(buf, size);
/* getcwd returns NULL and sets errno to ERANGE if the bufferspace
is insufficient to contain the entire working directory. */
while(r == NULL && errno == ERANGE) {
free(buf);
size <<= 1; /* double the size */
buf = xmalloc(size);
r = getcwd(buf, size);
}
return buf;
}
#endif
#ifndef HAVE_ASPRINTF
int asprintf(char **buf, const char *fmt, ...) {
int result;
@ -107,12 +140,10 @@ int vasprintf(char **buf, const char *fmt, va_list ap) {
va_copy(aq, ap);
status = vsnprintf(*buf, len, fmt, aq);
buf[len - 1] = 0;
va_end(aq);
if(status >= 0) {
if(status >= 0)
*buf = xrealloc(*buf, status + 1);
}
if(status > len - 1) {
len = status;

View file

@ -1,6 +1,3 @@
#ifndef TINC_DROPIN_H
#define TINC_DROPIN_H
/*
dropin.h -- header file for dropin.c
Copyright (C) 2000-2005 Ivo Timmermans,
@ -21,11 +18,14 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __DROPIN_H__
#define __DROPIN_H__
#include "fake-getaddrinfo.h"
#include "fake-getnameinfo.h"
#ifndef HAVE_DAEMON
extern int daemon(int nochdir, int noclose);
extern int daemon(int, int);
#endif
#ifndef HAVE_GET_CURRENT_DIR_NAME
@ -33,16 +33,16 @@ extern char *get_current_dir_name(void);
#endif
#ifndef HAVE_ASPRINTF
extern int asprintf(char **buf, const char *fmt, ...);
extern int vasprintf(char **buf, const char *fmt, va_list ap);
extern int asprintf(char **, const char *, ...);
extern int vasprintf(char **, const char *, va_list ap);
#endif
#ifndef HAVE_GETTIMEOFDAY
extern int gettimeofday(struct timeval *tv, void *tz);
extern int gettimeofday(struct timeval *, void *);
#endif
#ifndef HAVE_USLEEP
extern int usleep(long long usec);
#endif
#endif
#endif /* __DROPIN_H__ */

View file

@ -1,66 +0,0 @@
/*
device.c -- Dummy device
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "xalloc.h"
static const char *device_info = "dummy device";
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
device = xstrdup("dummy");
iface = xstrdup("dummy");
logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
static void close_device(void) {
free(device);
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
(void)packet;
return false;
}
static bool write_packet(vpn_packet_t *packet) {
device_total_out += packet->len;
return true;
}
static void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t dummy_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -20,7 +20,8 @@
#include "system.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "control_common.h"
#include "edge.h"
#include "logger.h"
#include "netutl.h"
@ -28,7 +29,7 @@
#include "utils.h"
#include "xalloc.h"
avl_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
splay_tree_t *edge_weight_tree; /* Tree with all edges, sorted on weight */
static int edge_compare(const edge_t *a, const edge_t *b) {
return strcmp(a->to->name, b->to->name);
@ -39,33 +40,31 @@ static int edge_weight_compare(const edge_t *a, const edge_t *b) {
result = a->weight - b->weight;
if(result) {
if(result)
return result;
}
result = strcmp(a->from->name, b->from->name);
if(result) {
if(result)
return result;
}
return strcmp(a->to->name, b->to->name);
}
void init_edges(void) {
edge_weight_tree = avl_alloc_tree((avl_compare_t) edge_weight_compare, NULL);
edge_weight_tree = splay_alloc_tree((splay_compare_t) edge_weight_compare, NULL);
}
avl_tree_t *new_edge_tree(void) {
return avl_alloc_tree((avl_compare_t) edge_compare, (avl_action_t) free_edge);
splay_tree_t *new_edge_tree(void) {
return splay_alloc_tree((splay_compare_t) edge_compare, (splay_action_t) free_edge);
}
void free_edge_tree(avl_tree_t *edge_tree) {
avl_delete_tree(edge_tree);
void free_edge_tree(splay_tree_t *edge_tree) {
splay_delete_tree(edge_tree);
}
void exit_edges(void) {
avl_delete_tree(edge_weight_tree);
splay_delete_tree(edge_weight_tree);
}
/* Creation and deletion of connection elements */
@ -81,53 +80,50 @@ void free_edge(edge_t *e) {
}
void edge_add(edge_t *e) {
avl_insert(edge_weight_tree, e);
avl_insert(e->from->edge_tree, e);
splay_insert(edge_weight_tree, e);
splay_insert(e->from->edge_tree, e);
e->reverse = lookup_edge(e->to, e->from);
if(e->reverse) {
if(e->reverse)
e->reverse->reverse = e;
}
}
void edge_del(edge_t *e) {
if(e->reverse) {
if(e->reverse)
e->reverse->reverse = NULL;
}
avl_delete(edge_weight_tree, e);
avl_delete(e->from->edge_tree, e);
splay_delete(edge_weight_tree, e);
splay_delete(e->from->edge_tree, e);
}
edge_t *lookup_edge(node_t *from, node_t *to) {
edge_t v;
v.from = from;
v.to = to;
return avl_search(from->edge_tree, &v);
return splay_search(from->edge_tree, &v);
}
void dump_edges(void) {
avl_node_t *node, *node2;
bool dump_edges(connection_t *c) {
splay_node_t *node, *node2;
node_t *n;
edge_t *e;
char *address;
logger(LOG_DEBUG, "Edges:");
for(node = node_tree->head; node; node = node->next) {
n = node->data;
for(node2 = n->edge_tree->head; node2; node2 = node2->next) {
e = node2->data;
address = sockaddr2hostname(&e->address);
logger(LOG_DEBUG, " %s to %s at %s options %x weight %d",
e->from->name, e->to->name, address, e->options, e->weight);
send_request(c, "%d %d %s to %s at %s options %x weight %d",
CONTROL, REQ_DUMP_EDGES,
e->from->name, e->to->name, address,
e->options, e->weight);
free(address);
}
}
logger(LOG_DEBUG, "End of edges.");
return send_request(c, "%d %d", CONTROL, REQ_DUMP_EDGES);
}

View file

@ -1,6 +1,3 @@
#ifndef TINC_EDGE_H
#define TINC_EDGE_H
/*
edge.h -- header for edge.c
Copyright (C) 2001-2006 Guus Sliepen <guus@tinc-vpn.org>,
@ -21,7 +18,10 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "avl_tree.h"
#ifndef __TINC_EDGE_H__
#define __TINC_EDGE_H__
#include "splay_tree.h"
#include "connection.h"
#include "net.h"
#include "node.h"
@ -31,24 +31,24 @@ typedef struct edge_t {
struct node_t *to;
sockaddr_t address;
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
} edge_t;
extern avl_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern splay_tree_t *edge_weight_tree; /* Tree with all known edges sorted on weight */
extern void init_edges(void);
extern void exit_edges(void);
extern edge_t *new_edge(void) __attribute__((__malloc__));
extern void free_edge(edge_t *e);
extern avl_tree_t *new_edge_tree(void) __attribute__((__malloc__));
extern void free_edge_tree(avl_tree_t *edge_tree);
extern void edge_add(edge_t *e);
extern void edge_del(edge_t *e);
extern edge_t *lookup_edge(struct node_t *from, struct node_t *to);
extern void dump_edges(void);
extern edge_t *new_edge(void) __attribute__ ((__malloc__));
extern void free_edge(edge_t *);
extern splay_tree_t *new_edge_tree(void) __attribute__ ((__malloc__));
extern void free_edge_tree(splay_tree_t *);
extern void edge_add(edge_t *);
extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
extern bool dump_edges(struct connection_t *);
#endif
#endif /* __TINC_EDGE_H__ */

View file

@ -1,6 +1,3 @@
#ifndef TINC_ETHERNET_H
#define TINC_ETHERNET_H
/*
ethernet.h -- missing Ethernet related definitions
Copyright (C) 2005 Ivo Timmermans
@ -21,6 +18,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_ETHERNET_H__
#define __TINC_ETHERNET_H__
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
@ -41,16 +41,12 @@
#define ETH_P_IPV6 0x86DD
#endif
#ifndef ETH_P_8021Q
#define ETH_P_8021Q 0x8100
#endif
#ifndef HAVE_STRUCT_ETHER_HEADER
struct ether_header {
uint8_t ether_dhost[ETH_ALEN];
uint8_t ether_shost[ETH_ALEN];
uint16_t ether_type;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#endif
#ifndef HAVE_STRUCT_ARPHDR
@ -58,17 +54,17 @@ struct arphdr {
uint16_t ar_hrd;
uint16_t ar_pro;
uint8_t ar_hln;
uint8_t ar_pln;
uint16_t ar_op;
} __attribute__((__packed__));
uint8_t ar_pln;
uint16_t ar_op;
} __attribute__ ((__packed__));
#define ARPOP_REQUEST 1
#define ARPOP_REPLY 2
#define ARPOP_RREQUEST 3
#define ARPOP_RREPLY 4
#define ARPOP_InREQUEST 8
#define ARPOP_InREPLY 9
#define ARPOP_NAK 10
#define ARPOP_REQUEST 1
#define ARPOP_REPLY 2
#define ARPOP_RREQUEST 3
#define ARPOP_RREPLY 4
#define ARPOP_InREQUEST 8
#define ARPOP_InREPLY 9
#define ARPOP_NAK 10
#endif
#ifndef HAVE_STRUCT_ETHER_ARP
@ -78,7 +74,7 @@ struct ether_arp {
uint8_t arp_spa[4];
uint8_t arp_tha[ETH_ALEN];
uint8_t arp_tpa[4];
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#define arp_hrd ea_hdr.ar_hrd
#define arp_pro ea_hdr.ar_pro
#define arp_hln ea_hdr.ar_hln
@ -86,4 +82,4 @@ struct ether_arp {
#define arp_op ea_hdr.ar_op
#endif
#endif
#endif /* __TINC_ETHERNET_H__ */

View file

@ -1,121 +0,0 @@
/*
event.c -- event queue
Copyright (C) 2002-2009 Guus Sliepen <guus@tinc-vpn.org>,
2002-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "avl_tree.h"
#include "event.h"
#include "utils.h"
#include "xalloc.h"
avl_tree_t *event_tree;
extern time_t now;
static int id;
static int event_compare(const event_t *a, const event_t *b) {
if(a->time > b->time) {
return 1;
}
if(a->time < b->time) {
return -1;
}
return a->id - b->id;
}
void init_events(void) {
event_tree = avl_alloc_tree((avl_compare_t) event_compare, (avl_action_t) free_event);
}
void exit_events(void) {
avl_delete_tree(event_tree);
}
void expire_events(void) {
avl_node_t *node;
event_t *event;
time_t diff;
/*
* Make all events appear expired by subtracting the difference between
* the expiration time of the last event and the current time.
*/
if(!event_tree->tail) {
return;
}
event = event_tree->tail->data;
if(event->time <= now) {
return;
}
diff = event->time - now;
for(node = event_tree->head; node; node = node->next) {
event = node->data;
event->time -= diff;
}
}
event_t *new_event(void) {
return xmalloc_and_zero(sizeof(event_t));
}
void free_event(event_t *event) {
free(event);
}
void event_add(event_t *event) {
event->id = ++id;
avl_insert(event_tree, event);
}
void event_del(event_t *event) {
avl_delete(event_tree, event);
}
event_t *get_expired_event(void) {
event_t *event;
if(event_tree->head) {
event = event_tree->head->data;
if(event->time <= now) {
avl_node_t *node = event_tree->head;
avl_unlink_node(event_tree, node);
free(node);
return event;
}
}
return NULL;
}
event_t *peek_next_event(void) {
if(event_tree->head) {
return event_tree->head->data;
}
return NULL;
}

19
src/fake-gai-errnos.h Normal file
View file

@ -0,0 +1,19 @@
/*
* fake library for ssh
*
* This file is included in getaddrinfo.c and getnameinfo.c.
* See getaddrinfo.c and getnameinfo.c.
*/
/* for old netdb.h */
#ifndef EAI_NODATA
#define EAI_NODATA 1
#endif
#ifndef EAI_MEMORY
#define EAI_MEMORY 2
#endif
#ifndef EAI_FAMILY
#define EAI_FAMILY 3
#endif

View file

@ -2,10 +2,10 @@
* fake library for ssh
*
* This file includes getaddrinfo(), freeaddrinfo() and gai_strerror().
* These functions are defined in rfc2133.
* These funtions are defined in rfc2133.
*
* But these functions are not implemented correctly. The minimum subset
* is implemented for ssh use only. For example, this routine assumes
* is implemented for ssh use only. For exapmle, this routine assumes
* that ai_family is AF_INET. Don't use it for another purpose.
*/
@ -16,22 +16,20 @@
#include "fake-getaddrinfo.h"
#include "xalloc.h"
#if !HAVE_DECL_GAI_STRERROR
char *gai_strerror(int ecode) {
switch(ecode) {
case EAI_NODATA:
return "No address associated with hostname";
case EAI_MEMORY:
return "Memory allocation failure";
case EAI_FAMILY:
return "Address family not supported";
default:
return "Unknown error";
switch (ecode) {
case EAI_NODATA:
return "No address associated with hostname";
case EAI_MEMORY:
return "Memory allocation failure";
case EAI_FAMILY:
return "Address family not supported";
default:
return "Unknown error";
}
}
}
#endif /* !HAVE_GAI_STRERROR */
#if !HAVE_DECL_FREEADDRINFO
@ -51,14 +49,14 @@ static struct addrinfo *malloc_ai(uint16_t port, uint32_t addr) {
struct addrinfo *ai;
ai = xmalloc_and_zero(sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
ai->ai_addr = (struct sockaddr *)(ai + 1);
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_addr->sa_family = ai->ai_family = AF_INET;
((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
return ai;
}
@ -69,36 +67,32 @@ int getaddrinfo(const char *hostname, const char *servname, const struct addrinf
int i;
uint16_t port = 0;
if(hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC) {
if(hints && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC)
return EAI_FAMILY;
}
if(servname) {
if (servname)
port = htons(atoi(servname));
}
if(hints && hints->ai_flags & AI_PASSIVE) {
if (hints && hints->ai_flags & AI_PASSIVE) {
*res = malloc_ai(port, htonl(0x00000000));
return 0;
}
if(!hostname) {
if (!hostname) {
*res = malloc_ai(port, htonl(0x7f000001));
return 0;
}
hp = gethostbyname(hostname);
if(!hp || !hp->h_addr_list || !hp->h_addr_list[0]) {
if(!hp || !hp->h_addr_list || !hp->h_addr_list[0])
return EAI_NODATA;
}
for(i = 0; hp->h_addr_list[i]; i++) {
for (i = 0; hp->h_addr_list[i]; i++) {
*res = malloc_ai(port, ((struct in_addr *)hp->h_addr_list[i])->s_addr);
if(prev) {
if(prev)
prev->ai_next = *res;
}
prev = *res;
}

View file

@ -1,17 +1,7 @@
#ifndef TINC_FAKE_GETADDRINFO_H
#define TINC_FAKE_GETADDRINFO_H
#ifndef _FAKE_GETADDRINFO_H
#define _FAKE_GETADDRINFO_H
#ifndef EAI_NODATA
#define EAI_NODATA 1
#endif
#ifndef EAI_MEMORY
#define EAI_MEMORY 2
#endif
#ifndef EAI_FAMILY
#define EAI_FAMILY 3
#endif
#include "fake-gai-errnos.h"
#ifndef AI_PASSIVE
# define AI_PASSIVE 1
@ -30,19 +20,19 @@
#ifndef HAVE_STRUCT_ADDRINFO
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
int ai_flags; /* AI_PASSIVE, AI_CANONNAME */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
size_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
#endif /* !HAVE_STRUCT_ADDRINFO */
#if !HAVE_DECL_GETADDRINFO
int getaddrinfo(const char *hostname, const char *servname,
int getaddrinfo(const char *hostname, const char *servname,
const struct addrinfo *hints, struct addrinfo **res);
#endif /* !HAVE_GETADDRINFO */
@ -54,4 +44,4 @@ char *gai_strerror(int ecode);
void freeaddrinfo(struct addrinfo *ai);
#endif /* !HAVE_FREEADDRINFO */
#endif
#endif /* _FAKE_GETADDRINFO_H */

View file

@ -2,10 +2,10 @@
* fake library for ssh
*
* This file includes getnameinfo().
* These functions are defined in rfc2133.
* These funtions are defined in rfc2133.
*
* But these functions are not implemented correctly. The minimum subset
* is implemented for ssh use only. For example, this routine assumes
* is implemented for ssh use only. For exapmle, this routine assumes
* that ai_family is AF_INET. Don't use it for another purpose.
*/
@ -21,43 +21,33 @@ int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t host
struct hostent *hp;
int len;
if(sa->sa_family != AF_INET) {
if(sa->sa_family != AF_INET)
return EAI_FAMILY;
}
if(serv && servlen) {
len = snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
if(len < 0 || len >= servlen) {
if(len < 0 || len >= servlen)
return EAI_MEMORY;
}
}
if(!host || !hostlen) {
if(!host || !hostlen)
return 0;
}
if(flags & NI_NUMERICHOST) {
len = snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
if(len < 0 || len >= hostlen) {
if(len < 0 || len >= hostlen)
return EAI_MEMORY;
}
return 0;
}
hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
if(!hp || !hp->h_name || !hp->h_name[0]) {
if(!hp || !hp->h_name || !hp->h_name[0])
return EAI_NODATA;
}
len = snprintf(host, hostlen, "%s", hp->h_name);
if(len < 0 || len >= hostlen) {
if(len < 0 || len >= hostlen)
return EAI_MEMORY;
}
return 0;
}

View file

@ -1,8 +1,8 @@
#ifndef TINC_FAKE_GETNAMEINFO_H
#define TINC_FAKE_GETNAMEINFO_H
#ifndef _FAKE_GETNAMEINFO_H
#define _FAKE_GETNAMEINFO_H
#if !HAVE_DECL_GETNAMEINFO
int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
size_t hostlen, char *serv, size_t servlen, int flags);
#endif /* !HAVE_GETNAMEINFO */
@ -13,4 +13,4 @@ int getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
# define NI_MAXHOST 1025
#endif /* !NI_MAXHOST */
#endif
#endif /* _FAKE_GETNAMEINFO_H */

280
src/gcrypt/cipher.c Normal file
View file

@ -0,0 +1,280 @@
/*
cipher.c -- Symmetric block cipher handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "cipher.h"
#include "logger.h"
#include "xalloc.h"
static struct {
const char *name;
int algo;
int mode;
int nid;
} ciphertable[] = {
{"none", GCRY_CIPHER_NONE, GCRY_CIPHER_MODE_NONE, 0},
{NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 92},
{"blowfish", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 91},
{NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93},
{NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94},
{NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418},
{"aes", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419},
{NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421},
{NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420},
{NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422},
{"aes192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423},
{NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425},
{NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424},
{NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426},
{"aes256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427},
{NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429},
{NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428},
};
static bool nametocipher(const char *name, int *algo, int *mode) {
size_t i;
for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
if(ciphertable[i].name && !strcasecmp(name, ciphertable[i].name)) {
*algo = ciphertable[i].algo;
*mode = ciphertable[i].mode;
return true;
}
}
return false;
}
static bool nidtocipher(int nid, int *algo, int *mode) {
size_t i;
for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
if(nid == ciphertable[i].nid) {
*algo = ciphertable[i].algo;
*mode = ciphertable[i].mode;
return true;
}
}
return false;
}
static bool ciphertonid(int algo, int mode, int *nid) {
size_t i;
for(i = 0; i < sizeof ciphertable / sizeof *ciphertable; i++) {
if(algo == ciphertable[i].algo && mode == ciphertable[i].mode) {
*nid = ciphertable[i].nid;
return true;
}
}
return false;
}
static bool cipher_open(cipher_t *cipher, int algo, int mode) {
gcry_error_t err;
if(!ciphertonid(algo, mode, &cipher->nid)) {
logger(LOG_DEBUG, "Cipher %d mode %d has no corresponding nid!", algo, mode);
return false;
}
if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) {
logger(LOG_DEBUG, "Unable to intialise cipher %d mode %d: %s", algo, mode, gcry_strerror(err));
return false;
}
cipher->keylen = gcry_cipher_get_algo_keylen(algo);
cipher->blklen = gcry_cipher_get_algo_blklen(algo);
cipher->key = xmalloc(cipher->keylen + cipher->blklen);
cipher->padding = mode == GCRY_CIPHER_MODE_ECB || mode == GCRY_CIPHER_MODE_CBC;
return true;
}
bool cipher_open_by_name(cipher_t *cipher, const char *name) {
int algo, mode;
if(!nametocipher(name, &algo, &mode)) {
logger(LOG_DEBUG, "Unknown cipher name '%s'!", name);
return false;
}
return cipher_open(cipher, algo, mode);
}
bool cipher_open_by_nid(cipher_t *cipher, int nid) {
int algo, mode;
if(!nidtocipher(nid, &algo, &mode)) {
logger(LOG_DEBUG, "Unknown cipher ID %d!", nid);
return false;
}
return cipher_open(cipher, algo, mode);
}
bool cipher_open_blowfish_ofb(cipher_t *cipher) {
return cipher_open(cipher, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB);
}
void cipher_close(cipher_t *cipher) {
if(cipher->handle) {
gcry_cipher_close(cipher->handle);
cipher->handle = NULL;
}
if(cipher->key) {
free(cipher->key);
cipher->key = NULL;
}
}
size_t cipher_keylength(const cipher_t *cipher) {
return cipher->keylen + cipher->blklen;
}
void cipher_get_key(const cipher_t *cipher, void *key) {
memcpy(key, cipher->key, cipher->keylen + cipher->blklen);
}
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
memcpy(cipher->key, key, cipher->keylen + cipher->blklen);
gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
return true;
}
bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
memcpy(cipher->key, key + len - cipher->keylen, cipher->keylen + cipher->blklen);
memcpy(cipher->key + cipher->keylen, key + len - cipher->keylen - cipher->blklen, cipher->blklen);
gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
return true;
}
bool cipher_regenerate_key(cipher_t *cipher, bool encrypt) {
gcry_create_nonce(cipher->key, cipher->keylen + cipher->blklen);
gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
return true;
}
bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
gcry_error_t err;
uint8_t pad[cipher->blklen];
if(cipher->padding) {
if(!oneshot)
return false;
size_t reqlen = ((inlen + cipher->blklen) / cipher->blklen) * cipher->blklen;
if(*outlen < reqlen) {
logger(LOG_ERR, "Error while encrypting: not enough room for padding");
return false;
}
uint8_t padbyte = reqlen - inlen;
inlen = reqlen - cipher->blklen;
for(int i = 0; i < cipher->blklen; i++)
if(i < cipher->blklen - padbyte)
pad[i] = ((uint8_t *)indata)[inlen + i];
else
pad[i] = padbyte;
}
if(oneshot)
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
if((err = gcry_cipher_encrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
return false;
}
if(cipher->padding) {
if((err = gcry_cipher_encrypt(cipher->handle, outdata + inlen, cipher->blklen, pad, cipher->blklen))) {
logger(LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
return false;
}
inlen += cipher->blklen;
}
*outlen = inlen;
return true;
}
bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
gcry_error_t err;
if(oneshot)
gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
if((err = gcry_cipher_decrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
logger(LOG_ERR, "Error while decrypting: %s", gcry_strerror(err));
return false;
}
if(cipher->padding) {
if(!oneshot)
return false;
uint8_t padbyte = ((uint8_t *)outdata)[inlen - 1];
if(padbyte == 0 || padbyte > cipher->blklen || padbyte > inlen) {
logger(LOG_ERR, "Error while decrypting: invalid padding");
return false;
}
size_t origlen = inlen - padbyte;
for(int i = inlen - 1; i >= origlen; i--)
if(((uint8_t *)outdata)[i] != padbyte) {
logger(LOG_ERR, "Error while decrypting: invalid padding");
return false;
}
*outlen = origlen;
} else
*outlen = inlen;
return true;
}
int cipher_get_nid(const cipher_t *cipher) {
return cipher->nid;
}
bool cipher_active(const cipher_t *cipher) {
return cipher->nid != 0;
}

52
src/gcrypt/cipher.h Normal file
View file

@ -0,0 +1,52 @@
/*
cipher.h -- header file cipher.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CIPHER_H__
#define __TINC_CIPHER_H__
#include <gcrypt.h>
#define CIPHER_MAX_BLOCK_SIZE 32
#define CIPHER_MAX_IV_SIZE 16
#define CIPHER_MAX_KEY_SIZE 32
typedef struct cipher {
gcry_cipher_hd_t handle;
char *key;
int nid;
uint16_t keylen;
uint16_t blklen;
bool padding;
} cipher_t;
extern bool cipher_open_by_name(struct cipher *, const char *);
extern bool cipher_open_by_nid(struct cipher *, int);
extern bool cipher_open_blowfish_ofb(struct cipher *);
extern void cipher_close(struct cipher *);
extern size_t cipher_keylength(const struct cipher *);
extern void cipher_get_key(const struct cipher *, void *);
extern bool cipher_set_key(struct cipher *, void *, bool);
extern bool cipher_set_key_from_rsa(struct cipher *, void *, size_t, bool);
extern bool cipher_regenerate_key(struct cipher *, bool);
extern bool cipher_encrypt(struct cipher *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot);
extern bool cipher_decrypt(struct cipher *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot);
extern int cipher_get_nid(const struct cipher *);
extern bool cipher_active(const struct cipher *);
#endif

34
src/gcrypt/crypto.c Normal file
View file

@ -0,0 +1,34 @@
/*
crypto.c -- Cryptographic miscellaneous functions and initialisation
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <gcrypt.h>
#include "crypto.h"
void crypto_init() {
}
void crypto_exit() {
}
void randomize(void *out, size_t outlen) {
gcry_create_nonce(out, outlen);
}

27
src/gcrypt/crypto.h Normal file
View file

@ -0,0 +1,27 @@
/*
crypto.h -- header for crypto.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CRYPTO_H__
#define __TINC_CRYPTO_H__
extern void crypto_init();
extern void crypto_exit();
extern void randomize(void *, size_t);
#endif

173
src/gcrypt/digest.c Normal file
View file

@ -0,0 +1,173 @@
/*
digest.c -- Digest handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "digest.h"
#include "logger.h"
static struct {
const char *name;
int algo;
int nid;
} digesttable[] = {
{"none", GCRY_MD_NONE, 0},
{"sha1", GCRY_MD_SHA1, 64},
{"sha256", GCRY_MD_SHA256, 672},
{"sha384", GCRY_MD_SHA384, 673},
{"sha512", GCRY_MD_SHA512, 674},
};
static bool nametodigest(const char *name, int *algo) {
int i;
for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
if(digesttable[i].name && !strcasecmp(name, digesttable[i].name)) {
*algo = digesttable[i].algo;
return true;
}
}
return false;
}
static bool nidtodigest(int nid, int *algo) {
int i;
for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
if(nid == digesttable[i].nid) {
*algo = digesttable[i].algo;
return true;
}
}
return false;
}
static bool digesttonid(int algo, int *nid) {
int i;
for(i = 0; i < sizeof digesttable / sizeof *digesttable; i++) {
if(algo == digesttable[i].algo) {
*nid = digesttable[i].nid;
return true;
}
}
return false;
}
static bool digest_open(digest_t *digest, int algo, int maclength) {
if(!digesttonid(algo, &digest->nid)) {
logger(LOG_DEBUG, "Digest %d has no corresponding nid!", algo);
return false;
}
unsigned int len = gcry_md_get_algo_dlen(algo);
if(maclength > len || maclength < 0)
digest->maclength = len;
else
digest->maclength = maclength;
digest->algo = algo;
digest->hmac = NULL;
return true;
}
bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
int algo;
if(!nametodigest(name, &algo)) {
logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
return false;
}
return digest_open(digest, algo, maclength);
}
bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
int algo;
if(!nidtodigest(nid, &algo)) {
logger(LOG_DEBUG, "Unknown digest ID %d!", nid);
return false;
}
return digest_open(digest, algo, maclength);
}
bool digest_open_sha1(digest_t *digest, int maclength) {
return digest_open(digest, GCRY_MD_SHA1, maclength);
}
void digest_close(digest_t *digest) {
if(digest->hmac)
gcry_md_close(digest->hmac);
digest->hmac = NULL;
}
bool digest_set_key(digest_t *digest, const void *key, size_t len) {
if(!digest->hmac)
gcry_md_open(&digest->hmac, digest->algo, GCRY_MD_FLAG_HMAC);
if(!digest->hmac)
return false;
return !gcry_md_setkey(digest->hmac, key, len);
}
bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
unsigned int len = gcry_md_get_algo_dlen(digest->algo);
if(digest->hmac) {
char *tmpdata;
gcry_md_reset(digest->hmac);
gcry_md_write(digest->hmac, indata, inlen);
tmpdata = gcry_md_read(digest->hmac, digest->algo);
if(!tmpdata)
return false;
memcpy(outdata, tmpdata, digest->maclength);
} else {
char tmpdata[len];
gcry_md_hash_buffer(digest->algo, tmpdata, indata, inlen);
memcpy(outdata, tmpdata, digest->maclength);
}
return true;
}
bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
unsigned int len = digest->maclength;
char outdata[len];
return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, len);
}
int digest_get_nid(const digest_t *digest) {
return digest->nid;
}
size_t digest_length(const digest_t *digest) {
return digest->maclength;
}
bool digest_active(const digest_t *digest) {
return digest->algo != GCRY_MD_NONE;
}

45
src/gcrypt/digest.h Normal file
View file

@ -0,0 +1,45 @@
/*
digest.h -- header file digest.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_DIGEST_H__
#define __TINC_DIGEST_H__
#include <gcrypt.h>
#define DIGEST_MAX_SIZE 64
typedef struct digest {
int algo;
int nid;
int maclength;
gcry_md_hd_t hmac;
} digest_t;
extern bool digest_open_by_name(struct digest *, const char *name, int maclength);
extern bool digest_open_by_nid(struct digest *, int nid, int maclength);
extern bool digest_open_sha1(struct digest *, int maclength);
extern void digest_close(struct digest *);
extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern bool digest_set_key(struct digest *, const void *key, size_t len);
extern int digest_get_nid(const struct digest *);
extern size_t digest_length(const struct digest *);
extern bool digest_active(const struct digest *);
#endif

302
src/gcrypt/rsa.c Normal file
View file

@ -0,0 +1,302 @@
/*
rsa.c -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <gcrypt.h>
#include "logger.h"
#include "rsa.h"
// Base64 decoding table
static const uint8_t b64d[128] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
0xff, 0xff
};
// PEM encoding/decoding functions
static bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
bool decode = false;
char line[1024];
uint16_t word = 0;
int shift = 10;
size_t i, j = 0;
while(!feof(fp)) {
if(!fgets(line, sizeof line, fp))
return false;
if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
if(!strncmp(line + 11, header, strlen(header)))
decode = true;
continue;
}
if(decode && !strncmp(line, "-----END", 8)) {
break;
}
if(!decode)
continue;
for(i = 0; line[i] >= ' '; i++) {
if((signed char)line[i] < 0 || b64d[(int)line[i]] == 0xff)
break;
word |= b64d[(int)line[i]] << shift;
shift -= 6;
if(shift <= 2) {
if(j > size) {
errno = ENOMEM;
return false;
}
buf[j++] = word >> 8;
word <<= 8;
shift += 8;
}
}
}
if(outsize)
*outsize = j;
return true;
}
// BER decoding functions
static int ber_read_id(unsigned char **p, size_t *buflen) {
if(*buflen <= 0)
return -1;
if((**p & 0x1f) == 0x1f) {
int id = 0;
bool more;
while(*buflen > 0) {
id <<= 7;
id |= **p & 0x7f;
more = *(*p)++ & 0x80;
(*buflen)--;
if(!more)
break;
}
return id;
} else {
(*buflen)--;
return *(*p)++ & 0x1f;
}
}
static size_t ber_read_len(unsigned char **p, size_t *buflen) {
if(*buflen <= 0)
return -1;
if(**p & 0x80) {
size_t result = 0;
int len = *(*p)++ & 0x7f;
(*buflen)--;
if(len > *buflen)
return 0;
while(len--) {
result <<= 8;
result |= *(*p)++;
(*buflen)--;
}
return result;
} else {
(*buflen)--;
return *(*p)++;
}
}
static bool ber_read_sequence(unsigned char **p, size_t *buflen, size_t *result) {
int tag = ber_read_id(p, buflen);
size_t len = ber_read_len(p, buflen);
if(tag == 0x10) {
if(result)
*result = len;
return true;
} else {
return false;
}
}
static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) {
int tag = ber_read_id(p, buflen);
size_t len = ber_read_len(p, buflen);
gcry_error_t err = 0;
if(tag != 0x02 || len > *buflen)
return false;
if(mpi)
err = gcry_mpi_scan(mpi, GCRYMPI_FMT_USG, *p, len, NULL);
*p += len;
*buflen -= len;
return mpi ? !err : true;
}
bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
gcry_error_t err = 0;
err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
?: gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL);
if(err) {
logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
return false;
}
return true;
}
bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
gcry_error_t err = 0;
err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
?: gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL)
?: gcry_mpi_scan(&rsa->d, GCRYMPI_FMT_HEX, d, 0, NULL);
if(err) {
logger(LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
return false;
}
return true;
}
// Read PEM RSA keys
bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
uint8_t derbuf[8096], *derp = derbuf;
size_t derlen;
if(!pem_decode(fp, "RSA PUBLIC KEY", derbuf, sizeof derbuf, &derlen)) {
logger(LOG_ERR, "Unable to read RSA public key: %s", strerror(errno));
return NULL;
}
if(!ber_read_sequence(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, &rsa->n)
|| !ber_read_mpi(&derp, &derlen, &rsa->e)
|| derlen) {
logger(LOG_ERR, "Error while decoding RSA public key");
return NULL;
}
return true;
}
bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
uint8_t derbuf[8096], *derp = derbuf;
size_t derlen;
if(!pem_decode(fp, "RSA PRIVATE KEY", derbuf, sizeof derbuf, &derlen)) {
logger(LOG_ERR, "Unable to read RSA private key: %s", strerror(errno));
return NULL;
}
if(!ber_read_sequence(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, &rsa->n)
|| !ber_read_mpi(&derp, &derlen, &rsa->e)
|| !ber_read_mpi(&derp, &derlen, &rsa->d)
|| !ber_read_mpi(&derp, &derlen, NULL) // p
|| !ber_read_mpi(&derp, &derlen, NULL) // q
|| !ber_read_mpi(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, NULL)
|| !ber_read_mpi(&derp, &derlen, NULL) // u
|| derlen) {
logger(LOG_ERR, "Error while decoding RSA private key");
return NULL;
}
return true;
}
size_t rsa_size(rsa_t *rsa) {
return (gcry_mpi_get_nbits(rsa->n) + 7) / 8;
}
/* Well, libgcrypt has functions to handle RSA keys, but they suck.
* So we just use libgcrypt's mpi functions, and do the math ourselves.
*/
// TODO: get rid of this macro, properly clean up gcry_ structures after use
#define check(foo) { gcry_error_t err = (foo); if(err) {logger(LOG_ERR, "gcrypt error %s/%s at %s:%d", gcry_strsource(err), gcry_strerror(err), __FILE__, __LINE__); return false; }}
bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
gcry_mpi_t inmpi;
check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL));
gcry_mpi_t outmpi = gcry_mpi_new(len * 8);
gcry_mpi_powm(outmpi, inmpi, rsa->e, rsa->n);
int pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
while(pad--)
*(char *)out++ = 0;
check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi));
return true;
}
bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
gcry_mpi_t inmpi;
check(gcry_mpi_scan(&inmpi, GCRYMPI_FMT_USG, in, len, NULL));
gcry_mpi_t outmpi = gcry_mpi_new(len * 8);
gcry_mpi_powm(outmpi, inmpi, rsa->d, rsa->n);
int pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
while(pad--)
*(char *)out++ = 0;
check(gcry_mpi_print(GCRYMPI_FMT_USG, out,len, NULL, outmpi));
return true;
}

39
src/gcrypt/rsa.h Normal file
View file

@ -0,0 +1,39 @@
/*
rsa.h -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_RSA_H__
#define __TINC_RSA_H__
#include <gcrypt.h>
typedef struct rsa {
gcry_mpi_t n;
gcry_mpi_t e;
gcry_mpi_t d;
} rsa_t;
extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e);
extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d);
extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp);
extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp);
extern size_t rsa_size(rsa_t *rsa);
extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out);
extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out);
#endif

220
src/gcrypt/rsagen.c Normal file
View file

@ -0,0 +1,220 @@
/*
rsagen.c -- RSA key generation and export
Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <gcrypt.h>
#include "rsagen.h"
#if 0
// Base64 encoding table
static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// PEM encoding
static bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
bool decode = false;
char line[1024];
uint32_t word = 0;
int shift = 0;
size_t i, j = 0;
fprintf(fp, "-----BEGIN %s-----\n", header);
for(i = 0; i < size; i += 3) {
if(i <= size - 3) {
word = buf[i] << 16 | buf[i + 1] << 8 | buf[i + 2];
} else {
word = buf[i] << 16;
if(i == size - 2)
word |= buf[i + 1] << 8;
}
line[j++] = b64e[(word >> 18) ];
line[j++] = b64e[(word >> 12) & 0x3f];
line[j++] = b64e[(word >> 6) & 0x3f];
line[j++] = b64e[(word ) & 0x3f];
if(j >= 64) {
line[j++] = '\n';
line[j] = 0;
fputs(line, fp);
j = 0;
}
}
if(size % 3 > 0) {
if(size % 3 > 1)
line[j++] = '=';
line[j++] = '=';
}
if(j) {
line[j++] = '\n';
line[j] = 0;
fputs(line, fp);
}
fprintf(fp, "-----END %s-----\n", header);
return true;
}
// BER encoding functions
static bool ber_write_id(uint8_t **p, size_t *buflen, int id) {
if(*buflen <= 0)
return false;
if(id >= 0x1f) {
while(id) {
if(*buflen <= 0)
return false;
(*buflen)--;
**p = id & 0x7f;
id >>= 7;
if(id)
**p |= 0x80;
(*p)++;
}
} else {
(*buflen)--;
*(*p)++ = id;
}
return true;
}
static bool ber_write_len(uint8_t **p, size_t *buflen, size_t len) {
do {
if(*buflen <= 0)
return false;
(*buflen)--;
**p = len & 0x7f;
len >>= 7;
if(len)
**p |= 0x80;
(*p)++;
} while(len);
return true;
}
static bool ber_write_sequence(uint8_t **p, size_t *buflen, uint8_t *seqbuf, size_t seqlen) {
if(!ber_write_id(p, buflen, 0x10) || !ber_write_len(p, buflen, seqlen) || *buflen < seqlen)
return false;
memcpy(*p, seqbuf, seqlen);
*p += seqlen;
*buflen -= seqlen;
return true;
}
static bool ber_write_mpi(uint8_t **p, size_t *buflen, gcry_mpi_t mpi) {
uint8_t tmpbuf[1024];
size_t tmplen = sizeof tmpbuf;
gcry_error_t err;
err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &tmpbuf, &tmplen, mpi);
if(err)
return false;
if(!ber_write_id(p, buflen, 0x02) || !ber_write_len(p, buflen, tmplen) || *buflen < tmplen)
return false;
memcpy(*p, tmpbuf, tmplen);
*p += tmplen;
*buflen -= tmplen;
return true;
}
// Write PEM RSA keys
bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
uint8_t derbuf1[8096];
uint8_t derbuf2[8096];
uint8_t *derp1 = derbuf1;
uint8_t *derp2 = derbuf2;
size_t derlen1 = sizeof derbuf1;
size_t derlen2 = sizeof derbuf2;
if(!ber_write_mpi(&derp1, &derlen1, &rsa->n)
|| !ber_write_mpi(&derp1, &derlen1, &rsa->e)
|| !ber_write_sequence(&derp2, &derlen2, derbuf1, derlen1)) {
logger(LOG_ERR, "Error while encoding RSA public key");
return false;
}
if(!pem_encode(fp, "RSA PUBLIC KEY", derbuf2, derlen2)) {
logger(LOG_ERR, "Unable to write RSA public key: %s", strerror(errno));
return false;
}
return true;
}
bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
uint8_t derbuf1[8096];
uint8_t derbuf2[8096];
uint8_t *derp1 = derbuf1;
uint8_t *derp2 = derbuf2;
size_t derlen1 = sizeof derbuf1;
size_t derlen2 = sizeof derbuf2;
if(!ber_write_mpi(&derp1, &derlen1, &bits)
|| ber_write_mpi(&derp1, &derlen1, &rsa->n) // modulus
|| ber_write_mpi(&derp1, &derlen1, &rsa->e) // public exponent
|| ber_write_mpi(&derp1, &derlen1, &rsa->d) // private exponent
|| ber_write_mpi(&derp1, &derlen1, &p)
|| ber_write_mpi(&derp1, &derlen1, &q)
|| ber_write_mpi(&derp1, &derlen1, &exp1)
|| ber_write_mpi(&derp1, &derlen1, &exp2)
|| ber_write_mpi(&derp1, &derlen1, &coeff))
logger(LOG_ERR, "Error while encoding RSA private key");
return false;
}
if(!pem_encode(fp, "RSA PRIVATE KEY", derbuf2, derlen2)) {
logger(LOG_ERR, "Unable to write RSA private key: %s", strerror(errno));
return false;
}
return true;
}
#endif
bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
return false;
}
bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
return false;
}
bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) {
fprintf(stderr, "Generating RSA keys with libgcrypt not implemented yet\n");
return false;
}

View file

@ -1,10 +1,6 @@
#ifndef TINC_SYSTEM_H
#define TINC_SYSTEM_H
/*
system.h -- system headers
Copyright (C) 1998-2005 Ivo Timmermans
2003-2006 Guus Sliepen <guus@tinc-vpn.org>
rsagen.h -- RSA key generation and export
Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,20 +17,13 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#ifndef __TINC_RSAGEN_H__
#define __TINC_RSAGEN_H__
#include "have.h"
#include "rsa.h"
#ifndef HAVE_STRSIGNAL
# define strsignal(p) ""
#endif
/* Other functions */
#include "dropin.h"
#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
extern bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent);
extern bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp);
extern bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,3 @@
#ifndef TINC_GETOPT_H
#define TINC_GETOPT_H
/* Declarations for getopt.
Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
@ -22,111 +19,115 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef cplusplus
#ifndef _GETOPT_H
#define _GETOPT_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here.
Also, when `ordering' is RETURN_IN_ORDER,
each non-option ARGV-element is returned here. */
/* For communication from `getopt' to the caller.
When `getopt' finds an option that takes an argument,
the argument value is returned here.
Also, when `ordering' is RETURN_IN_ORDER,
each non-option ARGV-element is returned here. */
extern char *optarg;
extern char *optarg;
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
/* Index in ARGV of the next element to be scanned.
This is used for communication to and from the caller
and for communication between successive calls to `getopt'.
On entry to `getopt', zero means this is the first call; initialize.
On entry to `getopt', zero means this is the first call; initialize.
When `getopt' returns -1, this is the index of the first of the
non-option elements that the caller should itself scan.
When `getopt' returns -1, this is the index of the first of the
non-option elements that the caller should itself scan.
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
Otherwise, `optind' communicates from one call to the next
how much of ARGV has been scanned so far. */
extern int optind;
extern int optind;
/* Callers store zero here to inhibit the error message `getopt' prints
for unrecognized options. */
/* Callers store zero here to inhibit the error message `getopt' prints
for unrecognized options. */
extern int opterr;
extern int opterr;
/* Set to an option character which was unrecognized. */
/* Set to an option character which was unrecognized. */
extern int optopt;
extern int optopt;
/* Describe the long-named options requested by the application.
The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
of `struct option' terminated by an element containing a name which is
zero.
/* Describe the long-named options requested by the application.
The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
of `struct option' terminated by an element containing a name which is
zero.
The field `has_arg' is:
no_argument (or 0) if the option does not take an argument,
required_argument (or 1) if the option requires an argument,
optional_argument (or 2) if the option takes an optional argument.
The field `has_arg' is:
no_argument (or 0) if the option does not take an argument,
required_argument (or 1) if the option requires an argument,
optional_argument (or 2) if the option takes an optional argument.
If the field `flag' is not NULL, it points to a variable that is set
to the value given in the field `val' when the option is found, but
left unchanged if the option is not found.
If the field `flag' is not NULL, it points to a variable that is set
to the value given in the field `val' when the option is found, but
left unchanged if the option is not found.
To have a long-named option do something other than set an `int' to
a compiled-in constant, such as set a value from `optarg', set the
option's `flag' field to zero and its `val' field to a nonzero
value (the equivalent single-letter option character, if there is
one). For long options that have a zero `flag' field, `getopt'
returns the contents of the `val' field. */
To have a long-named option do something other than set an `int' to
a compiled-in constant, such as set a value from `optarg', set the
option's `flag' field to zero and its `val' field to a nonzero
value (the equivalent single-letter option character, if there is
one). For long options that have a zero `flag' field, `getopt'
returns the contents of the `val' field. */
struct option {
struct option
{
#if defined (__STDC__) && __STDC__
const char *name;
const char *name;
#else
char *name;
char *name;
#endif
/* has_arg can't be an enum because some compilers complain about
type mismatches in all the code that assumes it is an int. */
int has_arg;
int *flag;
int val;
};
/* has_arg can't be an enum because some compilers complain about
type mismatches in all the code that assumes it is an int. */
int has_arg;
int *flag;
int val;
};
/* Names for the values of the `has_arg' field of `struct option'. */
/* Names for the values of the `has_arg' field of `struct option'. */
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#if defined (__STDC__) && __STDC__
#ifdef __GNU_LIBRARY__
/* Many other libraries have conflicting prototypes for getopt, with
differences in the consts, in stdlib.h. To avoid compilation
errors, only prototype getopt for the GNU C library. */
extern int getopt(int argc, char *const *argv, const char *shortopts);
/* Many other libraries have conflicting prototypes for getopt, with
differences in the consts, in stdlib.h. To avoid compilation
errors, only prototype getopt for the GNU C library. */
extern int getopt (int argc, char *const *argv, const char *shortopts);
#else /* not __GNU_LIBRARY__ */
extern int getopt();
extern int getopt ();
#endif /* __GNU_LIBRARY__ */
extern int getopt_long(int argc, char *const *argv, const char *shortopts,
const struct option *longopts, int *longind);
extern int getopt_long_only(int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind);
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
const struct option *longopts, int *longind);
extern int getopt_long_only (int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind);
/* Internal only. Users should not call this directly. */
extern int _getopt_internal(int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind,
int long_only);
/* Internal only. Users should not call this directly. */
extern int _getopt_internal (int argc, char *const *argv,
const char *shortopts,
const struct option *longopts, int *longind,
int long_only);
#else /* not __STDC__ */
extern int getopt();
extern int getopt_long();
extern int getopt_long_only();
extern int getopt ();
extern int getopt_long ();
extern int getopt_long_only ();
extern int _getopt_internal();
extern int _getopt_internal ();
#endif /* __STDC__ */
#ifdef cplusplus
#ifdef __cplusplus
}
#endif
#endif
#endif /* _GETOPT_H */

View file

@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#include <config.h>
#endif
#include "getopt.h"
@ -60,19 +60,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <stdlib.h>
#endif
#ifndef NULL
#ifndef NULL
#define NULL 0
#endif
int
getopt_long(argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
getopt_long (argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
{
return _getopt_internal(argc, argv, options, long_options, opt_index, 0);
return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}
/* Like getopt_long, but '-' as well as '--' can indicate a long option.
@ -81,115 +81,109 @@ int *opt_index;
instead. */
int
getopt_long_only(argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
getopt_long_only (argc, argv, options, long_options, opt_index)
int argc;
char *const *argv;
const char *options;
const struct option *long_options;
int *opt_index;
{
return _getopt_internal(argc, argv, options, long_options, opt_index, 1);
return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}
#endif /* Not ELIDE_CODE. */
#endif /* Not ELIDE_CODE. */
#ifdef TEST
#include <stdio.h>
int
main(argc, argv)
int argc;
char **argv;
main (argc, argv)
int argc;
char **argv;
{
int c;
int digit_optind = 0;
int c;
int digit_optind = 0;
while(1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"add", 1, 0, 0},
{"append", 0, 0, 0},
{"delete", 1, 0, 0},
{"verbose", 0, 0, 0},
{"create", 0, 0, 0},
{"file", 1, 0, 0},
{0, 0, 0, 0}
};
while (1)
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] =
{
{"add", 1, 0, 0},
{"append", 0, 0, 0},
{"delete", 1, 0, 0},
{"verbose", 0, 0, 0},
{"create", 0, 0, 0},
{"file", 1, 0, 0},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "abc:d:0123456789",
long_options, &option_index);
c = getopt_long (argc, argv, "abc:d:0123456789",
long_options, &option_index);
if (c == -1)
break;
if(c == -1) {
break;
}
switch (c)
{
case 0:
printf ("option %s", long_options[option_index].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
switch(c) {
case 0:
printf("option %s", long_options[option_index].name);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (digit_optind != 0 && digit_optind != this_option_optind)
printf ("digits occur in two different argv-elements.\n");
digit_optind = this_option_optind;
printf ("option %c\n", c);
break;
if(optarg) {
printf(" with arg %s", optarg);
}
case 'a':
printf ("option a\n");
break;
printf("\n");
break;
case 'b':
printf ("option b\n");
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if(digit_optind != 0 && digit_optind != this_option_optind) {
printf("digits occur in two different argv-elements.\n");
}
case 'c':
printf ("option c with value `%s'\n", optarg);
break;
digit_optind = this_option_optind;
printf("option %c\n", c);
break;
case 'd':
printf ("option d with value `%s'\n", optarg);
break;
case 'a':
printf("option a\n");
break;
case '?':
break;
case 'b':
printf("option b\n");
break;
case 'c':
printf("option c with value `%s'\n", optarg);
break;
case 'd':
printf("option d with value `%s'\n", optarg);
break;
case '?':
break;
default:
printf("?? getopt returned character code 0%o ??\n", c);
}
default:
printf ("?? getopt returned character code 0%o ??\n", c);
}
}
if(optind < argc) {
printf("non-option ARGV-elements: ");
if (optind < argc)
{
printf ("non-option ARGV-elements: ");
while (optind < argc)
printf ("%s ", argv[optind++]);
printf ("\n");
}
while(optind < argc) {
printf("%s ", argv[optind++]);
}
printf("\n");
}
exit(0);
exit (0);
}
#endif /* TEST */

View file

@ -1,6 +1,6 @@
/*
graph.c -- graph algorithms
Copyright (C) 2001-2014 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2011 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -44,8 +44,8 @@
#include "system.h"
#include "avl_tree.h"
#include "conf.h"
#include "splay_tree.h"
#include "config.h"
#include "connection.h"
#include "device.h"
#include "edge.h"
@ -58,22 +58,18 @@
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
static bool graph_changed = true;
#include "graph.h"
/* Implementation of Kruskal's algorithm.
Running time: O(EN)
Running time: O(E)
Please note that sorting on weight is already done by add_edge().
*/
static void mst_kruskal(void) {
avl_node_t *node, *next;
void mst_kruskal(void) {
splay_node_t *node, *next;
edge_t *e;
node_t *n;
connection_t *c;
int nodes = 0;
int safe_edges = 0;
bool skipped;
/* Clear MST status on connections */
@ -82,12 +78,6 @@ static void mst_kruskal(void) {
c->status.mst = false;
}
/* Do we have something to do at all? */
if(!edge_weight_tree->head) {
return;
}
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:");
/* Clear visited status on nodes */
@ -95,73 +85,144 @@ static void mst_kruskal(void) {
for(node = node_tree->head; node; node = node->next) {
n = node->data;
n->status.visited = false;
nodes++;
}
/* Starting point */
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
if(e->from->status.reachable) {
e->from->status.visited = true;
break;
}
}
/* Add safe edges */
for(skipped = false, node = edge_weight_tree->head; node; node = next) {
for(node = edge_weight_tree->head; node; node = next) {
next = node->next;
e = node->data;
if(!e->reverse || e->from->status.visited == e->to->status.visited) {
skipped = true;
if(!e->reverse || (e->from->status.visited && e->to->status.visited))
continue;
}
e->from->status.visited = true;
e->to->status.visited = true;
if(e->connection) {
if(e->connection)
e->connection->status.mst = true;
}
if(e->reverse->connection) {
if(e->reverse->connection)
e->reverse->connection->status.mst = true;
}
safe_edges++;
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name,
e->to->name, e->weight);
e->to->name, e->weight);
}
}
if(skipped) {
skipped = false;
next = edge_weight_tree->head;
continue;
/* Implementation of Dijkstra's algorithm.
Running time: O(N^2)
*/
static void sssp_dijkstra(void) {
splay_node_t *node, *to;
edge_t *e;
node_t *n, *m;
list_t *todo_list;
list_node_t *lnode, *nnode;
bool indirect;
todo_list = list_alloc(NULL);
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Dijkstra's algorithm:");
/* Clear visited status on nodes */
for(node = node_tree->head; node; node = node->next) {
n = node->data;
n->status.visited = false;
n->status.indirect = true;
n->distance = -1;
}
/* Begin with myself */
myself->status.indirect = false;
myself->nexthop = myself;
myself->via = myself;
myself->distance = 0;
list_insert_head(todo_list, myself);
/* Loop while todo_list is filled */
while(todo_list->head) {
n = NULL;
nnode = NULL;
/* Select node from todo_list with smallest distance */
for(lnode = todo_list->head; lnode; lnode = lnode->next) {
m = lnode->data;
if(!n || m->status.indirect < n->status.indirect || m->distance < n->distance) {
n = m;
nnode = lnode;
}
}
/* Mark this node as visited and remove it from the todo_list */
n->status.visited = true;
list_unlink_node(todo_list, nnode);
/* Update distance of neighbours and add them to the todo_list */
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
e = to->data;
if(e->to->status.visited || !e->reverse)
continue;
/* Situation:
/
/
----->(n)---e-->(e->to)
\
\
Where e is an edge, (n) and (e->to) are nodes.
n->address is set to the e->address of the edge left of n to n.
We are currently examining the edge e right of n from n:
- If edge e provides for better reachability of e->to, update e->to.
*/
if(e->to->distance < 0)
list_insert_tail(todo_list, e->to);
indirect = n->status.indirect || e->options & OPTION_INDIRECT || ((n != myself) && sockaddrcmp(&n->address, &e->reverse->address));
if(e->to->distance >= 0 && (!e->to->status.indirect || indirect) && e->to->distance <= n->distance + e->weight)
continue;
e->to->distance = n->distance + e->weight;
e->to->status.indirect = indirect;
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
update_node_udp(e->to, &e->address);
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Updating edge %s - %s weight %d distance %d", e->from->name,
e->to->name, e->weight, e->to->distance);
}
}
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes,
safe_edges);
list_free(todo_list);
}
/* Implementation of a simple breadth-first search algorithm.
Running time: O(E)
*/
static void sssp_bfs(void) {
avl_node_t *node, *next, *to;
void sssp_bfs(void) {
splay_node_t *node, *to;
edge_t *e;
node_t *n;
list_t *todo_list;
list_node_t *from, *todonext;
bool indirect;
char *name;
char *address, *port;
char *envp[8] = {NULL};
int i;
todo_list = list_alloc(NULL);
@ -178,29 +239,27 @@ static void sssp_bfs(void) {
myself->status.visited = true;
myself->status.indirect = false;
myself->nexthop = myself;
myself->prevedge = NULL;
myself->via = myself;
list_insert_head(todo_list, myself);
/* Loop while todo_list is filled */
for(from = todo_list->head; from; from = todonext) { /* "from" is the node from which we start */
for(from = todo_list->head; from; from = todonext) { /* "from" is the node from which we start */
n = from->data;
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */
e = to->data;
if(!e->reverse) {
if(!e->reverse)
continue;
}
/* Situation:
/
/
/
/
----->(n)---e-->(e->to)
\
\
\
\
Where e is an edge, (n) and (e->to) are nodes.
n->address is set to the e->address of the edge left of n to n.
@ -214,25 +273,17 @@ static void sssp_bfs(void) {
indirect = n->status.indirect || e->options & OPTION_INDIRECT;
if(e->to->status.visited
&& (!e->to->status.indirect || indirect)) {
&& (!e->to->status.indirect || indirect))
continue;
}
// Only update nexthop the first time we visit this node.
if(!e->to->status.visited) {
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
}
e->to->status.visited = true;
e->to->status.indirect = indirect;
e->to->prevedge = e;
e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop;
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN) {
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
update_node_udp(e->to, &e->address);
}
list_insert_tail(todo_list, e->to);
}
@ -242,6 +293,15 @@ static void sssp_bfs(void) {
}
list_free(todo_list);
}
static void check_reachability(void) {
splay_node_t *node, *next;
node_t *n;
char *name;
char *address, *port;
char *envp[7];
int i;
/* Check reachability status. */
@ -254,10 +314,10 @@ static void sssp_bfs(void) {
if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became reachable",
n->name, n->hostname);
n->name, n->hostname);
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became unreachable",
n->name, n->hostname);
n->name, n->hostname);
}
/* TODO: only clear status.validkey if node is unreachable? */
@ -269,122 +329,45 @@ static void sssp_bfs(void) {
n->minmtu = 0;
n->mtuprobes = 0;
if(n->mtuevent) {
event_del(n->mtuevent);
n->mtuevent = NULL;
}
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
xasprintf(&envp[0], "NETNAME=%s", netname ? netname : "");
xasprintf(&envp[1], "DEVICE=%s", device ? device : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? iface : "");
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NODE=%s", n->name);
sockaddr2str(&n->address, &address, &port);
xasprintf(&envp[4], "REMOTEADDRESS=%s", address);
xasprintf(&envp[5], "REMOTEPORT=%s", port);
xasprintf(&envp[6], "NAME=%s", myself->name);
envp[6] = NULL;
execute_script(n->status.reachable ? "host-up" : "host-down", envp);
xasprintf(&name,
n->status.reachable ? "hosts/%s-up" : "hosts/%s-down",
n->name);
n->status.reachable ? "hosts/%s-up" : "hosts/%s-down",
n->name);
execute_script(name, envp);
free(name);
free(address);
free(port);
for(i = 0; i < 7; i++) {
for(i = 0; i < 6; i++)
free(envp[i]);
}
subnet_update(n, NULL, n->status.reachable);
if(!n->status.reachable) {
if(!n->status.reachable)
update_node_udp(n, NULL);
memset(&n->status, 0, sizeof(n->status));
n->options = 0;
} else if(n->connection) {
else if(n->connection)
send_ans_key(n);
}
}
}
}
void graph(void) {
subnet_cache_flush();
sssp_bfs();
sssp_dijkstra();
check_reachability();
mst_kruskal();
graph_changed = true;
}
/* Dump nodes and edges to a graphviz file.
The file can be converted to an image with
dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true
*/
void dump_graph(void) {
avl_node_t *node;
node_t *n;
edge_t *e;
char *filename = NULL, *tmpname = NULL;
FILE *file, *pipe = NULL;
if(!graph_changed || !get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename)) {
return;
}
graph_changed = false;
ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph");
if(filename[0] == '|') {
file = pipe = popen(filename + 1, "w");
} else {
xasprintf(&tmpname, "%s.new", filename);
file = fopen(tmpname, "w");
}
if(!file) {
logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno));
free(filename);
free(tmpname);
return;
}
fprintf(file, "digraph {\n");
/* dump all nodes first */
for(node = node_tree->head; node; node = node->next) {
n = node->data;
fprintf(file, " %s [label = \"%s\"];\n", n->name, n->name);
}
/* now dump all edges */
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
fprintf(file, " %s -> %s;\n", e->from->name, e->to->name);
}
fprintf(file, "}\n");
if(pipe) {
pclose(pipe);
} else {
fclose(file);
#ifdef HAVE_MINGW
unlink(filename);
#endif
if(rename(tmpname, filename)) {
logger(LOG_ERR, "Could not rename %s to %s: %s\n", tmpname, filename, strerror(errno));
}
free(tmpname);
}
free(filename);
}

View file

@ -1,9 +1,6 @@
#ifndef TINC_GRAPH_H
#define TINC_GRAPH_H
/*
graph.h -- header for graph.c
Copyright (C) 2001-2012 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2006 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -21,7 +18,10 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_GRAPH_H__
#define __TINC_GRAPH_H__
extern void graph(void);
extern void dump_graph(void);
#endif
#endif /* __TINC_GRAPH_H__ */

View file

@ -1,214 +0,0 @@
#ifndef TINC_HAVE_H
#define TINC_HAVE_H
/*
have.h -- include headers which are known to exist
Copyright (C) 1998-2005 Ivo Timmermans
2003-2015 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.
*/
#ifdef HAVE_MINGW
#ifdef WITH_WINDOWS2000
#define WINVER Windows2000
#else
#define WINVER WindowsXP
#endif
#endif
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#ifdef HAVE_MINGW
#include <w32api.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#endif
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
/* Include system specific headers */
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
/* SunOS really wants sys/socket.h BEFORE net/if.h,
and FreeBSD wants these lines below the rest. */
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NET_IF_TYPES_H
#include <net/if_types.h>
#endif
#ifdef HAVE_NET_IF_TUN_H
#include <net/if_tun.h>
#endif
#ifdef HAVE_NET_TUN_IF_TUN_H
#include <net/tun/if_tun.h>
#endif
#ifdef HAVE_NET_IF_TAP_H
#include <net/if_tap.h>
#endif
#ifdef HAVE_NET_TAP_IF_TAP_H
#include <net/tap/if_tap.h>
#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
#ifdef HAVE_NETINET_IP6_H
#include <netinet/ip6.h>
#endif
#ifdef HAVE_NET_ETHERNET_H
#include <net/ethernet.h>
#endif
#ifdef HAVE_NET_IF_ARP_H
#include <net/if_arp.h>
#endif
#ifdef HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#ifdef HAVE_NETINET_ICMP6_H
#include <netinet/icmp6.h>
#endif
#ifdef HAVE_NETINET_IF_ETHER_H
#include <netinet/if_ether.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#ifdef STATUS
#undef STATUS
#endif
#endif
#ifdef HAVE_RESOLV_H
#include <resolv.h>
#endif
#ifdef HAVE_LINUX_IF_TUN_H
#include <linux/if_tun.h>
#endif
#endif

View file

@ -1,10 +1,7 @@
#ifndef TINC_IPV4_H
#define TINC_IPV4_H
/*
ipv4.h -- missing IPv4 related definitions
Copyright (C) 2005 Ivo Timmermans
2006-2012 Guus Sliepen <guus@tinc-vpn.org>
2006 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +18,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_IPV4_H__
#define __TINC_IPV4_H__
#ifndef AF_INET
#define AF_INET 2
#endif
@ -41,14 +41,6 @@
#define ICMP_NET_UNKNOWN 6
#endif
#ifndef ICMP_TIME_EXCEEDED
#define ICMP_TIME_EXCEEDED 11
#endif
#ifndef ICMP_EXC_TTL
#define ICMP_EXC_TTL 0
#endif
#ifndef ICMP_NET_UNREACH
#define ICMP_NET_UNREACH 0
#endif
@ -64,15 +56,15 @@
#ifndef HAVE_STRUCT_IP
struct ip {
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ip_hl: 4;
unsigned int ip_v: 4;
unsigned int ip_hl:4;
unsigned int ip_v:4;
#else
unsigned int ip_v: 4;
unsigned int ip_hl: 4;
unsigned int ip_v:4;
unsigned int ip_hl:4;
#endif
uint8_t ip_tos;
uint16_t ip_len;
uint16_t ip_id;
uint16_t ip_id;
uint16_t ip_off;
#define IP_RF 0x8000
#define IP_DF 0x4000
@ -81,7 +73,7 @@ struct ip {
uint8_t ip_p;
uint16_t ip_sum;
struct in_addr ip_src, ip_dst;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#endif
#ifndef IP_OFFMASK
@ -143,7 +135,7 @@ struct icmp {
#define icmp_radv icmp_dun.id_radv
#define icmp_mask icmp_dun.id_mask
#define icmp_data icmp_dun.id_data
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#endif
#endif
#endif /* __TINC_IPV4_H__ */

View file

@ -1,10 +1,7 @@
#ifndef TINC_IPV6_H
#define TINC_IPV6_H
/*
ipv6.h -- missing IPv6 related definitions
Copyright (C) 2005 Ivo Timmermans
2006-2012 Guus Sliepen <guus@tinc-vpn.org>
2006 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +18,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_IPV6_H__
#define __TINC_IPV6_H__
#ifndef AF_INET6
#define AF_INET6 10
#endif
@ -36,7 +36,7 @@ struct in6_addr {
uint16_t u6_addr16[8];
uint32_t u6_addr32[4];
} in6_u;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
@ -49,14 +49,14 @@ struct sockaddr_in6 {
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#endif
#ifndef IN6_IS_ADDR_V4MAPPED
#define IN6_IS_ADDR_V4MAPPED(a) \
((((const uint32_t *) (a))[0] == 0) \
&& (((const uint32_t *) (a))[1] == 0) \
&& (((const uint32_t *) (a))[2] == htonl (0xffff)))
((((__const uint32_t *) (a))[0] == 0) \
&& (((__const uint32_t *) (a))[1] == 0) \
&& (((__const uint32_t *) (a))[2] == htonl (0xffff)))
#endif
#ifndef HAVE_STRUCT_IP6_HDR
@ -72,7 +72,7 @@ struct ip6_hdr {
} ip6_ctlun;
struct in6_addr ip6_src;
struct in6_addr ip6_dst;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#define ip6_vfc ip6_ctlun.ip6_un2_vfc
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
@ -91,14 +91,12 @@ struct icmp6_hdr {
uint16_t icmp6_un_data16[2];
uint8_t icmp6_un_data8[4];
} icmp6_dataun;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#define ICMP6_DST_UNREACH_NOROUTE 0
#define ICMP6_DST_UNREACH 1
#define ICMP6_PACKET_TOO_BIG 2
#define ICMP6_TIME_EXCEEDED 3
#define ICMP6_DST_UNREACH_ADMIN 1
#define ICMP6_DST_UNREACH_ADDR 3
#define ICMP6_TIME_EXCEED_TRANSIT 0
#define ND_NEIGHBOR_SOLICIT 135
#define ND_NEIGHBOR_ADVERT 136
#define icmp6_data32 icmp6_dataun.icmp6_un_data32
@ -111,7 +109,7 @@ struct icmp6_hdr {
struct nd_neighbor_solicit {
struct icmp6_hdr nd_ns_hdr;
struct in6_addr nd_ns_target;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#define ND_OPT_SOURCE_LINKADDR 1
#define ND_OPT_TARGET_LINKADDR 2
#define nd_ns_type nd_ns_hdr.icmp6_type
@ -124,7 +122,7 @@ struct nd_neighbor_solicit {
struct nd_opt_hdr {
uint8_t nd_opt_type;
uint8_t nd_opt_len;
} __attribute__((__packed__));
} __attribute__ ((__packed__));
#endif
#endif
#endif /* __TINC_IPV6_H__ */

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Linux ethertap and tun/tap device
Copyright (C) 2001-2005 Ivo Timmermans,
2001-2014 Guus Sliepen <guus@tinc-vpn.org>
2001-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,25 +18,21 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../system.h"
#include "system.h"
#ifdef HAVE_LINUX_IF_TUN_H
#include <linux/if_tun.h>
#define DEFAULT_DEVICE "/dev/net/tun"
#else
#define DEFAULT_DEVICE "/dev/tap0"
#endif
#include "../conf.h"
#include "../device.h"
#include "../logger.h"
#include "../net.h"
#include "../route.h"
#include "../utils.h"
#include "../xalloc.h"
#include "conf.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#include "device.h"
typedef enum device_type_t {
DEVICE_TYPE_ETHERTAP,
DEVICE_TYPE_TUN,
DEVICE_TYPE_TAP,
} device_type_t;
@ -45,27 +41,22 @@ int device_fd = -1;
static device_type_t device_type;
char *device = NULL;
char *iface = NULL;
static char *type = NULL;
static char ifrname[IFNAMSIZ];
static const char *device_info;
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
uint64_t device_in_packets = 0;
uint64_t device_in_bytes = 0;
uint64_t device_out_packets = 0;
uint64_t device_out_bytes = 0;
static bool setup_device(void) {
struct ifreq ifr;
bool t1q = false;
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
bool setup_device(void) {
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(DEFAULT_DEVICE);
}
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
#ifdef HAVE_LINUX_IF_TUN_H
if(netname != NULL) {
if (netname != NULL)
iface = xstrdup(netname);
}
#else
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
#endif
@ -76,79 +67,38 @@ static bool setup_device(void) {
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
struct ifreq ifr = {{{0}}};
#ifdef HAVE_LINUX_IF_TUN_H
/* Ok now check if this is an old ethertap or a new tun/tap thingie */
memset(&ifr, 0, sizeof(ifr));
get_config_string(lookup_config(config_tree, "DeviceType"), &type);
if(type && strcasecmp(type, "tun") && strcasecmp(type, "tap")) {
logger(LOG_ERR, "Unknown device type %s!", type);
return false;
}
if((type && !strcasecmp(type, "tun")) || (!type && routing_mode == RMODE_ROUTER)) {
if(routing_mode == RMODE_ROUTER) {
ifr.ifr_flags = IFF_TUN;
device_type = DEVICE_TYPE_TUN;
device_info = "Linux tun/tap device (tun mode)";
} else {
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = true;
}
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
device_type = DEVICE_TYPE_TAP;
device_info = "Linux tun/tap device (tap mode)";
}
#ifdef IFF_ONE_QUEUE
/* Set IFF_ONE_QUEUE flag... */
if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q) {
ifr.ifr_flags |= IFF_ONE_QUEUE;
}
bool t1q = false;
if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q)
ifr.ifr_flags |= IFF_ONE_QUEUE;
#endif
if(iface) {
if(iface)
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
}
if(!ioctl(device_fd, TUNSETIFF, &ifr)) {
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
ifrname[IFNAMSIZ - 1] = 0;
free(iface);
if(iface) free(iface);
iface = xstrdup(ifrname);
} else if(errno == EPERM || errno == EBUSY) {
logger(LOG_ERR, "Error while trying to configure %s: %s", device, strerror(errno));
return false;
} else if(!ioctl(device_fd, (('T' << 8) | 202), &ifr)) {
logger(LOG_WARNING, "Old ioctl() request was needed for %s", device);
strncpy(ifrname, ifr.ifr_name, IFNAMSIZ);
ifrname[IFNAMSIZ - 1] = 0;
free(iface);
if(iface) free(iface);
iface = xstrdup(ifrname);
} else
#endif
{
if(routing_mode == RMODE_ROUTER) {
overwrite_mac = true;
}
device_info = "Linux ethertap device";
device_type = DEVICE_TYPE_ETHERTAP;
free(iface);
iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
}
if(overwrite_mac && !ioctl(device_fd, SIOCGIFHWADDR, &ifr)) {
memcpy(mymac.x, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
}
logger(LOG_INFO, "%s is a %s", device, device_info);
@ -156,116 +106,84 @@ static bool setup_device(void) {
return true;
}
static void close_device(void) {
void close_device(void) {
close(device_fd);
free(type);
free(device);
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
bool read_packet(vpn_packet_t *packet) {
int inlen;
switch(device_type) {
case DEVICE_TYPE_TUN:
lenin = read(device_fd, packet->data + 10, MTU - 10);
case DEVICE_TYPE_TUN:
inlen = read(device_fd, packet->data + 10, MTU - 10);
if(lenin <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
memset(packet->data, 0, 12);
packet->len = lenin + 10;
break;
packet->len = inlen + 10;
break;
case DEVICE_TYPE_TAP:
inlen = read(device_fd, packet->data, MTU);
case DEVICE_TYPE_TAP:
lenin = read(device_fd, packet->data, MTU);
if(inlen <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
if(lenin <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
packet->len = lenin;
break;
case DEVICE_TYPE_ETHERTAP:
lenin = read(device_fd, packet->data - 2, MTU + 2);
if(lenin <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s",
device_info, device, strerror(errno));
return false;
}
packet->len = lenin - 2;
break;
packet->len = inlen;
break;
default:
abort();
}
device_total_in += packet->len;
device_in_packets++;
device_in_bytes += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
packet->len, device_info);
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) {
logger(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) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
break;
case DEVICE_TYPE_ETHERTAP:
memcpy(packet->data - 2, &packet->len, 2);
if(write(device_fd, packet->data - 2, packet->len + 2) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
break;
case DEVICE_TYPE_TUN:
packet->data[10] = packet->data[11] = 0;
if(write(device_fd, packet->data + 10, packet->len - 10) < 0) {
logger(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) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
break;
default:
abort();
}
device_total_out += packet->len;
device_out_packets++;
device_out_bytes += packet->len;
return true;
}
static void dump_device_stats(void) {
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_in_bytes);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_out_bytes);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -43,9 +43,8 @@ list_node_t *list_alloc_node(void) {
}
void list_free_node(list_t *list, list_node_t *node) {
if(node->data && list->delete) {
if(node->data && list->delete)
list->delete(node->data);
}
free(node);
}
@ -62,11 +61,10 @@ list_node_t *list_insert_head(list_t *list, void *data) {
node->next = list->head;
list->head = node;
if(node->next) {
if(node->next)
node->next->prev = node;
} else {
else
list->tail = node;
}
list->count++;
@ -83,11 +81,50 @@ list_node_t *list_insert_tail(list_t *list, void *data) {
node->prev = list->tail;
list->tail = node;
if(node->prev) {
if(node->prev)
node->prev->next = node;
} else {
else
list->head = node;
list->count++;
return node;
}
list_node_t *list_insert_after(list_t *list, list_node_t *after, void *data) {
list_node_t *node;
node = list_alloc_node();
node->data = data;
node->next = after->next;
node->prev = after;
after->next = node;
if(node->next)
node->next->prev = node;
else
list->tail = node;
list->count++;
return node;
}
list_node_t *list_insert_before(list_t *list, list_node_t *before, void *data) {
list_node_t *node;
node = list_alloc_node();
node->data = data;
node->next = before;
node->prev = before->prev;
before->prev = node;
if(node->prev)
node->prev->next = node;
else
list->head = node;
}
list->count++;
@ -95,17 +132,15 @@ list_node_t *list_insert_tail(list_t *list, void *data) {
}
void list_unlink_node(list_t *list, list_node_t *node) {
if(node->prev) {
if(node->prev)
node->prev->next = node->next;
} else {
else
list->head = node->next;
}
if(node->next) {
if(node->next)
node->next->prev = node->prev;
} else {
else
list->tail = node->prev;
}
list->count--;
}
@ -126,19 +161,17 @@ void list_delete_tail(list_t *list) {
/* Head/tail lookup */
void *list_get_head(list_t *list) {
if(list->head) {
if(list->head)
return list->head->data;
} else {
else
return NULL;
}
}
void *list_get_tail(list_t *list) {
if(list->tail) {
if(list->tail)
return list->tail->data;
} else {
else
return NULL;
}
}
/* Fast list deletion */
@ -170,9 +203,7 @@ void list_foreach(list_t *list, list_action_t action) {
for(node = list->head; node; node = next) {
next = node->next;
if(node->data) {
if(node->data)
action(node->data);
}
}
}

View file

@ -1,6 +1,3 @@
#ifndef TINC_LIST_H
#define TINC_LIST_H
/*
list.h -- header file for list.c
Copyright (C) 2000-2005 Ivo Timmermans
@ -21,6 +18,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_LIST_H__
#define __TINC_LIST_H__
typedef struct list_node_t {
struct list_node_t *prev;
struct list_node_t *next;
@ -45,34 +45,36 @@ typedef struct list_t {
/* (De)constructors */
extern list_t *list_alloc(list_action_t) __attribute__((__malloc__));
extern void list_free(list_t *list);
extern list_t *list_alloc(list_action_t) __attribute__ ((__malloc__));
extern void list_free(list_t *);
extern list_node_t *list_alloc_node(void);
extern void list_free_node(list_t *list, list_node_t *node);
extern void list_free_node(list_t *, list_node_t *);
/* Insertion and deletion */
extern list_node_t *list_insert_head(list_t *list, void *data);
extern list_node_t *list_insert_tail(list_t *list, void *data);
extern list_node_t *list_insert_head(list_t *, void *);
extern list_node_t *list_insert_tail(list_t *, void *);
extern list_node_t *list_insert_after(list_t *, list_node_t *, void *);
extern list_node_t *list_insert_before(list_t *, list_node_t *, void *);
extern void list_unlink_node(list_t *list, list_node_t *node);
extern void list_delete_node(list_t *list, list_node_t *node);
extern void list_unlink_node(list_t *, list_node_t *);
extern void list_delete_node(list_t *, list_node_t *);
extern void list_delete_head(list_t *list);
extern void list_delete_tail(list_t *list);
extern void list_delete_head(list_t *);
extern void list_delete_tail(list_t *);
/* Head/tail lookup */
extern void *list_get_head(list_t *list);
extern void *list_get_tail(list_t *list);
extern void *list_get_head(list_t *);
extern void *list_get_tail(list_t *);
/* Fast list deletion */
extern void list_delete_list(list_t *list);
extern void list_delete_list(list_t *);
/* Traversing */
extern void list_foreach(list_t *list, list_action_t action);
extern void list_foreach_node(list_t *list, list_action_node_t action);
extern void list_foreach(list_t *, list_action_t);
extern void list_foreach_node(list_t *, list_action_node_t);
#endif
#endif /* __TINC_LIST_H__ */

View file

@ -1,6 +1,6 @@
/*
logger.c -- logging code
Copyright (C) 2004-2016 Guus Sliepen <guus@tinc-vpn.org>
Copyright (C) 2004-2006 Guus Sliepen <guus@tinc-vpn.org>
2004-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -36,58 +36,48 @@ static const char *logident = NULL;
void openlogger(const char *ident, logmode_t mode) {
logident = ident;
logmode = mode;
switch(mode) {
case LOGMODE_STDERR:
logpid = getpid();
break;
case LOGMODE_FILE:
logpid = getpid();
logfile = fopen(logfilename, "a");
if(!logfile) {
fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
logmode = LOGMODE_NULL;
}
break;
case LOGMODE_SYSLOG:
case LOGMODE_STDERR:
logpid = getpid();
break;
case LOGMODE_FILE:
logpid = getpid();
logfile = fopen(logfilename, "a");
if(!logfile) {
fprintf(stderr, "Could not open log file %s: %s\n", logfilename, strerror(errno));
logmode = LOGMODE_NULL;
}
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
loghandle = RegisterEventSource(NULL, logident);
if(!loghandle) {
fprintf(stderr, "Could not open log handle!");
logmode = LOGMODE_NULL;
}
break;
loghandle = RegisterEventSource(NULL, logident);
if(!loghandle) {
fprintf(stderr, "Could not open log handle!");
logmode = LOGMODE_NULL;
}
break;
#else
#ifdef HAVE_SYSLOG_H
openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
break;
openlog(logident, LOG_CONS | LOG_PID, LOG_DAEMON);
break;
#endif
#endif
case LOGMODE_NULL:
break;
case LOGMODE_NULL:
break;
}
}
void reopenlogger() {
if(logmode != LOGMODE_FILE) {
if(logmode != LOGMODE_FILE)
return;
}
fflush(logfile);
FILE *newfile = fopen(logfilename, "a");
if(!newfile) {
logger(LOG_ERR, "Unable to reopen log file %s: %s", logfilename, strerror(errno));
logger(LOG_ERR, "Unable to reopen log file %s: %s\n", logfilename, strerror(errno));
return;
}
fclose(logfile);
logfile = newfile;
}
@ -100,48 +90,43 @@ void logger(int priority, const char *format, ...) {
va_start(ap, format);
switch(logmode) {
case LOGMODE_STDERR:
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
break;
case LOGMODE_FILE:
now = time(NULL);
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(logfile, "%s %s[%ld]: ", timestr, logident, (long)logpid);
vfprintf(logfile, format, ap);
fprintf(logfile, "\n");
fflush(logfile);
break;
case LOGMODE_SYSLOG:
case LOGMODE_STDERR:
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
fflush(stderr);
break;
case LOGMODE_FILE:
now = time(NULL);
strftime(timestr, sizeof timestr, "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(logfile, "%s %s[%ld]: ", timestr, logident, (long)logpid);
vfprintf(logfile, format, ap);
fprintf(logfile, "\n");
fflush(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
{
char message[4096];
const char *messages[] = {message};
vsnprintf(message, sizeof(message), format, ap);
message[sizeof(message) - 1] = 0;
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
{
char message[4096];
const char *messages[] = {message};
vsnprintf(message, sizeof message, format, ap);
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
}
#else
#ifdef HAVE_SYSLOG_H
#ifdef HAVE_VSYSLOG
vsyslog(priority, format, ap);
vsyslog(priority, format, ap);
#else
{
char message[4096];
vsnprintf(message, sizeof(message), format, ap);
syslog(priority, "%s", message);
}
{
char message[4096];
vsnprintf(message, sizeof message, format, ap);
syslog(priority, "%s", message);
}
#endif
break;
break;
#endif
#endif
case LOGMODE_NULL:
break;
case LOGMODE_NULL:
break;
}
va_end(ap);
@ -149,23 +134,22 @@ void logger(int priority, const char *format, ...) {
void closelogger(void) {
switch(logmode) {
case LOGMODE_FILE:
fclose(logfile);
break;
case LOGMODE_SYSLOG:
case LOGMODE_FILE:
fclose(logfile);
break;
case LOGMODE_SYSLOG:
#ifdef HAVE_MINGW
DeregisterEventSource(loghandle);
break;
DeregisterEventSource(loghandle);
break;
#else
#ifdef HAVE_SYSLOG_H
closelog();
break;
closelog();
break;
#endif
#endif
case LOGMODE_NULL:
case LOGMODE_STDERR:
break;
case LOGMODE_NULL:
case LOGMODE_STDERR:
break;
break;
}
}

View file

@ -1,36 +1,17 @@
#ifndef TINC_LOGGER_H
#define TINC_LOGGER_H
/*
logger.h -- header file for logger.c
Copyright (C) 2003-2016 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_LOGGER_H__
#define __TINC_LOGGER_H__
typedef enum debug_t {
DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
DEBUG_NOTHING = 0, /* Quiet mode, only show starting/stopping of the daemon */
DEBUG_ALWAYS = 0,
DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
DEBUG_ERROR = 2, /* Show error messages received from other hosts */
DEBUG_STATUS = 2, /* Show status messages received from other hosts */
DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
DEBUG_META = 4, /* Show contents of every request that is sent/received */
DEBUG_TRAFFIC = 5, /* Show network traffic information */
DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
DEBUG_SCARY_THINGS = 10, /* You have been warned */
DEBUG_CONNECTIONS = 1, /* Show (dis)connects of other tinc daemons via TCP */
DEBUG_ERROR = 2, /* Show error messages received from other hosts */
DEBUG_STATUS = 2, /* Show status messages received from other hosts */
DEBUG_PROTOCOL = 3, /* Show the requests that are sent/received */
DEBUG_META = 4, /* Show contents of every request that is sent/received */
DEBUG_TRAFFIC = 5, /* Show network traffic information */
DEBUG_PACKET = 6, /* Show contents of each packet that is being sent/received */
DEBUG_SCARY_THINGS = 10 /* You have been warned */
} debug_t;
typedef enum logmode_t {
@ -65,11 +46,11 @@ enum {
#endif
extern debug_t debug_level;
extern void openlogger(const char *ident, logmode_t mode);
extern void openlogger(const char *, logmode_t);
extern void reopenlogger(void);
extern void logger(int priority, const char *format, ...) __attribute__((__format__(printf, 2, 3)));
extern void logger(int, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void closelogger(void);
#define ifdebug(l) if(debug_level >= DEBUG_##l)
#endif
#endif /* __TINC_LOGGER_H__ */

View file

@ -1,6 +1,6 @@
/*
meta.c -- handle the meta communication
Copyright (C) 2000-2017 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
2006 Scott Lamb <slamb@slamb.org>
@ -21,125 +21,60 @@
#include "system.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include "avl_tree.h"
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "protocol.h"
#include "proxy.h"
#include "utils.h"
#include "xalloc.h"
bool send_meta(connection_t *c, const char *buffer, int length) {
int outlen;
int result;
if(!c) {
logger(LOG_ERR, "send_meta() called with NULL pointer!");
abort();
}
ifdebug(META) logger(LOG_DEBUG, "Sending %d bytes of metadata to %s (%s)", length,
c->name, c->hostname);
if(!c->outbuflen) {
c->last_flushed_time = now;
}
/* Find room in connection's buffer */
if(length + c->outbuflen > c->outbufsize) {
c->outbufsize = length + c->outbuflen;
c->outbuf = xrealloc(c->outbuf, c->outbufsize);
}
if(length + c->outbuflen + c->outbufstart > c->outbufsize) {
memmove(c->outbuf, c->outbuf + c->outbufstart, c->outbuflen);
c->outbufstart = 0;
}
c->name, c->hostname);
/* Add our data to buffer */
if(c->status.encryptout) {
/* Check encryption limits */
if((uint64_t)length > c->outbudget) {
ifdebug(META) logger(LOG_ERR, "Byte limit exceeded for encryption to %s (%s)", c->name, c->hostname);
size_t outlen = length;
if(!cipher_encrypt(&c->outcipher, buffer, length, buffer_prepare(&c->outbuf, length), &outlen, false) || outlen != length) {
logger(LOG_ERR, "Error while encrypting metadata to %s (%s)",
c->name, c->hostname);
return false;
} else {
c->outbudget -= length;
}
result = EVP_EncryptUpdate(c->outctx, (unsigned char *)c->outbuf + c->outbufstart + c->outbuflen,
&outlen, (unsigned char *)buffer, length);
if(!result || outlen < length) {
logger(LOG_ERR, "Error while encrypting metadata to %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
} else if(outlen > length) {
logger(LOG_EMERG, "Encrypted data too long! Heap corrupted!");
abort();
}
c->outbuflen += outlen;
} else {
memcpy(c->outbuf + c->outbufstart + c->outbuflen, buffer, length);
c->outbuflen += length;
buffer_add(&c->outbuf, buffer, length);
}
return true;
}
event_add(&c->outevent, NULL);
bool flush_meta(connection_t *c) {
int result;
ifdebug(META) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s)",
c->outbuflen, c->name, c->hostname);
while(c->outbuflen) {
result = send(c->socket, c->outbuf + c->outbufstart, c->outbuflen, 0);
if(result <= 0) {
if(!errno || errno == EPIPE) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
c->name, c->hostname);
} else if(errno == EINTR) {
continue;
} else if(sockwouldblock(sockerrno)) {
ifdebug(META) logger(LOG_DEBUG, "Flushing %d bytes to %s (%s) would block",
c->outbuflen, c->name, c->hostname);
return true;
} else {
logger(LOG_ERR, "Flushing meta data to %s (%s) failed: %s", c->name,
c->hostname, sockstrerror(sockerrno));
}
return false;
}
c->outbufstart += result;
c->outbuflen -= result;
}
c->outbufstart = 0; /* avoid unnecessary memmoves */
return true;
}
void broadcast_meta(connection_t *from, const char *buffer, int length) {
avl_node_t *node;
splay_node_t *node;
connection_t *c;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c != from && c->status.active) {
if(c != from && c->status.active)
send_meta(c, buffer, length);
}
}
}
bool receive_meta(connection_t *c) {
int oldlen, i, result;
int lenin, lenout, reqlen;
bool decrypted = false;
int inlen;
char inbuf[MAXBUFSIZE];
char *bufp = inbuf, *endp;
/* Strategy:
- Read as much as possible from the TCP socket in one go.
@ -150,109 +85,79 @@ bool receive_meta(connection_t *c) {
- If not, keep stuff in buffer and exit.
*/
lenin = recv(c->socket, c->buffer + c->buflen, MAXBUFSIZE - c->buflen, 0);
if(lenin <= 0) {
if(!lenin || !errno) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
c->name, c->hostname);
} else if(sockwouldblock(sockerrno)) {
return true;
} else
logger(LOG_ERR, "Metadata socket read error for %s (%s): %s",
c->name, c->hostname, sockstrerror(sockerrno));
buffer_compact(&c->inbuf, MAXBUFSIZE);
if(sizeof inbuf <= c->inbuf.len) {
logger(LOG_ERR, "Input buffer full for %s (%s)", c->name, c->hostname);
return false;
}
oldlen = c->buflen;
c->buflen += lenin;
inlen = recv(c->socket, inbuf, sizeof inbuf - c->inbuf.len, 0);
while(lenin > 0) {
reqlen = 0;
if(inlen <= 0) {
if(!inlen || !errno) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection closed by %s (%s)",
c->name, c->hostname);
} else if(sockwouldblock(sockerrno))
return true;
else
logger(LOG_ERR, "Metadata socket read error for %s (%s): %s",
c->name, c->hostname, sockstrerror(sockerrno));
return false;
}
/* Is it proxy metadata? */
do {
if(!c->status.decryptin) {
endp = memchr(bufp, '\n', inlen);
if(endp)
endp++;
else
endp = bufp + inlen;
if(c->allow_request == PROXY) {
reqlen = receive_proxy_meta(c);
buffer_add(&c->inbuf, bufp, endp - bufp);
if(reqlen < 0) {
return false;
}
goto consume;
}
/* Decrypt */
if(c->status.decryptin && !decrypted) {
/* Check decryption limits */
if((uint64_t)lenin > c->inbudget) {
ifdebug(META) logger(LOG_ERR, "Byte limit exceeded for decryption from %s (%s)", c->name, c->hostname);
return false;
} else {
c->inbudget -= lenin;
}
result = EVP_DecryptUpdate(c->inctx, (unsigned char *)inbuf, &lenout, (unsigned char *)c->buffer + oldlen, lenin);
if(!result || lenout != lenin) {
logger(LOG_ERR, "Error while decrypting metadata from %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
}
memcpy(c->buffer + oldlen, inbuf, lenin);
decrypted = true;
}
/* Are we receiving a TCPpacket? */
if(c->tcplen) {
if(c->tcplen <= c->buflen) {
if(c->allow_request != ALL) {
logger(LOG_ERR, "Got unauthorized TCP packet from %s (%s)", c->name, c->hostname);
return false;
}
receive_tcppacket(c, c->buffer, c->tcplen);
reqlen = c->tcplen;
c->tcplen = 0;
}
inlen -= endp - bufp;
bufp = endp;
} else {
/* Otherwise we are waiting for a request */
size_t outlen = inlen;
ifdebug(META) logger(LOG_DEBUG, "Received encrypted %d bytes", inlen);
for(i = oldlen; i < c->buflen; i++) {
if(c->buffer[i] == '\n') {
c->buffer[i] = '\0'; /* replace end-of-line by end-of-string so we can use sscanf */
c->reqlen = reqlen = i + 1;
if(!cipher_decrypt(&c->incipher, bufp, inlen, buffer_prepare(&c->inbuf, inlen), &outlen, false) || inlen != outlen) {
logger(LOG_ERR, "Error while decrypting metadata from %s (%s)",
c->name, c->hostname);
return false;
}
inlen = 0;
}
while(c->inbuf.len) {
/* Are we receiving a TCPpacket? */
if(c->tcplen) {
char *tcpbuffer = buffer_read(&c->inbuf, c->tcplen);
if(tcpbuffer) {
receive_tcppacket(c, tcpbuffer, c->tcplen);
c->tcplen = 0;
continue;
} else {
break;
}
}
if(reqlen && !receive_request(c)) {
return false;
/* Otherwise we are waiting for a request */
char *request = buffer_readline(&c->inbuf);
if(request) {
bool result = receive_request(c, request);
if(!result)
return false;
continue;
} else {
break;
}
}
consume:
if(reqlen) {
c->buflen -= reqlen;
lenin -= reqlen - oldlen;
memmove(c->buffer, c->buffer + reqlen, c->buflen);
oldlen = 0;
continue;
} else {
break;
}
}
if(c->buflen >= MAXBUFSIZE) {
logger(LOG_ERR, "Metadata read buffer overflow for %s (%s)",
c->name, c->hostname);
return false;
}
} while(inlen);
return true;
}

View file

@ -1,6 +1,3 @@
#ifndef TINC_META_H
#define TINC_META_H
/*
meta.h -- header for meta.c
Copyright (C) 2000-2006 Guus Sliepen <guus@tinc-vpn.org>,
@ -21,11 +18,13 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_META_H__
#define __TINC_META_H__
#include "connection.h"
extern bool send_meta(struct connection_t *c, const char *buffer, int length);
extern void broadcast_meta(struct connection_t *c, const char *buffer, int length);
extern bool flush_meta(struct connection_t *c);
extern bool receive_meta(struct connection_t *c);
extern bool send_meta(struct connection_t *, const char *, int);
extern void broadcast_meta(struct connection_t *, const char *, int);
extern bool receive_meta(struct connection_t *);
#endif
#endif /* __TINC_META_H__ */

View file

@ -37,7 +37,7 @@
//=============
#define TAP_CONTROL_CODE(request,method) \
CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)

View file

@ -1,7 +1,7 @@
/*
device.c -- Interaction with Windows tap driver in a MinGW environment
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2016 Guus Sliepen <guus@tinc-vpn.org>
2002-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,87 +18,72 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../system.h"
#include "system.h"
#include <windows.h>
#include <winioctl.h>
#include "../conf.h"
#include "../device.h"
#include "../logger.h"
#include "../net.h"
#include "../route.h"
#include "../utils.h"
#include "../xalloc.h"
#include "conf.h"
#include "device.h"
#include "logger.h"
#include "net.h"
#include "route.h"
#include "utils.h"
#include "xalloc.h"
#include "common.h"
#include "mingw/common.h"
int device_fd = -1;
static HANDLE device_handle = INVALID_HANDLE_VALUE;
char *device = NULL;
char *iface = NULL;
static const char *device_info = "Windows tap device";
static char *device_info = NULL;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
extern char *myport;
OVERLAPPED r_overlapped;
OVERLAPPED w_overlapped;
static DWORD WINAPI tapreader(void *bla) {
int status;
DWORD len;
long len;
OVERLAPPED overlapped;
vpn_packet_t packet;
int errors = 0;
logger(LOG_DEBUG, "Tap reader running");
/* Read from tap device and send to parent */
r_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
ResetEvent(r_overlapped.hEvent);
overlapped.Offset = 0;
overlapped.OffsetHigh = 0;
ResetEvent(overlapped.hEvent);
status = ReadFile(device_handle, packet.data, MTU, &len, &r_overlapped);
status = ReadFile(device_handle, packet.data, MTU, &len, &overlapped);
if(!status) {
if(GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(r_overlapped.hEvent, INFINITE);
if(!GetOverlappedResult(device_handle, &r_overlapped, &len, FALSE)) {
WaitForSingleObject(overlapped.hEvent, INFINITE);
if(!GetOverlappedResult(device_handle, &overlapped, &len, FALSE))
continue;
}
} else {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
errors++;
if(errors >= 10) {
EnterCriticalSection(&mutex);
running = false;
LeaveCriticalSection(&mutex);
}
usleep(1000000);
continue;
device, strerror(errno));
return -1;
}
}
errors = 0;
EnterCriticalSection(&mutex);
packet.len = len;
packet.priority = 0;
EnterCriticalSection(&mutex);
route(myself, &packet);
LeaveCriticalSection(&mutex);
}
return 0;
}
static bool setup_device(void) {
bool setup_device(void) {
HKEY key, key2;
int i;
@ -106,7 +91,7 @@ static bool setup_device(void) {
char adapterid[1024];
char adaptername[1024];
char tapname[1024];
DWORD len;
long len;
unsigned long status;
bool found = false;
@ -117,10 +102,6 @@ static bool setup_device(void) {
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
if(device && iface) {
logger(LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
}
/* Open registry and look for network adapters */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
@ -128,51 +109,44 @@ static bool setup_device(void) {
return false;
}
for(i = 0; ; i++) {
len = sizeof(adapterid);
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
for (i = 0; ; i++) {
len = sizeof adapterid;
if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
break;
}
/* Find out more about this adapter */
snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
continue;
}
len = sizeof(adaptername);
err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
len = sizeof adaptername;
err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
RegCloseKey(key2);
if(err) {
if(err)
continue;
}
if(device) {
if(!strcmp(device, adapterid)) {
found = true;
break;
} else {
} else
continue;
}
}
if(iface) {
if(!strcmp(iface, adaptername)) {
found = true;
break;
} else {
} else
continue;
}
}
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
if(device_handle != INVALID_HANDLE_VALUE) {
found = true;
break;
@ -186,21 +160,19 @@ static bool setup_device(void) {
return false;
}
if(!device) {
if(!device)
device = xstrdup(adapterid);
}
if(!iface) {
if(!iface)
iface = xstrdup(adaptername);
}
/* Try to open the corresponding tap device */
if(device_handle == INVALID_HANDLE_VALUE) {
snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
}
if(device_handle == INVALID_HANDLE_VALUE) {
logger(LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
return false;
@ -208,7 +180,7 @@ static bool setup_device(void) {
/* Get MAC address from tap device */
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
return false;
}
@ -217,11 +189,6 @@ static bool setup_device(void) {
overwrite_mac = 1;
}
/* Create overlapped events for tap I/O */
r_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
w_overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
/* Start the tap reader */
thread = CreateThread(NULL, 0, tapreader, NULL, 0, NULL);
@ -234,72 +201,36 @@ static bool setup_device(void) {
/* Set media status for newer TAP-Win32 devices */
status = true;
DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
device_info = "Windows tap device";
logger(LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
return true;
}
static void close_device(void) {
void close_device(void) {
CloseHandle(device_handle);
free(device);
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
bool read_packet(vpn_packet_t *packet) {
return false;
}
static bool write_packet(vpn_packet_t *packet) {
DWORD lenout;
static vpn_packet_t queue;
bool write_packet(vpn_packet_t *packet) {
long outlen;
OVERLAPPED overlapped = {0};
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
packet->len, device_info);
/* Check if there is something in progress */
if(queue.len) {
DWORD size;
BOOL success = GetOverlappedResult(device_handle, &w_overlapped, &size, FALSE);
if(success) {
ResetEvent(&w_overlapped);
queue.len = 0;
} else {
int err = GetLastError();
if(err != ERROR_IO_INCOMPLETE) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error completing previously queued write: %s", winerror(err));
ResetEvent(&w_overlapped);
queue.len = 0;
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Previous overlapped write still in progress");
// drop this packet
return true;
}
}
}
/* Otherwise, try to write. */
memcpy(queue.data, packet->data, packet->len);
if(!WriteFile(device_handle, queue.data, packet->len, &lenout, &w_overlapped)) {
int err = GetLastError();
if(err != ERROR_IO_PENDING) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(err));
return false;
}
// Write is being done asynchronously.
queue.len = packet->len;
} else {
// Write was completed immediately.
ResetEvent(&w_overlapped);
if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
return false;
}
device_total_out += packet->len;
@ -307,16 +238,8 @@ static bool write_packet(vpn_packet_t *packet) {
return true;
}
static void dump_device_stats(void) {
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t os_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};

View file

@ -1,247 +0,0 @@
/*
device.c -- multicast socket
Copyright (C) 2002-2005 Ivo Timmermans,
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
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 "conf.h"
#include "device.h"
#include "net.h"
#include "logger.h"
#include "netutl.h"
#include "utils.h"
#include "route.h"
#include "xalloc.h"
static const char *device_info = "multicast socket";
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static struct addrinfo *ai = NULL;
static mac_t ignore_src = {{0}};
static bool setup_device(void) {
char *host;
char *port;
char *space;
int ttl = 1;
get_config_string(lookup_config(config_tree, "Interface"), &iface);
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
logger(LOG_ERR, "Device variable required for %s", device_info);
return false;
}
host = xstrdup(device);
space = strchr(host, ' ');
if(!space) {
logger(LOG_ERR, "Port number required for %s", device_info);
free(host);
return false;
}
*space++ = 0;
port = space;
space = strchr(port, ' ');
if(space) {
*space++ = 0;
ttl = atoi(space);
}
ai = str2addrinfo(host, port, SOCK_DGRAM);
if(!ai) {
free(host);
return false;
}
device_fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
if(device_fd < 0) {
logger(LOG_ERR, "Creating socket failed: %s", sockstrerror(sockerrno));
free(host);
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
static const int one = 1;
setsockopt(device_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
if(bind(device_fd, ai->ai_addr, ai->ai_addrlen)) {
closesocket(device_fd);
logger(LOG_ERR, "Can't bind to %s %s: %s", host, port, sockstrerror(sockerrno));
free(host);
return false;
}
switch(ai->ai_family) {
#ifdef IP_ADD_MEMBERSHIP
case AF_INET: {
struct ip_mreq mreq;
struct sockaddr_in in;
memcpy(&in, ai->ai_addr, sizeof(in));
mreq.imr_multiaddr.s_addr = in.sin_addr.s_addr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(device_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq))) {
logger(LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
closesocket(device_fd);
free(host);
return false;
}
#ifdef IP_MULTICAST_LOOP
setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&one, sizeof(one));
#endif
#ifdef IP_MULTICAST_TTL
setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof(ttl));
#endif
}
break;
#endif
#ifdef IPV6_JOIN_GROUP
case AF_INET6: {
struct ipv6_mreq mreq;
struct sockaddr_in6 in6;
memcpy(&in6, ai->ai_addr, sizeof(in6));
memcpy(&mreq.ipv6mr_multiaddr, &in6.sin6_addr, sizeof(mreq.ipv6mr_multiaddr));
mreq.ipv6mr_interface = in6.sin6_scope_id;
if(setsockopt(device_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mreq, sizeof(mreq))) {
logger(LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
closesocket(device_fd);
free(host);
return false;
}
#ifdef IPV6_MULTICAST_LOOP
setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&one, sizeof(one));
#endif
#ifdef IPV6_MULTICAST_HOPS
setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof(ttl));
#endif
}
break;
#endif
default:
logger(LOG_ERR, "Multicast for address family %x unsupported", ai->ai_family);
closesocket(device_fd);
free(host);
return false;
}
free(host);
logger(LOG_INFO, "%s is a %s", device, device_info);
return true;
}
static void close_device(void) {
close(device_fd);
free(device);
free(iface);
if(ai) {
freeaddrinfo(ai);
}
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
if((lenin = recv(device_fd, (void *)packet->data, MTU, 0)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
return false;
}
if(!memcmp(&ignore_src, packet->data + 6, sizeof(ignore_src))) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
packet->len = 0;
return true;
}
packet->len = lenin;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(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) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
return false;
}
device_total_out += packet->len;
memcpy(&ignore_src, packet->data + 6, sizeof(ignore_src));
return true;
}
static void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t multicast_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};
#if 0
static bool not_supported(void) {
logger(LOG_ERR, "Raw socket device not supported on this platform");
return false;
}
const devops_t multicast_devops = {
.setup = not_supported,
.close = NULL,
.read = NULL,
.write = NULL,
.dump_stats = NULL,
};
#endif

729
src/net.c
View file

@ -1,9 +1,9 @@
/*
net.c -- most of the network code
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2011 Loïc Grenié <loic.grenie@gmail.com>
2011 Loïc Grenié <loic.grenie@gmail.com>
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
@ -22,14 +22,11 @@
#include "system.h"
#include <openssl/rand.h>
#include "utils.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "device.h"
#include "event.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
@ -37,25 +34,17 @@
#include "netutl.h"
#include "process.h"
#include "protocol.h"
#include "route.h"
#include "subnet.h"
#include "xalloc.h"
bool do_purge = false;
volatile bool running = false;
#ifdef HAVE_PSELECT
bool graph_dump = false;
#endif
time_t now = 0;
int contradicting_add_edge = 0;
int contradicting_del_edge = 0;
static int sleeptime = 10;
/* Purge edges and subnets of unreachable nodes. Use carefully. */
static void purge(void) {
avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
void purge(void) {
splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
node_t *n;
edge_t *e;
subnet_t *s;
@ -70,26 +59,21 @@ static void purge(void) {
if(!n->status.reachable) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Purging node %s (%s)", n->name,
n->hostname);
n->hostname);
for(snode = n->subnet_tree->head; snode; snode = snext) {
snext = snode->next;
s = snode->data;
send_del_subnet(everyone, s);
if(!strictsubnets) {
send_del_subnet(broadcast, s);
if(!strictsubnets)
subnet_del(n, s);
}
}
for(enode = n->edge_tree->head; enode; enode = enext) {
enext = enode->next;
e = enode->data;
if(!tunnelserver) {
send_del_edge(everyone, e);
}
if(!tunnelserver)
send_del_edge(broadcast, e);
edge_del(e);
}
}
@ -106,96 +90,17 @@ static void purge(void) {
enext = enode->next;
e = enode->data;
if(e->to == n) {
if(e->to == n)
break;
}
}
if(!enode && (!strictsubnets || !n->subnet_tree->head))
/* in strictsubnets mode do not delete nodes with subnets */
{
node_del(n);
}
}
}
}
/*
put all file descriptors in an fd_set array
While we're at it, purge stuff that needs to be removed.
*/
static int build_fdset(fd_set *readset, fd_set *writeset) {
avl_node_t *node, *next;
connection_t *c;
int i, max = 0;
FD_ZERO(readset);
FD_ZERO(writeset);
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = node->data;
if(c->status.remove) {
connection_del(c);
if(!connection_tree->head) {
purge();
}
} else {
FD_SET(c->socket, readset);
if(c->outbuflen > 0 || c->status.connecting) {
FD_SET(c->socket, writeset);
}
if(c->socket > max) {
max = c->socket;
}
}
}
for(i = 0; i < listen_sockets; i++) {
FD_SET(listen_socket[i].tcp, readset);
if(listen_socket[i].tcp > max) {
max = listen_socket[i].tcp;
}
FD_SET(listen_socket[i].udp, readset);
if(listen_socket[i].udp > max) {
max = listen_socket[i].udp;
}
}
if(device_fd >= 0) {
FD_SET(device_fd, readset);
}
if(device_fd > max) {
max = device_fd;
}
return max;
}
/* Put a misbehaving connection in the tarpit */
void tarpit(int fd) {
static int pits[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
static int next_pit = 0;
if(pits[next_pit] != -1) {
closesocket(pits[next_pit]);
}
pits[next_pit++] = fd;
if(next_pit >= (int)(sizeof pits / sizeof pits[0])) {
next_pit = 0;
}
}
/*
Terminate a connection:
- Close the socket
@ -204,41 +109,19 @@ void tarpit(int fd) {
- Deactivate the host
*/
void terminate_connection(connection_t *c, bool report) {
if(c->status.remove) {
return;
}
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Closing connection with %s (%s)",
c->name, c->hostname);
c->name, c->hostname);
c->status.remove = true;
c->status.active = false;
if(c->node) {
if(c->node)
c->node->connection = NULL;
}
if(c->socket) {
if(c->status.tarpit) {
tarpit(c->socket);
} else {
closesocket(c->socket);
}
}
if(c->edge) {
if(!c->node) {
logger(LOG_ERR, "Connection to %s (%s) has an edge but node is NULL!", c->name, c->hostname);
// And that should never happen.
abort();
}
if(report && !tunnelserver) {
send_del_edge(everyone, c->edge);
}
if(report && !tunnelserver)
send_del_edge(broadcast, c->edge);
edge_del(c->edge);
c->edge = NULL;
/* Run MST and SSSP algorithms */
@ -249,32 +132,20 @@ void terminate_connection(connection_t *c, bool report) {
if(report && !c->node->status.reachable) {
edge_t *e;
e = lookup_edge(c->node, myself);
if(e) {
if(!tunnelserver) {
send_del_edge(everyone, e);
}
if(!tunnelserver)
send_del_edge(broadcast, e);
edge_del(e);
}
}
}
free_connection_partially(c);
/* Check if this was our outgoing connection */
if(c->outgoing) {
c->status.remove = false;
do_outgoing_connection(c);
}
if(c->outgoing)
retry_outgoing(c->outgoing);
#ifndef HAVE_MINGW
/* Clean up dead proxy processes */
while(waitpid(-1, NULL, WNOHANG) > 0);
#endif
connection_del(c);
}
/*
@ -285,9 +156,10 @@ void terminate_connection(connection_t *c, bool report) {
end does not reply in time, we consider them dead
and close the connection.
*/
static void check_dead_connections(void) {
avl_node_t *node, *next;
static void timeout_handler(int fd, short events, void *event) {
splay_node_t *node, *next;
connection_t *c;
time_t now = time(NULL);
for(node = connection_tree->head; node; node = next) {
next = node->next;
@ -297,124 +169,182 @@ static void check_dead_connections(void) {
if(c->status.active) {
if(c->status.pinged) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
c->name, c->hostname, (long)(now - c->last_ping_time));
c->status.timeout = true;
c->name, c->hostname, now - c->last_ping_time);
terminate_connection(c, true);
continue;
} else if(c->last_ping_time + pinginterval <= now) {
send_ping(c);
}
} else {
if(c->status.remove) {
logger(LOG_WARNING, "Old connection_t for %s (%s) status %04x still lingering, deleting...",
c->name, c->hostname, bitfield_to_int(&c->status, sizeof(c->status)));
connection_del(c);
continue;
}
ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication",
c->name, c->hostname);
if(c->status.connecting) {
ifdebug(CONNECTIONS)
logger(LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
c->status.connecting = false;
closesocket(c->socket);
do_outgoing_connection(c);
} else {
c->status.tarpit = true;
ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
terminate_connection(c, false);
}
}
}
if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout <= now) {
if(c->status.active) {
ifdebug(CONNECTIONS) logger(LOG_INFO,
"%s (%s) could not flush for %ld seconds (%d bytes remaining)",
c->name, c->hostname, (long)(now - c->last_flushed_time), c->outbuflen);
c->status.timeout = true;
terminate_connection(c, true);
}
}
}
}
/*
check all connections to see if anything
happened on their sockets
*/
static void check_network_activity(fd_set *readset, fd_set *writeset) {
connection_t *c;
avl_node_t *node;
int result, i;
socklen_t len = sizeof(result);
vpn_packet_t packet;
static int errors = 0;
/* check input from kernel */
if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
if(devops.read(&packet)) {
if(packet.len) {
errors = 0;
packet.priority = 0;
route(myself, &packet);
}
} else {
usleep(errors * 50000);
errors++;
if(errors > 10) {
logger(LOG_ERR, "Too many errors from %s, exiting!", device);
running = false;
}
}
}
/* check meta connections */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c->status.remove) {
continue;
}
if(FD_ISSET(c->socket, writeset)) {
if(c->status.connecting) {
c->status.connecting = false;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
if(!result) {
finish_connecting(c);
} else {
ifdebug(CONNECTIONS) logger(LOG_DEBUG,
"Error while connecting to %s (%s): %s",
c->name, c->hostname, sockstrerror(result));
closesocket(c->socket);
do_outgoing_connection(c);
continue;
}
}
}
}
if(!flush_meta(c)) {
terminate_connection(c, c->status.active);
continue;
}
if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
usleep(sleeptime * 1000000LL);
sleeptime *= 2;
if(sleeptime < 0)
sleeptime = 3600;
} else {
sleeptime /= 2;
if(sleeptime < 10)
sleeptime = 10;
}
contradicting_add_edge = 0;
contradicting_del_edge = 0;
event_add(event, &(struct timeval){pingtimeout, 0});
}
void handle_meta_connection_data(int fd, short events, void *data) {
connection_t *c = data;
int result;
socklen_t len = sizeof result;
if(c->status.connecting) {
c->status.connecting = false;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
if(!result)
finish_connecting(c);
else {
ifdebug(CONNECTIONS) logger(LOG_DEBUG,
"Error while connecting to %s (%s): %s",
c->name, c->hostname, sockstrerror(result));
closesocket(c->socket);
do_outgoing_connection(c);
return;
}
}
if (!receive_meta(c)) {
terminate_connection(c, c->status.active);
return;
}
}
static void sigterm_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
event_loopexit(NULL);
}
static void sighup_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
reopenlogger();
reload_configuration();
}
static void sigalrm_handler(int signal, short events, void *data) {
logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
retry();
}
int reload_configuration(void) {
connection_t *c;
splay_node_t *node, *next;
char *fname;
struct stat s;
static time_t last_config_check = 0;
/* Reread our own configuration file */
exit_configuration(&config_tree);
init_configuration(&config_tree);
if(!read_server_config()) {
logger(LOG_ERR, "Unable to reread configuration file, exitting.");
event_loopexit(NULL);
return EINVAL;
}
/* Close connections to hosts that have a changed or deleted host config file */
for(node = connection_tree->head; node; node = next) {
c = node->data;
next = node->next;
if(c->outgoing) {
free(c->outgoing->name);
if(c->outgoing->ai)
freeaddrinfo(c->outgoing->ai);
free(c->outgoing);
c->outgoing = NULL;
}
xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
if(stat(fname, &s) || s.st_mtime > last_config_check)
terminate_connection(c, c->status.active);
free(fname);
}
last_config_check = time(NULL);
/* If StrictSubnet is set, expire deleted Subnets and read new ones in */
if(strictsubnets) {
subnet_t *subnet;
for(node = subnet_tree->head; node; node = node->next) {
subnet = node->data;
subnet->expires = 1;
}
if(FD_ISSET(c->socket, readset)) {
if(!receive_meta(c)) {
c->status.tarpit = true;
terminate_connection(c, c->status.active);
continue;
load_all_subnets();
for(node = subnet_tree->head; node; node = next) {
next = node->next;
subnet = node->data;
if(subnet->expires == 1) {
send_del_subnet(broadcast, subnet);
if(subnet->owner->status.reachable)
subnet_update(subnet->owner, subnet, false);
subnet_del(subnet->owner, subnet);
} else if(subnet->expires == -1) {
subnet->expires = 0;
} else {
send_add_subnet(broadcast, subnet);
if(subnet->owner->status.reachable)
subnet_update(subnet->owner, subnet, true);
}
}
}
for(i = 0; i < listen_sockets; i++) {
if(FD_ISSET(listen_socket[i].udp, readset)) {
handle_incoming_vpn_data(i);
}
/* Try to make outgoing connections */
try_outgoing_connections();
if(FD_ISSET(listen_socket[i].tcp, readset)) {
handle_new_meta_connection(listen_socket[i].tcp);
return 0;
}
void retry(void) {
connection_t *c;
splay_node_t *node;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c->outgoing && !c->node) {
if(timeout_initialized(&c->outgoing->ev))
event_del(&c->outgoing->ev);
if(c->status.connecting)
close(c->socket);
c->outgoing->timeout = 0;
do_outgoing_connection(c);
}
}
}
@ -423,289 +353,40 @@ static void check_network_activity(fd_set *readset, fd_set *writeset) {
this is where it all happens...
*/
int main_loop(void) {
fd_set readset, writeset;
#ifdef HAVE_PSELECT
struct timespec tv;
sigset_t omask, block_mask;
time_t next_event;
#else
struct timeval tv;
struct event timeout_event;
timeout_set(&timeout_event, timeout_handler, &timeout_event);
event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
#ifndef HAVE_MINGW
struct event sighup_event;
struct event sigterm_event;
struct event sigquit_event;
struct event sigalrm_event;
signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
signal_add(&sighup_event, NULL);
signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
signal_add(&sigterm_event, NULL);
signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
signal_add(&sigquit_event, NULL);
signal_set(&sigalrm_event, SIGALRM, sigalrm_handler, NULL);
signal_add(&sigalrm_event, NULL);
#endif
int r, maxfd;
time_t last_ping_check, last_config_check, last_graph_dump;
event_t *event;
last_ping_check = now;
last_config_check = now;
last_graph_dump = now;
srand(now);
#ifdef HAVE_PSELECT
if(lookup_config(config_tree, "GraphDumpFile")) {
graph_dump = true;
if(event_loop(0) < 0) {
logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
return 1;
}
/* Block SIGHUP & SIGALRM */
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGHUP);
sigaddset(&block_mask, SIGALRM);
sigprocmask(SIG_BLOCK, &block_mask, &omask);
#ifndef HAVE_MINGW
signal_del(&sighup_event);
signal_del(&sigterm_event);
signal_del(&sigquit_event);
signal_del(&sigalrm_event);
#endif
running = true;
while(running) {
#ifdef HAVE_PSELECT
next_event = last_ping_check + pingtimeout;
if(graph_dump && next_event > last_graph_dump + 60) {
next_event = last_graph_dump + 60;
}
if((event = peek_next_event()) && next_event > event->time) {
next_event = event->time;
}
if(next_event <= now) {
tv.tv_sec = 0;
} else {
tv.tv_sec = next_event - now;
}
tv.tv_nsec = 0;
#else
tv.tv_sec = 1;
tv.tv_usec = 0;
#endif
maxfd = build_fdset(&readset, &writeset);
#ifdef HAVE_MINGW
LeaveCriticalSection(&mutex);
#endif
#ifdef HAVE_PSELECT
r = pselect(maxfd + 1, &readset, &writeset, NULL, &tv, &omask);
#else
r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
#endif
now = time(NULL);
#ifdef HAVE_MINGW
EnterCriticalSection(&mutex);
#endif
if(r < 0) {
if(!sockwouldblock(sockerrno)) {
logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
dump_connections();
return 1;
}
}
if(r > 0) {
check_network_activity(&readset, &writeset);
}
if(do_purge) {
purge();
do_purge = false;
}
/* Let's check if everybody is still alive */
if(last_ping_check + pingtimeout <= now) {
check_dead_connections();
last_ping_check = now;
if(routing_mode == RMODE_SWITCH) {
age_subnets();
}
age_past_requests();
/* Should we regenerate our key? */
if(keyexpires <= now) {
avl_node_t *node;
node_t *n;
ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
for(node = node_tree->head; node; node = node->next) {
n = node->data;
if(n->inkey) {
free(n->inkey);
n->inkey = NULL;
}
}
send_key_changed();
keyexpires = now + keylifetime;
}
/* Detect ADD_EDGE/DEL_EDGE storms that are caused when
* two tinc daemons with the same name are on the VPN.
* If so, sleep a while. If this happens multiple times
* in a row, sleep longer. */
if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
logger(LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
usleep(sleeptime * 1000000LL);
sleeptime *= 2;
if(sleeptime < 0) {
sleeptime = 3600;
}
} else {
sleeptime /= 2;
if(sleeptime < 10) {
sleeptime = 10;
}
}
contradicting_add_edge = 0;
contradicting_del_edge = 0;
}
if(sigalrm) {
avl_node_t *node;
logger(LOG_INFO, "Flushing event queue");
expire_events();
for(node = connection_tree->head; node; node = node->next) {
connection_t *c = node->data;
if(c->status.active) {
send_ping(c);
}
}
sigalrm = false;
}
while((event = get_expired_event())) {
event->handler(event->data);
free_event(event);
}
if(sighup) {
connection_t *c;
avl_node_t *node, *next;
char *fname;
struct stat s;
sighup = false;
reopenlogger();
/* Reread our own configuration file */
exit_configuration(&config_tree);
init_configuration(&config_tree);
if(!read_server_config()) {
logger(LOG_ERR, "Unable to reread configuration file, exitting.");
return 1;
}
/* Cancel non-active outgoing connections */
for(node = connection_tree->head; node; node = next) {
next = node->next;
c = node->data;
c->outgoing = NULL;
if(c->status.connecting) {
terminate_connection(c, false);
connection_del(c);
}
}
/* Wipe list of outgoing connections */
for(list_node_t *node = outgoing_list->head; node; node = node->next) {
outgoing_t *outgoing = node->data;
if(outgoing->event) {
event_del(outgoing->event);
}
}
list_delete_list(outgoing_list);
/* Close connections to hosts that have a changed or deleted host config file */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
if(stat(fname, &s) || s.st_mtime > last_config_check) {
terminate_connection(c, c->status.active);
}
free(fname);
}
last_config_check = now;
/* If StrictSubnet is set, expire deleted Subnets and read new ones in */
if(strictsubnets) {
subnet_t *subnet;
for(node = subnet_tree->head; node; node = node->next) {
subnet = node->data;
subnet->expires = 1;
}
load_all_subnets();
for(node = subnet_tree->head; node; node = next) {
next = node->next;
subnet = node->data;
if(subnet->expires == 1) {
send_del_subnet(everyone, subnet);
if(subnet->owner->status.reachable) {
subnet_update(subnet->owner, subnet, false);
}
subnet_del(subnet->owner, subnet);
} else if(subnet->expires == -1) {
subnet->expires = 0;
} else {
send_add_subnet(everyone, subnet);
if(subnet->owner->status.reachable) {
subnet_update(subnet->owner, subnet, true);
}
}
}
}
/* Try to make outgoing connections */
try_outgoing_connections();
}
/* Dump graph if wanted every 60 seconds*/
if(last_graph_dump + 60 <= now) {
dump_graph();
last_graph_dump = now;
}
}
#ifdef HAVE_PSELECT
/* Restore SIGHUP & SIGALARM mask */
sigprocmask(SIG_SETMASK, &omask, NULL);
#endif
event_del(&timeout_event);
return 0;
}

View file

@ -1,10 +1,7 @@
#ifndef TINC_NET_H
#define TINC_NET_H
/*
net.h -- header for net.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,20 +18,23 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <openssl/evp.h>
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
#include "ipv6.h"
#include "cipher.h"
#include "digest.h"
#ifdef ENABLE_JUMBOGRAMS
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#define MTU 9018 /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#else
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
#endif
#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MTU + seqno + padding + HMAC + compressor overhead */
#define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
#define MAXSOCKETS 128 /* Overkill... */
#define MAXSOCKETS 8 /* Probably overkill... */
typedef struct mac_t {
uint8_t x[6];
@ -48,7 +48,7 @@ typedef struct ipv6_t {
uint16_t x[8];
} ipv6_t;
typedef uint16_t length_t;
typedef short length_t;
#define AF_UNKNOWN 255
@ -77,17 +77,18 @@ typedef union sockaddr_t {
#endif
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
length_t len; /* the actual number of bytes in the `data' field */
int priority; /* priority or TOS */
uint32_t seqno; /* 32 bits sequence number (network byte order of course) */
uint8_t data[MAXSIZE];
} vpn_packet_t;
typedef struct listen_socket_t {
struct event ev_tcp;
struct event ev_udp;
int tcp;
int udp;
sockaddr_t sa;
int priority;
} listen_socket_t;
#include "conf.h"
@ -99,7 +100,7 @@ typedef struct outgoing_t {
struct config_t *cfg;
struct addrinfo *ai;
struct addrinfo *aip;
struct event *event;
struct event ev;
} outgoing_t;
extern list_t *outgoing_list;
@ -108,49 +109,49 @@ extern int maxoutbufsize;
extern int seconds_till_retry;
extern int addressfamily;
extern unsigned replaywin;
extern bool localdiscovery;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
extern int keyexpires;
extern int keylifetime;
extern int udp_rcvbuf;
extern int udp_sndbuf;
extern bool do_prune;
extern bool do_purge;
extern char *myport;
extern time_t now;
extern int contradicting_add_edge;
extern int contradicting_del_edge;
extern volatile bool running;
/* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
#include "connection.h"
#include "node.h"
extern void retry_outgoing(outgoing_t *outgoing);
extern void handle_incoming_vpn_data(int sock);
extern void finish_connecting(struct connection_t *c);
extern void do_outgoing_connection(struct connection_t *c);
extern bool handle_new_meta_connection(int sock);
extern int setup_listen_socket(const sockaddr_t *sa);
extern int setup_vpn_in_socket(const sockaddr_t *sa);
extern void send_packet(const struct node_t *n, vpn_packet_t *packet);
extern void receive_tcppacket(struct connection_t *c, const char *buffer, length_t len);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *packet);
extern char *get_name(void);
extern void retry_outgoing(outgoing_t *);
extern void handle_incoming_vpn_data(int, short, void *);
extern void finish_connecting(struct connection_t *);
extern bool do_outgoing_connection(struct connection_t *);
extern void handle_new_meta_connection(int, short, void *);
extern int setup_listen_socket(const sockaddr_t *);
extern int setup_vpn_in_socket(const sockaddr_t *);
extern void send_packet(struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, const char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *outgoing);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);
extern int main_loop(void);
extern void terminate_connection(struct connection_t *c, bool report);
extern void flush_queue(struct node_t *n);
extern bool read_rsa_public_key(struct connection_t *c);
extern void send_mtu_probe(struct node_t *n);
extern void terminate_connection(struct connection_t *, bool);
extern void flush_queue(struct node_t *);
extern bool node_read_ecdsa_public_key(struct node_t *);
extern bool read_ecdsa_public_key(struct connection_t *);
extern bool read_rsa_public_key(struct connection_t *);
extern void send_mtu_probe(struct node_t *);
extern void handle_device_data(int, short, void *);
extern void handle_meta_connection_data(int, short, void *);
extern void regenerate_key(void);
extern void purge(void);
extern void retry(void);
extern int reload_configuration(void);
extern void load_all_subnets(void);
extern void tarpit(int fd);
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
@ -158,4 +159,4 @@ extern void tarpit(int fd);
extern CRITICAL_SECTION mutex;
#endif
#endif
#endif /* __TINC_NET_H__ */

View file

@ -1,7 +1,7 @@
/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
2010 Timothy Redaelli <timothy@redaelli.eu>
2010 Brandon Black <blblack@gmail.com>
@ -36,12 +36,14 @@
#include LZO1X_H
#endif
#include "avl_tree.h"
#include "splay_tree.h"
#include "cipher.h"
#include "conf.h"
#include "connection.h"
#include "crypto.h"
#include "digest.h"
#include "device.h"
#include "ethernet.h"
#include "event.h"
#include "graph.h"
#include "logger.h"
#include "net.h"
@ -53,7 +55,6 @@
#include "xalloc.h"
int keylifetime = 0;
int keyexpires = 0;
#ifdef HAVE_LZO
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
#endif
@ -61,33 +62,21 @@ 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;
#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
// 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.
*/
void send_mtu_probe(node_t *n) {
static void send_mtu_probe_handler(int fd, short events, void *data) {
node_t *n = data;
vpn_packet_t packet;
int len, i;
int timeout = 1;
n->mtuprobes++;
n->mtuevent = NULL;
if(!n->status.reachable || !n->status.validkey) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
@ -114,12 +103,10 @@ void send_mtu_probe(node_t *n) {
}
if(n->mtuprobes == 30 || (n->mtuprobes < 30 && n->minmtu >= n->maxmtu)) {
if(n->minmtu > n->maxmtu) {
if(n->minmtu > n->maxmtu)
n->minmtu = n->maxmtu;
} else {
else
n->maxmtu = n->minmtu;
}
n->mtu = n->minmtu;
ifdebug(TRAFFIC) logger(LOG_INFO, "Fixing MTU of %s (%s) to %d after %d probes", n->name, n->hostname, n->mtu, n->mtuprobes);
n->mtuprobes = 31;
@ -132,32 +119,19 @@ void send_mtu_probe(node_t *n) {
timeout = pingtimeout;
}
for(i = 0; i < 4 + localdiscovery; i++) {
if(i == 0) {
if(n->mtuprobes < 30 || n->maxmtu + 8 >= MTU) {
continue;
}
len = n->maxmtu + 8;
} else if(n->maxmtu <= n->minmtu) {
for(i = 0; i < 3; i++) {
if(n->maxmtu <= n->minmtu)
len = n->maxmtu;
} else {
else
len = n->minmtu + 1 + rand() % (n->maxmtu - n->minmtu);
}
if(len < 64) {
if(len < 64)
len = 64;
}
memset(packet.data, 0, 14);
RAND_bytes(packet.data + 14, len - 14);
randomize(packet.data + 14, len - 14);
packet.len = len;
if(i >= 4 && n->mtuprobes <= 10) {
packet.priority = -1;
} else {
packet.priority = 0;
}
packet.priority = 0;
ifdebug(TRAFFIC) logger(LOG_INFO, "Sending MTU probe length %d to %s (%s)", len, n->name, n->hostname);
@ -165,14 +139,16 @@ void send_mtu_probe(node_t *n) {
}
end:
n->mtuevent = new_event();
n->mtuevent->handler = (event_handler_t)send_mtu_probe;
n->mtuevent->data = n;
n->mtuevent->time = now + timeout;
event_add(n->mtuevent);
event_add(&n->mtuevent, &(struct timeval){timeout, 0});
}
void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
void send_mtu_probe(node_t *n) {
if(!timeout_initialized(&n->mtuevent))
timeout_set(&n->mtuevent, send_mtu_probe_handler, n);
send_mtu_probe_handler(0, 0, n);
}
static void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Got MTU probe length %d from %s (%s)", packet->len, n->name, n->hostname);
if(!packet->data[0]) {
@ -180,27 +156,16 @@ void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
send_udppacket(n, packet);
} else {
if(n->mtuprobes > 30) {
if(len == n->maxmtu + 8) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Increase in PMTU to %s (%s) detected, restarting PMTU discovery", n->name, n->hostname);
n->maxmtu = MTU;
n->mtuprobes = 10;
return;
}
if(n->minmtu) {
if(n->minmtu)
n->mtuprobes = 30;
} else {
else
n->mtuprobes = 1;
}
}
if(len > n->maxmtu) {
if(len > n->maxmtu)
len = n->maxmtu;
}
if(n->minmtu < len) {
if(n->minmtu < len)
n->minmtu = len;
}
}
}
@ -214,28 +179,27 @@ static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t l
lzo1x_1_compress(source, len, dest, &lzolen, lzo_wrkmem);
return lzolen;
#else
return 0;
return -1;
#endif
} else if(level < 10) {
#ifdef HAVE_ZLIB
unsigned long destlen = MAXSIZE;
if(compress2(dest, &destlen, source, len, level) == Z_OK) {
if(compress2(dest, &destlen, source, len, level) == Z_OK)
return destlen;
} else
else
#endif
return 0;
return -1;
} else {
#ifdef HAVE_LZO
lzo_uint lzolen = MAXSIZE;
lzo1x_999_compress(source, len, dest, &lzolen, lzo_wrkmem);
return lzolen;
#else
return 0;
return -1;
#endif
}
return 0;
return -1;
}
static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level) {
@ -245,25 +209,20 @@ static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t
} else if(level > 9) {
#ifdef HAVE_LZO
lzo_uint lzolen = MAXSIZE;
if(lzo1x_decompress_safe(source, len, dest, &lzolen, NULL) == LZO_E_OK) {
if(lzo1x_decompress_safe(source, len, dest, &lzolen, NULL) == LZO_E_OK)
return lzolen;
} else
else
#endif
return 0;
return -1;
}
#ifdef HAVE_ZLIB
else {
unsigned long destlen = MAXSIZE;
if(uncompress(dest, &destlen, source, len) == Z_OK) {
if(uncompress(dest, &destlen, source, len) == Z_OK)
return destlen;
} else {
return 0;
}
else
return -1;
}
#endif
return -1;
@ -273,104 +232,91 @@ static length_t uncompress_packet(uint8_t *dest, const uint8_t *source, length_t
static void receive_packet(node_t *n, vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Received packet of %d bytes from %s (%s)",
packet->len, n->name, n->hostname);
packet->len, n->name, n->hostname);
n->in_packets++;
n->in_bytes += packet->len;
route(n, packet);
}
static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) {
unsigned char hmac[EVP_MAX_MD_SIZE];
if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
return false;
}
HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
return !memcmp_constant_time(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
vpn_packet_t *outpkt;
int outlen, outpad;
unsigned char hmac[EVP_MAX_MD_SIZE];
vpn_packet_t *outpkt = pkt[0];
size_t outlen;
if(!n->inkey) {
if(!cipher_active(&n->incipher)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
n->name, n->hostname);
n->name, n->hostname);
return;
}
/* Check packet length */
if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
n->name, n->hostname);
n->name, n->hostname);
return;
}
/* Check the message authentication code */
if(n->indigest && n->inmaclength) {
inpkt->len -= n->inmaclength;
HMAC(n->indigest, n->inkey, n->inkeylength,
(unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
if(memcmp_constant_time(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)",
n->name, n->hostname);
if(digest_active(&n->indigest)) {
inpkt->len -= n->indigest.maclength;
if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
return;
}
}
/* Decrypt the packet */
if(n->incipher) {
if(cipher_active(&n->incipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!EVP_DecryptInit_ex(n->inctx, NULL, NULL, NULL, NULL)
|| !EVP_DecryptUpdate(n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
(unsigned char *) &inpkt->seqno, inpkt->len)
|| !EVP_DecryptFinal_ex(n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s): %s",
n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
return;
}
outpkt->len = outlen + outpad;
outpkt->len = outlen;
inpkt = outpkt;
}
/* Check the sequence number */
inpkt->len -= sizeof(inpkt->seqno);
inpkt->len -= sizeof inpkt->seqno;
inpkt->seqno = ntohl(inpkt->seqno);
if(replaywin) {
if(inpkt->seqno != n->received_seqno + 1) {
if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
if(n->farfuture++ < replaywin >> 2) {
ifdebug(TRAFFIC) logger(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);
logger(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;
}
ifdebug(TRAFFIC) logger(LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
logger(LOG_WARNING, "Lost %d packets from %s (%s)",
inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
memset(n->late, 0, replaywin);
} else if(inpkt->seqno <= n->received_seqno) {
} 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))) {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
n->name, n->hostname, inpkt->seqno, n->received_seqno);
logger(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;
}
} else {
for(uint32_t i = n->received_seqno + 1; i < inpkt->seqno; i++) {
for(int i = n->received_seqno + 1; i < inpkt->seqno; i++)
n->late[(i / 8) % replaywin] |= 1 << i % 8;
}
}
}
@ -378,13 +324,11 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
n->late[(inpkt->seqno / 8) % replaywin] &= ~(1 << inpkt->seqno % 8);
}
if(inpkt->seqno > n->received_seqno) {
if(inpkt->seqno > n->received_seqno)
n->received_seqno = inpkt->seqno;
}
if(n->received_seqno > MAX_SEQNO) {
keyexpires = 0;
}
if(n->received_seqno > MAX_SEQNO)
regenerate_key();
/* Decompress the packet */
@ -393,41 +337,33 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
if(n->incompression) {
outpkt = pkt[nextpkt++];
if(!(outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression))) {
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while uncompressing packet from %s (%s)",
n->name, n->hostname);
n->name, n->hostname);
return;
}
inpkt = outpkt;
origlen -= MTU / 64 + 20;
origlen -= MTU/64 + 20;
}
inpkt->priority = 0;
if(!inpkt->data[12] && !inpkt->data[13]) {
if(!inpkt->data[12] && !inpkt->data[13])
mtu_probe_h(n, inpkt, origlen);
} else {
else
receive_packet(n, inpkt);
}
}
void receive_tcppacket(connection_t *c, const char *buffer, length_t len) {
void receive_tcppacket(connection_t *c, const char *buffer, int len) {
vpn_packet_t outpkt;
if(len > sizeof(outpkt.data)) {
return;
}
outpkt.len = len;
if(c->options & OPTION_TCPONLY) {
if(c->options & OPTION_TCPONLY)
outpkt.priority = 0;
} else {
else
outpkt.priority = -1;
}
memcpy(outpkt.data, buffer, len);
receive_packet(c->node, &outpkt);
@ -439,9 +375,13 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
vpn_packet_t *inpkt = origpkt;
int nextpkt = 0;
vpn_packet_t *outpkt;
int origlen;
int outlen, outpad;
int origpriority;
int origlen = origpkt->len;
size_t outlen;
#if defined(SOL_IP) && defined(IP_TOS)
static int priority = 0;
int origpriority = origpkt->priority;
#endif
int sock;
if(!n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
@ -451,9 +391,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Make sure we have a valid key */
if(!n->status.validkey) {
time_t now = time(NULL);
ifdebug(TRAFFIC) logger(LOG_INFO,
"No valid key known yet for %s (%s), forwarding via TCP",
n->name, n->hostname);
"No valid key known yet for %s (%s), forwarding via TCP",
n->name, n->hostname);
if(n->last_req_key + 10 <= now) {
send_req_key(n);
@ -467,29 +409,25 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
if(n->options & OPTION_PMTU_DISCOVERY && inpkt->len > n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
ifdebug(TRAFFIC) logger(LOG_INFO,
"Packet for %s (%s) larger than minimum MTU, forwarding via %s",
n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
"Packet for %s (%s) larger than minimum MTU, forwarding via %s",
n->name, n->hostname, n != n->nexthop ? n->nexthop->name : "TCP");
if(n != n->nexthop) {
if(n != n->nexthop)
send_packet(n->nexthop, origpkt);
} else {
else
send_tcppacket(n->nexthop->connection, origpkt);
}
return;
}
origlen = inpkt->len;
origpriority = inpkt->priority;
/* Compress the packet */
if(n->outcompression) {
outpkt = pkt[nextpkt++];
if(!(outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression))) {
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while compressing packet to %s (%s)",
n->name, n->hostname);
n->name, n->hostname);
return;
}
@ -499,127 +437,59 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add sequence number */
inpkt->seqno = htonl(++(n->sent_seqno));
inpkt->len += sizeof(inpkt->seqno);
inpkt->len += sizeof inpkt->seqno;
/* Encrypt the packet */
if(n->outcipher) {
if(cipher_active(&n->outcipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!EVP_EncryptInit_ex(n->outctx, NULL, NULL, NULL, NULL)
|| !EVP_EncryptUpdate(n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
(unsigned char *) &inpkt->seqno, inpkt->len)
|| !EVP_EncryptFinal_ex(n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s): %s",
n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
goto end;
}
outpkt->len = outlen + outpad;
outpkt->len = outlen;
inpkt = outpkt;
}
/* Add the message authentication code */
if(n->outdigest && n->outmaclength) {
HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
inpkt->len += n->outmaclength;
if(digest_active(&n->outdigest)) {
digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->outdigest);
}
/* Determine which socket we have to use */
if(n->address.sa.sa_family != listen_socket[n->sock].sa.sa.sa_family) {
for(int sock = 0; sock < listen_sockets; sock++) {
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family) {
n->sock = sock;
break;
}
}
}
for(sock = 0; sock < listen_sockets; sock++)
if(n->address.sa.sa_family == listen_socket[sock].sa.sa.sa_family)
break;
if(sock >= listen_sockets)
sock = 0; /* If none is available, just use the first and hope for the best. */
/* Send the packet */
struct sockaddr *sa;
socklen_t sl;
int sock;
sockaddr_t broadcast;
/* Overloaded use of priority field: -1 means local broadcast */
if(origpriority == -1 && n->prevedge) {
sock = rand() % listen_sockets;
memset(&broadcast, 0, sizeof(broadcast));
if(listen_socket[sock].sa.sa.sa_family == AF_INET6) {
broadcast.in6.sin6_family = AF_INET6;
broadcast.in6.sin6_addr.s6_addr[0x0] = 0xff;
broadcast.in6.sin6_addr.s6_addr[0x1] = 0x02;
broadcast.in6.sin6_addr.s6_addr[0xf] = 0x01;
broadcast.in6.sin6_port = n->prevedge->address.in.sin_port;
broadcast.in6.sin6_scope_id = listen_socket[sock].sa.in6.sin6_scope_id;
} else {
broadcast.in.sin_family = AF_INET;
broadcast.in.sin_addr.s_addr = -1;
broadcast.in.sin_port = n->prevedge->address.in.sin_port;
}
sa = &broadcast.sa;
sl = SALEN(broadcast.sa);
} else {
if(origpriority == -1) {
origpriority = 0;
}
sa = &(n->address.sa);
sl = SALEN(n->address.sa);
sock = n->sock;
#if defined(SOL_IP) && defined(IP_TOS)
if(priorityinheritance && origpriority != priority
&& listen_socket[sock].sa.sa.sa_family == AF_INET) {
priority = origpriority;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof priority)) /* SO_PRIORITY doesn't seem to work */
logger(LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
}
if(priorityinheritance && origpriority != listen_socket[n->sock].priority) {
listen_socket[n->sock].priority = origpriority;
switch(listen_socket[n->sock].sa.sa.sa_family) {
#if defined(IP_TOS)
case AF_INET:
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting IPv4 outgoing packet priority to %d", origpriority);
if(setsockopt(listen_socket[n->sock].udp, IPPROTO_IP, IP_TOS, (void *)&origpriority, sizeof(origpriority))) { /* SO_PRIORITY doesn't seem to work */
logger(LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
}
break;
#endif
#if defined(IPV6_TCLASS)
case AF_INET6:
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting IPv6 outgoing packet priority to %d", origpriority);
if(setsockopt(listen_socket[n->sock].udp, IPPROTO_IPV6, IPV6_TCLASS, (void *)&origpriority, sizeof(origpriority))) {
logger(LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
}
break;
#endif
default:
break;
}
}
if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, sa, sl) < 0 && !sockwouldblock(sockerrno)) {
if(sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
}
if(n->mtu >= origlen) {
if(n->mtu >= origlen)
n->mtu = origlen - 1;
}
} else {
ifdebug(TRAFFIC) logger(LOG_WARNING, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
}
} else
logger(LOG_ERR, "Error sending packet to %s (%s): %s", n->name, n->hostname, sockstrerror(sockerrno));
}
end:
@ -629,170 +499,154 @@ end:
/*
send a packet to the given vpn ip.
*/
void send_packet(const node_t *n, vpn_packet_t *packet) {
void send_packet(node_t *n, vpn_packet_t *packet) {
node_t *via;
if(n == myself) {
if(overwrite_mac) {
memcpy(packet->data, mymac.x, ETH_ALEN);
}
devops.write(packet);
if(overwrite_mac)
memcpy(packet->data, mymac.x, ETH_ALEN);
n->out_packets++;
n->out_bytes += packet->len;
write_packet(packet);
return;
}
ifdebug(TRAFFIC) logger(LOG_ERR, "Sending packet of %d bytes to %s (%s)",
packet->len, n->name, n->hostname);
packet->len, n->name, n->hostname);
if(!n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_INFO, "Node %s (%s) is not reachable",
n->name, n->hostname);
n->name, n->hostname);
return;
}
n->out_packets++;
n->out_bytes += packet->len;
via = (packet->priority == -1 || n->via == myself) ? n->nexthop : n->via;
if(via != n)
ifdebug(TRAFFIC) logger(LOG_INFO, "Sending packet to %s via %s (%s)",
n->name, via->name, n->via->hostname);
n->name, via->name, n->via->hostname);
if(packet->priority == -1 || ((myself->options | via->options) & OPTION_TCPONLY)) {
if(!send_tcppacket(via->connection, packet)) {
if(!send_tcppacket(via->connection, packet))
terminate_connection(via->connection, true);
}
} else {
} else
send_udppacket(via, packet);
}
}
/* Broadcast a packet using the minimum spanning tree */
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
avl_node_t *node;
splay_node_t *node;
connection_t *c;
node_t *n;
// Always give ourself a copy of the packet.
if(from != myself) {
send_packet(myself, packet);
}
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver || broadcast_mode == BMODE_NONE) {
return;
}
ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
packet->len, from->name, from->hostname);
packet->len, from->name, from->hostname);
switch(broadcast_mode) {
// In MST mode, broadcast packets travel via the Minimum Spanning Tree.
// This guarantees all nodes receive the broadcast packet, and
// usually distributes the sending of broadcast packets over all nodes.
case BMODE_MST:
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(from != myself) {
send_packet(myself, packet);
if(c->status.active && c->status.mst && c != from->nexthop->connection) {
send_packet(c->node, packet);
}
}
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver)
return;
}
break;
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
// In direct mode, we send copies to each node we know of.
// However, this only reaches nodes that can be reached in a single hop.
// We don't have enough information to forward broadcast packets in this case.
case BMODE_DIRECT:
if(from != myself) {
break;
}
for(node = node_udp_tree->head; node; node = node->next) {
n = node->data;
if(n->status.reachable && n != myself && ((n->via == myself && n->nexthop == n) || n->via == n)) {
send_packet(n, packet);
}
}
break;
default:
break;
if(c->status.active && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
}
}
static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
avl_node_t *node;
splay_node_t *node;
edge_t *e;
node_t *n = NULL;
bool hard = false;
static time_t last_hard_try = 0;
time_t now = time(NULL);
if(last_hard_try == now)
return NULL;
else
last_hard_try = now;
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
if(e->to == myself) {
if(e->to == myself)
continue;
if(sockaddrcmp_noport(from, &e->address)) {
if(last_hard_try == now)
continue;
hard = true;
}
if(last_hard_try == now && sockaddrcmp_noport(from, &e->address)) {
if(!try_mac(e->to, pkt))
continue;
}
if(!try_mac(e->to, pkt)) {
continue;
}
n = e->to;
break;
}
last_hard_try = now;
if(hard)
last_hard_try = now;
return n;
}
void handle_incoming_vpn_data(int sock) {
void handle_incoming_vpn_data(int sock, short events, void *data) {
vpn_packet_t pkt;
char *hostname;
sockaddr_t from;
socklen_t fromlen = sizeof(from);
socklen_t fromlen = sizeof from;
node_t *n;
int len;
ssize_t len = recvfrom(listen_socket[sock].udp, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
if(len <= 0 || len > UINT16_MAX) {
if(len >= 0) {
logger(LOG_ERR, "Receiving packet with invalid size");
} else if(!sockwouldblock(sockerrno)) {
if(len <= 0 || len > MAXSIZE) {
if(!sockwouldblock(sockerrno))
logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
}
return;
}
pkt.len = len;
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */
n = lookup_node_udp(&from);
if(!n) {
n = try_harder(&from, &pkt);
if(n) {
if(n)
update_node_udp(n, &from);
} else ifdebug(PROTOCOL) {
else ifdebug(PROTOCOL) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
free(hostname);
return;
} else {
return;
}
else
return;
}
n->sock = sock;
receive_udppacket(n, &pkt);
}
void handle_device_data(int sock, short events, void *data) {
vpn_packet_t packet;
packet.priority = 0;
if(read_packet(&packet)) {
myself->in_packets++;
myself->in_bytes += packet.len;
route(myself, &packet);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2005 Ivo Timmermans,
2000-2017 Guus Sliepen <guus@tinc-vpn.org>
2000-2010 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2009 Florian Forster <octo@verplant.org>
@ -22,26 +22,25 @@
#include "system.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "event.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
#include "proxy.h"
#include "utils.h"
#include "xalloc.h"
#include <assert.h>
/* Needed on Mac OS/X */
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif
int addressfamily = AF_UNSPEC;
int mintimeout = 0;
int maxtimeout = 900;
int seconds_till_retry = 5;
int udp_rcvbuf = 0;
@ -62,29 +61,22 @@ static void configure_tcp(connection_t *c) {
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, "fcntl for %s: %s", c->hostname, strerror(errno));
}
#elif defined(WIN32)
unsigned long arg = 1;
if(ioctlsocket(c->socket, FIONBIO, &arg) != 0) {
logger(LOG_ERR, "ioctlsocket for %s: %s", c->hostname, sockstrerror(sockerrno));
logger(LOG_ERR, "ioctlsocket for %s: %d", c->hostname, sockstrerror(sockerrno));
}
#endif
#if defined(SOL_TCP) && defined(TCP_NODELAY)
option = 1;
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option);
#endif
#if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(c->socket, IPPROTO_IP, IP_TOS, (void *)&option, sizeof(option));
#endif
#if defined(IPV6_TCLASS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(c->socket, IPPROTO_IPV6, IPV6_TCLASS, (void *)&option, sizeof(option));
setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
#endif
}
@ -96,23 +88,20 @@ static bool bind_to_interface(int sd) {
int status;
#endif /* defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) */
if(!get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
if(!get_config_string (lookup_config (config_tree, "BindToInterface"), &iface))
return true;
}
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
free(iface);
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
if(status) {
logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(errno));
return false;
}
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
logger(LOG_WARNING, "%s not supported on this platform", "BindToInterface");
#endif
@ -120,6 +109,63 @@ static bool bind_to_interface(int sd) {
return true;
}
static bool bind_to_address(connection_t *c) {
char *node;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
int status;
assert(c != NULL);
assert(c->socket >= 0);
node = NULL;
if(!get_config_string(lookup_config(config_tree, "BindToAddress"),
&node))
return true;
assert(node != NULL);
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = c->address.sa.sa_family;
/* We're called from `do_outgoing_connection' only. */
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
ai_list = NULL;
status = getaddrinfo(node, /* service = */ NULL,
&ai_hints, &ai_list);
if(status) {
logger(LOG_WARNING, "Error looking up %s port %s: %s",
node, "any", gai_strerror(status));
free(node);
return false;
}
assert(ai_list != NULL);
status = -1;
for(ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
status = bind(c->socket,
ai_list->ai_addr, ai_list->ai_addrlen);
if(!status)
break;
}
if(status) {
logger(LOG_ERR, "Can't bind to %s/tcp: %s", node, sockstrerror(sockerrno));
} else ifdebug(CONNECTIONS) {
logger(LOG_DEBUG, "Successfully bound outgoing "
"TCP socket to %s", node);
}
free(node);
freeaddrinfo(ai_list);
return status ? false : true;
}
int setup_listen_socket(const sockaddr_t *sa) {
int nfd;
char *addrstr;
@ -133,40 +179,30 @@ int setup_listen_socket(const sockaddr_t *sa) {
return -1;
}
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
/* Optimize TCP settings */
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
#if defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6) {
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
}
#else
#warning IPV6_V6ONLY not defined
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
setsockopt(nfd, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
#endif
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
if(get_config_string
(lookup_config(config_tree, "BindToInterface"), &iface)) {
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
free(iface);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) {
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(sockerrno));
logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
strerror(sockerrno));
return -1;
}
#else
logger(LOG_WARNING, "%s not supported on this platform", "BindToInterface");
#endif
@ -201,10 +237,6 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
return -1;
}
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
#ifdef O_NONBLOCK
{
int flags = fcntl(nfd, F_GETFL);
@ -212,14 +244,13 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(nfd);
logger(LOG_ERR, "System call `%s' failed: %s", "fcntl",
strerror(errno));
strerror(errno));
return -1;
}
}
#elif defined(WIN32)
{
unsigned long arg = 1;
if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
closesocket(nfd);
logger(LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
@ -229,62 +260,52 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
#endif
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf))) {
if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
}
if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf))) {
if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
logger(LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
}
#if defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6) {
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
}
#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
if(sa->sa.sa_family == AF_INET6)
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
#endif
#if defined(IP_DONTFRAG) && !defined(IP_DONTFRAGMENT)
#define IP_DONTFRAGMENT IP_DONTFRAG
#endif
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
#if defined(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = IP_PMTUDISC_DO;
setsockopt(nfd, IPPROTO_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
}
#elif defined(IP_DONTFRAGMENT)
#elif defined(IPPROTO_IP) && defined(IP_DONTFRAGMENT)
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = 1;
setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
}
#else
#warning No way to disable IPv4 fragmentation
#endif
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
#if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = IPV6_PMTUDISC_DO;
setsockopt(nfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
}
#elif defined(IPV6_DONTFRAG)
#elif defined(IPPROTO_IPV6) && defined(IPV6_DONTFRAG)
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = 1;
setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
}
#else
#warning No way to disable IPv6 fragmentation
#endif
if(!bind_to_interface(nfd)) {
if (!bind_to_interface(nfd)) {
closesocket(nfd);
return -1;
}
@ -300,109 +321,37 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
return nfd;
} /* int setup_vpn_in_socket */
static void retry_outgoing_handler(int fd, short events, void *data) {
setup_outgoing_connection(data);
}
void retry_outgoing(outgoing_t *outgoing) {
outgoing->timeout += 5;
if(outgoing->timeout < mintimeout) {
outgoing->timeout = mintimeout;
}
if(outgoing->timeout > maxtimeout) {
if(outgoing->timeout > maxtimeout)
outgoing->timeout = maxtimeout;
}
if(outgoing->event) {
event_del(outgoing->event);
}
outgoing->event = new_event();
outgoing->event->handler = (event_handler_t) setup_outgoing_connection;
outgoing->event->time = now + outgoing->timeout;
outgoing->event->data = outgoing;
event_add(outgoing->event);
timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
ifdebug(CONNECTIONS) logger(LOG_NOTICE,
"Trying to re-establish outgoing connection in %d seconds",
outgoing->timeout);
"Trying to re-establish outgoing connection in %d seconds",
outgoing->timeout);
}
void finish_connecting(connection_t *c) {
ifdebug(CONNECTIONS) logger(LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
c->last_ping_time = now;
configure_tcp(c);
c->last_ping_time = time(NULL);
c->status.connecting = false;
send_id(c);
}
static void do_outgoing_pipe(connection_t *c, char *command) {
#ifndef HAVE_MINGW
int fd[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
logger(LOG_ERR, "Could not create socketpair: %s\n", strerror(errno));
return;
}
if(fork()) {
c->socket = fd[0];
close(fd[1]);
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Using proxy %s", command);
return;
}
close(0);
close(1);
close(fd[0]);
dup2(fd[1], 0);
dup2(fd[1], 1);
close(fd[1]);
// Other filedescriptors should be closed automatically by CLOEXEC
char *host = NULL;
char *port = NULL;
sockaddr2str(&c->address, &host, &port);
setenv("REMOTEADDRESS", host, true);
setenv("REMOTEPORT", port, true);
setenv("NODE", c->name, true);
setenv("NAME", myself->name, true);
if(netname) {
setenv("NETNAME", netname, true);
}
int result = system(command);
if(result < 0) {
logger(LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
} else if(result) {
logger(LOG_ERR, "%s exited with non-zero status %d", command, result);
}
exit(result);
#else
logger(LOG_ERR, "Proxy type exec not supported on this platform!");
return;
#endif
}
static bool is_valid_host_port(const char *host, const char *port) {
for(const char *p = host; *p; p++)
if(!isalnum(*p) && *p != '-' && *p != '.') {
return false;
}
for(const char *p = port; *p; p++)
if(!isalnum(*p)) {
return false;
}
return true;
}
void do_outgoing_connection(connection_t *c) {
struct addrinfo *proxyai = NULL;
bool do_outgoing_connection(connection_t *c) {
char *address, *port, *space;
int result;
if(!c->outgoing) {
@ -411,58 +360,38 @@ void do_outgoing_connection(connection_t *c) {
}
begin:
if(!c->outgoing->ai) {
if(!c->outgoing->cfg) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
c->name);
c->status.remove = true;
c->name);
retry_outgoing(c->outgoing);
c->outgoing = NULL;
return;
connection_del(c);
return false;
}
char *address, *port, *space;
get_config_string(c->outgoing->cfg, &address);
space = strchr(address, ' ');
if(space) {
port = xstrdup(space + 1);
*space = 0;
} else {
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port)) {
if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
port = xstrdup("655");
}
}
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
// If we cannot resolve the address, maybe we are using a proxy that can?
if(!c->outgoing->ai && proxytype != PROXY_NONE && is_valid_host_port(address, port)) {
memset(&c->address, 0, sizeof(c->address));
c->address.sa.sa_family = AF_UNKNOWN;
c->address.unknown.address = address;
c->address.unknown.port = port;
} else {
free(address);
free(port);
}
free(address);
free(port);
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
if(!c->outgoing->ai && proxytype != PROXY_NONE) {
goto connect;
}
}
if(!c->outgoing->aip) {
if(c->outgoing->ai) {
if(c->outgoing->ai)
freeaddrinfo(c->outgoing->ai);
}
c->outgoing->ai = NULL;
goto begin;
}
@ -470,106 +399,42 @@ begin:
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
connect:
if(c->hostname) {
if(c->hostname)
free(c->hostname);
}
c->hostname = sockaddr2hostname(&c->address);
ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
c->hostname);
c->hostname);
if(!proxytype) {
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
} else if(proxytype == PROXY_EXEC) {
c->status.proxy_passed = true;
do_outgoing_pipe(c, proxyhost);
} else {
proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
if(!proxyai) {
goto begin;
}
ifdebug(CONNECTIONS) logger(LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
}
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
if(c->socket == -1) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
goto begin;
}
if(proxytype != PROXY_EXEC) {
configure_tcp(c);
}
#ifdef FD_CLOEXEC
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
int option = 1;
if(c->address.sa.sa_family == AF_INET6)
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
#endif
if(proxytype != PROXY_EXEC) {
#if defined(IPV6_V6ONLY)
int option = 1;
bind_to_interface(c->socket);
bind_to_address(c);
if(c->address.sa.sa_family == AF_INET6) {
setsockopt(c->socket, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
}
/* Optimize TCP settings */
#endif
bind_to_interface(c->socket);
int b = -1;
for(int i = 0; i < listen_sockets; i++) {
if(listen_socket[i].sa.sa.sa_family == c->address.sa.sa_family) {
if(b == -1) {
b = i;
} else {
b = -1;
break;
}
}
}
if(b != -1) {
sockaddr_t sa = listen_socket[b].sa;
if(sa.sa.sa_family == AF_INET) {
sa.in.sin_port = 0;
} else if(sa.sa.sa_family == AF_INET6) {
sa.in6.sin6_port = 0;
}
if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
char *addrstr = sockaddr2hostname(&sa);
logger(LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
free(addrstr);
}
}
}
configure_tcp(c);
/* Connect */
if(!proxytype) {
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
} else if(proxytype == PROXY_EXEC) {
result = 0;
} else {
result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
freeaddrinfo(proxyai);
}
now = time(NULL);
result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
if(result == -1) {
if(sockinprogress(sockerrno)) {
c->last_ping_time = now;
c->status.connecting = true;
return;
return true;
}
closesocket(c->socket);
@ -581,14 +446,32 @@ connect:
finish_connecting(c);
return;
return true;
}
static void handle_meta_write(int sock, short events, void *data) {
ifdebug(META) logger(LOG_DEBUG, "handle_meta_write() called");
connection_t *c = data;
ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
if(outlen <= 0) {
logger(LOG_ERR, "Onoes, outlen = %d (%s)", (int)outlen, strerror(errno));
terminate_connection(c, c->status.active);
return;
}
buffer_read(&c->outbuf, outlen);
if(!c->outbuf.len && event_initialized(&c->outevent))
event_del(&c->outevent);
}
void setup_outgoing_connection(outgoing_t *outgoing) {
connection_t *c;
node_t *n;
outgoing->event = NULL;
if(event_initialized(&outgoing->ev))
event_del(&outgoing->ev);
n = lookup_node(outgoing->name);
@ -608,66 +491,43 @@ void setup_outgoing_connection(outgoing_t *outgoing) {
c->outcompression = myself->connection->outcompression;
init_configuration(&c->config_tree);
if(!read_connection_config(c)) {
free_connection(c);
outgoing->timeout = maxtimeout;
retry_outgoing(outgoing);
return;
}
read_connection_config(c);
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg) {
logger(LOG_ERR, "No address specified for %s", c->name);
free_connection(c);
outgoing->timeout = maxtimeout;
retry_outgoing(outgoing);
return;
}
c->outgoing = outgoing;
c->last_ping_time = now;
c->last_ping_time = time(NULL);
connection_add(c);
do_outgoing_connection(c);
if (do_outgoing_connection(c)) {
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
}
}
/*
accept a new tcp connect and create a
new connection
*/
bool handle_new_meta_connection(int sock) {
static const int max_accept_burst = 10;
static int last_accept_burst;
static int last_accept_time;
void handle_new_meta_connection(int sock, short events, void *data) {
connection_t *c;
sockaddr_t sa;
int fd;
socklen_t len = sizeof(sa);
socklen_t len = sizeof sa;
fd = accept(sock, &sa.sa, &len);
if(fd < 0) {
logger(LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
return false;
}
if(last_accept_time == now) {
last_accept_burst++;
if(last_accept_burst >= max_accept_burst) {
if(last_accept_burst == max_accept_burst) {
ifdebug(CONNECTIONS) logger(LOG_WARNING, "Throttling incoming connections");
}
tarpit(fd);
return false;
}
} else {
last_accept_burst = 0;
last_accept_time = now;
return;
}
sockaddrunmap(&sa);
@ -682,27 +542,28 @@ bool handle_new_meta_connection(int sock) {
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now;
c->last_ping_time = time(NULL);
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection from %s", c->hostname);
event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
event_set(&c->outevent, c->socket, EV_WRITE | EV_PERSIST, handle_meta_write, c);
event_add(&c->inevent, NULL);
configure_tcp(c);
connection_add(c);
c->allow_request = ID;
return true;
send_id(c);
}
static void free_outgoing(outgoing_t *outgoing) {
if(outgoing->ai) {
if(outgoing->ai)
freeaddrinfo(outgoing->ai);
}
if(outgoing->name) {
if(outgoing->name)
free(outgoing->name);
}
free(outgoing);
}
@ -711,21 +572,21 @@ void try_outgoing_connections(void) {
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
outgoing_list = list_alloc((list_action_t)free_outgoing);
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
get_config_string(cfg, &name);
if(!check_id(name)) {
logger(LOG_ERR,
"Invalid name for outgoing connection in %s line %d",
cfg->file, cfg->line);
"Invalid name for outgoing connection in %s line %d",
cfg->file, cfg->line);
free(name);
continue;
}
outgoing = xmalloc_and_zero(sizeof(*outgoing));
outgoing = xmalloc_and_zero(sizeof *outgoing);
outgoing->name = name;
list_insert_tail(outgoing_list, outgoing);
setup_outgoing_connection(outgoing);

View file

@ -1,7 +1,7 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998-2005 Ivo Timmermans
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -33,21 +33,17 @@ bool hostnames = false;
Return NULL on failure.
*/
struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) {
struct addrinfo *ai = NULL, hint = {0};
struct addrinfo *ai, hint = {0};
int err;
hint.ai_family = addressfamily;
hint.ai_socktype = socktype;
#if HAVE_DECL_RES_INIT
// ensure glibc reloads /etc/resolv.conf.
res_init();
#endif
err = getaddrinfo(address, service, &hint, &ai);
if(err) {
logger(LOG_WARNING, "Error looking up %s port %s: %s", address,
service, gai_strerror(err));
service, gai_strerror(err));
return NULL;
}
@ -55,7 +51,7 @@ struct addrinfo *str2addrinfo(const char *address, const char *service, int sock
}
sockaddr_t str2sockaddr(const char *address, const char *port) {
struct addrinfo *ai = NULL, hint = {0};
struct addrinfo *ai, hint = {0};
sockaddr_t result;
int err;
@ -67,7 +63,7 @@ sockaddr_t str2sockaddr(const char *address, const char *port) {
if(err || !ai) {
ifdebug(SCARY_THINGS)
logger(LOG_DEBUG, "Unknown type address %s port %s", address, port);
logger(LOG_DEBUG, "Unknown type address %s port %s", address, port);
result.sa.sa_family = AF_UNKNOWN;
result.unknown.address = xstrdup(address);
result.unknown.port = xstrdup(port);
@ -87,38 +83,28 @@ void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr) {
int err;
if(sa->sa.sa_family == AF_UNKNOWN) {
if(addrstr) {
*addrstr = xstrdup(sa->unknown.address);
}
if(portstr) {
*portstr = xstrdup(sa->unknown.port);
}
*addrstr = xstrdup(sa->unknown.address);
*portstr = xstrdup(sa->unknown.port);
return;
}
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV);
if(err) {
logger(LOG_ERR, "Error while translating addresses: %s",
gai_strerror(err));
gai_strerror(err));
abort();
}
scopeid = strchr(address, '%');
if(scopeid) {
*scopeid = '\0'; /* Descope. */
}
if(scopeid)
*scopeid = '\0'; /* Descope. */
if(addrstr) {
if(addrstr)
*addrstr = xstrdup(address);
}
if(portstr) {
if(portstr)
*portstr = xstrdup(port);
}
}
char *sockaddr2hostname(const sockaddr_t *sa) {
@ -132,12 +118,11 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
return str;
}
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof(address), port, sizeof(port),
hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
err = getnameinfo(&sa->sa, SALEN(sa->sa), address, sizeof address, port, sizeof port,
hostnames ? 0 : (NI_NUMERICHOST | NI_NUMERICSERV));
if(err) {
logger(LOG_ERR, "Error while looking up hostname: %s",
gai_strerror(err));
gai_strerror(err));
}
xasprintf(&str, "%s port %s", address, port);
@ -150,27 +135,26 @@ int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b) {
result = a->sa.sa_family - b->sa.sa_family;
if(result) {
if(result)
return result;
}
switch(a->sa.sa_family) {
case AF_UNSPEC:
return 0;
switch (a->sa.sa_family) {
case AF_UNSPEC:
return 0;
case AF_UNKNOWN:
return strcmp(a->unknown.address, b->unknown.address);
case AF_UNKNOWN:
return strcmp(a->unknown.address, b->unknown.address);
case AF_INET:
return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
case AF_INET:
return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
case AF_INET6:
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
case AF_INET6:
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
}
}
@ -179,45 +163,41 @@ int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
result = a->sa.sa_family - b->sa.sa_family;
if(result) {
if(result)
return result;
}
switch(a->sa.sa_family) {
case AF_UNSPEC:
return 0;
switch (a->sa.sa_family) {
case AF_UNSPEC:
return 0;
case AF_UNKNOWN:
result = strcmp(a->unknown.address, b->unknown.address);
case AF_UNKNOWN:
result = strcmp(a->unknown.address, b->unknown.address);
if(result) {
return result;
}
if(result)
return result;
return strcmp(a->unknown.port, b->unknown.port);
return strcmp(a->unknown.port, b->unknown.port);
case AF_INET:
result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
case AF_INET:
result = memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof a->in.sin_addr);
if(result) {
return result;
}
if(result)
return result;
return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof(a->in.sin_port));
return memcmp(&a->in.sin_port, &b->in.sin_port, sizeof a->in.sin_port);
case AF_INET6:
result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
case AF_INET6:
result = memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof a->in6.sin6_addr);
if(result) {
return result;
}
if(result)
return result;
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof(a->in6.sin6_port));
return memcmp(&a->in6.sin6_port, &b->in6.sin6_port, sizeof a->in6.sin6_port);
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
default:
logger(LOG_ERR, "sockaddrcmp() was called with unknown address family %d, exitting!",
a->sa.sa_family);
abort();
}
}
@ -237,7 +217,7 @@ void sockaddrfree(sockaddr_t *a) {
free(a->unknown.port);
}
}
void sockaddrunmap(sockaddr_t *sa) {
if(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->in6.sin6_addr)) {
sa->in.sin_addr.s_addr = ((uint32_t *) & sa->in6.sin6_addr)[3];
@ -245,31 +225,6 @@ void sockaddrunmap(sockaddr_t *sa) {
}
}
void sockaddr_setport(sockaddr_t *sa, const char *port) {
uint16_t portnum = htons(atoi(port));
if(!portnum) {
return;
}
switch(sa->sa.sa_family) {
case AF_INET:
sa->in.sin_port = portnum;
break;
case AF_INET6:
sa->in6.sin6_port = portnum;
break;
case AF_UNKNOWN:
free(sa->unknown.port);
sa->unknown.port = xstrdup(port);
default:
return;
}
}
/* Subnet mask handling */
int maskcmp(const void *va, const void *vb, int masklen) {
@ -279,15 +234,13 @@ int maskcmp(const void *va, const void *vb, int masklen) {
for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
result = a[i] - b[i];
if(result) {
if(result)
return result;
}
}
if(m)
return (a[i] & (0x100 - (1 << (8 - m)))) -
(b[i] & (0x100 - (1 << (8 - m))));
(b[i] & (0x100 - (1 << (8 - m))));
return 0;
}
@ -299,13 +252,11 @@ void mask(void *va, int masklen, int len) {
i = masklen / 8;
masklen %= 8;
if(masklen) {
if(masklen)
a[i++] &= (0x100 - (1 << (8 - masklen)));
}
for(; i < len; i++) {
for(; i < len; i++)
a[i] = 0;
}
}
void maskcpy(void *va, const void *vb, int masklen, int len) {
@ -313,18 +264,16 @@ void maskcpy(void *va, const void *vb, int masklen, int len) {
char *a = va;
const char *b = vb;
for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
for(m = masklen, i = 0; m >= 8; m -= 8, i++)
a[i] = b[i];
}
if(m) {
a[i] = b[i] & (0x100 - (1 << (8 - m)));
i++;
}
for(; i < len; i++) {
for(; i < len; i++)
a[i] = 0;
}
}
bool maskcheck(const void *va, int masklen, int len) {
@ -334,14 +283,12 @@ bool maskcheck(const void *va, int masklen, int len) {
i = masklen / 8;
masklen %= 8;
if(masklen && a[i++] & (0xff >> masklen)) {
if(masklen && a[i++] & (0xff >> masklen))
return false;
}
for(; i < len; i++)
if(a[i] != 0) {
if(a[i] != 0)
return false;
}
return true;
}

View file

@ -1,10 +1,7 @@
#ifndef TINC_NETUTL_H
#define TINC_NETUTL_H
/*
netutl.h -- header file for netutl.c
Copyright (C) 1998-2005 Ivo Timmermans
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,23 +18,25 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_NETUTL_H__
#define __TINC_NETUTL_H__
#include "net.h"
extern bool hostnames;
extern struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype);
extern sockaddr_t str2sockaddr(const char *address, const char *port);
extern void sockaddr2str(const sockaddr_t *sa, char **addrstr, char **portstr);
extern char *sockaddr2hostname(const sockaddr_t *sa);
extern int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b);
extern int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b);
extern void sockaddrunmap(sockaddr_t *sa);
extern void sockaddrfree(sockaddr_t *sa);
extern void sockaddrcpy(sockaddr_t *dest, const sockaddr_t *src);
extern void sockaddr_setport(sockaddr_t *sa, const char *port);
extern int maskcmp(const void *a, const void *b, int masklen);
extern void maskcpy(void *dest, const void *src, int masklen, int len);
extern void mask(void *mask, int masklen, int len);
extern bool maskcheck(const void *mask, int masklen, int len);
extern struct addrinfo *str2addrinfo(const char *, const char *, int);
extern sockaddr_t str2sockaddr(const char *, const char *);
extern void sockaddr2str(const sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(const sockaddr_t *);
extern int sockaddrcmp(const sockaddr_t *, const sockaddr_t *);
extern int sockaddrcmp_noport(const sockaddr_t *, const sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);
extern int maskcmp(const void *, const void *, int);
extern void maskcpy(void *, const void *, int, int);
extern void mask(void *, int, int);
extern bool maskcheck(const void *, int, int);
#endif
#endif /* __TINC_NETUTL_H__ */

View file

@ -1,6 +1,6 @@
/*
node.c -- node tree management
Copyright (C) 2001-2016 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2011 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -20,7 +20,8 @@
#include "system.h"
#include "avl_tree.h"
#include "control_common.h"
#include "splay_tree.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
@ -28,8 +29,8 @@
#include "utils.h"
#include "xalloc.h"
avl_tree_t *node_tree; /* Known nodes, sorted by name */
avl_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
splay_tree_t *node_tree; /* Known nodes, sorted by name */
splay_tree_t *node_udp_tree; /* Known nodes, sorted by address and port */
node_t *myself;
@ -38,35 +39,32 @@ static int node_compare(const node_t *a, const node_t *b) {
}
static int node_udp_compare(const node_t *a, const node_t *b) {
return sockaddrcmp(&a->address, &b->address);
int result;
result = sockaddrcmp(&a->address, &b->address);
if(result)
return result;
return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
}
void init_nodes(void) {
node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node);
node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL);
node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL);
}
void exit_nodes(void) {
avl_delete_tree(node_udp_tree);
avl_delete_tree(node_tree);
splay_delete_tree(node_udp_tree);
splay_delete_tree(node_tree);
}
node_t *new_node(void) {
node_t *n = xmalloc_and_zero(sizeof(*n));
if(replaywin) {
n->late = xmalloc_and_zero(replaywin);
}
node_t *n = xmalloc_and_zero(sizeof *n);
if(replaywin) n->late = xmalloc_and_zero(replaywin);
n->subnet_tree = new_subnet_tree();
n->edge_tree = new_edge_tree();
n->inctx = EVP_CIPHER_CTX_new();
n->outctx = EVP_CIPHER_CTX_new();
if(!n->inctx || !n->outctx) {
abort();
}
n->mtu = MTU;
n->maxmtu = MTU;
@ -74,52 +72,43 @@ node_t *new_node(void) {
}
void free_node(node_t *n) {
if(n->inkey) {
free(n->inkey);
}
if(n->outkey) {
free(n->outkey);
}
if(n->subnet_tree) {
if(n->subnet_tree)
free_subnet_tree(n->subnet_tree);
}
if(n->edge_tree) {
if(n->edge_tree)
free_edge_tree(n->edge_tree);
}
sockaddrfree(&n->address);
EVP_CIPHER_CTX_free(n->outctx);
EVP_CIPHER_CTX_free(n->inctx);
cipher_close(&n->incipher);
digest_close(&n->indigest);
cipher_close(&n->outcipher);
digest_close(&n->outdigest);
if(n->mtuevent) {
event_del(n->mtuevent);
}
ecdh_free(&n->ecdh);
ecdsa_free(&n->ecdsa);
if(n->hostname) {
if(timeout_initialized(&n->mtuevent))
event_del(&n->mtuevent);
if(n->hostname)
free(n->hostname);
}
if(n->name) {
if(n->name)
free(n->name);
}
if(n->late) {
if(n->late)
free(n->late);
}
free(n);
}
void node_add(node_t *n) {
avl_insert(node_tree, n);
splay_insert(node_tree, n);
}
void node_del(node_t *n) {
avl_node_t *node, *next;
splay_node_t *node, *next;
edge_t *e;
subnet_t *s;
@ -135,25 +124,25 @@ void node_del(node_t *n) {
edge_del(e);
}
avl_delete(node_udp_tree, n);
avl_delete(node_tree, n);
splay_delete(node_udp_tree, n);
splay_delete(node_tree, n);
}
node_t *lookup_node(char *name) {
node_t n = {0};
node_t n = {NULL};
n.name = name;
return avl_search(node_tree, &n);
return splay_search(node_tree, &n);
}
node_t *lookup_node_udp(const sockaddr_t *sa) {
node_t n = {0};
node_t n = {NULL};
n.address = *sa;
n.name = NULL;
return avl_search(node_udp_tree, &n);
return splay_search(node_udp_tree, &n);
}
void update_node_udp(node_t *n, const sockaddr_t *sa) {
@ -162,38 +151,48 @@ void update_node_udp(node_t *n, const sockaddr_t *sa) {
return;
}
avl_delete(node_udp_tree, n);
splay_delete(node_udp_tree, n);
if(n->hostname) {
if(n->hostname)
free(n->hostname);
}
if(sa) {
n->address = *sa;
n->hostname = sockaddr2hostname(&n->address);
avl_insert(node_udp_tree, n);
splay_insert(node_udp_tree, n);
ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
} else {
memset(&n->address, 0, sizeof(n->address));
memset(&n->address, 0, sizeof n->address);
n->hostname = NULL;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s cleared", n->name);
}
}
void dump_nodes(void) {
avl_node_t *node;
bool dump_nodes(connection_t *c) {
splay_node_t *node;
node_t *n;
logger(LOG_DEBUG, "Nodes:");
for(node = node_tree->head; node; node = node->next) {
n = node->data;
logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)",
n->name, n->hostname, n->outcipher ? EVP_CIPHER_nid(n->outcipher) : 0,
n->outdigest ? EVP_MD_type(n->outdigest) : 0, n->outmaclength, n->outcompression,
n->options, bitfield_to_int(&n->status, sizeof(n->status)), n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
send_request(c, "%d %d %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s distance %d pmtu %hd (min %hd max %hd)", CONTROL, REQ_DUMP_NODES,
n->name, n->hostname, cipher_get_nid(&n->outcipher),
digest_get_nid(&n->outdigest), (int)digest_length(&n->outdigest), n->outcompression,
n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name ?: "-" : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
}
logger(LOG_DEBUG, "End of nodes.");
return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
}
bool dump_traffic(connection_t *c) {
splay_node_t *node;
node_t *n;
for(node = node_tree->head; node; node = node->next) {
n = node->data;
send_request(c, "%d %d %s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, CONTROL, REQ_DUMP_TRAFFIC,
n->name, n->in_packets, n->in_bytes, n->out_packets, n->out_bytes);
}
return send_request(c, "%d %d", CONTROL, REQ_DUMP_TRAFFIC);
}

View file

@ -1,9 +1,6 @@
#ifndef TINC_NODE_H
#define TINC_NODE_H
/*
node.h -- header for node.c
Copyright (C) 2001-2016 Guus Sliepen <guus@tinc-vpn.org>,
Copyright (C) 2001-2010 Guus Sliepen <guus@tinc-vpn.org>,
2001-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
@ -21,86 +18,90 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "avl_tree.h"
#ifndef __TINC_NODE_H__
#define __TINC_NODE_H__
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "event.h"
#include "digest.h"
#include "ecdh.h"
#include "subnet.h"
typedef struct node_status_t {
unsigned int unused_active: 1; /* 1 if active (not used for nodes) */
unsigned int validkey: 1; /* 1 if we currently have a valid key for him */
unsigned int unused_waitingforkey: 1; /* 1 if we already sent out a request */
unsigned int visited: 1; /* 1 if this node has been visited by one of the graph algorithms */
unsigned int reachable: 1; /* 1 if this node is reachable in the graph */
unsigned int indirect: 1; /* 1 if this node is not directly reachable by us */
unsigned int unused: 26;
unsigned int unused_active:1; /* 1 if active (not used for nodes) */
unsigned int validkey:1; /* 1 if we currently have a valid key for him */
unsigned int unused_waitingforkey:1; /* 1 if we already sent out a request */
unsigned int visited:1; /* 1 if this node has been visited by one of the graph algorithms */
unsigned int reachable:1; /* 1 if this node is reachable in the graph */
unsigned int indirect:1; /* 1 if this node is not directly reachable by us */
unsigned int ecdh:1; /* 1 if this node supports ECDH key exchange */
unsigned int unused:25;
} node_status_t;
typedef struct node_t {
char *name; /* name of this node */
uint32_t options; /* options turned on for this node */
char *name; /* name of this node */
uint32_t options; /* options turned on for this node */
int sock; /* Socket to use for outgoing UDP packets */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
sockaddr_t address; /* his real (internet) ip to send UDP packets to */
char *hostname; /* the hostname of its real ip */
node_status_t status;
time_t last_req_key;
const EVP_CIPHER *incipher; /* Cipher type for UDP packets received from him */
char *inkey; /* Cipher key and iv */
int inkeylength; /* Cipher key and iv length */
EVP_CIPHER_CTX *inctx; /* Cipher context */
ecdsa_t ecdsa; /* His public ECDSA key */
ecdh_t ecdh; /* State for ECDH key exchange */
const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/
char *outkey; /* Cipher key and iv */
int outkeylength; /* Cipher key and iv length */
EVP_CIPHER_CTX *outctx; /* Cipher context */
cipher_t incipher; /* Cipher for UDP packets */
digest_t indigest; /* Digest for UDP packets */
const EVP_MD *indigest; /* Digest type for MAC of packets received from him */
int inmaclength; /* Length of MAC */
cipher_t outcipher; /* Cipher for UDP packets */
digest_t outdigest; /* Digest for UDP packets */
const EVP_MD *outdigest; /* Digest type for MAC of packets sent to him*/
int outmaclength; /* Length of MAC */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int distance;
struct node_t *nexthop; /* nearest node from us to him */
struct node_t *via; /* next hop for UDP packets */
struct node_t *nexthop; /* nearest node from us to him */
struct edge_t *prevedge; /* nearest node from him to us */
struct node_t *via; /* next hop for UDP packets */
splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
avl_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
avl_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
uint32_t farfuture; /* Packets in a row that have arrived from the far future */
unsigned char* late; /* Bitfield marking late packets */
uint32_t sent_seqno; /* Sequence number last sent to this node */
uint32_t received_seqno; /* Sequence number last received from this node */
uint32_t farfuture; /* Packets in a row that have arrived from the far future */
unsigned char *late; /* Bitfield marking late 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 */
struct event mtuevent; /* Probe event */
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 */
event_t *mtuevent; /* Probe event */
uint64_t in_packets;
uint64_t in_bytes;
uint64_t out_packets;
uint64_t out_bytes;
} node_t;
extern struct node_t *myself;
extern avl_tree_t *node_tree;
extern avl_tree_t *node_udp_tree;
extern splay_tree_t *node_tree;
extern splay_tree_t *node_udp_tree;
extern void init_nodes(void);
extern void exit_nodes(void);
extern node_t *new_node(void) __attribute__((__malloc__));
extern void free_node(node_t *n);
extern void node_add(node_t *n);
extern void node_del(node_t *n);
extern node_t *lookup_node(char *name);
extern node_t *lookup_node_udp(const sockaddr_t *sa);
extern void update_node_udp(node_t *n, const sockaddr_t *sa);
extern void dump_nodes(void);
extern node_t *new_node(void) __attribute__ ((__malloc__));
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_udp(const sockaddr_t *);
extern bool dump_nodes(struct connection_t *);
extern bool dump_traffic(struct connection_t *);
extern void update_node_udp(node_t *, const sockaddr_t *);
#endif
#endif /* __TINC_NODE_H__ */

146
src/openssl/cipher.c Normal file
View file

@ -0,0 +1,146 @@
/*
cipher.c -- Symmetric block cipher handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/rand.h>
#include <openssl/err.h>
#include "cipher.h"
#include "logger.h"
#include "xalloc.h"
static bool cipher_open(cipher_t *cipher) {
EVP_CIPHER_CTX_init(&cipher->ctx);
return true;
}
bool cipher_open_by_name(cipher_t *cipher, const char *name) {
cipher->cipher = EVP_get_cipherbyname(name);
if(cipher->cipher)
return cipher_open(cipher);
logger(LOG_ERR, "Unknown cipher name '%s'!", name);
return false;
}
bool cipher_open_by_nid(cipher_t *cipher, int nid) {
cipher->cipher = EVP_get_cipherbynid(nid);
if(cipher->cipher)
return cipher_open(cipher);
logger(LOG_ERR, "Unknown cipher nid %d!", nid);
return false;
}
bool cipher_open_blowfish_ofb(cipher_t *cipher) {
cipher->cipher = EVP_bf_ofb();
return cipher_open(cipher);
}
void cipher_close(cipher_t *cipher) {
EVP_CIPHER_CTX_cleanup(&cipher->ctx);
}
size_t cipher_keylength(const cipher_t *cipher) {
return cipher->cipher->key_len + cipher->cipher->iv_len;
}
bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
bool result;
if(encrypt)
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
else
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key, (unsigned char *)key + cipher->cipher->key_len);
if(result)
return true;
logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
bool result;
if(encrypt)
result = EVP_EncryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
else
result = EVP_DecryptInit_ex(&cipher->ctx, cipher->cipher, NULL, (unsigned char *)key + len - cipher->cipher->key_len, (unsigned char *)key + len - cipher->cipher->iv_len - cipher->cipher->key_len);
if(result)
return true;
logger(LOG_ERR, "Error while setting key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
if(oneshot) {
int len, pad;
if(EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
*outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
*outlen = len;
return true;
}
}
logger(LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
if(oneshot) {
int len, pad;
if(EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, NULL)
&& EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, indata, inlen)
&& EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) {
*outlen = len + pad;
return true;
}
} else {
int len;
if(EVP_EncryptUpdate(&cipher->ctx, outdata, &len, indata, inlen)) {
*outlen = len;
return true;
}
}
logger(LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
int cipher_get_nid(const cipher_t *cipher) {
return cipher->cipher ? cipher->cipher->nid : 0;
}
bool cipher_active(const cipher_t *cipher) {
return cipher->cipher && cipher->cipher->nid != 0;
}

46
src/openssl/cipher.h Normal file
View file

@ -0,0 +1,46 @@
/*
cipher.h -- header file cipher.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CIPHER_H__
#define __TINC_CIPHER_H__
#include <openssl/evp.h>
#define CIPHER_MAX_BLOCK_SIZE EVP_MAX_BLOCK_LENGTH
#define CIPHER_MAX_KEY_SIZE EVP_MAX_KEY_LENGTH
#define CIPHER_MAX_IV_SIZE EVP_MAX_IV_LENGTH
typedef struct cipher {
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher;
} cipher_t;
extern bool cipher_open_by_name(cipher_t *, const char *);
extern bool cipher_open_by_nid(cipher_t *, int);
extern bool cipher_open_blowfish_ofb(cipher_t *);
extern void cipher_close(cipher_t *);
extern size_t cipher_keylength(const cipher_t *);
extern bool cipher_set_key(cipher_t *, void *, bool);
extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool);
extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool);
extern int cipher_get_nid(const cipher_t *);
extern bool cipher_active(const cipher_t *);
#endif

43
src/openssl/crypto.c Normal file
View file

@ -0,0 +1,43 @@
/*
crypto.c -- Cryptographic miscellaneous functions and initialisation
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include "crypto.h"
void crypto_init(void) {
RAND_load_file("/dev/urandom", 1024);
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
OpenSSL_add_all_algorithms();
}
void crypto_exit(void) {
EVP_cleanup();
}
void randomize(void *out, size_t outlen) {
RAND_pseudo_bytes(out, outlen);
}

27
src/openssl/crypto.h Normal file
View file

@ -0,0 +1,27 @@
/*
crypto.h -- header for crypto.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_CRYPTO_H__
#define __TINC_CRYPTO_H__
extern void crypto_init();
extern void crypto_exit();
extern void randomize(void *, size_t);
#endif

124
src/openssl/digest.c Normal file
View file

@ -0,0 +1,124 @@
/*
digest.c -- Digest handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "utils.h"
#include "xalloc.h"
#include <openssl/err.h>
#include <openssl/hmac.h>
#include "digest.h"
#include "logger.h"
static void set_maclength(digest_t *digest, int maclength) {
int digestlen = EVP_MD_size(digest->digest);
if(maclength > digestlen || maclength < 0)
digest->maclength = digestlen;
else
digest->maclength = maclength;
}
bool digest_open_by_name(digest_t *digest, const char *name, int maclength) {
digest->digest = EVP_get_digestbyname(name);
digest->key = NULL;
if(!digest->digest) {
logger(LOG_DEBUG, "Unknown digest name '%s'!", name);
return false;
}
set_maclength(digest, maclength);
return true;
}
bool digest_open_by_nid(digest_t *digest, int nid, int maclength) {
digest->digest = EVP_get_digestbynid(nid);
digest->key = NULL;
if(!digest->digest) {
logger(LOG_DEBUG, "Unknown digest nid %d!", nid);
return false;
}
set_maclength(digest, maclength);
return true;
}
bool digest_open_sha1(digest_t *digest, int maclength) {
digest->digest = EVP_sha1();
digest->key = NULL;
set_maclength(digest, maclength);
return true;
}
bool digest_set_key(digest_t *digest, const void *key, size_t len) {
digest->key = xrealloc(digest->key, len);
memcpy(digest->key, key, len);
digest->keylength = len;
return true;
}
void digest_close(digest_t *digest) {
if(digest->key)
free(digest->key);
digest->key = NULL;
}
bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
size_t len = EVP_MD_size(digest->digest);
unsigned char tmpdata[len];
if(digest->key) {
HMAC(digest->digest, digest->key, digest->keylength, indata, inlen, tmpdata, NULL);
} else {
EVP_MD_CTX ctx;
if(!EVP_DigestInit(&ctx, digest->digest)
|| !EVP_DigestUpdate(&ctx, indata, inlen)
|| !EVP_DigestFinal(&ctx, tmpdata, NULL)) {
logger(LOG_DEBUG, "Error creating digest: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
}
memcpy(outdata, tmpdata, digest->maclength);
return true;
}
bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
size_t len = digest->maclength;
unsigned char outdata[len];
return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, digest->maclength);
}
int digest_get_nid(const digest_t *digest) {
return digest->digest ? digest->digest->type : 0;
}
size_t digest_length(const digest_t *digest) {
return digest->maclength;
}
bool digest_active(const digest_t *digest) {
return digest->digest && digest->digest->type != 0;
}

45
src/openssl/digest.h Normal file
View file

@ -0,0 +1,45 @@
/*
digest.h -- header file digest.c
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_DIGEST_H__
#define __TINC_DIGEST_H__
#include <openssl/evp.h>
#define DIGEST_MAX_SIZE EVP_MAX_MD_SIZE
typedef struct digest {
const EVP_MD *digest;
int maclength;
int keylength;
char *key;
} digest_t;
extern bool digest_open_by_name(struct digest *, const char *name, int maclength);
extern bool digest_open_by_nid(struct digest *, int nid, int maclength);
extern bool digest_open_sha1(struct digest *, int maclength);
extern void digest_close(struct digest *);
extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern bool digest_set_key(struct digest *, const void *key, size_t len);
extern int digest_get_nid(const struct digest *);
extern size_t digest_length(const struct digest *);
extern bool digest_active(const struct digest *);
#endif

84
src/openssl/ecdh.c Normal file
View file

@ -0,0 +1,84 @@
/*
ecdh.c -- Diffie-Hellman key exchange handling
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "utils.h"
#include "xalloc.h"
#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include "ecdh.h"
#include "logger.h"
bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey) {
*ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!EC_KEY_generate_key(*ecdh)) {
logger(LOG_ERR, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
const EC_POINT *point = EC_KEY_get0_public_key(*ecdh);
if(!point) {
logger(LOG_ERR, "Getting public key failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
size_t result = EC_POINT_point2oct(EC_KEY_get0_group(*ecdh), point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL);
if(!result) {
logger(LOG_ERR, "Converting EC_POINT to binary failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
return true;
}
bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared) {
EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(*ecdh));
if(!point) {
logger(LOG_ERR, "EC_POINT_new() failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
int result = EC_POINT_oct2point(EC_KEY_get0_group(*ecdh), point, pubkey, ECDH_SIZE, NULL);
if(!result) {
logger(LOG_ERR, "Converting binary to EC_POINT failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
result = ECDH_compute_key(shared, ECDH_SIZE, point, *ecdh, NULL);
EC_POINT_free(point);
EC_KEY_free(*ecdh);
*ecdh = NULL;
if(!result) {
logger(LOG_ERR, "Computing Elliptic Curve Diffie-Hellman shared key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
void ecdh_free(ecdh_t *ecdh) {
if(*ecdh) {
EC_KEY_free(*ecdh);
*ecdh = NULL;
}
}

View file

@ -1,9 +1,6 @@
#ifndef TINC_PROXY_H
#define TINC_PROXY_H
/*
proxy.h -- header for proxy.c
Copyright (C) 2015 Guus Sliepen <guus@tinc-vpn.org>
ecdh.h -- header file for ecdh.c
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,24 +17,18 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "connection.h"
#ifndef __TINC_ECDH_H__
#define __TINC_ECDH_H__
typedef enum proxytype_t {
PROXY_NONE = 0,
PROXY_SOCKS4,
PROXY_SOCKS4A,
PROXY_SOCKS5,
PROXY_HTTP,
PROXY_EXEC,
} proxytype_t;
#include <openssl/ecdh.h>
extern proxytype_t proxytype;
extern char *proxyhost;
extern char *proxyport;
extern char *proxyuser;
extern char *proxypass;
#define ECDH_SIZE 67
#define ECDH_SHARED_SIZE 66
extern bool send_proxyrequest(struct connection_t *c);
extern int receive_proxy_meta(struct connection_t *c);
typedef EC_KEY *ecdh_t;
extern bool ecdh_generate_public(ecdh_t *ecdh, void *pubkey);
extern bool ecdh_compute_shared(ecdh_t *ecdh, const void *pubkey, void *shared);
extern void ecdh_free(ecdh_t *ecdh);
#endif

130
src/openssl/ecdsa.c Normal file
View file

@ -0,0 +1,130 @@
/*
ecdsa.c -- ECDSA key handling
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include "logger.h"
#include "ecdsa.h"
#include "utils.h"
// Get and set ECDSA keys
//
bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p) {
*ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
int len = strlen(p);
unsigned char pubkey[len / 4 * 3 + 3];
const unsigned char *ppubkey = pubkey;
len = b64decode(p, pubkey, len);
if(!o2i_ECPublicKey(ecdsa, &ppubkey, len)) {
logger(LOG_DEBUG, "o2i_ECPublicKey failed: %s", ERR_error_string(ERR_get_error(), NULL));
abort();
}
return true;
}
char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
unsigned char *pubkey = NULL;
int len = i2o_ECPublicKey(*ecdsa, &pubkey);
char *base64 = malloc(len * 4 / 3 + 5);
b64encode(pubkey, base64, len);
free(pubkey);
return base64;
}
// Read PEM ECDSA keys
bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
*ecdsa = PEM_read_EC_PUBKEY(fp, ecdsa, NULL, NULL);
if(*ecdsa)
return true;
logger(LOG_ERR, "Unable to read ECDSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
*ecdsa = PEM_read_ECPrivateKey(fp, NULL, NULL, NULL);
if(*ecdsa)
return true;
logger(LOG_ERR, "Unable to read ECDSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
size_t ecdsa_size(ecdsa_t *ecdsa) {
return ECDSA_size(*ecdsa);
}
// TODO: standardise output format?
bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t len, void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
memset(sig, 0, siglen);
if(!ECDSA_sign(0, hash, sizeof hash, sig, &siglen, *ecdsa)) {
logger(LOG_DEBUG, "ECDSA_sign() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
if(siglen != ECDSA_size(*ecdsa)) {
logger(LOG_ERR, "Signature length %d != %d", siglen, ECDSA_size(*ecdsa));
}
return true;
}
bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t len, const void *sig) {
unsigned int siglen = ECDSA_size(*ecdsa);
char hash[SHA512_DIGEST_LENGTH];
SHA512(in, len, hash);
if(!ECDSA_verify(0, hash, sizeof hash, sig, siglen, *ecdsa)) {
logger(LOG_DEBUG, "ECDSA_verify() failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
return true;
}
bool ecdsa_active(ecdsa_t *ecdsa) {
return *ecdsa;
}
void ecdsa_free(ecdsa_t *ecdsa) {
if(*ecdsa) {
EC_KEY_free(*ecdsa);
*ecdsa = NULL;
}
}

37
src/openssl/ecdsa.h Normal file
View file

@ -0,0 +1,37 @@
/*
ecdsa.h -- ECDSA key handling
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_ECDSA_H__
#define __TINC_ECDSA_H__
#include <openssl/ec.h>
typedef EC_KEY *ecdsa_t;
extern bool ecdsa_set_base64_public_key(ecdsa_t *ecdsa, const char *p);
extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
extern bool ecdsa_read_pem_public_key(ecdsa_t *ecdsa, FILE *fp);
extern bool ecdsa_read_pem_private_key(ecdsa_t *ecdsa, FILE *fp);
extern size_t ecdsa_size(ecdsa_t *ecdsa);
extern bool ecdsa_sign(ecdsa_t *ecdsa, const void *in, size_t inlen, void *out);
extern bool ecdsa_verify(ecdsa_t *ecdsa, const void *in, size_t inlen, const void *out);
extern bool ecdsa_active(ecdsa_t *ecdsa);
extern void ecdsa_free(ecdsa_t *ecdsa);
#endif

75
src/openssl/ecdsagen.c Normal file
View file

@ -0,0 +1,75 @@
/*
ecdsagen.c -- ECDSA key generation and export
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/obj_mac.h>
#include "ecdsagen.h"
#include "utils.h"
// Generate ECDSA key
bool ecdsa_generate(ecdsa_t *ecdsa) {
*ecdsa = EC_KEY_new_by_curve_name(NID_secp521r1);
if(!EC_KEY_generate_key(*ecdsa)) {
fprintf(stderr, "Generating EC key failed: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
EC_KEY_set_asn1_flag(*ecdsa, OPENSSL_EC_NAMED_CURVE);
EC_KEY_set_conv_form(*ecdsa, POINT_CONVERSION_COMPRESSED);
return true;
}
// Write PEM ECDSA keys
bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
BIO *out = BIO_new(BIO_s_file());
BIO_set_fp(out,fp,BIO_NOCLOSE);
PEM_write_bio_EC_PUBKEY(out, *ecdsa);
BIO_free(out);
return true;
}
bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
BIO *out = BIO_new(BIO_s_file());
BIO_set_fp(out,fp,BIO_NOCLOSE);
PEM_write_bio_ECPrivateKey(out, *ecdsa, NULL, NULL, 0, NULL, NULL);
BIO_free(out);
return true;
}
// Convert ECDSA public key to base64 format
char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
unsigned char *pubkey = NULL;
int len = i2o_ECPublicKey(*ecdsa, &pubkey);
char *base64 = malloc(len * 4 / 3 + 5);
b64encode(pubkey, base64, len);
free(pubkey);
return base64;
}

30
src/openssl/ecdsagen.h Normal file
View file

@ -0,0 +1,30 @@
/*
ecdsagen.h -- ECDSA key generation and export
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_ECDSAGEN_H__
#define __TINC_ECDSAGEN_H__
#include "ecdsa.h"
extern bool ecdsa_generate(ecdsa_t *ecdsa);
extern bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp);
extern bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp);
extern char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa);
#endif

76
src/openssl/prf.c Normal file
View file

@ -0,0 +1,76 @@
/*
prf.c -- Pseudo-Random Function for key material generation
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "digest.h"
#include "prf.h"
/* Generate key material from a master secret and a seed, based on RFC 4346 section 5.
We use SHA512 and Whirlpool instead of MD5 and SHA1.
*/
static bool prf_xor(int nid, char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
digest_t digest;
if(!digest_open_by_nid(&digest, nid, -1))
return false;
if(!digest_set_key(&digest, secret, secretlen))
return false;
size_t len = digest_length(&digest);
/* Data is what the "inner" HMAC function processes.
It consists of the previous HMAC result plus the seed.
*/
char data[len + seedlen];
memset(data, 0, len);
memcpy(data + len, seed, seedlen);
char hash[len];
while(outlen > 0) {
/* Inner HMAC */
digest_create(&digest, data, len + seedlen, data);
/* Outer HMAC */
digest_create(&digest, data, len + seedlen, hash);
/* XOR the results of the outer HMAC into the out buffer */
for(int i = 0; i < len && i < outlen; i++)
*out++ ^= hash[i];
outlen -= len;
}
digest_close(&digest);
return true;
}
bool prf(char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
/* Split secret in half, generate outlen bits with two different hash algorithms,
and XOR the results. */
memset(out, 0, outlen);
return prf_xor(NID_sha512, secret, (secretlen + 1) / 2, seed, seedlen, out, outlen)
&& prf_xor(NID_whirlpool, secret + secretlen / 2, (secretlen + 1) / 2, seed, seedlen, out, outlen);
}

25
src/openssl/prf.h Normal file
View file

@ -0,0 +1,25 @@
/*
prf.h -- header file for prf.c
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_PRF_H__
#define __TINC_PRF_H__
extern bool prf(char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen);
#endif

101
src/openssl/rsa.c Normal file
View file

@ -0,0 +1,101 @@
/*
rsa.c -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include "logger.h"
#include "rsa.h"
// Set RSA keys
bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
*rsa = RSA_new();
BN_hex2bn(&(*rsa)->n, n);
BN_hex2bn(&(*rsa)->e, e);
return true;
}
bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
*rsa = RSA_new();
BN_hex2bn(&(*rsa)->n, n);
BN_hex2bn(&(*rsa)->e, e);
BN_hex2bn(&(*rsa)->d, d);
return true;
}
// Read PEM RSA keys
bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
*rsa = PEM_read_RSAPublicKey(fp, rsa, NULL, NULL);
if(*rsa)
return true;
*rsa = PEM_read_RSA_PUBKEY(fp, rsa, NULL, NULL);
if(*rsa)
return true;
logger(LOG_ERR, "Unable to read RSA public key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
*rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
if(*rsa)
return true;
logger(LOG_ERR, "Unable to read RSA private key: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
size_t rsa_size(rsa_t *rsa) {
return RSA_size(*rsa);
}
bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
if(RSA_public_encrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
return true;
logger(LOG_ERR, "Unable to perform RSA encryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
if(RSA_private_decrypt(len, in, out, *rsa, RSA_NO_PADDING) == len)
return true;
logger(LOG_ERR, "Unable to perform RSA decryption: %s", ERR_error_string(ERR_get_error(), NULL));
return false;
}
bool rsa_active(rsa_t *rsa) {
return *rsa;
}
void rsa_free(rsa_t *rsa) {
if(*rsa) {
RSA_free(*rsa);
*rsa = NULL;
}
}

View file

@ -1,10 +1,6 @@
#ifndef TINC_EVENT_H
#define TINC_EVENT_H
/*
event.h -- header for event.c
Copyright (C) 2002-2009 Guus Sliepen <guus@tinc-vpn.org>,
2002-2005 Ivo Timmermans
rsa.h -- RSA key handling
Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,27 +17,22 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "avl_tree.h"
#ifndef __TINC_RSA_H__
#define __TINC_RSA_H__
extern avl_tree_t *event_tree;
#include <openssl/rsa.h>
typedef void (*event_handler_t)(void *);
typedef RSA *rsa_t;
typedef struct event {
time_t time;
int id;
event_handler_t handler;
void *data;
} event_t;
extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e);
extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d);
extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp);
extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp);
extern size_t rsa_size(rsa_t *rsa);
extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t inlen, void *out);
extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t inlen, void *out);
extern bool rsa_active(rsa_t *rsa);
extern void rsa_free(rsa_t *rsa);
extern void init_events(void);
extern void exit_events(void);
extern void expire_events(void);
extern event_t *new_event(void) __attribute__((__malloc__));
extern void free_event(event_t *event);
extern void event_add(event_t *event);
extern void event_del(event_t *event);
extern event_t *get_expired_event(void);
extern event_t *peek_next_event(void);
#endif

83
src/openssl/rsagen.c Normal file
View file

@ -0,0 +1,83 @@
/*
rsagen.c -- RSA key generation and export
Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include <openssl/pem.h>
#include <openssl/err.h>
#include "logger.h"
#include "rsagen.h"
/* This function prettyprints the key generation process */
static void indicator(int a, int b, void *p) {
switch (a) {
case 0:
fprintf(stderr, ".");
break;
case 1:
fprintf(stderr, "+");
break;
case 2:
fprintf(stderr, "-");
break;
case 3:
switch (b) {
case 0:
fprintf(stderr, " p\n");
break;
case 1:
fprintf(stderr, " q\n");
break;
default:
fprintf(stderr, "?");
}
break;
default:
fprintf(stderr, "?");
}
}
// Generate RSA key
bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) {
*rsa = RSA_generate_key(bits, exponent, indicator, NULL);
return *rsa;
}
// Write PEM RSA keys
bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
PEM_write_RSAPublicKey(fp, *rsa);
return true;
}
bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
PEM_write_RSAPrivateKey(fp, *rsa, NULL, NULL, 0, NULL, NULL);
return true;
}

29
src/openssl/rsagen.h Normal file
View file

@ -0,0 +1,29 @@
/*
rsagen.h -- RSA key generation and export
Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_RSAGEN_H__
#define __TINC_RSAGEN_H__
#include "rsa.h"
extern bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent);
extern bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp);
extern bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp);
#endif

View file

@ -1,142 +0,0 @@
/*
pidfile.c - interact with pidfiles
Copyright (c) 1995 Martin Schulze <Martin.Schulze@Linux.DE>
This file is part of the sysklogd package, a kernel and system log daemon.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* left unaltered for tinc -- Ivo Timmermans */
/*
* Sat Aug 19 13:24:33 MET DST 1995: Martin Schulze
* First version (v0.2) released
*/
#include "system.h"
#include "pidfile.h"
#ifndef HAVE_MINGW
/* read_pid
*
* Reads the specified pidfile and returns the read pid.
* 0 is returned if either there's no pidfile, it's empty
* or no pid can be read.
*/
pid_t read_pid(const char *pidfile) {
FILE *f;
long pid;
if(!(f = fopen(pidfile, "r"))) {
return 0;
}
if(fscanf(f, "%20ld", &pid) != 1) {
pid = 0;
}
fclose(f);
return (pid_t)pid;
}
/* check_pid
*
* Reads the pid using read_pid and looks up the pid in the process
* table (using /proc) to determine if the process already exists. If
* so the pid is returned, otherwise 0.
*/
pid_t check_pid(const char *pidfile) {
pid_t pid = read_pid(pidfile);
/* Amazing ! _I_ am already holding the pid file... */
if((!pid) || (pid == getpid())) {
return 0;
}
/*
* The 'standard' method of doing this is to try and do a 'fake' kill
* of the process. If an ESRCH error is returned the process cannot
* be found -- GW
*/
/* But... errno is usually changed only on error.. */
errno = 0;
if(kill(pid, 0) && errno == ESRCH) {
return 0;
}
return pid;
}
/* write_pid
*
* Writes the pid to the specified file. If that fails 0 is
* returned, otherwise the pid.
*/
pid_t write_pid(const char *pidfile) {
FILE *f;
int fd;
pid_t pid;
if((fd = open(pidfile, O_RDWR | O_CREAT, 0644)) == -1) {
return 0;
}
if((f = fdopen(fd, "r+")) == NULL) {
close(fd);
return 0;
}
#ifdef HAVE_FLOCK
if(flock(fd, LOCK_EX | LOCK_NB) == -1) {
fclose(f);
return 0;
}
#endif
pid = getpid();
if(!fprintf(f, "%ld\n", (long)pid)) {
fclose(f);
return 0;
}
fflush(f);
#ifdef HAVE_FLOCK
if(flock(fd, LOCK_UN) == -1) {
fclose(f);
return 0;
}
#endif
fclose(f);
return pid;
}
/* remove_pid
*
* Remove the the specified file. The result from unlink(2)
* is returned
*/
int remove_pid(const char *pidfile) {
return unlink(pidfile);
}
#endif

View file

@ -1,57 +0,0 @@
#ifndef TINC_PIDFILE_H
#define TINC_PIDFILE_H
/*
pidfile.h - interact with pidfiles
Copyright (c) 1995 Martin Schulze <Martin.Schulze@Linux.DE>
This file is part of the sysklogd package, a kernel and system log daemon.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef HAVE_MINGW
/* read_pid
*
* Reads the specified pidfile and returns the read pid.
* 0 is returned if either there's no pidfile, it's empty
* or no pid can be read.
*/
extern pid_t read_pid(const char *pidfile);
/* check_pid
*
* Reads the pid using read_pid and looks up the pid in the process
* table (using /proc) to determine if the process already exists. If
* so 1 is returned, otherwise 0.
*/
extern pid_t check_pid(const char *pidfile);
/* write_pid
*
* Writes the pid to the specified file. If that fails 0 is
* returned, otherwise the pid.
*/
extern pid_t write_pid(const char *pidfile);
/* remove_pid
*
* Remove the the specified file. The result from unlink(2)
* is returned
*/
extern int remove_pid(const char *pidfile);
#endif
#endif

View file

@ -1,7 +1,7 @@
/*
process.c -- process management functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,12 +22,12 @@
#include "conf.h"
#include "connection.h"
#include "control.h"
#include "device.h"
#include "edge.h"
#include "logger.h"
#include "net.h"
#include "node.h"
#include "pidfile.h"
#include "process.h"
#include "subnet.h"
#include "utils.h"
@ -35,18 +35,12 @@
/* If zero, don't detach from the terminal. */
bool do_detach = true;
bool sighup = false;
bool sigalrm = false;
extern char *identname;
extern char *pidfilename;
extern char **g_argv;
extern bool use_logfile;
#ifndef HAVE_MINGW
static sigset_t emptysigset;
#endif
/* Some functions the less gifted operating systems might lack... */
#ifdef HAVE_MINGW
@ -59,54 +53,49 @@ static SC_HANDLE service = NULL;
static SERVICE_STATUS status = {0};
static SERVICE_STATUS_HANDLE statushandle = 0;
bool install_service(void) {
static bool install_service(void) {
char command[4096] = "\"";
char **argp;
bool space;
SERVICE_DESCRIPTION description = {"Virtual Private Network daemon"};
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!manager) {
logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
return false;
}
if(!strchr(program_name, '\\')) {
GetCurrentDirectory(sizeof(command) - 1, command + 1);
strncat(command, "\\", sizeof(command) - strlen(command));
GetCurrentDirectory(sizeof command - 1, command + 1);
strncat(command, "\\", sizeof command - strlen(command));
}
strncat(command, program_name, sizeof(command) - strlen(command));
strncat(command, program_name, sizeof command - strlen(command));
strncat(command, "\"", sizeof(command) - strlen(command));
strncat(command, "\"", sizeof command - strlen(command));
for(argp = g_argv + 1; *argp; argp++) {
space = strchr(*argp, ' ');
strncat(command, " ", sizeof(command) - strlen(command));
strncat(command, " ", sizeof command - strlen(command));
if(space)
strncat(command, "\"", sizeof command - strlen(command));
strncat(command, *argp, sizeof command - strlen(command));
if(space) {
strncat(command, "\"", sizeof(command) - strlen(command));
}
strncat(command, *argp, sizeof(command) - strlen(command));
if(space) {
strncat(command, "\"", sizeof(command) - strlen(command));
}
if(space)
strncat(command, "\"", sizeof command - strlen(command));
}
service = CreateService(manager, identname, identname,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
command, NULL, NULL, NULL, NULL, NULL);
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
command, NULL, NULL, NULL, NULL, NULL);
if(!service) {
DWORD lasterror = GetLastError();
logger(LOG_ERR, "Could not create %s service: %s", identname, winerror(lasterror));
if(lasterror != ERROR_SERVICE_EXISTS) {
if(lasterror != ERROR_SERVICE_EXISTS)
return false;
}
}
if(service) {
@ -116,106 +105,67 @@ bool install_service(void) {
service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
}
if(!StartService(service, 0, NULL)) {
if(!StartService(service, 0, NULL))
logger(LOG_WARNING, "Could not start %s service: %s", identname, winerror(GetLastError()));
} else {
else
logger(LOG_INFO, "%s service started", identname);
}
return true;
}
bool remove_service(void) {
manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!manager) {
logger(LOG_ERR, "Could not open service manager: %s", winerror(GetLastError()));
return false;
}
service = OpenService(manager, identname, SERVICE_ALL_ACCESS);
if(!service) {
logger(LOG_ERR, "Could not open %s service: %s", identname, winerror(GetLastError()));
return false;
}
if(!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
logger(LOG_ERR, "Could not stop %s service: %s", identname, winerror(GetLastError()));
} else {
logger(LOG_INFO, "%s service stopped", identname);
}
if(!DeleteService(service)) {
logger(LOG_ERR, "Could not remove %s service: %s", identname, winerror(GetLastError()));
return false;
}
logger(LOG_INFO, "%s service removed", identname);
return true;
}
DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
switch(request) {
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus(statushandle, &status);
return NO_ERROR;
case SERVICE_CONTROL_STOP:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
break;
case SERVICE_CONTROL_SHUTDOWN:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
break;
default:
logger(LOG_WARNING, "Got unexpected request %d", (int)request);
return ERROR_CALL_NOT_IMPLEMENTED;
}
if(running) {
running = false;
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
return NO_ERROR;
} else {
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(statushandle, &status);
exit(1);
case SERVICE_CONTROL_INTERROGATE:
SetServiceStatus(statushandle, &status);
return NO_ERROR;
case SERVICE_CONTROL_STOP:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_STOP");
break;
case SERVICE_CONTROL_SHUTDOWN:
logger(LOG_NOTICE, "Got %s request", "SERVICE_CONTROL_SHUTDOWN");
break;
default:
logger(LOG_WARNING, "Got unexpected request %d", request);
return ERROR_CALL_NOT_IMPLEMENTED;
}
event_loopexit(NULL);
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(statushandle, &status);
return NO_ERROR;
}
VOID WINAPI run_service(DWORD argc, LPTSTR *argv) {
VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
int err = 1;
extern int main2(int argc, char **argv);
status.dwServiceType = SERVICE_WIN32;
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
status.dwWin32ExitCode = 0;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = 0;
statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL);
statushandle = RegisterServiceCtrlHandlerEx(identname, controlhandler, NULL);
if(!statushandle) {
if (!statushandle) {
logger(LOG_ERR, "System call `%s' failed: %s", "RegisterServiceCtrlHandlerEx", winerror(GetLastError()));
err = 1;
} else {
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_START_PENDING;
status.dwWaitHint = 30000;
status.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(statushandle, &status);
status.dwWaitHint = 0;
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(statushandle, &status);
main2(argc, argv);
err = main2(argc, argv);
status.dwWaitHint = 0;
status.dwCurrentState = SERVICE_STOPPED;
status.dwCurrentState = SERVICE_STOPPED;
//status.dwWin32ExitCode = err;
SetServiceStatus(statushandle, &status);
}
@ -231,39 +181,9 @@ bool init_service(void) {
if(!StartServiceCtrlDispatcher(services)) {
if(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
return false;
} else {
}
else
logger(LOG_ERR, "System call `%s' failed: %s", "StartServiceCtrlDispatcher", winerror(GetLastError()));
}
}
return true;
}
#endif
#ifndef HAVE_MINGW
/*
check for an existing tinc for this net, and write pid to pidfile
*/
static bool write_pidfile(void) {
pid_t pid;
pid = check_pid(pidfilename);
if(pid) {
if(netname)
fprintf(stderr, "A tincd is already running for net `%s' with pid %ld.\n",
netname, (long)pid);
else {
fprintf(stderr, "A tincd is already running with pid %ld.\n", (long)pid);
}
return false;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename)) {
fprintf(stderr, "Couldn't write pid file %s: %s\n", pidfilename, strerror(errno));
return false;
}
return true;
@ -271,193 +191,72 @@ static bool write_pidfile(void) {
#endif
/*
kill older tincd for this net
*/
bool kill_other(int signal) {
#ifndef HAVE_MINGW
pid_t pid;
pid = read_pid(pidfilename);
if(!pid) {
if(netname)
fprintf(stderr, "No other tincd is running for net `%s'.\n",
netname);
else {
fprintf(stderr, "No other tincd is running.\n");
}
return false;
}
errno = 0; /* No error, sometimes errno is only changed on error */
/* ESRCH is returned when no process with that pid is found */
if(kill(pid, signal) && errno == ESRCH) {
if(netname)
fprintf(stderr, "The tincd for net `%s' is no longer running. ",
netname);
else {
fprintf(stderr, "The tincd is no longer running. ");
}
fprintf(stderr, "Removing stale lock file.\n");
remove_pid(pidfilename);
}
return true;
#else
return remove_service();
#endif
}
/*
Detach from current terminal, write pidfile, kill parent
Detach from current terminal
*/
bool detach(void) {
setup_signals();
/* First check if we can open a fresh new pidfile */
#ifndef HAVE_MINGW
if(!write_pidfile()) {
return false;
}
/* If we succeeded in doing that, detach */
signal(SIGPIPE, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
signal(SIGWINCH, SIG_IGN);
closelogger();
#endif
if(do_detach) {
#ifndef HAVE_MINGW
if(daemon(0, 0)) {
fprintf(stderr, "Couldn't detach from terminal: %s",
strerror(errno));
strerror(errno));
return false;
}
/* Now UPDATE the pid in the pidfile, because we changed it... */
if(!write_pid(pidfilename)) {
fprintf(stderr, "Could not write pid file %s: %s\n", pidfilename, strerror(errno));
return false;
}
#else
if(!statushandle) {
if(!statushandle)
exit(install_service());
}
#endif
}
openlogger(identname, use_logfile ? LOGMODE_FILE : (do_detach ? LOGMODE_SYSLOG : LOGMODE_STDERR));
openlogger(identname, use_logfile?LOGMODE_FILE:(do_detach?LOGMODE_SYSLOG:LOGMODE_STDERR));
logger(LOG_NOTICE, "tincd %s starting, debug level %d",
VERSION, debug_level);
logger(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d",
VERSION, __DATE__, __TIME__, debug_level);
return true;
}
#ifdef HAVE_PUTENV
void unputenv(char *p) {
char *e = strchr(p, '=');
if(!e) {
return;
}
int len = e - p;
#ifndef HAVE_UNSETENV
#ifdef HAVE_MINGW
// Windows requires putenv("FOO=") to unset %FOO%
len++;
#endif
#endif
char var[len + 1];
memcpy(var, p, len);
var[len] = 0;
#ifdef HAVE_UNSETENV
unsetenv(var);
#else
// We must keep what we putenv() around in memory.
// To do this without memory leaks, keep things in a list and reuse if possible.
static list_t list = {};
for(list_node_t *node = list.head; node; node = node->next) {
char *data = node->data;
if(!strcmp(data, var)) {
putenv(data);
return;
}
}
char *data = xstrdup(var);
list_insert_tail(&list, data);
putenv(data);
#endif
}
#else
void putenv(const char *p) {}
void unputenv(const char *p) {}
#endif
bool execute_script(const char *name, char **envp) {
#ifdef HAVE_SYSTEM
int status, len;
char *scriptname;
char *interpreter = NULL;
config_t *cfg_interpreter;
int status, len, i;
int i;
cfg_interpreter = lookup_config(config_tree, "ScriptsInterpreter");
#ifndef HAVE_MINGW
len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
#else
if(cfg_interpreter) {
len = xasprintf(&scriptname, "\"%s/%s\"", confbase, name);
} else {
len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
}
len = xasprintf(&scriptname, "\"%s/%s.bat\"", confbase, name);
#endif
if(len < 0) {
if(len < 0)
return false;
}
scriptname[len - 1] = '\0';
#ifndef HAVE_TUNEMU
/* First check if there is a script */
if(access(scriptname + 1, F_OK)) {
free(scriptname);
return true;
}
// Custom scripts interpreter
if(get_config_string(cfg_interpreter, &interpreter)) {
// Force custom scripts interpreter allowing execution of scripts on android without execution flag (such as on /sdcard)
free(scriptname);
len = xasprintf(&scriptname, "%s \"%s/%s\"", interpreter, confbase, name);
free(interpreter);
if(len < 0) {
return false;
}
}
#endif
ifdebug(STATUS) logger(LOG_INFO, "Executing script %s", name);
#ifdef HAVE_PUTENV
/* Set environment */
for(i = 0; envp[i]; i++) {
for(i = 0; envp[i]; i++)
putenv(envp[i]);
}
#endif
scriptname[len - 1] = '\"';
status = system(scriptname);
@ -467,212 +266,36 @@ bool execute_script(const char *name, char **envp) {
/* Unset environment */
for(i = 0; envp[i]; i++) {
unputenv(envp[i]);
char *e = strchr(envp[i], '=');
if(e) {
char p[e - envp[i] + 1];
strncpy(p, envp[i], e - envp[i]);
p[e - envp[i]] = '\0';
putenv(p);
}
}
if(status != -1) {
#ifdef WEXITSTATUS
if(WIFEXITED(status)) { /* Child exited by itself */
if(status != -1) {
if(WIFEXITED(status)) { /* Child exited by itself */
if(WEXITSTATUS(status)) {
logger(LOG_ERR, "Script %s exited with non-zero status %d",
name, WEXITSTATUS(status));
name, WEXITSTATUS(status));
return false;
}
} else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
} else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
logger(LOG_ERR, "Script %s was killed by signal %d (%s)",
name, WTERMSIG(status), strsignal(WTERMSIG(status)));
name, WTERMSIG(status), strsignal(WTERMSIG(status)));
return false;
} else { /* Something strange happened */
} else { /* Something strange happened */
logger(LOG_ERR, "Script %s terminated abnormally", name);
return false;
}
#endif
} else {
logger(LOG_ERR, "System call `%s' failed: %s", "system", strerror(errno));
return false;
}
#endif
#endif
return true;
}
/*
Signal handlers.
*/
#ifndef HAVE_MINGW
static RETSIGTYPE sigterm_handler(int a) {
(void)a;
logger(LOG_NOTICE, "Got %s signal", "TERM");
if(running) {
running = false;
} else {
exit(1);
}
}
static RETSIGTYPE sigquit_handler(int a) {
(void)a;
logger(LOG_NOTICE, "Got %s signal", "QUIT");
if(running) {
running = false;
} else {
exit(1);
}
}
static RETSIGTYPE fatal_signal_square(int a) {
logger(LOG_ERR, "Got another fatal signal %d (%s): not restarting.", a,
strsignal(a));
exit(1);
}
static RETSIGTYPE fatal_signal_handler(int a) {
struct sigaction act;
logger(LOG_ERR, "Got fatal signal %d (%s)", a, strsignal(a));
if(do_detach) {
logger(LOG_NOTICE, "Trying to re-execute in 5 seconds...");
act.sa_handler = fatal_signal_square;
act.sa_mask = emptysigset;
act.sa_flags = 0;
sigaction(SIGSEGV, &act, NULL);
close_network_connections();
sleep(5);
remove_pid(pidfilename);
execvp(g_argv[0], g_argv);
} else {
logger(LOG_NOTICE, "Not restarting.");
exit(1);
}
}
static RETSIGTYPE sighup_handler(int a) {
(void)a;
logger(LOG_NOTICE, "Got %s signal", "HUP");
sighup = true;
}
static RETSIGTYPE sigint_handler(int a) {
(void)a;
static int saved_debug_level = -1;
logger(LOG_NOTICE, "Got %s signal", "INT");
if(saved_debug_level != -1) {
logger(LOG_NOTICE, "Reverting to old debug level (%d)",
saved_debug_level);
debug_level = saved_debug_level;
saved_debug_level = -1;
} else {
logger(LOG_NOTICE,
"Temporarily setting debug level to 5. Kill me with SIGINT again to go back to level %d.",
debug_level);
saved_debug_level = debug_level;
debug_level = 5;
}
}
static RETSIGTYPE sigalrm_handler(int a) {
(void)a;
logger(LOG_NOTICE, "Got %s signal", "ALRM");
sigalrm = true;
}
static RETSIGTYPE sigusr1_handler(int a) {
(void)a;
dump_connections();
}
static RETSIGTYPE sigusr2_handler(int a) {
(void)a;
devops.dump_stats();
dump_nodes();
dump_edges();
dump_subnets();
}
static RETSIGTYPE sigwinch_handler(int a) {
(void)a;
do_purge = true;
}
static RETSIGTYPE unexpected_signal_handler(int a) {
(void)a;
logger(LOG_WARNING, "Got unexpected signal %d (%s)", a, strsignal(a));
}
static RETSIGTYPE ignore_signal_handler(int a) {
(void)a;
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Ignored signal %d (%s)", a, strsignal(a));
}
static struct {
int signal;
void (*handler)(int);
} sighandlers[] = {
{SIGHUP, sighup_handler},
{SIGTERM, sigterm_handler},
{SIGQUIT, sigquit_handler},
{SIGSEGV, fatal_signal_handler},
{SIGBUS, fatal_signal_handler},
{SIGILL, fatal_signal_handler},
{SIGPIPE, ignore_signal_handler},
{SIGINT, sigint_handler},
{SIGUSR1, sigusr1_handler},
{SIGUSR2, sigusr2_handler},
{SIGCHLD, ignore_signal_handler},
{SIGALRM, sigalrm_handler},
{SIGWINCH, sigwinch_handler},
{SIGABRT, SIG_DFL},
{0, NULL}
};
#endif
void setup_signals(void) {
#ifndef HAVE_MINGW
int i;
struct sigaction act;
sigemptyset(&emptysigset);
act.sa_handler = NULL;
act.sa_mask = emptysigset;
act.sa_flags = 0;
/* Set a default signal handler for every signal, errors will be
ignored. */
for(i = 1; i < NSIG; i++) {
if(!do_detach) {
act.sa_handler = SIG_DFL;
} else {
act.sa_handler = unexpected_signal_handler;
}
sigaction(i, &act, NULL);
}
/* If we didn't detach, allow coredumps */
if(!do_detach) {
sighandlers[3].handler = SIG_DFL;
}
/* Then, for each known signal that we want to catch, assign a
handler to the signal, with error checking this time. */
for(i = 0; sighandlers[i].signal; i++) {
act.sa_handler = sighandlers[i].handler;
if(sigaction(sighandlers[i].signal, &act, NULL) < 0)
fprintf(stderr, "Installing signal handler for signal %d (%s) failed: %s\n",
sighandlers[i].signal, strsignal(sighandlers[i].signal),
strerror(errno));
}
#endif
}

View file

@ -1,6 +1,3 @@
#ifndef TINC_PROCESS_H
#define TINC_PROCESS_H
/*
process.h -- header file for process.c
Copyright (C) 1999-2005 Ivo Timmermans,
@ -21,17 +18,19 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_PROCESS_H__
#define __TINC_PROCESS_H__
extern bool do_detach;
extern bool sighup;
extern bool sigalrm;
extern void setup_signals(void);
extern bool execute_script(const char *name, char **envp);
extern bool execute_script(const char *, char **);
extern bool detach(void);
extern bool kill_other(int signal);
extern bool kill_other(int);
#ifdef HAVE_MINGW
extern bool init_service(void);
#endif
#endif
#endif /* __TINC_PROCESS_H__ */

View file

@ -1,7 +1,7 @@
/*
protocol.c -- handle the meta-protocol, basic functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -30,35 +30,35 @@
bool tunnelserver = false;
bool strictsubnets = false;
bool experimental = false;
/* Jumptable for the request handlers */
static bool (*request_handlers[])(connection_t *) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
NULL, NULL, NULL,
ping_h, pong_h,
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h, tcppacket_h,
static bool (*request_handlers[])(connection_t *, char *) = {
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
};
/* Request names */
static char (*request_name[]) = {
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET",
"ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK",
"STATUS", "ERROR", "TERMREQ",
"PING", "PONG",
"ADD_SUBNET", "DEL_SUBNET",
"ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL",
};
static avl_tree_t *past_request_tree;
static splay_tree_t *past_request_tree;
bool check_id(const char *id) {
for(; *id; id++)
if(!isalnum(*id) && *id != '_') {
if(!isalnum(*id) && *id != '_')
return false;
}
return true;
}
@ -68,104 +68,97 @@ bool check_id(const char *id) {
bool send_request(connection_t *c, const char *format, ...) {
va_list args;
char buffer[MAXBUFSIZE];
int len, request = 0;
char request[MAXBUFSIZE];
int len;
/* Use vsnprintf instead of vxasprintf: faster, no memory
fragmentation, cleanup is automatic, and there is a limit on the
input buffer anyway */
va_start(args, format);
len = vsnprintf(buffer, sizeof(buffer), format, args);
buffer[sizeof(buffer) - 1] = 0;
len = vsnprintf(request, MAXBUFSIZE, format, args);
va_end(args);
if(len < 0 || (size_t)len > sizeof(buffer) - 1) {
if(len < 0 || len > MAXBUFSIZE - 1) {
logger(LOG_ERR, "Output buffer overflow while sending request to %s (%s)",
c->name, c->hostname);
c->name, c->hostname);
return false;
}
ifdebug(PROTOCOL) {
sscanf(buffer, "%d", &request);
ifdebug(META)
logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
request_name[request], c->name, c->hostname, buffer);
logger(LOG_DEBUG, "Sending %s to %s (%s): %s",
request_name[atoi(request)], c->name, c->hostname, request);
else
logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[request],
c->name, c->hostname);
logger(LOG_DEBUG, "Sending %s to %s (%s)", request_name[atoi(request)],
c->name, c->hostname);
}
buffer[len++] = '\n';
request[len++] = '\n';
if(c == everyone) {
broadcast_meta(NULL, buffer, len);
if(c == broadcast) {
broadcast_meta(NULL, request, len);
return true;
} else {
return send_meta(c, buffer, len);
}
} else
return send_meta(c, request, len);
}
void forward_request(connection_t *from) {
int request;
void forward_request(connection_t *from, char *request) {
/* Note: request is not zero terminated anymore after a call to this function! */
ifdebug(PROTOCOL) {
sscanf(from->buffer, "%d", &request);
ifdebug(META)
logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
request_name[request], from->name, from->hostname,
from->buffer);
logger(LOG_DEBUG, "Forwarding %s from %s (%s): %s",
request_name[atoi(request)], from->name, from->hostname, request);
else
logger(LOG_DEBUG, "Forwarding %s from %s (%s)",
request_name[request], from->name, from->hostname);
request_name[atoi(request)], from->name, from->hostname);
}
from->buffer[from->reqlen - 1] = '\n';
broadcast_meta(from, from->buffer, from->reqlen);
int len = strlen(request);
request[len++] = '\n';
broadcast_meta(from, request, len);
}
bool receive_request(connection_t *c) {
int request;
bool receive_request(connection_t *c, char *request) {
int reqno = atoi(request);
if(sscanf(c->buffer, "%d", &request) == 1) {
if((request < 0) || (request >= LAST) || !request_handlers[request]) {
if(reqno || *request == '0') {
if((reqno < 0) || (reqno >= LAST) || !request_handlers[reqno]) {
ifdebug(META)
logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
c->name, c->hostname, c->buffer);
logger(LOG_DEBUG, "Unknown request from %s (%s): %s",
c->name, c->hostname, request);
else
logger(LOG_ERR, "Unknown request from %s (%s)",
c->name, c->hostname);
c->name, c->hostname);
return false;
} else {
ifdebug(PROTOCOL) {
ifdebug(META)
logger(LOG_DEBUG, "Got %s from %s (%s): %s",
request_name[request], c->name, c->hostname,
c->buffer);
logger(LOG_DEBUG, "Got %s from %s (%s): %s",
request_name[reqno], c->name, c->hostname, request);
else
logger(LOG_DEBUG, "Got %s from %s (%s)",
request_name[request], c->name, c->hostname);
request_name[reqno], c->name, c->hostname);
}
}
if((c->allow_request != ALL) && (c->allow_request != request)) {
if((c->allow_request != ALL) && (c->allow_request != reqno)) {
logger(LOG_ERR, "Unauthorized request from %s (%s)", c->name,
c->hostname);
c->hostname);
return false;
}
if(!request_handlers[request](c)) {
if(!request_handlers[reqno](c, request)) {
/* Something went wrong. Probably scriptkiddies. Terminate. */
logger(LOG_ERR, "Error while processing %s from %s (%s)",
request_name[request], c->name, c->hostname);
request_name[reqno], c->name, c->hostname);
return false;
}
} else {
logger(LOG_ERR, "Bogus data received from %s (%s)",
c->name, c->hostname);
c->name, c->hostname);
return false;
}
@ -177,55 +170,65 @@ static int past_request_compare(const past_request_t *a, const past_request_t *b
}
static void free_past_request(past_request_t *r) {
if(r->request) {
if(r->request)
free(r->request);
}
free(r);
}
void init_requests(void) {
past_request_tree = avl_alloc_tree((avl_compare_t) past_request_compare, (avl_action_t) free_past_request);
}
void exit_requests(void) {
avl_delete_tree(past_request_tree);
}
static struct event past_request_event;
bool seen_request(char *request) {
past_request_t *new, p = {0};
past_request_t *new, p = {NULL};
p.request = request;
if(avl_search(past_request_tree, &p)) {
if(splay_search(past_request_tree, &p)) {
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Already seen request");
return true;
} else {
new = xmalloc(sizeof(*new));
new = xmalloc(sizeof *new);
new->request = xstrdup(request);
new->firstseen = now;
avl_insert(past_request_tree, new);
new->firstseen = time(NULL);
splay_insert(past_request_tree, new);
event_add(&past_request_event, &(struct timeval){10, 0});
return false;
}
}
void age_past_requests(void) {
avl_node_t *node, *next;
static void age_past_requests(int fd, short events, void *data) {
splay_node_t *node, *next;
past_request_t *p;
int left = 0, deleted = 0;
time_t now = time(NULL);
for(node = past_request_tree->head; node; node = next) {
next = node->next;
p = node->data;
if(p->firstseen + pinginterval <= now) {
avl_delete_node(past_request_tree, node), deleted++;
} else {
if(p->firstseen + pinginterval <= now)
splay_delete_node(past_request_tree, node), deleted++;
else
left++;
}
}
if(left || deleted)
ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Aging past requests: deleted %d, left %d",
deleted, left);
deleted, left);
if(left)
event_add(&past_request_event, &(struct timeval){10, 0});
}
void init_requests(void) {
past_request_tree = splay_alloc_tree((splay_compare_t) past_request_compare, (splay_action_t) free_past_request);
timeout_set(&past_request_event, age_past_requests, NULL);
}
void exit_requests(void) {
splay_delete_tree(past_request_tree);
if(timeout_initialized(&past_request_event))
event_del(&past_request_event);
}

View file

@ -1,10 +1,7 @@
#ifndef TINC_PROTOCOL_H
#define TINC_PROTOCOL_H
/*
protocol.h -- header for protocol.c
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2015 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,11 +18,13 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Protocol version. Different versions are incompatible,
incompatible version have different protocols.
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#define PROT_CURRENT 17
/* Protocol version. Different major versions are incompatible. */
#define PROT_MAJOR 17
#define PROT_MINOR 2
/* Silly Windows */
@ -36,8 +35,7 @@
/* Request numbers */
typedef enum request_t {
PROXY = -2,
ALL = -1, /* Guardian for allow_request */
ALL = -1, /* Guardian for allow_request */
ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK,
STATUS, ERROR, TERMREQ,
PING, PONG,
@ -45,7 +43,8 @@ typedef enum request_t {
ADD_EDGE, DEL_EDGE,
KEY_CHANGED, REQ_KEY, ANS_KEY,
PACKET,
LAST /* Guardian for the highest request number */
CONTROL,
LAST /* Guardian for the highest request number */
} request_t;
typedef struct past_request_t {
@ -55,6 +54,7 @@ typedef struct past_request_t {
extern bool tunnelserver;
extern bool strictsubnets;
extern bool experimental;
/* Maximum size of strings in a request.
* scanf terminates %2048s with a NUL character,
@ -71,50 +71,57 @@ extern bool strictsubnets;
/* Basic functions */
extern bool send_request(struct connection_t *c, const char *format, ...) __attribute__((__format__(printf, 2, 3)));
extern void forward_request(struct connection_t *c);
extern bool receive_request(struct connection_t *c);
extern bool check_id(const char *name);
extern bool send_request(struct connection_t *, const char *, ...) __attribute__ ((__format__(printf, 2, 3)));
extern void forward_request(struct connection_t *, char *);
extern bool receive_request(struct connection_t *, char *);
extern bool check_id(const char *);
extern void init_requests(void);
extern void exit_requests(void);
extern bool seen_request(char *request);
extern void age_past_requests(void);
extern bool seen_request(char *);
/* Requests */
extern bool send_id(struct connection_t *c);
extern bool send_metakey(struct connection_t *c);
extern bool send_challenge(struct connection_t *c);
extern bool send_chal_reply(struct connection_t *c);
extern bool send_ack(struct connection_t *c);
extern bool send_ping(struct connection_t *c);
extern bool send_pong(struct connection_t *c);
extern bool send_add_subnet(struct connection_t *c, const struct subnet_t *subnet);
extern bool send_del_subnet(struct connection_t *c, const struct subnet_t *subnet);
extern bool send_add_edge(struct connection_t *c, const struct edge_t *e);
extern bool send_del_edge(struct connection_t *c, const struct edge_t *e);
extern bool send_id(struct connection_t *);
extern bool send_metakey(struct connection_t *);
extern bool send_metakey_ec(struct connection_t *);
extern bool send_challenge(struct connection_t *);
extern bool send_chal_reply(struct connection_t *);
extern bool send_ack(struct connection_t *);
extern bool send_status(struct connection_t *, int, const char *);
extern bool send_error(struct connection_t *, int,const char *);
extern bool send_termreq(struct connection_t *);
extern bool send_ping(struct connection_t *);
extern bool send_pong(struct connection_t *);
extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);
extern bool send_del_edge(struct connection_t *, const struct edge_t *);
extern void send_key_changed(void);
extern bool send_req_key(struct node_t *n);
extern bool send_ans_key(struct node_t *n);
extern bool send_tcppacket(struct connection_t *c, const struct vpn_packet_t *packet);
extern bool send_req_key(struct node_t *);
extern bool send_ans_key(struct node_t *);
extern bool send_tcppacket(struct connection_t *, const struct vpn_packet_t *);
/* Request handlers */
extern bool id_h(struct connection_t *c);
extern bool metakey_h(struct connection_t *c);
extern bool challenge_h(struct connection_t *c);
extern bool chal_reply_h(struct connection_t *c);
extern bool ack_h(struct connection_t *c);
extern bool ping_h(struct connection_t *c);
extern bool pong_h(struct connection_t *c);
extern bool add_subnet_h(struct connection_t *c);
extern bool del_subnet_h(struct connection_t *c);
extern bool add_edge_h(struct connection_t *c);
extern bool del_edge_h(struct connection_t *c);
extern bool key_changed_h(struct connection_t *c);
extern bool req_key_h(struct connection_t *c);
extern bool ans_key_h(struct connection_t *c);
extern bool tcppacket_h(struct connection_t *c);
extern bool id_h(struct connection_t *, char *);
extern bool metakey_h(struct connection_t *, char *);
extern bool challenge_h(struct connection_t *, char *);
extern bool chal_reply_h(struct connection_t *, char *);
extern bool ack_h(struct connection_t *, char *);
extern bool status_h(struct connection_t *, char *);
extern bool error_h(struct connection_t *, char *);
extern bool termreq_h(struct connection_t *, char *);
extern bool ping_h(struct connection_t *, char *);
extern bool pong_h(struct connection_t *, char *);
extern bool add_subnet_h(struct connection_t *, char *);
extern bool del_subnet_h(struct connection_t *, char *);
extern bool add_edge_h(struct connection_t *, char *);
extern bool del_edge_h(struct connection_t *, char *);
extern bool key_changed_h(struct connection_t *, char *);
extern bool req_key_h(struct connection_t *, char *);
extern bool ans_key_h(struct connection_t *, char *);
extern bool tcppacket_h(struct connection_t *, char *);
extern bool control_h(struct connection_t *, char *);
#endif
#endif /* __TINC_PROTOCOL_H__ */

View file

@ -1,7 +1,7 @@
/*
protocol_auth.c -- handle the meta-protocol, authentication
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2010 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
@ -20,49 +20,64 @@
#include "system.h"
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include "avl_tree.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "control.h"
#include "control_common.h"
#include "cipher.h"
#include "crypto.h"
#include "digest.h"
#include "edge.h"
#include "graph.h"
#include "logger.h"
#include "meta.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "prf.h"
#include "protocol.h"
#include "proxy.h"
#include "rsa.h"
#include "utils.h"
#include "xalloc.h"
bool send_id(connection_t *c) {
if(proxytype && c->outgoing && !c->status.proxy_passed) {
return send_proxyrequest(c);
gettimeofday(&c->start, NULL);
int minor = 0;
if(experimental) {
if(c->config_tree && !read_ecdsa_public_key(c))
minor = 1;
else
minor = myself->connection->protocol_minor;
}
return send_request(c, "%d %s %d", ID, myself->connection->name,
myself->connection->protocol_version);
return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
}
bool id_h(connection_t *c) {
bool id_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
c->hostname);
c->hostname);
return false;
}
/* Check if this is a control connection */
if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
c->status.control = true;
c->allow_request = CONTROL;
c->last_ping_time = time(NULL) + 3600;
return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
}
/* Check if identity is a valid name */
if(!check_id(name) || !strcmp(name, myself->name)) {
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ID", c->name,
c->hostname, "invalid name");
c->hostname, "invalid name");
return false;
}
@ -71,36 +86,27 @@ bool id_h(connection_t *c) {
if(c->outgoing) {
if(strcmp(c->name, name)) {
logger(LOG_ERR, "Peer %s is %s instead of %s", c->hostname, name,
c->name);
c->name);
return false;
}
} else {
if(c->name) {
if(c->name)
free(c->name);
}
c->name = xstrdup(name);
}
/* Check if version matches */
if(c->protocol_version != myself->connection->protocol_version) {
logger(LOG_ERR, "Peer %s (%s) uses incompatible version %d",
c->name, c->hostname, c->protocol_version);
if(c->protocol_major != myself->connection->protocol_major) {
logger(LOG_ERR, "Peer %s (%s) uses incompatible version %d.%d",
c->name, c->hostname, c->protocol_major, c->protocol_minor);
return false;
}
if(bypass_security) {
if(!c->config_tree) {
if(!c->config_tree)
init_configuration(&c->config_tree);
}
c->allow_request = ACK;
if(!c->outgoing) {
send_id(c);
}
return send_ack(c);
}
@ -109,66 +115,67 @@ bool id_h(connection_t *c) {
if(!read_connection_config(c)) {
logger(LOG_ERR, "Peer %s had unknown identity (%s)", c->hostname,
c->name);
c->name);
return false;
}
if(experimental && c->protocol_minor >= 2)
if(!read_ecdsa_public_key(c))
return false;
} else {
if(!ecdsa_active(&c->ecdsa))
c->protocol_minor = 1;
}
if(!read_rsa_public_key(c)) {
return false;
}
if(!experimental)
c->protocol_minor = 0;
c->allow_request = METAKEY;
if(!c->outgoing) {
send_id(c);
}
return send_metakey(c);
if(c->protocol_minor >= 2)
return send_metakey_ec(c);
else
return send_metakey(c);
}
static uint64_t byte_budget(const EVP_CIPHER *cipher) {
/* Hopefully some failsafe way to calculate the maximum amount of bytes to
send/receive with a given cipher before we might run into birthday paradox
attacks. Because we might use different modes, the block size of the mode
might be 1 byte. In that case, use the IV length. Ensure the whole thing
is limited to what can be represented with a 64 bits integer.
*/
bool send_metakey_ec(connection_t *c) {
logger(LOG_DEBUG, "Sending ECDH metakey to %s", c->name);
int ivlen = EVP_CIPHER_iv_length(cipher);
int blklen = EVP_CIPHER_block_size(cipher);
int len = blklen > 1 ? blklen : ivlen > 1 ? ivlen : 8;
int bits = len * 4 - 1;
return bits < 64 ? UINT64_C(1) << bits : UINT64_MAX;
size_t siglen = ecdsa_size(&myself->connection->ecdsa);
char key[(ECDH_SIZE + siglen) * 2 + 1];
// TODO: include nonce? Use relevant parts of SSH or TLS protocol
if(!ecdh_generate_public(&c->ecdh, key))
return false;
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
return false;
b64encode(key, key, ECDH_SIZE + siglen);
return send_request(c, "%d %s", METAKEY, key);
}
bool send_metakey(connection_t *c) {
bool x;
int len = RSA_size(c->rsa_key);
/* Allocate buffers for the meta key */
char buffer[2 * len + 1];
c->outkey = xrealloc(c->outkey, len);
if(!c->outctx) {
c->outctx = EVP_CIPHER_CTX_new();
if(!c->outctx) {
abort();
}
}
/* Copy random data to the buffer */
if(1 != RAND_bytes((unsigned char *)c->outkey, len)) {
int err = ERR_get_error();
logger(LOG_ERR, "Failed to generate meta key (%s)", ERR_error_string(err, NULL));
if(!read_rsa_public_key(c))
return false;
}
if(!cipher_open_blowfish_ofb(&c->outcipher))
return false;
if(!digest_open_sha1(&c->outdigest, -1))
return false;
size_t len = rsa_size(&c->rsa);
char key[len];
char enckey[len];
char hexkey[2 * len + 1];
/* Create a random key */
randomize(key, len);
/* The message we send must be smaller than the modulus of the RSA key.
By definition, for a key of k bits, the following formula holds:
@ -180,13 +187,13 @@ bool send_metakey(connection_t *c) {
This can be done by setting the most significant bit to zero.
*/
c->outkey[0] &= 0x7F;
key[0] &= 0x7F;
cipher_set_key_from_rsa(&c->outcipher, key, len, true);
ifdebug(SCARY_THINGS) {
bin2hex(c->outkey, buffer, len);
buffer[len * 2] = '\0';
logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s",
buffer);
bin2hex(key, hexkey, len);
logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
}
/* Encrypt the random data
@ -196,144 +203,152 @@ bool send_metakey(connection_t *c) {
with a length equal to that of the modulus of the RSA key.
*/
if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) {
logger(LOG_ERR, "Error during encryption of meta key for %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) {
logger(LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
/* Convert the encrypted random data to a hexadecimal formatted string */
bin2hex(buffer, buffer, len);
buffer[len * 2] = '\0';
bin2hex(enckey, hexkey, len);
/* Send the meta key */
x = send_request(c, "%d %d %d %d %d %s", METAKEY,
c->outcipher ? EVP_CIPHER_nid(c->outcipher) : 0,
c->outdigest ? EVP_MD_type(c->outdigest) : 0, c->outmaclength,
c->outcompression, buffer);
/* Further outgoing requests are encrypted with the key we just generated */
if(c->outcipher) {
if(!EVP_EncryptInit(c->outctx, c->outcipher,
(unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher),
(unsigned char *)c->outkey + len - EVP_CIPHER_key_length(c->outcipher) -
EVP_CIPHER_iv_length(c->outcipher))) {
logger(LOG_ERR, "Error during initialisation of cipher for %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
}
c->outbudget = byte_budget(c->outcipher);
c->status.encryptout = true;
}
return x;
bool result = send_request(c, "%d %d %d %d %d %s", METAKEY,
cipher_get_nid(&c->outcipher),
digest_get_nid(&c->outdigest), c->outmaclength,
c->outcompression, hexkey);
c->status.encryptout = true;
return result;
}
bool metakey_h(connection_t *c) {
char buffer[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
int len;
static bool metakey_ec_h(connection_t *c, const char *request) {
size_t siglen = ecdsa_size(&c->ecdsa);
char key[MAX_STRING_SIZE];
char sig[siglen];
if(sscanf(c->buffer, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name,
c->hostname);
logger(LOG_DEBUG, "Got ECDH metakey from %s", c->name);
if(sscanf(request, "%*d " MAX_STRING, key) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
return false;
}
len = RSA_size(myself->connection->rsa_key);
int inlen = b64decode(key, key, sizeof key);
/* Check if the length of the meta key is all right */
if(strlen(buffer) != (size_t)len * 2) {
if(inlen != (ECDH_SIZE + siglen)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
return false;
}
/* Allocate buffers for the meta key */
if(!ecdsa_verify(&c->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "invalid ECDSA signature");
return false;
}
c->inkey = xrealloc(c->inkey, len);
char shared[ECDH_SHARED_SIZE];
if(!c->inctx) {
c->inctx = EVP_CIPHER_CTX_new();
if(!ecdh_compute_shared(&c->ecdh, key, shared))
return false;
if(!c->inctx) {
abort();
}
/* Update our crypto end */
if(!cipher_open_by_name(&c->incipher, "aes-256-ofb"))
return false;
if(!digest_open_by_name(&c->indigest, "sha512", -1))
return false;
if(!cipher_open_by_name(&c->outcipher, "aes-256-ofb"))
return false;
if(!digest_open_by_name(&c->outdigest, "sha512", -1))
return false;
size_t mykeylen = cipher_keylength(&c->incipher);
size_t hiskeylen = cipher_keylength(&c->outcipher);
char *mykey;
char *hiskey;
char *seed;
if(strcmp(myself->name, c->name) < 0) {
mykey = key;
hiskey = key + mykeylen * 2;
xasprintf(&seed, "tinc TCP key expansion %s %s", myself->name, c->name);
} else {
mykey = key + hiskeylen * 2;
hiskey = key;
xasprintf(&seed, "tinc TCP key expansion %s %s", c->name, myself->name);
}
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
return false;
free(seed);
cipher_set_key(&c->incipher, mykey, false);
digest_set_key(&c->indigest, mykey + mykeylen, mykeylen);
cipher_set_key(&c->outcipher, hiskey, true);
digest_set_key(&c->outdigest, hiskey + hiskeylen, hiskeylen);
c->status.decryptin = true;
c->status.encryptout = true;
c->allow_request = CHALLENGE;
return send_challenge(c);
}
bool metakey_h(connection_t *c, char *request) {
if(c->protocol_minor >= 2)
return metakey_ec_h(c, request);
char hexkey[MAX_STRING_SIZE];
int cipher, digest, maclength, compression;
size_t len = rsa_size(&myself->connection->rsa);
char enckey[len];
char key[len];
if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
return false;
}
/* Convert the challenge from hexadecimal back to binary */
if(!hex2bin(buffer, buffer, len)) {
logger(LOG_ERR, "Got bad %s from %s(%s): %s", "METAKEY", c->name, c->hostname, "invalid key");
int inlen = hex2bin(hexkey, enckey, sizeof enckey);
/* Check if the length of the meta key is all right */
if(inlen != len) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
return false;
}
/* Decrypt the meta key */
if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) { /* See challenge() */
logger(LOG_ERR, "Error during decryption of meta key for %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) {
logger(LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
return false;
}
ifdebug(SCARY_THINGS) {
bin2hex(c->inkey, buffer, len);
buffer[len * 2] = '\0';
logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", buffer);
bin2hex(key, hexkey, len);
logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
}
/* All incoming requests will now be encrypted. */
/* Check and lookup cipher and digest algorithms */
if(cipher) {
c->incipher = EVP_get_cipherbynid(cipher);
if(!c->incipher) {
logger(LOG_ERR, "%s (%s) uses unknown cipher!", c->name, c->hostname);
return false;
}
if(!EVP_DecryptInit(c->inctx, c->incipher,
(unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher),
(unsigned char *)c->inkey + len - EVP_CIPHER_key_length(c->incipher) -
EVP_CIPHER_iv_length(c->incipher))) {
logger(LOG_ERR, "Error during initialisation of cipher from %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
}
c->inbudget = byte_budget(c->incipher);
c->status.decryptin = true;
} else {
logger(LOG_ERR, "%s (%s) uses null cipher!", c->name, c->hostname);
if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) {
logger(LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
return false;
}
c->inmaclength = maclength;
if(digest) {
c->indigest = EVP_get_digestbynid(digest);
if(!c->indigest) {
logger(LOG_ERR, "Node %s (%s) uses unknown digest!", c->name, c->hostname);
return false;
}
if(c->inmaclength > EVP_MD_size(c->indigest) || c->inmaclength < 0) {
logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname);
return false;
}
} else {
logger(LOG_ERR, "%s (%s) uses null digest!", c->name, c->hostname);
if(!digest_open_by_nid(&c->indigest, digest, -1)) {
logger(LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
return false;
}
c->incompression = compression;
c->status.decryptin = true;
c->allow_request = CHALLENGE;
@ -341,166 +356,87 @@ bool metakey_h(connection_t *c) {
}
bool send_challenge(connection_t *c) {
/* CHECKME: what is most reasonable value for len? */
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa);
char buffer[len * 2 + 1];
int len = RSA_size(c->rsa_key);
/* Allocate buffers for the challenge */
char buffer[2 * len + 1];
c->hischallenge = xrealloc(c->hischallenge, len);
if(!c->hischallenge)
c->hischallenge = xrealloc(c->hischallenge, len);
/* Copy random data to the buffer */
if(1 != RAND_bytes((unsigned char *)c->hischallenge, len)) {
int err = ERR_get_error();
logger(LOG_ERR, "Failed to generate challenge (%s)", ERR_error_string(err, NULL));
return false; // Do not send predictable challenges, let connection attempt fail.
}
randomize(c->hischallenge, len);
/* Convert to hex */
bin2hex(c->hischallenge, buffer, len);
buffer[len * 2] = '\0';
/* Send the challenge */
return send_request(c, "%d %s", CHALLENGE, buffer);
}
bool challenge_h(connection_t *c) {
bool challenge_h(connection_t *c, char *request) {
char buffer[MAX_STRING_SIZE];
int len;
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&myself->connection->rsa);
size_t digestlen = digest_length(&c->indigest);
char digest[digestlen];
if(sscanf(c->buffer, "%*d " MAX_STRING, buffer) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name,
c->hostname);
if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
return false;
}
len = RSA_size(myself->connection->rsa_key);
/* Check if the length of the challenge is all right */
if(strlen(buffer) != (size_t)len * 2) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
c->hostname, "wrong challenge length");
return false;
}
/* Allocate buffers for the challenge */
c->mychallenge = xrealloc(c->mychallenge, len);
/* Convert the challenge from hexadecimal back to binary */
if(!hex2bin(buffer, c->mychallenge, len)) {
logger(LOG_ERR, "Got bad %s from %s(%s): %s", "CHALLENGE", c->name, c->hostname, "invalid challenge");
int inlen = hex2bin(buffer, buffer, sizeof buffer);
/* Check if the length of the challenge is all right */
if(inlen != len) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
return false;
}
c->allow_request = CHAL_REPLY;
/* Rest is done by send_chal_reply() */
if(c->outgoing) {
return send_chal_reply(c);
} else {
return true;
}
}
bool send_chal_reply(connection_t *c) {
char hash[EVP_MAX_MD_SIZE * 2 + 1];
EVP_MD_CTX *ctx;
/* Calculate the hash from the challenge we received */
ctx = EVP_MD_CTX_create();
if(!ctx) {
abort();
}
if(!EVP_DigestInit(ctx, c->indigest)
|| !EVP_DigestUpdate(ctx, c->mychallenge, RSA_size(myself->connection->rsa_key))
|| !EVP_DigestFinal(ctx, (unsigned char *)hash, NULL)) {
EVP_MD_CTX_destroy(ctx);
logger(LOG_ERR, "Error during calculation of response for %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
}
EVP_MD_CTX_destroy(ctx);
digest_create(&c->indigest, buffer, len, digest);
/* Convert the hash to a hexadecimal formatted string */
bin2hex(hash, hash, EVP_MD_size(c->indigest));
hash[EVP_MD_size(c->indigest) * 2] = '\0';
bin2hex(digest, buffer, digestlen);
/* Send the reply */
return send_request(c, "%d %s", CHAL_REPLY, hash);
return send_request(c, "%d %s", CHAL_REPLY, buffer);
}
bool chal_reply_h(connection_t *c) {
bool chal_reply_h(connection_t *c, char *request) {
char hishash[MAX_STRING_SIZE];
char myhash[EVP_MAX_MD_SIZE];
EVP_MD_CTX *ctx;
if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) {
if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
c->hostname);
return false;
}
/* Check if the length of the hash is all right */
if(strlen(hishash) != (size_t)EVP_MD_size(c->outdigest) * 2) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
c->hostname, "wrong challenge reply length");
c->hostname);
return false;
}
/* Convert the hash to binary format */
if(!hex2bin(hishash, hishash, EVP_MD_size(c->outdigest))) {
logger(LOG_ERR, "Got bad %s from %s(%s): %s", "CHAL_REPLY", c->name, c->hostname, "invalid hash");
int inlen = hex2bin(hishash, hishash, sizeof hishash);
/* Check if the length of the hash is all right */
if(inlen != digest_length(&c->outdigest)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
return false;
}
/* Calculate the hash from the challenge we sent */
ctx = EVP_MD_CTX_create();
if(!ctx) {
abort();
}
if(!EVP_DigestInit(ctx, c->outdigest)
|| !EVP_DigestUpdate(ctx, c->hischallenge, RSA_size(c->rsa_key))
|| !EVP_DigestFinal(ctx, (unsigned char *)myhash, NULL)) {
EVP_MD_CTX_destroy(ctx);
logger(LOG_ERR, "Error during calculation of response from %s (%s): %s",
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
}
EVP_MD_CTX_destroy(ctx);
/* Verify the incoming hash with the calculated hash */
if(memcmp(hishash, myhash, EVP_MD_size(c->outdigest))) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
c->hostname, "wrong challenge reply");
ifdebug(SCARY_THINGS) {
bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
hishash[SHA_DIGEST_LENGTH * 2] = '\0';
logger(LOG_DEBUG, "Expected challenge reply: %s", hishash);
}
/* Verify the hash */
if(!digest_verify(&c->outdigest, c->hischallenge, c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa), hishash)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
return false;
}
@ -508,16 +444,31 @@ bool chal_reply_h(connection_t *c) {
Send an acknowledgement with the rest of the information needed.
*/
free(c->hischallenge);
c->hischallenge = NULL;
c->allow_request = ACK;
if(!c->outgoing) {
send_chal_reply(c);
}
return send_ack(c);
}
static bool send_upgrade(connection_t *c) {
/* Special case when protocol_minor is 1: the other end is ECDSA capable,
* but doesn't know our key yet. So send it now. */
char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
if(!pubkey)
return false;
bool result = send_request(c, "%d %s", ACK, pubkey);
free(pubkey);
return result;
}
bool send_ack(connection_t *c) {
if(c->protocol_minor == 1)
return send_upgrade(c);
/* ACK message contains rest of the information the other end needs
to create node_t and edge_t structures. */
@ -531,24 +482,19 @@ bool send_ack(connection_t *c) {
/* Check some options */
if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &choice) && choice) || myself->options & OPTION_INDIRECT) {
if((get_config_bool(lookup_config(c->config_tree, "IndirectData"), &choice) && choice) || myself->options & OPTION_INDIRECT)
c->options |= OPTION_INDIRECT;
}
if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY) {
if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY)
c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
}
if(myself->options & OPTION_PMTU_DISCOVERY && !(c->options & OPTION_TCPONLY)) {
if(myself->options & OPTION_PMTU_DISCOVERY)
c->options |= OPTION_PMTU_DISCOVERY;
}
choice = myself->options & OPTION_CLAMP_MSS;
get_config_bool(lookup_config(c->config_tree, "ClampMSS"), &choice);
if(choice) {
if(choice)
c->options |= OPTION_CLAMP_MSS;
}
get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight);
@ -556,7 +502,7 @@ bool send_ack(connection_t *c) {
}
static void send_everything(connection_t *c) {
avl_node_t *node, *node2;
splay_node_t *node, *node2;
node_t *n;
subnet_t *s;
edge_t *e;
@ -587,16 +533,39 @@ static void send_everything(connection_t *c) {
}
}
bool ack_h(connection_t *c) {
static bool upgrade_h(connection_t *c, char *request) {
char pubkey[MAX_STRING_SIZE];
if(sscanf(request, "%*d " MAX_STRING, pubkey) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname);
return false;
}
if(ecdsa_active(&c->ecdsa) || read_ecdsa_public_key(c)) {
logger(LOG_INFO, "Already have ECDSA public key from %s (%s), not upgrading.", c->name, c->hostname);
return false;
}
logger(LOG_INFO, "Got ECDSA public key from %s (%s), upgrading!", c->name, c->hostname);
append_config_file(c->name, "ECDSAPublicKey", pubkey);
c->allow_request = TERMREQ;
return send_termreq(c);
}
bool ack_h(connection_t *c, char *request) {
if(c->protocol_minor == 1)
return upgrade_h(c, request);
char hisport[MAX_STRING_SIZE];
char *hisaddress;
int weight, mtu;
uint32_t options;
node_t *n;
bool choice;
if(sscanf(c->buffer, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
c->hostname);
c->hostname);
return false;
}
@ -611,8 +580,17 @@ bool ack_h(connection_t *c) {
} else {
if(n->connection) {
/* Oh dear, we already have a connection to this node. */
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection",
n->name, n->hostname);
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
if(n->connection->outgoing) {
if(c->outgoing)
logger(LOG_WARNING, "Two outgoing connections to the same node!");
else
c->outgoing = n->connection->outgoing;
n->connection->outgoing = NULL;
}
terminate_connection(n->connection, false);
/* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
graph();
@ -621,37 +599,35 @@ bool ack_h(connection_t *c) {
n->connection = c;
c->node = n;
if(!(c->options & options & OPTION_PMTU_DISCOVERY)) {
c->options &= ~OPTION_PMTU_DISCOVERY;
options &= ~OPTION_PMTU_DISCOVERY;
}
c->options |= options;
if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu) {
if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu)
n->mtu = mtu;
}
if(get_config_int(lookup_config(config_tree, "PMTU"), &mtu) && mtu < n->mtu) {
if(get_config_int(lookup_config(config_tree, "PMTU"), &mtu) && mtu < n->mtu)
n->mtu = mtu;
}
if(get_config_bool(lookup_config(c->config_tree, "ClampMSS"), &choice)) {
if(choice) {
if(choice)
c->options |= OPTION_CLAMP_MSS;
} else {
else
c->options &= ~OPTION_CLAMP_MSS;
}
}
if(c->protocol_minor > 0)
c->node->status.ecdh = true;
/* Activate this connection */
c->allow_request = ALL;
c->status.active = true;
ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection with %s (%s) activated", c->name,
c->hostname);
c->hostname);
/* Send him everything we know */
@ -662,8 +638,9 @@ bool ack_h(connection_t *c) {
c->edge = new_edge();
c->edge->from = myself;
c->edge->to = n;
sockaddrcpy(&c->edge->address, &c->address);
sockaddr_setport(&c->edge->address, hisport);
sockaddr2str(&c->address, &hisaddress, NULL);
c->edge->address = str2sockaddr(hisaddress, hisport);
free(hisaddress);
c->edge->weight = (weight + c->estimated_weight) / 2;
c->edge->connection = c;
c->edge->options = c->options;
@ -672,11 +649,10 @@ bool ack_h(connection_t *c) {
/* Notify everyone of the new edge */
if(tunnelserver) {
if(tunnelserver)
send_add_edge(c, c->edge);
} else {
send_add_edge(everyone, c->edge);
}
else
send_add_edge(broadcast, c->edge);
/* Run MST and SSSP algorithms */

View file

@ -1,7 +1,7 @@
/*
protocol_edge.c -- handle the meta-protocol, edges
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
2009 Michael Tokarev <mjt@corpit.ru>
This program is free software; you can redistribute it and/or modify
@ -21,7 +21,7 @@
#include "system.h"
#include "avl_tree.h"
#include "splay_tree.h"
#include "conf.h"
#include "connection.h"
#include "edge.h"
@ -42,15 +42,15 @@ bool send_add_edge(connection_t *c, const edge_t *e) {
sockaddr2str(&e->address, &address, &port);
x = send_request(c, "%d %x %s %s %s %s %x %d", ADD_EDGE, rand(),
e->from->name, e->to->name, address, port,
e->options, e->weight);
e->from->name, e->to->name, address, port,
e->options, e->weight);
free(address);
free(port);
return x;
}
bool add_edge_h(connection_t *c) {
bool add_edge_h(connection_t *c, char *request) {
edge_t *e;
node_t *from, *to;
char from_name[MAX_STRING_SIZE];
@ -61,24 +61,23 @@ bool add_edge_h(connection_t *c) {
uint32_t options;
int weight;
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
from_name, to_name, to_address, to_port, &options, &weight) != 6) {
if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
from_name, to_name, to_address, to_port, &options, &weight) != 6) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
c->hostname);
c->hostname);
return false;
}
/* Check if names are valid */
if(!check_id(from_name) || !check_id(to_name) || !strcmp(from_name, to_name)) {
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name,
c->hostname, "invalid name");
c->hostname, "invalid name");
return false;
}
if(seen_request(c->buffer)) {
if(seen_request(request))
return true;
}
/* Lookup nodes */
@ -86,12 +85,12 @@ bool add_edge_h(connection_t *c) {
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
"Ignoring indirect %s from %s (%s)",
"ADD_EDGE", c->name, c->hostname);
"Ignoring indirect %s from %s (%s)",
"ADD_EDGE", c->name, c->hostname);
return true;
}
@ -120,33 +119,20 @@ bool add_edge_h(connection_t *c) {
if(e->weight != weight || e->options != options || sockaddrcmp(&e->address, &address)) {
if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself which does not match existing entry",
"ADD_EDGE", c->name, c->hostname);
"ADD_EDGE", c->name, c->hostname);
send_add_edge(c, e);
return true;
} else {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) which does not match existing entry",
"ADD_EDGE", c->name, c->hostname);
e->options = options;
if(sockaddrcmp(&e->address, &address)) {
sockaddrfree(&e->address);
e->address = address;
}
if(e->weight != weight) {
avl_node_t *node = avl_unlink(edge_weight_tree, e);
e->weight = weight;
avl_insert_node(edge_weight_tree, node);
}
goto done;
"ADD_EDGE", c->name, c->hostname);
edge_del(e);
graph();
}
} else {
} else
return true;
}
} else if(from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself which does not exist",
"ADD_EDGE", c->name, c->hostname);
"ADD_EDGE", c->name, c->hostname);
contradicting_add_edge++;
e = new_edge();
e->from = from;
@ -164,12 +150,10 @@ bool add_edge_h(connection_t *c) {
e->weight = weight;
edge_add(e);
done:
/* Tell the rest about the new edge */
if(!tunnelserver) {
forward_request(c);
}
if(!tunnelserver)
forward_request(c, request);
/* Run MST before or after we tell the rest? */
@ -180,32 +164,31 @@ done:
bool send_del_edge(connection_t *c, const edge_t *e) {
return send_request(c, "%d %x %s %s", DEL_EDGE, rand(),
e->from->name, e->to->name);
e->from->name, e->to->name);
}
bool del_edge_h(connection_t *c) {
bool del_edge_h(connection_t *c, char *request) {
edge_t *e;
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
c->hostname);
c->hostname);
return false;
}
/* Check if names are valid */
if(!check_id(from_name) || !check_id(to_name) || !strcmp(from_name, to_name)) {
if(!check_id(from_name) || !check_id(to_name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_EDGE", c->name,
c->hostname, "invalid name");
c->hostname, "invalid name");
return false;
}
if(seen_request(c->buffer)) {
if(seen_request(request))
return true;
}
/* Lookup nodes */
@ -213,24 +196,24 @@ bool del_edge_h(connection_t *c) {
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
"Ignoring indirect %s from %s (%s)",
"DEL_EDGE", c->name, c->hostname);
"Ignoring indirect %s from %s (%s)",
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
ifdebug(PROTOCOL) logger(LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!to) {
ifdebug(PROTOCOL) logger(LOG_ERR, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
"DEL_EDGE", c->name, c->hostname);
return true;
}
@ -240,23 +223,22 @@ bool del_edge_h(connection_t *c) {
if(!e) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) which does not appear in the edge tree",
"DEL_EDGE", c->name, c->hostname);
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(e->from == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
"DEL_EDGE", c->name, c->hostname);
"DEL_EDGE", c->name, c->hostname);
contradicting_del_edge++;
send_add_edge(c, e); /* Send back a correction */
send_add_edge(c, e); /* Send back a correction */
return true;
}
/* Tell the rest about the deleted edge */
if(!tunnelserver) {
forward_request(c);
}
if(!tunnelserver)
forward_request(c, request);
/* Delete the edge */
@ -270,12 +252,9 @@ bool del_edge_h(connection_t *c) {
if(!to->status.reachable) {
e = lookup_edge(to, myself);
if(e) {
if(!tunnelserver) {
send_del_edge(everyone, e);
}
if(!tunnelserver)
send_del_edge(broadcast, e);
edge_del(e);
}
}

View file

@ -1,7 +1,7 @@
/*
protocol_key.c -- handle the meta-protocol, key exchange
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2000-2011 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,16 +20,16 @@
#include "system.h"
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include "avl_tree.h"
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "crypto.h"
#include "ecdh.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "prf.h"
#include "protocol.h"
#include "utils.h"
#include "xalloc.h"
@ -37,46 +37,38 @@
static bool mykeyused = false;
void send_key_changed(void) {
avl_node_t *node;
splay_node_t *node;
connection_t *c;
send_request(everyone, "%d %x %s", KEY_CHANGED, rand(), myself->name);
send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
/* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c->status.active && c->node && c->node->status.reachable) {
if(c->status.active && c->node && c->node->status.reachable)
send_ans_key(c->node);
}
}
}
bool key_changed_h(connection_t *c) {
bool key_changed_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
node_t *n;
if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
c->name, c->hostname);
c->name, c->hostname);
return false;
}
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "KEY_CHANGED", c->name, c->hostname, "invalid name");
return false;
}
if(seen_request(c->buffer)) {
if(seen_request(request))
return true;
}
n = lookup_node(name);
if(!n) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
"KEY_CHANGED", c->name, c->hostname, name);
"KEY_CHANGED", c->name, c->hostname, name);
return true;
}
@ -85,25 +77,25 @@ bool key_changed_h(connection_t *c) {
/* Tell the others */
if(!tunnelserver) {
forward_request(c);
}
if(!tunnelserver)
forward_request(c, request);
return true;
}
bool send_req_key(node_t *to) {
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
return send_request(to->nexthop->connection, "%d %s %s %d", REQ_KEY, myself->name, to->name, experimental ? 1 : 0);
}
bool req_key_h(connection_t *c) {
bool req_key_h(connection_t *c, char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
int kx_version = 0;
if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &kx_version) < 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
c->hostname);
c->hostname);
return false;
}
@ -116,7 +108,7 @@ bool req_key_h(connection_t *c) {
if(!from) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
"REQ_KEY", c->name, c->hostname, from_name);
"REQ_KEY", c->name, c->hostname, from_name);
return true;
}
@ -124,89 +116,106 @@ bool req_key_h(connection_t *c) {
if(!to) {
logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
"REQ_KEY", c->name, c->hostname, to_name);
"REQ_KEY", c->name, c->hostname, to_name);
return true;
}
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
if(!send_ans_key(from)) {
return false;
if(to == myself) { /* Yes, send our own key back */
if(experimental && kx_version >= 1) {
logger(LOG_DEBUG, "Got ECDH key request from %s", from->name);
from->status.ecdh = true;
}
send_ans_key(from);
} else {
if(tunnelserver) {
if(tunnelserver)
return true;
}
if(!to->status.reachable) {
logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
"REQ_KEY", c->name, c->hostname, to_name);
"REQ_KEY", c->name, c->hostname, to_name);
return true;
}
send_request(to->nexthop->connection, "%s", c->buffer);
send_request(to->nexthop->connection, "%s", request);
}
return true;
}
bool send_ans_key_ecdh(node_t *to) {
int siglen = ecdsa_size(&myself->connection->ecdsa);
char key[(ECDH_SIZE + siglen) * 2 + 1];
if(!ecdh_generate_public(&to->ecdh, key))
return false;
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE))
return false;
b64encode(key, key, ECDH_SIZE + siglen);
char *pubkey = ecdsa_get_base64_public_key(&myself->connection->ecdsa);
if(!pubkey)
return false;
int result = send_request(to->nexthop->connection, "%d %s %s ECDH:%s:%s %d %d %zu %d", ANS_KEY,
myself->name, to->name, key, pubkey,
cipher_get_nid(&myself->incipher),
digest_get_nid(&myself->indigest),
digest_length(&myself->indigest),
myself->incompression);
free(pubkey);
return result;
}
bool send_ans_key(node_t *to) {
// Set key parameters
to->incipher = myself->incipher;
to->inkeylength = myself->inkeylength;
to->indigest = myself->indigest;
to->inmaclength = myself->inmaclength;
if(experimental && to->status.ecdh)
return send_ans_key_ecdh(to);
size_t keylen = cipher_keylength(&myself->incipher);
char key[keylen * 2 + 1];
cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
to->incompression = myself->incompression;
// Allocate memory for key
to->inkey = xrealloc(to->inkey, to->inkeylength);
randomize(key, keylen);
cipher_set_key(&to->incipher, key, false);
digest_set_key(&to->indigest, key, keylen);
// Create a new key
if(1 != RAND_bytes((unsigned char *)to->inkey, to->inkeylength)) {
int err = ERR_get_error();
logger(LOG_ERR, "Failed to generate random for key (%s)", ERR_error_string(err, NULL));
return false; // Do not send insecure keys, let connection attempt fail.
}
if(to->incipher) {
EVP_DecryptInit_ex(to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + EVP_CIPHER_key_length(to->incipher));
}
bin2hex(key, key, keylen);
// Reset sequence number and late packet window
mykeyused = true;
to->received_seqno = 0;
if(replaywin) memset(to->late, 0, replaywin);
if(replaywin) {
memset(to->late, 0, replaywin);
}
// Convert to hexadecimal and send
char key[2 * to->inkeylength + 1];
bin2hex(to->inkey, key, to->inkeylength);
key[to->inkeylength * 2] = '\0';
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
myself->name, to->name, key,
to->incipher ? EVP_CIPHER_nid(to->incipher) : 0,
to->indigest ? EVP_MD_type(to->indigest) : 0, to->inmaclength,
to->incompression);
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
myself->name, to->name, key,
cipher_get_nid(&to->incipher),
digest_get_nid(&to->indigest),
digest_length(&to->indigest),
to->incompression);
}
bool ans_key_h(connection_t *c) {
bool ans_key_h(connection_t *c, char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
char address[MAX_STRING_SIZE] = "";
char port[MAX_STRING_SIZE] = "";
int cipher, digest, maclength, compression;
char address[MAX_STRING_SIZE] = "";
char port[MAX_STRING_SIZE] = "";
int cipher, digest, maclength, compression, keylen;
node_t *from, *to;
if(sscanf(c->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
from_name, to_name, key, &cipher, &digest, &maclength,
&compression, address, port) < 7) {
if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING,
from_name, to_name, key, &cipher, &digest, &maclength,
&compression, address, port) < 7) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
c->hostname);
c->hostname);
return false;
}
@ -219,7 +228,7 @@ bool ans_key_h(connection_t *c) {
if(!from) {
logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
"ANS_KEY", c->name, c->hostname, from_name);
"ANS_KEY", c->name, c->hostname, from_name);
return true;
}
@ -227,91 +236,50 @@ bool ans_key_h(connection_t *c) {
if(!to) {
logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
"ANS_KEY", c->name, c->hostname, to_name);
"ANS_KEY", c->name, c->hostname, to_name);
return true;
}
/* Forward it if necessary */
if(to != myself) {
if(tunnelserver) {
if(tunnelserver)
return true;
}
if(!to->status.reachable) {
logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
"ANS_KEY", c->name, c->hostname, to_name);
"ANS_KEY", c->name, c->hostname, to_name);
return true;
}
if(!*address && from->address.sa.sa_family != AF_UNSPEC && to->minmtu) {
if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
char *address, *port;
ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
sockaddr2str(&from->address, &address, &port);
send_request(to->nexthop->connection, "%s %s %s", c->buffer, address, port);
send_request(to->nexthop->connection, "%s %s %s", request, address, port);
free(address);
free(port);
return true;
}
return send_request(to->nexthop->connection, "%s", c->buffer);
}
/* Don't use key material until every check has passed. */
from->status.validkey = false;
/* Update our copy of the origin's packet key */
from->outkey = xrealloc(from->outkey, strlen(key) / 2);
from->outkeylength = strlen(key) / 2;
if(!hex2bin(key, from->outkey, from->outkeylength)) {
logger(LOG_ERR, "Got bad %s from %s(%s): %s", "ANS_KEY", from->name, from->hostname, "invalid key");
return true;
return send_request(to->nexthop->connection, "%s", request);
}
/* Check and lookup cipher and digest algorithms */
if(cipher) {
from->outcipher = EVP_get_cipherbynid(cipher);
if(!from->outcipher) {
logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name,
from->hostname);
return true;
}
if(from->outkeylength != EVP_CIPHER_key_length(from->outcipher) + EVP_CIPHER_iv_length(from->outcipher)) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name,
from->hostname);
return true;
}
} else {
if(from->outkeylength != 1) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
return true;
}
from->outcipher = NULL;
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
return false;
}
from->outmaclength = maclength;
if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
return false;
}
if(digest) {
from->outdigest = EVP_get_digestbynid(digest);
if(!from->outdigest) {
logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name,
from->hostname);
return true;
}
if(from->outmaclength > EVP_MD_size(from->outdigest) || from->outmaclength < 0) {
logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!",
from->name, from->hostname);
return true;
}
} else {
from->outdigest = NULL;
if(maclength != digest_length(&from->outdigest)) {
logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
return false;
}
if(compression < 0 || compression > 11) {
@ -321,13 +289,113 @@ bool ans_key_h(connection_t *c) {
from->outcompression = compression;
if(from->outcipher)
if(!EVP_EncryptInit_ex(from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + EVP_CIPHER_key_length(from->outcipher))) {
logger(LOG_ERR, "Error during initialisation of key from %s (%s): %s",
from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
/* ECDH or old-style key exchange? */
if(experimental && !strncmp(key, "ECDH:", 5)) {
char *pubkey = strchr(key + 5, ':');
if(pubkey)
*pubkey++ = 0;
/* Check if we already have an ECDSA public key for this node.
* If not, use the one from the key exchange, and store it. */
if(!node_read_ecdsa_public_key(from)) {
if(!pubkey) {
logger(LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
return true;
}
if(!ecdsa_set_base64_public_key(&from->ecdsa, pubkey))
return true;
append_config_file(from->name, "ECDSAPublicKey", pubkey);
}
int siglen = ecdsa_size(&from->ecdsa);
int keylen = b64decode(key + 5, key + 5, sizeof key - 5);
if(keylen != ECDH_SIZE + siglen) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
return true;
}
if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
return true;
}
if(!ecdsa_verify(&from->ecdsa, key + 5, ECDH_SIZE, key + 5 + ECDH_SIZE)) {
logger(LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
return true;
}
if(!from->ecdh) {
from->status.ecdh = true;
if(!send_ans_key(from))
return true;
}
char shared[ECDH_SHARED_SIZE * 2 + 1];
if(!ecdh_compute_shared(&from->ecdh, key + 5, shared))
return true;
/* Update our crypto end */
size_t mykeylen = cipher_keylength(&myself->incipher);
size_t hiskeylen = cipher_keylength(&from->outcipher);
char *mykey;
char *hiskey;
char *seed;
if(strcmp(myself->name, from->name) < 0) {
mykey = key;
hiskey = key + mykeylen * 2;
xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
} else {
mykey = key + hiskeylen * 2;
hiskey = key;
xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
}
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
return true;
free(seed);
cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
from->incompression = myself->incompression;
cipher_set_key(&from->incipher, mykey, false);
digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
cipher_set_key(&from->outcipher, hiskey, true);
digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
// Reset sequence number and late packet window
mykeyused = true;
from->received_seqno = 0;
if(replaywin)
memset(from->late, 0, replaywin);
if(strcmp(myself->name, from->name) < 0)
memmove(key, key + mykeylen * 2, hiskeylen * 2);
} else {
keylen = hex2bin(key, key, sizeof key);
if(keylen != cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
return true;
}
/* Update our copy of the origin's packet key */
cipher_set_key(&from->outcipher, key, true);
digest_set_key(&from->outdigest, key, keylen);
}
from->status.validkey = true;
from->sent_seqno = 0;
@ -337,9 +405,8 @@ bool ans_key_h(connection_t *c) {
update_node_udp(from, &sa);
}
if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuevent) {
if(from->options & OPTION_PMTU_DISCOVERY)
send_mtu_probe(from);
}
return true;
}

View file

@ -1,7 +1,7 @@
/*
protocol_misc.c -- handle the meta-protocol, miscellaneous functions
Copyright (C) 1999-2005 Ivo Timmermans,
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2000-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -31,14 +31,70 @@
int maxoutbufsize = 0;
/* Status and error notification routines */
bool send_status(connection_t *c, int statusno, const char *statusstring) {
if(!statusstring)
statusstring = "Status";
return send_request(c, "%d %d %s", STATUS, statusno, statusstring);
}
bool status_h(connection_t *c, char *request) {
int statusno;
char statusstring[MAX_STRING_SIZE];
if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "STATUS",
c->name, c->hostname);
return false;
}
ifdebug(STATUS) logger(LOG_NOTICE, "Status message from %s (%s): %d: %s",
c->name, c->hostname, statusno, statusstring);
return true;
}
bool send_error(connection_t *c, int err, const char *errstring) {
if(!errstring)
errstring = "Error";
return send_request(c, "%d %d %s", ERROR, err, errstring);
}
bool error_h(connection_t *c, char *request) {
int err;
char errorstring[MAX_STRING_SIZE];
if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ERROR",
c->name, c->hostname);
return false;
}
ifdebug(ERROR) logger(LOG_NOTICE, "Error message from %s (%s): %d: %s",
c->name, c->hostname, err, errorstring);
return false;
}
bool send_termreq(connection_t *c) {
return send_request(c, "%d", TERMREQ);
}
bool termreq_h(connection_t *c, char *request) {
return false;
}
bool send_ping(connection_t *c) {
c->status.pinged = true;
c->last_ping_time = now;
c->last_ping_time = time(NULL);
return send_request(c, "%d", PING);
}
bool ping_h(connection_t *c) {
bool ping_h(connection_t *c, char *request) {
return send_pong(c);
}
@ -46,22 +102,13 @@ bool send_pong(connection_t *c) {
return send_request(c, "%d", PONG);
}
bool pong_h(connection_t *c) {
bool pong_h(connection_t *c, char *request) {
c->status.pinged = false;
/* Successful connection, reset timeout if this is an outgoing connection. */
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing) {
if(c->outgoing)
c->outgoing->timeout = 0;
c->outgoing->cfg = NULL;
if(c->outgoing->ai) {
freeaddrinfo(c->outgoing->ai);
}
c->outgoing->ai = NULL;
c->outgoing->aip = NULL;
}
return true;
}
@ -70,25 +117,23 @@ bool pong_h(connection_t *c) {
bool send_tcppacket(connection_t *c, const vpn_packet_t *packet) {
/* If there already is a lot of data in the outbuf buffer, discard this packet.
We use a very simple Random Early Drop algorithm. */
We use a very simple Random Early Drop algorithm. */
if(2.0 * c->outbuflen / (float)maxoutbufsize - 1 > (float)rand() / (float)RAND_MAX) {
if(2.0 * c->outbuf.len / (float)maxoutbufsize - 1 > (float)rand()/(float)RAND_MAX)
return true;
}
if(!send_request(c, "%d %hd", PACKET, packet->len)) {
if(!send_request(c, "%d %hd", PACKET, packet->len))
return false;
}
return send_meta(c, (char *)packet->data, packet->len) && flush_meta(c);
return send_meta(c, (char *)packet->data, packet->len);
}
bool tcppacket_h(connection_t *c) {
length_t len;
bool tcppacket_h(connection_t *c, char *request) {
short int len;
if(sscanf(c->buffer, "%*d %hu", &len) != 1) {
if(sscanf(request, "%*d %hd", &len) != 1) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "PACKET", c->name,
c->hostname);
c->hostname);
return false;
}

View file

@ -35,22 +35,21 @@
bool send_add_subnet(connection_t *c, const subnet_t *subnet) {
char netstr[MAXNETSTR];
if(!net2str(netstr, sizeof(netstr), subnet)) {
if(!net2str(netstr, sizeof netstr, subnet))
return false;
}
return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
}
bool add_subnet_h(connection_t *c) {
bool add_subnet_h(connection_t *c, char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t s = {0}, *new, *old;
subnet_t s = {NULL}, *new, *old;
if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
c->hostname);
c->hostname);
return false;
}
@ -58,7 +57,7 @@ bool add_subnet_h(connection_t *c) {
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
c->hostname, "invalid name");
c->hostname, "invalid name");
return false;
}
@ -66,13 +65,12 @@ bool add_subnet_h(connection_t *c) {
if(!str2net(&s, subnetstr)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
c->hostname, "invalid subnet string");
c->hostname, "invalid subnet string");
return false;
}
if(seen_request(c->buffer)) {
if(seen_request(request))
return true;
}
/* Check if the owner of the new subnet is in the connection list */
@ -81,7 +79,7 @@ bool add_subnet_h(connection_t *c) {
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet registrations */
ifdebug(PROTOCOL) logger(LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
@ -93,15 +91,14 @@ bool add_subnet_h(connection_t *c) {
/* Check if we already know this subnet */
if(lookup_subnet(owner, &s)) {
if(lookup_subnet(owner, &s))
return true;
}
/* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
"ADD_SUBNET", c->name, c->hostname);
"ADD_SUBNET", c->name, c->hostname);
s.owner = myself;
send_del_subnet(c, &s);
return true;
@ -111,7 +108,7 @@ bool add_subnet_h(connection_t *c) {
if(tunnelserver) {
logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
@ -119,8 +116,8 @@ bool add_subnet_h(connection_t *c) {
if(strictsubnets) {
logger(LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
"ADD_SUBNET", c->name, c->hostname, subnetstr);
forward_request(c);
"ADD_SUBNET", c->name, c->hostname, subnetstr);
forward_request(c, request);
return true;
}
@ -129,19 +126,18 @@ bool add_subnet_h(connection_t *c) {
*(new = new_subnet()) = s;
subnet_add(owner, new);
if(owner->status.reachable) {
if(owner->status.reachable)
subnet_update(owner, new, true);
}
/* Tell the rest */
forward_request(c);
if(!tunnelserver)
forward_request(c, request);
/* Fast handoff of roaming MAC addresses */
if(s.type == SUBNET_MAC && owner != myself && (old = lookup_subnet(myself, &s)) && old->expires) {
old->expires = now;
}
if(s.type == SUBNET_MAC && owner != myself && (old = lookup_subnet(myself, &s)) && old->expires)
old->expires = 1;
return true;
}
@ -149,22 +145,21 @@ bool add_subnet_h(connection_t *c) {
bool send_del_subnet(connection_t *c, const subnet_t *s) {
char netstr[MAXNETSTR];
if(!net2str(netstr, sizeof(netstr), s)) {
if(!net2str(netstr, sizeof netstr, s))
return false;
}
return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
}
bool del_subnet_h(connection_t *c) {
bool del_subnet_h(connection_t *c, char *request) {
char subnetstr[MAX_STRING_SIZE];
char name[MAX_STRING_SIZE];
node_t *owner;
subnet_t s = {0}, *find;
subnet_t s = {NULL}, *find;
if(sscanf(c->buffer, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
c->hostname);
c->hostname);
return false;
}
@ -172,7 +167,7 @@ bool del_subnet_h(connection_t *c) {
if(!check_id(name)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
c->hostname, "invalid name");
c->hostname, "invalid name");
return false;
}
@ -180,13 +175,12 @@ bool del_subnet_h(connection_t *c) {
if(!str2net(&s, subnetstr)) {
logger(LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
c->hostname, "invalid subnet string");
c->hostname, "invalid subnet string");
return false;
}
if(seen_request(c->buffer)) {
if(seen_request(request))
return true;
}
/* Check if the owner of the subnet being deleted is in the connection list */
@ -195,13 +189,13 @@ bool del_subnet_h(connection_t *c) {
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet deletion */
ifdebug(PROTOCOL) logger(LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
"DEL_SUBNET", c->name, c->hostname, subnetstr);
"DEL_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for %s which is not in our node tree",
"DEL_SUBNET", c->name, c->hostname, name);
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
@ -213,12 +207,9 @@ bool del_subnet_h(connection_t *c) {
if(!find) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for %s which does not appear in his subnet tree",
"DEL_SUBNET", c->name, c->hostname, name);
if(strictsubnets) {
forward_request(c);
}
"DEL_SUBNET", c->name, c->hostname, name);
if(strictsubnets)
forward_request(c, request);
return true;
}
@ -226,28 +217,25 @@ bool del_subnet_h(connection_t *c) {
if(owner == myself) {
ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
"DEL_SUBNET", c->name, c->hostname);
"DEL_SUBNET", c->name, c->hostname);
send_add_subnet(c, find);
return true;
}
if(tunnelserver) {
if(tunnelserver)
return true;
}
/* Tell the rest */
forward_request(c);
if(strictsubnets) {
if(!tunnelserver)
forward_request(c, request);
if(strictsubnets)
return true;
}
/* Finally, delete it. */
if(owner->status.reachable) {
if(owner->status.reachable)
subnet_update(owner, find, false);
}
subnet_del(owner, find);

View file

@ -1,366 +0,0 @@
/*
proxy.c -- Proxy handling functions.
Copyright (C) 2015-2017 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 "connection.h"
#include "logger.h"
#include "meta.h"
#include "netutl.h"
#include "protocol.h"
#include "proxy.h"
#include "utils.h" //
proxytype_t proxytype;
char *proxyhost;
char *proxyport;
char *proxyuser;
char *proxypass;
static void update_address_ipv4(connection_t *c, void *address, void *port) {
sockaddrfree(&c->address);
memset(&c->address, 0, sizeof(c->address));
c->address.sa.sa_family = AF_INET;
if(address) {
memcpy(&c->address.in.sin_addr, address, sizeof(ipv4_t));
}
if(port) {
memcpy(&c->address.in.sin_port, port, sizeof(uint16_t));
}
// OpenSSH -D returns all zero address, set it to 0.0.0.1 to prevent spamming ourselves.
if(!memcmp(&c->address.in.sin_addr, "\0\0\0\0", 4)) {
memcpy(&c->address.in.sin_addr, "\0\0\0\01", 4);
}
}
static void update_address_ipv6(connection_t *c, void *address, void *port) {
sockaddrfree(&c->address);
memset(&c->address, 0, sizeof(c->address));
c->address.sa.sa_family = AF_INET6;
if(address) {
memcpy(&c->address.in6.sin6_addr, address, sizeof(ipv6_t));
}
if(port) {
memcpy(&c->address.in6.sin6_port, port, sizeof(uint16_t));
}
// OpenSSH -D returns all zero address, set it to 0100:: to prevent spamming ourselves.
if(!memcmp(&c->address.in6.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) {
memcpy(&c->address.in6.sin6_addr, "\01\0\0\0\0\0\0\0", 8);
}
}
bool send_proxyrequest(connection_t *c) {
switch(proxytype) {
case PROXY_SOCKS4:
if(c->address.sa.sa_family != AF_INET) {
logger(LOG_ERR, "Can only connect to numeric IPv4 addresses through a SOCKS 4 proxy!");
return false;
}
// fallthrough
case PROXY_SOCKS4A: {
if(c->address.sa.sa_family != AF_INET && c->address.sa.sa_family != AF_UNKNOWN) {
logger(LOG_ERR, "Can only connect to IPv4 addresses or hostnames through a SOCKS 4a proxy!");
return false;
}
int len = 9;
if(proxyuser) {
len += strlen(proxyuser);
}
if(c->address.sa.sa_family == AF_UNKNOWN) {
len += 1 + strlen(c->address.unknown.address);
}
char s4req[len];
s4req[0] = 4;
s4req[1] = 1;
if(c->address.sa.sa_family == AF_INET) {
memcpy(s4req + 2, &c->address.in.sin_port, 2);
memcpy(s4req + 4, &c->address.in.sin_addr, 4);
} else {
uint16_t port = htons(atoi(c->address.unknown.port));
memcpy(s4req + 2, &port, 2);
memcpy(s4req + 4, "\0\0\0\1", 4);
strcpy(s4req + (9 + (proxyuser ? strlen(proxyuser) : 0)), c->address.unknown.address);
}
if(proxyuser) {
strcpy(s4req + 8, proxyuser);
} else {
s4req[8] = 0;
}
s4req[sizeof(s4req) - 1] = 0;
c->allow_request = PROXY;
return send_meta(c, s4req, sizeof(s4req));
}
case PROXY_SOCKS5: {
int len = 3 + 6;
if(c->address.sa.sa_family == AF_INET) {
len += 4;
} else if(c->address.sa.sa_family == AF_INET6) {
len += 16;
} else if(c->address.sa.sa_family == AF_UNKNOWN) {
len += 1 + strlen(c->address.unknown.address);
} else {
logger(LOG_ERR, "Address family %x not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
return false;
}
if(proxypass) {
len += 3 + strlen(proxyuser) + strlen(proxypass);
}
char s5req[len];
int i = 0;
s5req[i++] = 5;
s5req[i++] = 1;
if(proxypass) {
s5req[i++] = 2;
s5req[i++] = 1;
s5req[i++] = strlen(proxyuser);
strcpy(s5req + i, proxyuser);
i += strlen(proxyuser);
s5req[i++] = strlen(proxypass);
strcpy(s5req + i, proxypass);
i += strlen(proxypass);
} else {
s5req[i++] = 0;
}
s5req[i++] = 5;
s5req[i++] = 1;
s5req[i++] = 0;
if(c->address.sa.sa_family == AF_INET) {
s5req[i++] = 1;
memcpy(s5req + i, &c->address.in.sin_addr, 4);
i += 4;
memcpy(s5req + i, &c->address.in.sin_port, 2);
i += 2;
} else if(c->address.sa.sa_family == AF_INET6) {
s5req[i++] = 4;
memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
i += 16;
memcpy(s5req + i, &c->address.in6.sin6_port, 2);
i += 2;
} else if(c->address.sa.sa_family == AF_UNKNOWN) {
s5req[i++] = 3;
int len = strlen(c->address.unknown.address);
s5req[i++] = len;
memcpy(s5req + i, c->address.unknown.address, len);
i += len;
uint16_t port = htons(atoi(c->address.unknown.port));
memcpy(s5req + i, &port, 2);
i += 2;
} else {
logger(LOG_ERR, "Unknown address family while trying to connect to SOCKS5 proxy");
return false;
}
if(i > len) {
abort();
}
c->allow_request = PROXY;
return send_meta(c, s5req, sizeof(s5req));
}
case PROXY_HTTP: {
char *host;
char *port;
sockaddr2str(&c->address, &host, &port);
send_request(c, "CONNECT %s:%s HTTP/1.1\r\n\r", host, port);
free(host);
free(port);
c->allow_request = PROXY;
return true;
}
case PROXY_EXEC:
abort();
default:
logger(LOG_ERR, "Unknown proxy type");
return false;
}
}
int receive_proxy_meta(connection_t *c) {
switch(proxytype) {
case PROXY_SOCKS4:
case PROXY_SOCKS4A:
if(c->buflen < 8) {
return 0;
}
if(c->buffer[0] == 0 && c->buffer[1] == 0x5a) {
if(c->address.sa.sa_family == AF_UNKNOWN) {
update_address_ipv4(c, c->buffer + 4, c->buffer + 2);
}
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
c->allow_request = ID;
c->status.proxy_passed = true;
send_id(c);
return 8;
} else {
logger(LOG_ERR, "Proxy request rejected");
return -1;
}
case PROXY_SOCKS5:
if(c->buflen < 2) {
return 0;
}
if(c->buffer[0] != 0x05 || c->buffer[1] == (char)0xff) {
logger(LOG_ERR, "Proxy authentication method rejected");
return -1;
}
int offset = 2;
if(c->buffer[1] == 0x02) {
if(c->buflen < 4) {
return 0;
}
if(c->buffer[2] != 0x05 || c->buffer[3] != 0x00) {
logger(LOG_ERR, "Proxy username/password rejected");
return -1;
}
offset += 2;
}
if(c->buflen - offset < 7) {
return 0;
}
if(c->buffer[offset] != 0x05 || c->buffer[offset + 1] != 0x00) {
logger(LOG_ERR, "Proxy request rejected");
return -1;
}
int replen = offset + 6;
switch(c->buffer[offset + 3]) {
case 0x01: // IPv4
if(c->address.sa.sa_family == AF_UNKNOWN) {
update_address_ipv4(c, c->buffer + offset + 4, c->buffer + offset + 8);
}
replen += 4;
break;
case 0x03: // Hostname
if(c->address.sa.sa_family == AF_UNKNOWN) {
update_address_ipv4(c, "\0\0\0\1", "\0\0");
}
replen += ((uint8_t *)c->buffer)[offset + 4];
break;
case 0x04: // IPv6
if(c->address.sa.sa_family == AF_UNKNOWN) {
update_address_ipv6(c, c->buffer + offset + 4, c->buffer + offset + 20);
}
replen += 16;
break;
default:
logger(LOG_ERR, "Proxy reply malformed");
return -1;
}
if(c->buflen < replen) {
return 0;
} else {
ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Proxy request granted");
c->allow_request = ID;
c->status.proxy_passed = true;
send_id(c);
return replen;
}
case PROXY_HTTP: {
char *p = memchr(c->buffer, '\n', c->buflen);
if(!p || p - c->buffer >= c->buflen) {
return 0;
}
while((p = memchr(p + 1, '\n', c->buflen - (p + 1 - c->buffer)))) {
if(p > c->buffer + 3 && !memcmp(p - 3, "\r\n\r\n", 4)) {
break;
}
}
if(!p) {
return 0;
}
if(c->buflen < 9) {
return 0;
}
if(!strncasecmp(c->buffer, "HTTP/1.1 ", 9)) {
if(!strncmp(c->buffer + 9, "200", 3)) {
if(c->address.sa.sa_family == AF_UNKNOWN) {
update_address_ipv4(c, "\0\0\0\1", "\0\0");
}
logger(LOG_DEBUG, "Proxy request granted");
replen = p + 1 - c->buffer;
c->allow_request = ID;
c->status.proxy_passed = true;
send_id(c);
return replen;
} else {
p = memchr(c->buffer, '\n', c->buflen);
p[-1] = 0;
logger(LOG_ERR, "Proxy request rejected: %s", c->buffer + 9);
return false;
}
} else {
logger(LOG_ERR, "Proxy reply malformed");
return -1;
}
}
default:
abort();
}
}

View file

@ -1,7 +1,7 @@
/*
device.c -- raw socket
Copyright (C) 2002-2005 Ivo Timmermans,
2002-2014 Guus Sliepen <guus@tinc-vpn.org>
2002-2009 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,9 +20,7 @@
#include "system.h"
#ifdef HAVE_NETPACKET_PACKET_H
#include <netpacket/packet.h>
#endif
#include "conf.h"
#include "device.h"
@ -32,51 +30,49 @@
#include "route.h"
#include "xalloc.h"
#if defined(PF_PACKET) && defined(ETH_P_ALL) && defined(AF_PACKET) && defined(SIOCGIFINDEX)
static const char *device_info = "raw_socket";
int device_fd = -1;
char *device = NULL;
char *iface = NULL;
static char ifrname[IFNAMSIZ];
static char *device_info;
static uint64_t device_total_in = 0;
static uint64_t device_total_out = 0;
static bool setup_device(void) {
bool setup_device(void) {
struct ifreq ifr;
struct sockaddr_ll sa;
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) {
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
iface = xstrdup("eth0");
}
if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
if(!get_config_string(lookup_config(config_tree, "Device"), &device))
device = xstrdup(iface);
}
device_info = "raw socket";
if((device_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
logger(LOG_ERR, "Could not open %s: %s", device_info,
strerror(errno));
strerror(errno));
return false;
}
#ifdef FD_CLOEXEC
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
#endif
memset(&ifr, 0, sizeof(ifr));
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
close(device_fd);
logger(LOG_ERR, "Can't find interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
logger(LOG_ERR, "Can't find interface %s: %s", iface,
strerror(errno));
return false;
}
memset(&sa, 0, sizeof(sa));
memset(&sa, '0', sizeof sa);
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = ifr.ifr_ifindex;
if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
logger(LOG_ERR, "Could not bind %s to %s: %s", device, ifr.ifr_ifrn.ifrn_name, strerror(errno));
if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof sa)) {
logger(LOG_ERR, "Could not bind %s to %s: %s", device, iface, strerror(errno));
return false;
}
@ -85,39 +81,39 @@ static bool setup_device(void) {
return true;
}
static void close_device(void) {
void close_device(void) {
close(device_fd);
free(device);
free(iface);
}
static bool read_packet(vpn_packet_t *packet) {
int lenin;
bool read_packet(vpn_packet_t *packet) {
int inlen;
if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
device, strerror(errno));
device, strerror(errno));
return false;
}
packet->len = lenin;
packet->len = inlen;
device_total_in += packet->len;
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
device_info);
device_info);
return true;
}
static bool write_packet(vpn_packet_t *packet) {
bool write_packet(vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
packet->len, device_info);
packet->len, device_info);
if(write(device_fd, packet->data, packet->len) < 0) {
logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
strerror(errno));
strerror(errno));
return false;
}
@ -126,32 +122,8 @@ static bool write_packet(vpn_packet_t *packet) {
return true;
}
static void dump_device_stats(void) {
void dump_device_stats(void) {
logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
logger(LOG_DEBUG, " total bytes in: %10"PRIu64, device_total_in);
logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
}
const devops_t raw_socket_devops = {
.setup = setup_device,
.close = close_device,
.read = read_packet,
.write = write_packet,
.dump_stats = dump_device_stats,
};
#else
static bool not_supported(void) {
logger(LOG_ERR, "Raw socket device not supported on this platform");
return false;
}
const devops_t raw_socket_devops = {
.setup = not_supported,
.close = NULL,
.read = NULL,
.write = NULL,
.dump_stats = NULL,
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,7 @@
#ifndef TINC_ROUTE_H
#define TINC_ROUTE_H
/*
route.h -- header file for route.c
Copyright (C) 2000-2005 Ivo Timmermans
2000-2012 Guus Sliepen <guus@tinc-vpn.org>
2000-2006 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +18,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __TINC_ROUTE_H__
#define __TINC_ROUTE_H__
#include "net.h"
#include "node.h"
@ -36,24 +36,16 @@ typedef enum fmode_t {
FMODE_KERNEL,
} fmode_t;
typedef enum bmode_t {
BMODE_NONE = 0,
BMODE_MST,
BMODE_DIRECT,
} bmode_t;
extern rmode_t routing_mode;
extern fmode_t forwarding_mode;
extern bmode_t broadcast_mode;
extern bool decrement_ttl;
extern bool directonly;
extern bool overwrite_mac;
extern bool priorityinheritance;
extern int macexpire;
extern bool pcap;
extern mac_t mymac;
extern void age_subnets(void);
extern void route(struct node_t *source, struct vpn_packet_t *packet);
extern void route(struct node_t *, struct vpn_packet_t *);
#endif
#endif /* __TINC_ROUTE_H__ */

Some files were not shown because too many files have changed in this diff Show more