Import Upstream version 1.1~pre2
This commit is contained in:
parent
a04a7bcd67
commit
02de1cd2f1
172 changed files with 32291 additions and 25994 deletions
101
src/Makefile.am
101
src/Makefile.am
|
|
@ -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`
|
||||
|
|
|
|||
728
src/Makefile.in
728
src/Makefile.in
|
|
@ -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.
|
||||
|
|
|
|||
757
src/avl_tree.c
757
src/avl_tree.c
|
|
@ -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
|
||||
142
src/avl_tree.h
142
src/avl_tree.h
|
|
@ -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
|
||||
578
src/bsd/device.c
578
src/bsd/device.c
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
233
src/bsd/tunemu.c
233
src/bsd/tunemu.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
108
src/buffer.c
Normal 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
18
src/buffer.h
Normal 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
|
||||
410
src/conf.c
410
src/conf.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
52
src/conf.h
52
src/conf.h
|
|
@ -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__ */
|
||||
|
|
|
|||
143
src/connection.c
143
src/connection.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
135
src/connection.h
135
src/connection.h
|
|
@ -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
182
src/control.c
Normal 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
27
src/control.h
Normal 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
46
src/control_common.h
Normal 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
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
36
src/device.h
36
src/device.h
|
|
@ -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__ */
|
||||
|
|
|
|||
45
src/dropin.c
45
src/dropin.c
|
|
@ -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;
|
||||
|
|
|
|||
16
src/dropin.h
16
src/dropin.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
56
src/edge.c
56
src/edge.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
36
src/edge.h
36
src/edge.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
121
src/event.c
121
src/event.c
|
|
@ -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
19
src/fake-gai-errnos.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
280
src/gcrypt/cipher.c
Normal 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
52
src/gcrypt/cipher.h
Normal 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
34
src/gcrypt/crypto.c
Normal 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
27
src/gcrypt/crypto.h
Normal 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
173
src/gcrypt/digest.c
Normal 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
45
src/gcrypt/digest.h
Normal 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
302
src/gcrypt/rsa.c
Normal 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
39
src/gcrypt/rsa.h
Normal 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
220
src/gcrypt/rsagen.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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
|
||||
1321
src/getopt.c
1321
src/getopt.c
File diff suppressed because it is too large
Load diff
151
src/getopt.h
151
src/getopt.h
|
|
@ -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 */
|
||||
|
|
|
|||
182
src/getopt1.c
182
src/getopt1.c
|
|
@ -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 */
|
||||
|
|
|
|||
311
src/graph.c
311
src/graph.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
10
src/graph.h
10
src/graph.h
|
|
@ -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__ */
|
||||
|
|
|
|||
214
src/have.h
214
src/have.h
|
|
@ -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
|
||||
32
src/ipv4.h
32
src/ipv4.h
|
|
@ -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__ */
|
||||
|
|
|
|||
30
src/ipv6.h
30
src/ipv6.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
77
src/list.c
77
src/list.c
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
src/list.h
38
src/list.h
|
|
@ -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__ */
|
||||
|
|
|
|||
150
src/logger.c
150
src/logger.c
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
src/logger.h
47
src/logger.h
|
|
@ -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__ */
|
||||
|
|
|
|||
247
src/meta.c
247
src/meta.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
15
src/meta.h
15
src/meta.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
729
src/net.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
81
src/net.h
81
src/net.h
|
|
@ -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__ */
|
||||
|
|
|
|||
552
src/net_packet.c
552
src/net_packet.c
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1119
src/net_setup.c
1119
src/net_setup.c
File diff suppressed because it is too large
Load diff
487
src/net_socket.c
487
src/net_socket.c
|
|
@ -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);
|
||||
|
|
|
|||
177
src/netutl.c
177
src/netutl.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
37
src/netutl.h
37
src/netutl.h
|
|
@ -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__ */
|
||||
|
|
|
|||
133
src/node.c
133
src/node.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
117
src/node.h
117
src/node.h
|
|
@ -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
146
src/openssl/cipher.c
Normal 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
46
src/openssl/cipher.h
Normal 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
43
src/openssl/crypto.c
Normal 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
27
src/openssl/crypto.h
Normal 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
124
src/openssl/digest.c
Normal 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
45
src/openssl/digest.h
Normal 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
84
src/openssl/ecdh.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
130
src/openssl/ecdsa.c
Normal 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
37
src/openssl/ecdsa.h
Normal 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
75
src/openssl/ecdsagen.c
Normal 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
30
src/openssl/ecdsagen.h
Normal 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
76
src/openssl/prf.c
Normal 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
25
src/openssl/prf.h
Normal 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
101
src/openssl/rsa.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
83
src/openssl/rsagen.c
Normal 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
29
src/openssl/rsagen.h
Normal 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
|
||||
142
src/pidfile.c
142
src/pidfile.c
|
|
@ -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
|
||||
|
|
@ -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
|
||||
559
src/process.c
559
src/process.c
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
165
src/protocol.c
165
src/protocol.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
101
src/protocol.h
101
src/protocol.h
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
366
src/proxy.c
366
src/proxy.c
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
1055
src/route.c
1055
src/route.c
File diff suppressed because it is too large
Load diff
22
src/route.h
22
src/route.h
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue