summaryrefslogtreecommitdiff
path: root/src/exit
diff options
context:
space:
mode:
Diffstat (limited to 'src/exit')
-rw-r--r--src/exit/Makefile.am43
-rw-r--r--src/exit/Makefile.in714
-rw-r--r--src/exit/exit.conf47
-rw-r--r--src/exit/exit.h313
-rw-r--r--src/exit/gnunet-daemon-exit.c3240
-rw-r--r--src/exit/gnunet-helper-exit.c767
6 files changed, 5124 insertions, 0 deletions
diff --git a/src/exit/Makefile.am b/src/exit/Makefile.am
new file mode 100644
index 0000000..5a047a1
--- /dev/null
+++ b/src/exit/Makefile.am
@@ -0,0 +1,43 @@
+INCLUDES = -I$(top_srcdir)/src/include
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+endif
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+plugindir = $(libdir)/gnunet
+
+dist_pkgcfg_DATA = \
+ exit.conf
+
+if LINUX
+EXITBIN = gnunet-helper-exit
+install-exec-hook:
+ $(SUDO_BINARY) chown root:root $(bindir)/gnunet-helper-exit || true
+ $(SUDO_BINARY) chmod u+s $(bindir)/gnunet-helper-exit || true
+else
+install-exec-hook:
+endif
+
+
+bin_PROGRAMS = \
+ gnunet-daemon-exit $(EXITBIN)
+
+
+gnunet_helper_exit_SOURCES = \
+ gnunet-helper-exit.c
+
+gnunet_daemon_exit_SOURCES = \
+ gnunet-daemon-exit.c exit.h
+gnunet_daemon_exit_LDADD = \
+ $(top_builddir)/src/core/libgnunetcore.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(GN_LIBINTL)
diff --git a/src/exit/Makefile.in b/src/exit/Makefile.in
new file mode 100644
index 0000000..8e334e2
--- /dev/null
+++ b/src/exit/Makefile.in
@@ -0,0 +1,714 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# 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.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = gnunet-daemon-exit$(EXEEXT) $(am__EXEEXT_1)
+subdir = src/exit
+DIST_COMMON = $(dist_pkgcfg_DATA) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \
+ $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/gnunet_config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@LINUX_TRUE@am__EXEEXT_1 = gnunet-helper-exit$(EXEEXT)
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_gnunet_daemon_exit_OBJECTS = gnunet-daemon-exit.$(OBJEXT)
+gnunet_daemon_exit_OBJECTS = $(am_gnunet_daemon_exit_OBJECTS)
+am__DEPENDENCIES_1 =
+gnunet_daemon_exit_DEPENDENCIES = \
+ $(top_builddir)/src/core/libgnunetcore.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am_gnunet_helper_exit_OBJECTS = gnunet-helper-exit.$(OBJEXT)
+gnunet_helper_exit_OBJECTS = $(am_gnunet_helper_exit_OBJECTS)
+gnunet_helper_exit_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(gnunet_daemon_exit_SOURCES) $(gnunet_helper_exit_SOURCES)
+DIST_SOURCES = $(gnunet_daemon_exit_SOURCES) \
+ $(gnunet_helper_exit_SOURCES)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+DATA = $(dist_pkgcfg_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ARGZ_H = @ARGZ_H@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_INTERFACE = @DEFAULT_INTERFACE@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLDIR = @DLLDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXT_LIBS = @EXT_LIBS@
+EXT_LIB_PATH = @EXT_LIB_PATH@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUNETDNS_GROUP = @GNUNETDNS_GROUP@
+GN_DAEMON_CONFIG_DIR = @GN_DAEMON_CONFIG_DIR@
+GN_DAEMON_HOME_DIR = @GN_DAEMON_HOME_DIR@
+GN_INTLINCL = @GN_INTLINCL@
+GN_LIBINTL = @GN_LIBINTL@
+GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@
+GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@
+GN_USER_HOME_DIR = @GN_USER_HOME_DIR@
+GREP = @GREP@
+HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@
+INCLTDL = @INCLTDL@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBLTDL = @LIBLTDL@
+LIBOBJS = @LIBOBJS@
+LIBPREFIX = @LIBPREFIX@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTDLDEPS = @LTDLDEPS@
+LTDLINCL = @LTDLINCL@
+LTDLOPEN = @LTDLOPEN@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBUNISTRING = @LTLIBUNISTRING@
+LT_CONFIG_H = @LT_CONFIG_H@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LDFLAGS = @MYSQL_LDFLAGS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJC = @OBJC@
+OBJCDEPMODE = @OBJCDEPMODE@
+OBJCFLAGS = @OBJCFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@
+POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CPPFLAGS = @SQLITE_CPPFLAGS@
+SQLITE_LDFLAGS = @SQLITE_LDFLAGS@
+STRIP = @STRIP@
+SUDO_BINARY = @SUDO_BINARY@
+UNIXONLY = @UNIXONLY@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+_libcurl_config = @_libcurl_config@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_OBJC = @ac_ct_OBJC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_target = @build_target@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+ltdl_LIBOBJS = @ltdl_LIBOBJS@
+ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sys_symbol_underscore = @sys_symbol_underscore@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I$(top_srcdir)/src/include
+@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0
+pkgcfgdir = $(pkgdatadir)/config.d/
+plugindir = $(libdir)/gnunet
+dist_pkgcfg_DATA = \
+ exit.conf
+
+@LINUX_TRUE@EXITBIN = gnunet-helper-exit
+gnunet_helper_exit_SOURCES = \
+ gnunet-helper-exit.c
+
+gnunet_daemon_exit_SOURCES = \
+ gnunet-daemon-exit.c exit.h
+
+gnunet_daemon_exit_LDADD = \
+ $(top_builddir)/src/core/libgnunetcore.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(GN_LIBINTL)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/exit/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/exit/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__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)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ 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 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+gnunet-daemon-exit$(EXEEXT): $(gnunet_daemon_exit_OBJECTS) $(gnunet_daemon_exit_DEPENDENCIES)
+ @rm -f gnunet-daemon-exit$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_daemon_exit_OBJECTS) $(gnunet_daemon_exit_LDADD) $(LIBS)
+gnunet-helper-exit$(EXEEXT): $(gnunet_helper_exit_OBJECTS) $(gnunet_helper_exit_DEPENDENCIES)
+ @rm -f gnunet-helper-exit$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_helper_exit_OBJECTS) $(gnunet_helper_exit_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-daemon-exit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-helper-exit.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-dist_pkgcfgDATA: $(dist_pkgcfg_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)"
+ @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgcfgdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \
+ done
+
+uninstall-dist_pkgcfgDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files
+
+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`; \
+ 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; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+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
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(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:
+
+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)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_pkgcfgDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-dist_pkgcfgDATA
+
+.MAKE: install-am install-exec-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am \
+ install-dist_pkgcfgDATA install-dvi install-dvi-am \
+ install-exec install-exec-am install-exec-hook install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-dist_pkgcfgDATA
+
+@LINUX_TRUE@install-exec-hook:
+@LINUX_TRUE@ $(SUDO_BINARY) chown root:root $(bindir)/gnunet-helper-exit || true
+@LINUX_TRUE@ $(SUDO_BINARY) chmod u+s $(bindir)/gnunet-helper-exit || true
+@LINUX_FALSE@install-exec-hook:
+
+# 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.
+.NOEXPORT:
diff --git a/src/exit/exit.conf b/src/exit/exit.conf
new file mode 100644
index 0000000..0d48de9
--- /dev/null
+++ b/src/exit/exit.conf
@@ -0,0 +1,47 @@
+[exit]
+CONFIG = $DEFAULTCONFIG
+BINARY = gnunet-daemon-exit
+
+# IPv6 address for the TUN interface (must be changed as this
+# must be within the global IPv6 range of your system!)
+IPV6ADDR = 2001:DB8::1
+
+# Prefix for our IPv6 subnet on the TUN interface.
+IPV6PREFIX = 64
+
+# IPv4 address to use on our TUN interface (may need to be
+# changed to avoid conflicts with existing addresses on your system).
+# Use RFC 3927-style link-local address
+IPV4ADDR = 169.254.86.1
+
+# Netmask for the IPv4 subnet on the TUN interface.
+IPV4MASK = 255.255.255.0
+
+
+# Name of the (virtual) tunnel interface the exit daemon will manage
+TUN_IFNAME = exit-gnunet
+
+# Name of the "real" interface that IPv4 traffic from this system will
+# leave from; this is the name of the interface where we need to
+# enable NAT on postrouting (typically something like 'eth0' or 'eth1'
+# or 'wlan0'). Not needed if EXIT_IPv4 is disabled AND if all
+# offered services run on 'localhost'. In this case, the value
+# of the option can instead be set to "%" (to not enable NAT on any
+# interface).
+EXIT_IFNAME = eth0
+
+# Set this to YES to allow exiting this system via IPv4 to the Internet
+EXIT_IPV4 = NO
+
+# Set this to YES to allow exiting this system via IPv6 to the Internet
+EXIT_IPV6 = NO
+
+# For IPv4-services offered by this peer, we need to at least enable IPv4
+ENABLE_IPV4 = YES
+
+# For IPv6-services offered by this peer, we need to at least enable IPv6
+ENABLE_IPV6 = YES
+
+
+# Maximum number of concurrent connections this exit supports.
+MAX_CONNECTIONS = 256
diff --git a/src/exit/exit.h b/src/exit/exit.h
new file mode 100644
index 0000000..dcc50f1
--- /dev/null
+++ b/src/exit/exit.h
@@ -0,0 +1,313 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 Christian Grothoff
+
+ GNUnet 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, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file exit/exit.h
+ * @brief format for mesh messages exchanged between VPN service and exit daemon
+ * @author Christian Grothoff
+ */
+#ifndef EXIT_H
+#define EXIT_H
+
+#include "gnunet_util_lib.h"
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Message send via mesh to an exit daemon to initiate forwarding of
+ * TCP data to a local service.
+ */
+struct GNUNET_EXIT_TcpServiceStartMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_TCP_TO_SERVICE_START
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Always 0.
+ */
+ uint32_t reserved GNUNET_PACKED;
+
+ /**
+ * Identification for the desired service.
+ */
+ GNUNET_HashCode service_descriptor GNUNET_PACKED;
+
+ /**
+ * Skeleton of the TCP header to send. Port numbers are to
+ * be replaced and the checksum may be updated as necessary.
+ */
+ struct GNUNET_TUN_TcpHeader tcp_header;
+
+ /* followed by TCP payload */
+};
+
+
+/**
+ * Message send via mesh to an exit daemon to initiate forwarding of
+ * TCP data to the Internet.
+ */
+struct GNUNET_EXIT_TcpInternetStartMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_TCP_TO_INTERNET_START
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family, AF_INET or AF_INET6, in network byte order.
+ */
+ int32_t af GNUNET_PACKED;
+
+ /**
+ * Skeleton of the TCP header to send. Port numbers are to
+ * be replaced and the checksum may be updated as necessary.
+ */
+ struct GNUNET_TUN_TcpHeader tcp_header;
+
+ /* followed by IP address of the destination; either
+ 'struct in_addr' or 'struct in6_addr', depending on af */
+
+ /* followed by TCP payload */
+};
+
+
+/**
+ * Message send via mesh between VPN and entry and an exit daemon to
+ * transmit TCP data between the VPN entry and an exit session. This
+ * format is used for both Internet-exits and service-exits and
+ * in both directions (VPN to exit and exit to VPN).
+ */
+struct GNUNET_EXIT_TcpDataMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_TCP_DATA
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Always 0.
+ */
+ uint32_t reserved GNUNET_PACKED;
+
+ /**
+ * Skeleton of the TCP header to send. Port numbers are to
+ * be replaced and the checksum may be updated as necessary. (The destination port number should not be changed, as it contains the desired destination port.)
+ */
+ struct GNUNET_TUN_TcpHeader tcp_header;
+
+ /* followed by TCP payload */
+};
+
+
+/**
+ * Message send via mesh to an exit daemon to send
+ * UDP data to a local service.
+ */
+struct GNUNET_EXIT_UdpServiceMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_UDP_TO_SERVICE
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Source port to use for the UDP request (0 to use a random port). In NBO.
+ */
+ uint16_t source_port GNUNET_PACKED;
+
+ /**
+ * Destination port to use for the UDP request. In NBO.
+ */
+ uint16_t destination_port GNUNET_PACKED;
+
+ /**
+ * Identification for the desired service.
+ */
+ GNUNET_HashCode service_descriptor GNUNET_PACKED;
+
+ /* followed by UDP payload */
+};
+
+
+/**
+ * Message send via mesh to an exit daemon to forward
+ * UDP data to the Internet.
+ */
+struct GNUNET_EXIT_UdpInternetMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_UDP_TO_INTERNET
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family, AF_INET or AF_INET6, in network byte order.
+ */
+ int32_t af GNUNET_PACKED;
+
+ /**
+ * Source port to use for the UDP request (0 to use a random port). In NBO.
+ */
+ uint16_t source_port GNUNET_PACKED;
+
+ /**
+ * Destination port to use for the UDP request. In NBO.
+ */
+ uint16_t destination_port GNUNET_PACKED;
+
+ /* followed by IP address of the destination; either
+ 'struct in_addr' or 'struct in6_addr', depending on af */
+
+ /* followed by UDP payload */
+};
+
+
+/**
+ * Message send from exit daemon back to the UDP entry point
+ * (used for both Internet and Service exit replies).
+ */
+struct GNUNET_EXIT_UdpReplyMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_UDP_REPLY
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Source port to use for the UDP reply (0 to use the same
+ * port as for the original request). In NBO.
+ */
+ uint16_t source_port GNUNET_PACKED;
+
+ /**
+ * Destination port to use for the UDP reply (0 to use the same
+ * port as for the original request). In NBO.
+ */
+ uint16_t destination_port GNUNET_PACKED;
+
+ /* followed by UDP payload */
+};
+
+
+/**
+ * Message send via mesh to an exit daemon to send
+ * ICMP data to a local service.
+ */
+struct GNUNET_EXIT_IcmpServiceMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_SERVICE
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family, AF_INET or AF_INET6, in network byte order. This
+ * AF value determines if the 'icmp_header' is ICMPv4 or ICMPv6.
+ * The receiver (exit) may still have to translate (PT) to the services'
+ * ICMP version (if possible).
+ */
+ int32_t af GNUNET_PACKED;
+
+ /**
+ * Identification for the desired service.
+ */
+ GNUNET_HashCode service_descriptor GNUNET_PACKED;
+
+ /**
+ * ICMP header to use.
+ */
+ struct GNUNET_TUN_IcmpHeader icmp_header;
+
+ /* followed by ICMP payload; however, for certain ICMP message
+ types where the payload is the original IP packet, the payload
+ is omitted as it is useless for the receiver (who will need
+ to create some fake payload manually) */
+};
+
+
+/**
+ * Message send via mesh to an exit daemon to forward
+ * ICMP data to the Internet.
+ */
+struct GNUNET_EXIT_IcmpInternetMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_INTERNET
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family, AF_INET or AF_INET6, in network byte order.
+ * Determines both the ICMP version used in the 'icmp_header' and
+ * the IP address format that is used for the target IP. If
+ * PT is necessary, the sender has already done it.
+ */
+ int32_t af GNUNET_PACKED;
+
+ /**
+ * ICMP header to use. Must match the target 'af' given
+ * above.
+ */
+ struct GNUNET_TUN_IcmpHeader icmp_header;
+
+ /* followed by IP address of the destination; either
+ 'struct in_addr' or 'struct in6_addr', depending on af */
+
+ /* followed by ICMP payload; however, for certain ICMP message
+ types where the payload is the original IP packet, the payload
+ is omitted as it is useless for the receiver (who will need
+ to create some fake payload manually) */
+};
+
+
+/**
+ * Message send via mesh to the vpn service to send
+ * ICMP data to the VPN's TUN interface.
+ */
+struct GNUNET_EXIT_IcmpToVPNMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_VPN
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family, AF_INET or AF_INET6, in network byte order.
+ * Useful to determine if this is an ICMPv4 or ICMPv6 header.
+ */
+ int32_t af GNUNET_PACKED;
+
+ /**
+ * ICMP header to use. ICMPv4 or ICMPv6, depending on 'af'.
+ */
+ struct GNUNET_TUN_IcmpHeader icmp_header;
+
+ /* followed by ICMP payload; however, for certain ICMP message
+ types where the payload is the original IP packet, the payload
+ is omitted as it is useless for the receiver (who will need
+ to create some fake payload manually) */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c
new file mode 100644
index 0000000..3bf26d7
--- /dev/null
+++ b/src/exit/gnunet-daemon-exit.c
@@ -0,0 +1,3240 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010, 2012 Christian Grothoff
+
+ GNUnet 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, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file exit/gnunet-daemon-exit.c
+ * @brief tool to allow IP traffic exit from the GNUnet mesh to the Internet
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ *
+ * TODO:
+ * - test
+ *
+ * Design:
+ * - which code should advertise services? the service model is right
+ * now a bit odd, especially as this code DOES the exit and knows
+ * the DNS "name", but OTOH this is clearly NOT the place to advertise
+ * the service's existence; maybe the daemon should turn into a
+ * service with an API to add local-exit services dynamically?
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_applications.h"
+#include "gnunet_mesh_service.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_constants.h"
+#include "gnunet_tun_lib.h"
+#include "exit.h"
+
+/**
+ * Information about an address.
+ */
+struct SocketAddress
+{
+ /**
+ * AF_INET or AF_INET6.
+ */
+ int af;
+
+ /**
+ * Remote address information.
+ */
+ union
+ {
+ /**
+ * Address, if af is AF_INET.
+ */
+ struct in_addr ipv4;
+
+ /**
+ * Address, if af is AF_INET6.
+ */
+ struct in6_addr ipv6;
+ } address;
+
+ /**
+ * IPPROTO_TCP or IPPROTO_UDP;
+ */
+ uint8_t proto;
+
+ /**
+ * Remote port, in host byte order!
+ */
+ uint16_t port;
+
+};
+
+/**
+ * This struct is saved into the services-hashmap to represent
+ * a service this peer is specifically offering an exit for
+ * (for a specific domain name).
+ */
+struct LocalService
+{
+
+ /**
+ * Remote address to use for the service.
+ */
+ struct SocketAddress address;
+
+ /**
+ * DNS name of the service.
+ */
+ char *name;
+
+ /**
+ * Port I am listening on within GNUnet for this service, in host
+ * byte order. (as we may redirect ports).
+ */
+ uint16_t my_port;
+
+};
+
+/**
+ * Information we use to track a connection (the classical 6-tuple of
+ * IP-version, protocol, source-IP, destination-IP, source-port and
+ * destinatin-port.
+ */
+struct RedirectInformation
+{
+
+ /**
+ * Address information for the other party (equivalent of the
+ * arguments one would give to "connect").
+ */
+ struct SocketAddress remote_address;
+
+ /**
+ * Address information we used locally (AF and proto must match
+ * "remote_address"). Equivalent of the arguments one would give to
+ * "bind".
+ */
+ struct SocketAddress local_address;
+
+ /*
+ Note 1: additional information might be added here in the
+ future to support protocols that require special handling,
+ such as ftp/tftp
+
+ Note 2: we might also sometimes not match on all components
+ of the tuple, to support protocols where things do not always
+ fully map.
+ */
+};
+
+
+/**
+ * Queue of messages to a tunnel.
+ */
+struct TunnelMessageQueue
+{
+ /**
+ * This is a doubly-linked list.
+ */
+ struct TunnelMessageQueue *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct TunnelMessageQueue *prev;
+
+ /**
+ * Payload to send via the tunnel.
+ */
+ const void *payload;
+
+ /**
+ * Number of bytes in 'payload'.
+ */
+ size_t len;
+};
+
+
+/**
+ * This struct is saved into connections_map to allow finding the
+ * right tunnel given an IP packet from TUN. It is also associated
+ * with the tunnel's closure so we can find it again for the next
+ * message from the tunnel.
+ */
+struct TunnelState
+{
+ /**
+ * Mesh tunnel that is used for this connection.
+ */
+ struct GNUNET_MESH_Tunnel *tunnel;
+
+ /**
+ * Heap node for this state in the connections_heap.
+ */
+ struct GNUNET_CONTAINER_HeapNode *heap_node;
+
+ /**
+ * Key this state has in the connections_map.
+ */
+ GNUNET_HashCode state_key;
+
+ /**
+ * Associated service record, or NULL for no service.
+ */
+ struct LocalService *serv;
+
+ /**
+ * Head of DLL of messages for this tunnel.
+ */
+ struct TunnelMessageQueue *head;
+
+ /**
+ * Tail of DLL of messages for this tunnel.
+ */
+ struct TunnelMessageQueue *tail;
+
+ /**
+ * Active tunnel transmission request (or NULL).
+ */
+ struct GNUNET_MESH_TransmitHandle *th;
+
+ /**
+ * Primary redirection information for this connection.
+ */
+ struct RedirectInformation ri;
+
+};
+
+
+/**
+ * Return value from 'main'.
+ */
+static int global_ret;
+
+/**
+ * The handle to the configuration used throughout the process
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * The handle to the helper
+ */
+static struct GNUNET_HELPER_Handle *helper_handle;
+
+/**
+ * Arguments to the exit helper.
+ */
+static char *exit_argv[8];
+
+/**
+ * IPv6 address of our TUN interface.
+ */
+static struct in6_addr exit_ipv6addr;
+
+/**
+ * IPv6 prefix (0..127) from configuration file.
+ */
+static unsigned long long ipv6prefix;
+
+/**
+ * IPv4 address of our TUN interface.
+ */
+static struct in_addr exit_ipv4addr;
+
+/**
+ * IPv4 netmask of our TUN interface.
+ */
+static struct in_addr exit_ipv4mask;
+
+
+/**
+ * Statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * The handle to mesh
+ */
+static struct GNUNET_MESH_Handle *mesh_handle;
+
+/**
+ * This hashmaps contains the mapping from peer, service-descriptor,
+ * source-port and destination-port to a struct TunnelState
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *connections_map;
+
+/**
+ * Heap so we can quickly find "old" connections.
+ */
+static struct GNUNET_CONTAINER_Heap *connections_heap;
+
+/**
+ * If there are at least this many connections, old ones will be removed
+ */
+static long long unsigned int max_connections;
+
+/**
+ * This hashmaps saves interesting things about the configured UDP services
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *udp_services;
+
+/**
+ * This hashmaps saves interesting things about the configured TCP services
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *tcp_services;
+
+/**
+ * Are we an IPv4-exit?
+ */
+static int ipv4_exit;
+
+/**
+ * Are we an IPv6-exit?
+ */
+static int ipv6_exit;
+
+/**
+ * Do we support IPv4 at all on the TUN interface?
+ */
+static int ipv4_enabled;
+
+/**
+ * Do we support IPv6 at all on the TUN interface?
+ */
+static int ipv6_enabled;
+
+
+/**
+ * Given IP information about a connection, calculate the respective
+ * hash we would use for the 'connections_map'.
+ *
+ * @param hash resulting hash
+ * @param ri information about the connection
+ */
+static void
+hash_redirect_info (GNUNET_HashCode *hash,
+ const struct RedirectInformation *ri)
+{
+ char *off;
+
+ memset (hash, 0, sizeof (GNUNET_HashCode));
+ /* the GNUnet hashmap only uses the first sizeof(unsigned int) of the hash,
+ so we put the IP address in there (and hope for few collisions) */
+ off = (char*) hash;
+ switch (ri->remote_address.af)
+ {
+ case AF_INET:
+ memcpy (off, &ri->remote_address.address.ipv4, sizeof (struct in_addr));
+ off += sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ memcpy (off, &ri->remote_address.address.ipv6, sizeof (struct in6_addr));
+ off += sizeof (struct in_addr);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ memcpy (off, &ri->remote_address.port, sizeof (uint16_t));
+ off += sizeof (uint16_t);
+ switch (ri->local_address.af)
+ {
+ case AF_INET:
+ memcpy (off, &ri->local_address.address.ipv4, sizeof (struct in_addr));
+ off += sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ memcpy (off, &ri->local_address.address.ipv6, sizeof (struct in6_addr));
+ off += sizeof (struct in_addr);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ memcpy (off, &ri->local_address.port, sizeof (uint16_t));
+ off += sizeof (uint16_t);
+ memcpy (off, &ri->remote_address.proto, sizeof (uint8_t));
+ off += sizeof (uint8_t);
+}
+
+
+/**
+ * Get our connection tracking state. Warns if it does not exists,
+ * refreshes the timestamp if it does exist.
+ *
+ * @param af address family
+ * @param protocol IPPROTO_UDP or IPPROTO_TCP
+ * @param destination_ip target IP
+ * @param destination_port target port
+ * @param local_ip local IP
+ * @param local_port local port
+ * @param state_key set to hash's state if non-NULL
+ * @return NULL if we have no tracking information for this tuple
+ */
+static struct TunnelState *
+get_redirect_state (int af,
+ int protocol,
+ const void *destination_ip,
+ uint16_t destination_port,
+ const void *local_ip,
+ uint16_t local_port,
+ GNUNET_HashCode *state_key)
+{
+ struct RedirectInformation ri;
+ GNUNET_HashCode key;
+ struct TunnelState *state;
+
+ if ( ( (af == AF_INET) && (protocol == IPPROTO_ICMP) ) ||
+ ( (af == AF_INET6) && (protocol == IPPROTO_ICMPV6) ) )
+ {
+ /* ignore ports */
+ destination_port = 0;
+ local_port = 0;
+ }
+ ri.remote_address.af = af;
+ if (af == AF_INET)
+ ri.remote_address.address.ipv4 = *((struct in_addr*) destination_ip);
+ else
+ ri.remote_address.address.ipv6 = * ((struct in6_addr*) destination_ip);
+ ri.remote_address.port = destination_port;
+ ri.remote_address.proto = protocol;
+ ri.local_address.af = af;
+ if (af == AF_INET)
+ ri.local_address.address.ipv4 = *((struct in_addr*) local_ip);
+ else
+ ri.local_address.address.ipv6 = * ((struct in6_addr*) local_ip);
+ ri.local_address.port = local_port;
+ ri.local_address.proto = protocol;
+ hash_redirect_info (&key, &ri);
+ if (NULL != state_key)
+ *state_key = key;
+ state = GNUNET_CONTAINER_multihashmap_get (connections_map, &key);
+ if (NULL == state)
+ return NULL;
+ /* Mark this connection as freshly used */
+ if (NULL == state_key)
+ GNUNET_CONTAINER_heap_update_cost (connections_heap,
+ state->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
+ return state;
+}
+
+
+/**
+ * Given a service descriptor and a destination port, find the
+ * respective service entry.
+ *
+ * @param service_map map of services (TCP or UDP)
+ * @param desc service descriptor
+ * @param destination_port destination port
+ * @return NULL if we are not aware of such a service
+ */
+static struct LocalService *
+find_service (struct GNUNET_CONTAINER_MultiHashMap *service_map,
+ const GNUNET_HashCode *desc,
+ uint16_t destination_port)
+{
+ char key[sizeof (GNUNET_HashCode) + sizeof (uint16_t)];
+
+ memcpy (&key[0], &destination_port, sizeof (uint16_t));
+ memcpy (&key[sizeof(uint16_t)], desc, sizeof (GNUNET_HashCode));
+ return GNUNET_CONTAINER_multihashmap_get (service_map,
+ (GNUNET_HashCode *) key);
+}
+
+
+/**
+ * Free memory associated with a service record.
+ *
+ * @param cls unused
+ * @param key service descriptor
+ * @param value service record to free
+ * @return GNUNET_OK
+ */
+static int
+free_service_record (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct LocalService *service = value;
+
+ GNUNET_free_non_null (service->name);
+ GNUNET_free (service);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Given a service descriptor and a destination port, find the
+ * respective service entry.
+ *
+ * @param service_map map of services (TCP or UDP)
+ * @param name name of the service
+ * @param destination_port destination port
+ * @param service service information record to store (service->name will be set).
+ */
+static void
+store_service (struct GNUNET_CONTAINER_MultiHashMap *service_map,
+ const char *name,
+ uint16_t destination_port,
+ struct LocalService *service)
+{
+ char key[sizeof (GNUNET_HashCode) + sizeof (uint16_t)];
+ GNUNET_HashCode desc;
+
+ GNUNET_CRYPTO_hash (name, strlen (name) + 1, &desc);
+ service->name = GNUNET_strdup (name);
+ memcpy (&key[0], &destination_port, sizeof (uint16_t));
+ memcpy (&key[sizeof(uint16_t)], &desc, sizeof (GNUNET_HashCode));
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_multihashmap_put (service_map,
+ (GNUNET_HashCode *) key,
+ service,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ free_service_record (NULL, (GNUNET_HashCode *) key, service);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Got duplicate service records for `%s:%u'\n"),
+ name,
+ (unsigned int) destination_port);
+ }
+}
+
+
+/**
+ * MESH is ready to receive a message for the tunnel. Transmit it.
+ *
+ * @param cls the 'struct TunnelState'.
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message
+ * @return number of bytes copied to buf
+ */
+static size_t
+send_to_peer_notify_callback (void *cls, size_t size, void *buf)
+{
+ struct TunnelState *s = cls;
+ struct GNUNET_MESH_Tunnel *tunnel = s->tunnel;
+ struct TunnelMessageQueue *tnq;
+
+ s->th = NULL;
+ tnq = s->head;
+ if (NULL == tnq)
+ return 0;
+ if (0 == size)
+ {
+ s->th = GNUNET_MESH_notify_transmit_ready (tunnel,
+ GNUNET_NO /* corking */,
+ 0 /* priority */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL,
+ tnq->len,
+ &send_to_peer_notify_callback,
+ s);
+ return 0;
+ }
+ GNUNET_assert (size >= tnq->len);
+ memcpy (buf, tnq->payload, tnq->len);
+ size = tnq->len;
+ GNUNET_CONTAINER_DLL_remove (s->head,
+ s->tail,
+ tnq);
+ GNUNET_free (tnq);
+ if (NULL != (tnq = s->head))
+ s->th = GNUNET_MESH_notify_transmit_ready (tunnel,
+ GNUNET_NO /* corking */,
+ 0 /* priority */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL,
+ tnq->len,
+ &send_to_peer_notify_callback,
+ s);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes transmitted via mesh tunnels"),
+ size, GNUNET_NO);
+ return size;
+}
+
+
+/**
+ * Send the given packet via the mesh tunnel.
+ *
+ * @param mesh_tunnel destination
+ * @param tnq message to queue
+ */
+static void
+send_packet_to_mesh_tunnel (struct GNUNET_MESH_Tunnel *mesh_tunnel,
+ struct TunnelMessageQueue *tnq)
+{
+ struct TunnelState *s;
+
+ s = GNUNET_MESH_tunnel_get_data (mesh_tunnel);
+ GNUNET_assert (NULL != s);
+ GNUNET_CONTAINER_DLL_insert_tail (s->head, s->tail, tnq);
+ if (NULL == s->th)
+ s->th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel, GNUNET_NO /* cork */, 0 /* priority */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL, tnq->len,
+ &send_to_peer_notify_callback,
+ s);
+}
+
+
+/**
+ * @brief Handles an ICMP packet received from the helper.
+ *
+ * @param icmp A pointer to the Packet
+ * @param pktlen number of bytes in 'icmp'
+ * @param af address family (AFINET or AF_INET6)
+ * @param destination_ip destination IP-address of the IP packet (should
+ * be our local address)
+ * @param source_ip original source IP-address of the IP packet (should
+ * be the original destination address)
+ */
+static void
+icmp_from_helper (const struct GNUNET_TUN_IcmpHeader *icmp,
+ size_t pktlen,
+ int af,
+ const void *destination_ip,
+ const void *source_ip)
+{
+ struct TunnelState *state;
+ struct TunnelMessageQueue *tnq;
+ struct GNUNET_EXIT_IcmpToVPNMessage *i2v;
+ const struct GNUNET_TUN_IPv4Header *ipv4;
+ const struct GNUNET_TUN_IPv6Header *ipv6;
+ const struct GNUNET_TUN_UdpHeader *udp;
+ size_t mlen;
+ uint16_t source_port;
+ uint16_t destination_port;
+ uint8_t protocol;
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received ICMP packet going from %s to %s\n",
+ inet_ntop (af,
+ source_ip,
+ sbuf, sizeof (sbuf)),
+ inet_ntop (af,
+ destination_ip,
+ dbuf, sizeof (dbuf)));
+ }
+ if (pktlen < sizeof (struct GNUNET_TUN_IcmpHeader))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+
+ /* Find out if this is an ICMP packet in response to an existing
+ TCP/UDP packet and if so, figure out ports / protocol of the
+ existing session from the IP data in the ICMP payload */
+ source_port = 0;
+ destination_port = 0;
+ switch (af)
+ {
+ case AF_INET:
+ protocol = IPPROTO_ICMP;
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ if (pktlen <
+ sizeof (struct GNUNET_TUN_IcmpHeader) +
+ sizeof (struct GNUNET_TUN_IPv4Header) + 8)
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ ipv4 = (const struct GNUNET_TUN_IPv4Header *) &icmp[1];
+ protocol = ipv4->protocol;
+ /* could be TCP or UDP, but both have the ports in the right
+ place, so that doesn't matter here */
+ udp = (const struct GNUNET_TUN_UdpHeader *) &ipv4[1];
+ /* swap ports, as they are from the original message */
+ destination_port = ntohs (udp->source_port);
+ source_port = ntohs (udp->destination_port);
+ /* throw away ICMP payload, won't be useful for the other side anyway */
+ pktlen = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return;
+ }
+ break;
+ case AF_INET6:
+ protocol = IPPROTO_ICMPV6;
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ if (pktlen <
+ sizeof (struct GNUNET_TUN_IcmpHeader) +
+ sizeof (struct GNUNET_TUN_IPv6Header) + 8)
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ ipv6 = (const struct GNUNET_TUN_IPv6Header *) &icmp[1];
+ protocol = ipv6->next_header;
+ /* could be TCP or UDP, but both have the ports in the right
+ place, so that doesn't matter here */
+ udp = (const struct GNUNET_TUN_UdpHeader *) &ipv6[1];
+ /* swap ports, as they are from the original message */
+ destination_port = ntohs (udp->source_port);
+ source_port = ntohs (udp->destination_port);
+ /* throw away ICMP payload, won't be useful for the other side anyway */
+ pktlen = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ switch (protocol)
+ {
+ case IPPROTO_ICMP:
+ state = get_redirect_state (af, IPPROTO_ICMP,
+ source_ip, 0,
+ destination_ip, 0,
+ NULL);
+ break;
+ case IPPROTO_ICMPV6:
+ state = get_redirect_state (af, IPPROTO_ICMPV6,
+ source_ip, 0,
+ destination_ip, 0,
+ NULL);
+ break;
+ case IPPROTO_UDP:
+ state = get_redirect_state (af, IPPROTO_UDP,
+ source_ip,
+ source_port,
+ destination_ip,
+ destination_port,
+ NULL);
+ break;
+ case IPPROTO_TCP:
+ state = get_redirect_state (af, IPPROTO_TCP,
+ source_ip,
+ source_port,
+ destination_ip,
+ destination_port,
+ NULL);
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP packets dropped (not allowed)"),
+ 1, GNUNET_NO);
+ return;
+ }
+ if (NULL == state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("ICMP Packet dropped, have no matching connection information\n"));
+ return;
+ }
+ mlen = sizeof (struct GNUNET_EXIT_IcmpToVPNMessage) + pktlen - sizeof (struct GNUNET_TUN_IcmpHeader);
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueue) + mlen);
+ tnq->payload = &tnq[1];
+ tnq->len = mlen;
+ i2v = (struct GNUNET_EXIT_IcmpToVPNMessage *) &tnq[1];
+ i2v->header.size = htons ((uint16_t) mlen);
+ i2v->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_VPN);
+ i2v->af = htonl (af);
+ memcpy (&i2v->icmp_header,
+ icmp,
+ pktlen);
+ send_packet_to_mesh_tunnel (state->tunnel,
+ tnq);
+}
+
+
+/**
+ * @brief Handles an UDP packet received from the helper.
+ *
+ * @param udp A pointer to the Packet
+ * @param pktlen number of bytes in 'udp'
+ * @param af address family (AFINET or AF_INET6)
+ * @param destination_ip destination IP-address of the IP packet (should
+ * be our local address)
+ * @param source_ip original source IP-address of the IP packet (should
+ * be the original destination address)
+ */
+static void
+udp_from_helper (const struct GNUNET_TUN_UdpHeader *udp,
+ size_t pktlen,
+ int af,
+ const void *destination_ip,
+ const void *source_ip)
+{
+ struct TunnelState *state;
+ struct TunnelMessageQueue *tnq;
+ struct GNUNET_EXIT_UdpReplyMessage *urm;
+ size_t mlen;
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received UDP packet going from %s:%u to %s:%u\n",
+ inet_ntop (af,
+ source_ip,
+ sbuf, sizeof (sbuf)),
+ (unsigned int) ntohs (udp->source_port),
+ inet_ntop (af,
+ destination_ip,
+ dbuf, sizeof (dbuf)),
+ (unsigned int) ntohs (udp->destination_port));
+ }
+ if (pktlen < sizeof (struct GNUNET_TUN_UdpHeader))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ if (pktlen != ntohs (udp->len))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ state = get_redirect_state (af, IPPROTO_UDP,
+ source_ip,
+ ntohs (udp->source_port),
+ destination_ip,
+ ntohs (udp->destination_port),
+ NULL);
+ if (NULL == state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("UDP Packet dropped, have no matching connection information\n"));
+ return;
+ }
+ mlen = sizeof (struct GNUNET_EXIT_UdpReplyMessage) + pktlen - sizeof (struct GNUNET_TUN_UdpHeader);
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueue) + mlen);
+ tnq->payload = &tnq[1];
+ tnq->len = mlen;
+ urm = (struct GNUNET_EXIT_UdpReplyMessage *) &tnq[1];
+ urm->header.size = htons ((uint16_t) mlen);
+ urm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_UDP_REPLY);
+ urm->source_port = htons (0);
+ urm->destination_port = htons (0);
+ memcpy (&urm[1],
+ &udp[1],
+ pktlen - sizeof (struct GNUNET_TUN_UdpHeader));
+ send_packet_to_mesh_tunnel (state->tunnel,
+ tnq);
+}
+
+
+/**
+ * @brief Handles a TCP packet received from the helper.
+ *
+ * @param tcp A pointer to the Packet
+ * @param pktlen the length of the packet, including its TCP header
+ * @param af address family (AFINET or AF_INET6)
+ * @param destination_ip destination IP-address of the IP packet (should
+ * be our local address)
+ * @param source_ip original source IP-address of the IP packet (should
+ * be the original destination address)
+ */
+static void
+tcp_from_helper (const struct GNUNET_TUN_TcpHeader *tcp,
+ size_t pktlen,
+ int af,
+ const void *destination_ip,
+ const void *source_ip)
+{
+ struct TunnelState *state;
+ char buf[pktlen];
+ struct GNUNET_TUN_TcpHeader *mtcp;
+ struct GNUNET_EXIT_TcpDataMessage *tdm;
+ struct TunnelMessageQueue *tnq;
+ size_t mlen;
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received TCP packet with %u bytes going from %s:%u to %s:%u\n",
+ pktlen - sizeof (struct GNUNET_TUN_TcpHeader),
+ inet_ntop (af,
+ source_ip,
+ sbuf, sizeof (sbuf)),
+ (unsigned int) ntohs (tcp->source_port),
+ inet_ntop (af,
+ destination_ip,
+ dbuf, sizeof (dbuf)),
+ (unsigned int) ntohs (tcp->destination_port));
+ }
+ if (pktlen < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ state = get_redirect_state (af, IPPROTO_TCP,
+ source_ip,
+ ntohs (tcp->source_port),
+ destination_ip,
+ ntohs (tcp->destination_port),
+ NULL);
+ if (NULL == state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("TCP Packet dropped, have no matching connection information\n"));
+
+ return;
+ }
+ /* mug port numbers and crc to avoid information leakage;
+ sender will need to lookup the correct values anyway */
+ memcpy (buf, tcp, pktlen);
+ mtcp = (struct GNUNET_TUN_TcpHeader *) buf;
+ mtcp->source_port = 0;
+ mtcp->destination_port = 0;
+ mtcp->crc = 0;
+
+ mlen = sizeof (struct GNUNET_EXIT_TcpDataMessage) + (pktlen - sizeof (struct GNUNET_TUN_TcpHeader));
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueue) + mlen);
+ tnq->payload = &tnq[1];
+ tnq->len = mlen;
+ tdm = (struct GNUNET_EXIT_TcpDataMessage *) &tnq[1];
+ tdm->header.size = htons ((uint16_t) mlen);
+ tdm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_VPN);
+ tdm->reserved = htonl (0);
+ memcpy (&tdm->tcp_header,
+ buf,
+ pktlen);
+ send_packet_to_mesh_tunnel (state->tunnel,
+ tnq);
+}
+
+
+/**
+ * Receive packets from the helper-process
+ *
+ * @param cls unused
+ * @param client unsued
+ * @param message message received from helper
+ */
+static void
+message_token (void *cls GNUNET_UNUSED, void *client GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct GNUNET_TUN_Layer2PacketHeader *pkt_tun;
+ size_t size;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got %u-byte message of type %u from gnunet-helper-exit\n",
+ ntohs (message->size),
+ ntohs (message->type));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Packets received from TUN"),
+ 1, GNUNET_NO);
+ if (ntohs (message->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ size = ntohs (message->size);
+ if (size < sizeof (struct GNUNET_TUN_Layer2PacketHeader) + sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from TUN"),
+ size, GNUNET_NO);
+ pkt_tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
+ size -= sizeof (struct GNUNET_TUN_Layer2PacketHeader) + sizeof (struct GNUNET_MessageHeader);
+ switch (ntohs (pkt_tun->proto))
+ {
+ case ETH_P_IPV4:
+ {
+ const struct GNUNET_TUN_IPv4Header *pkt4;
+
+ if (size < sizeof (struct GNUNET_TUN_IPv4Header))
+ {
+ /* Kernel to blame? */
+ GNUNET_break (0);
+ return;
+ }
+ pkt4 = (const struct GNUNET_TUN_IPv4Header *) &pkt_tun[1];
+ if (size != ntohs (pkt4->total_length))
+ {
+ /* Kernel to blame? */
+ GNUNET_break (0);
+ return;
+ }
+ if (pkt4->header_length * 4 != sizeof (struct GNUNET_TUN_IPv4Header))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("IPv4 packet options received. Ignored.\n"));
+ return;
+ }
+
+ size -= sizeof (struct GNUNET_TUN_IPv4Header);
+ switch (pkt4->protocol)
+ {
+ case IPPROTO_UDP:
+ udp_from_helper ((const struct GNUNET_TUN_UdpHeader *) &pkt4[1], size,
+ AF_INET,
+ &pkt4->destination_address,
+ &pkt4->source_address);
+ break;
+ case IPPROTO_TCP:
+ tcp_from_helper ((const struct GNUNET_TUN_TcpHeader *) &pkt4[1], size,
+ AF_INET,
+ &pkt4->destination_address,
+ &pkt4->source_address);
+ break;
+ case IPPROTO_ICMP:
+ icmp_from_helper ((const struct GNUNET_TUN_IcmpHeader *) &pkt4[1], size,
+ AF_INET,
+ &pkt4->destination_address,
+ &pkt4->source_address);
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("IPv4 packet with unsupported next header %u received. Ignored.\n"),
+ (int) pkt4->protocol);
+ return;
+ }
+ }
+ break;
+ case ETH_P_IPV6:
+ {
+ const struct GNUNET_TUN_IPv6Header *pkt6;
+
+ if (size < sizeof (struct GNUNET_TUN_IPv6Header))
+ {
+ /* Kernel to blame? */
+ GNUNET_break (0);
+ return;
+ }
+ pkt6 = (struct GNUNET_TUN_IPv6Header *) &pkt_tun[1];
+ if (size != ntohs (pkt6->payload_length) + sizeof (struct GNUNET_TUN_IPv6Header))
+ {
+ /* Kernel to blame? */
+ GNUNET_break (0);
+ return;
+ }
+ size -= sizeof (struct GNUNET_TUN_IPv6Header);
+ switch (pkt6->next_header)
+ {
+ case IPPROTO_UDP:
+ udp_from_helper ((const struct GNUNET_TUN_UdpHeader *) &pkt6[1], size,
+ AF_INET6,
+ &pkt6->destination_address,
+ &pkt6->source_address);
+ break;
+ case IPPROTO_TCP:
+ tcp_from_helper ((const struct GNUNET_TUN_TcpHeader *) &pkt6[1], size,
+ AF_INET6,
+ &pkt6->destination_address,
+ &pkt6->source_address);
+ break;
+ case IPPROTO_ICMPV6:
+ icmp_from_helper ((const struct GNUNET_TUN_IcmpHeader *) &pkt6[1], size,
+ AF_INET6,
+ &pkt6->destination_address,
+ &pkt6->source_address);
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("IPv6 packet with unsupported next header %d received. Ignored.\n"),
+ pkt6->next_header);
+ return;
+ }
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Packet from unknown protocol %u received. Ignored.\n"),
+ ntohs (pkt_tun->proto));
+ break;
+ }
+}
+
+
+/**
+ * We need to create a (unique) fresh local address (IP+port).
+ * Fill one in.
+ *
+ * @param af desired address family
+ * @param proto desired protocol (IPPROTO_UDP or IPPROTO_TCP)
+ * @param local_address address to initialize
+ */
+static void
+setup_fresh_address (int af,
+ uint8_t proto,
+ struct SocketAddress *local_address)
+{
+ local_address->af = af;
+ local_address->proto = (uint8_t) proto;
+ /* default "local" port range is often 32768--61000,
+ so we pick a random value in that range */
+ if ( ( (af == AF_INET) && (proto == IPPROTO_ICMP) ) ||
+ ( (af == AF_INET6) && (proto == IPPROTO_ICMPV6) ) )
+ local_address->port = 0;
+ else
+ local_address->port
+ = (uint16_t) 32768 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ 28232);
+ switch (af)
+ {
+ case AF_INET:
+ {
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr rnd;
+
+ addr = exit_ipv4addr;
+ mask = exit_ipv4mask;
+ if (0 == ~mask.s_addr)
+ {
+ /* only one valid IP anyway */
+ local_address->address.ipv4 = addr;
+ return;
+ }
+ /* Given 192.168.0.1/255.255.0.0, we want a mask
+ of '192.168.255.255', thus: */
+ mask.s_addr = addr.s_addr | ~mask.s_addr;
+ /* Pick random IPv4 address within the subnet, except 'addr' or 'mask' itself */
+ do
+ {
+ rnd.s_addr = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT32_MAX);
+ local_address->address.ipv4.s_addr = (addr.s_addr | rnd.s_addr) & mask.s_addr;
+ }
+ while ( (local_address->address.ipv4.s_addr == addr.s_addr) ||
+ (local_address->address.ipv4.s_addr == mask.s_addr) );
+ }
+ break;
+ case AF_INET6:
+ {
+ struct in6_addr addr;
+ struct in6_addr mask;
+ struct in6_addr rnd;
+ int i;
+
+ addr = exit_ipv6addr;
+ GNUNET_assert (ipv6prefix < 128);
+ if (ipv6prefix == 127)
+ {
+ /* only one valid IP anyway */
+ local_address->address.ipv6 = addr;
+ return;
+ }
+ /* Given ABCD::/96, we want a mask of 'ABCD::FFFF:FFFF,
+ thus: */
+ mask = addr;
+ for (i=127;i>=ipv6prefix;i--)
+ mask.s6_addr[i / 8] |= (1 << (i % 8));
+
+ /* Pick random IPv6 address within the subnet, except 'addr' or 'mask' itself */
+ do
+ {
+ for (i=0;i<16;i++)
+ {
+ rnd.s6_addr[i] = (unsigned char) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ 256);
+ local_address->address.ipv6.s6_addr[i]
+ = (addr.s6_addr[i] | rnd.s6_addr[i]) & mask.s6_addr[i];
+ }
+ }
+ while ( (0 == memcmp (&local_address->address.ipv6,
+ &addr,
+ sizeof (struct in6_addr))) ||
+ (0 == memcmp (&local_address->address.ipv6,
+ &mask,
+ sizeof (struct in6_addr))) );
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * We are starting a fresh connection (TCP or UDP) and need
+ * to pick a source port and IP address (within the correct
+ * range and address family) to associate replies with the
+ * connection / correct mesh tunnel. This function generates
+ * a "fresh" source IP and source port number for a connection
+ * After picking a good source address, this function sets up
+ * the state in the 'connections_map' and 'connections_heap'
+ * to allow finding the state when needed later. The function
+ * also makes sure that we remain within memory limits by
+ * cleaning up 'old' states.
+ *
+ * @param state skeleton state to setup a record for; should
+ * 'state->ri.remote_address' filled in so that
+ * this code can determine which AF/protocol is
+ * going to be used (the 'tunnel' should also
+ * already be set); after calling this function,
+ * heap_node and the local_address will be
+ * also initialized (heap_node != NULL can be
+ * used to test if a state has been fully setup).
+ */
+static void
+setup_state_record (struct TunnelState *state)
+{
+ GNUNET_HashCode key;
+ struct TunnelState *s;
+
+ /* generate fresh, unique address */
+ do
+ {
+ if (NULL == state->serv)
+ setup_fresh_address (state->ri.remote_address.af,
+ state->ri.remote_address.proto,
+ &state->ri.local_address);
+ else
+ setup_fresh_address (state->serv->address.af,
+ state->serv->address.proto,
+ &state->ri.local_address);
+ } while (NULL != get_redirect_state (state->ri.remote_address.af,
+ state->ri.remote_address.proto,
+ &state->ri.remote_address.address,
+ state->ri.remote_address.port,
+ &state->ri.local_address.address,
+ state->ri.local_address.port,
+ &key));
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Picked local address %s:%u for new connection\n",
+ inet_ntop (state->ri.local_address.af,
+ &state->ri.local_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) state->ri.local_address.port);
+ }
+ state->state_key = key;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (connections_map,
+ &key, state,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ state->heap_node = GNUNET_CONTAINER_heap_insert (connections_heap,
+ state,
+ GNUNET_TIME_absolute_get ().abs_value);
+ while (GNUNET_CONTAINER_heap_get_size (connections_heap) > max_connections)
+ {
+ s = GNUNET_CONTAINER_heap_remove_root (connections_heap);
+ GNUNET_assert (state != s);
+ s->heap_node = NULL;
+ GNUNET_MESH_tunnel_destroy (s->tunnel);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (connections_map,
+ &s->state_key,
+ s));
+ GNUNET_free (s);
+ }
+}
+
+
+/**
+ * Prepare an IPv4 packet for transmission via the TUN interface.
+ * Initializes the IP header and calculates checksums (IP+UDP/TCP).
+ * For UDP, the UDP header will be fully created, whereas for TCP
+ * only the ports and checksum will be filled in. So for TCP,
+ * a skeleton TCP header must be part of the provided payload.
+ *
+ * @param payload payload of the packet (starting with UDP payload or
+ * TCP header, depending on protocol)
+ * @param payload_length number of bytes in 'payload'
+ * @param protocol IPPROTO_UDP or IPPROTO_TCP
+ * @param tcp_header skeleton of the TCP header, NULL for UDP
+ * @param src_address source address to use (IP and port)
+ * @param dst_address destination address to use (IP and port)
+ * @param pkt4 where to write the assembled packet; must
+ * contain enough space for the IP header, UDP/TCP header
+ * AND the payload
+ */
+static void
+prepare_ipv4_packet (const void *payload, size_t payload_length,
+ int protocol,
+ const struct GNUNET_TUN_TcpHeader *tcp_header,
+ const struct SocketAddress *src_address,
+ const struct SocketAddress *dst_address,
+ struct GNUNET_TUN_IPv4Header *pkt4)
+{
+ size_t len;
+
+ len = payload_length;
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ len += sizeof (struct GNUNET_TUN_UdpHeader);
+ break;
+ case IPPROTO_TCP:
+ len += sizeof (struct GNUNET_TUN_TcpHeader);
+ GNUNET_assert (NULL != tcp_header);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ if (len + sizeof (struct GNUNET_TUN_IPv4Header) > UINT16_MAX)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ GNUNET_TUN_initialize_ipv4_header (pkt4,
+ protocol,
+ len,
+ &src_address->address.ipv4,
+ &dst_address->address.ipv4);
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ {
+ struct GNUNET_TUN_UdpHeader *pkt4_udp = (struct GNUNET_TUN_UdpHeader *) &pkt4[1];
+
+ pkt4_udp->source_port = htons (src_address->port);
+ pkt4_udp->destination_port = htons (dst_address->port);
+ pkt4_udp->len = htons ((uint16_t) payload_length);
+ GNUNET_TUN_calculate_udp4_checksum (pkt4,
+ pkt4_udp,
+ payload, payload_length);
+ memcpy (&pkt4_udp[1], payload, payload_length);
+ }
+ break;
+ case IPPROTO_TCP:
+ {
+ struct GNUNET_TUN_TcpHeader *pkt4_tcp = (struct GNUNET_TUN_TcpHeader *) &pkt4[1];
+
+ *pkt4_tcp = *tcp_header;
+ pkt4_tcp->source_port = htons (src_address->port);
+ pkt4_tcp->destination_port = htons (dst_address->port);
+ GNUNET_TUN_calculate_tcp4_checksum (pkt4,
+ pkt4_tcp,
+ payload,
+ payload_length);
+ memcpy (&pkt4_tcp[1], payload, payload_length);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
+/**
+ * Prepare an IPv6 packet for transmission via the TUN interface.
+ * Initializes the IP header and calculates checksums (IP+UDP/TCP).
+ * For UDP, the UDP header will be fully created, whereas for TCP
+ * only the ports and checksum will be filled in. So for TCP,
+ * a skeleton TCP header must be part of the provided payload.
+ *
+ * @param payload payload of the packet (starting with UDP payload or
+ * TCP header, depending on protocol)
+ * @param payload_length number of bytes in 'payload'
+ * @param protocol IPPROTO_UDP or IPPROTO_TCP
+ * @param tcp_header skeleton TCP header data to send, NULL for UDP
+ * @param src_address source address to use (IP and port)
+ * @param dst_address destination address to use (IP and port)
+ * @param pkt6 where to write the assembled packet; must
+ * contain enough space for the IP header, UDP/TCP header
+ * AND the payload
+ */
+static void
+prepare_ipv6_packet (const void *payload, size_t payload_length,
+ int protocol,
+ const struct GNUNET_TUN_TcpHeader *tcp_header,
+ const struct SocketAddress *src_address,
+ const struct SocketAddress *dst_address,
+ struct GNUNET_TUN_IPv6Header *pkt6)
+{
+ size_t len;
+
+ len = payload_length;
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ len += sizeof (struct GNUNET_TUN_UdpHeader);
+ break;
+ case IPPROTO_TCP:
+ len += sizeof (struct GNUNET_TUN_TcpHeader);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ if (len > UINT16_MAX)
+ {
+ GNUNET_break (0);
+ return;
+ }
+
+ GNUNET_TUN_initialize_ipv6_header (pkt6,
+ protocol,
+ len,
+ &src_address->address.ipv6,
+ &dst_address->address.ipv6);
+
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ {
+ struct GNUNET_TUN_UdpHeader *pkt6_udp = (struct GNUNET_TUN_UdpHeader *) &pkt6[1];
+
+ pkt6_udp->source_port = htons (src_address->port);
+ pkt6_udp->destination_port = htons (dst_address->port);
+ pkt6_udp->len = htons ((uint16_t) payload_length);
+ GNUNET_TUN_calculate_udp6_checksum (pkt6,
+ pkt6_udp,
+ payload,
+ payload_length);
+ memcpy (&pkt6_udp[1], payload, payload_length);
+ }
+ break;
+ case IPPROTO_TCP:
+ {
+ struct GNUNET_TUN_TcpHeader *pkt6_tcp = (struct GNUNET_TUN_TcpHeader *) &pkt6[1];
+
+ /* memcpy first here as some TCP header fields are initialized this way! */
+ *pkt6_tcp = *tcp_header;
+ pkt6_tcp->source_port = htons (src_address->port);
+ pkt6_tcp->destination_port = htons (dst_address->port);
+ GNUNET_TUN_calculate_tcp6_checksum (pkt6,
+ pkt6_tcp,
+ payload,
+ payload_length);
+ memcpy (&pkt6_tcp[1], payload, payload_length);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+}
+
+
+/**
+ * Send a TCP packet via the TUN interface.
+ *
+ * @param destination_address IP and port to use for the TCP packet's destination
+ * @param source_address IP and port to use for the TCP packet's source
+ * @param tcp_header header template to use
+ * @param payload payload of the TCP packet
+ * @param payload_length number of bytes in 'payload'
+ */
+static void
+send_tcp_packet_via_tun (const struct SocketAddress *destination_address,
+ const struct SocketAddress *source_address,
+ const struct GNUNET_TUN_TcpHeader *tcp_header,
+ const void *payload, size_t payload_length)
+{
+ size_t len;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP packets sent via TUN"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending packet with %u bytes TCP payload via TUN\n",
+ (unsigned int) payload_length);
+ len = sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ len += sizeof (struct GNUNET_TUN_IPv4Header);
+ break;
+ case AF_INET6:
+ len += sizeof (struct GNUNET_TUN_IPv6Header);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ len += sizeof (struct GNUNET_TUN_TcpHeader);
+ len += payload_length;
+ if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ {
+ char buf[len];
+ struct GNUNET_MessageHeader *hdr;
+ struct GNUNET_TUN_Layer2PacketHeader *tun;
+
+ hdr = (struct GNUNET_MessageHeader *) buf;
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (len);
+ tun = (struct GNUNET_TUN_Layer2PacketHeader*) &hdr[1];
+ tun->flags = htons (0);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ {
+ struct GNUNET_TUN_IPv4Header * ipv4 = (struct GNUNET_TUN_IPv4Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV4);
+ prepare_ipv4_packet (payload, payload_length,
+ IPPROTO_TCP,
+ tcp_header,
+ source_address,
+ destination_address,
+ ipv4);
+ }
+ break;
+ case AF_INET6:
+ {
+ struct GNUNET_TUN_IPv6Header * ipv6 = (struct GNUNET_TUN_IPv6Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV6);
+ prepare_ipv6_packet (payload, payload_length,
+ IPPROTO_TCP,
+ tcp_header,
+ source_address,
+ destination_address,
+ ipv6);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ (void) GNUNET_HELPER_send (helper_handle,
+ (const struct GNUNET_MessageHeader*) buf,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+}
+
+
+/**
+ * Process a request via mesh to send a request to a TCP service
+ * offered by this system.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_tcp_service (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpServiceStartMessage *start;
+ uint16_t pkt_len = ntohs (message->size);
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP service creation requests received via mesh"),
+ 1, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ /* check that we got at least a valid header */
+ if (pkt_len < sizeof (struct GNUNET_EXIT_TcpServiceStartMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ start = (const struct GNUNET_EXIT_TcpServiceStartMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_TcpServiceStartMessage);
+ if ( (NULL == state) ||
+ (NULL != state->serv) ||
+ (NULL != state->heap_node) )
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (start->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_break_op (ntohl (start->reserved) == 0);
+ /* setup fresh connection */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for forwarding to TCP service %s on port %u\n",
+ GNUNET_i2s (sender),
+ GNUNET_h2s (&start->service_descriptor),
+ (unsigned int) ntohs (start->tcp_header.destination_port));
+ if (NULL == (state->serv = find_service (tcp_services, &start->service_descriptor,
+ ntohs (start->tcp_header.destination_port))))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("No service found for %s on port %d!\n"),
+ "TCP",
+ ntohs (start->tcp_header.destination_port));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP requests dropped (no such service)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ state->ri.remote_address = state->serv->address;
+ setup_state_record (state);
+ send_tcp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &start->tcp_header,
+ &start[1], pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Process a request to forward TCP data to the Internet via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_tcp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpInternetStartMessage *start;
+ uint16_t pkt_len = ntohs (message->size);
+ const struct in_addr *v4;
+ const struct in6_addr *v6;
+ const void *payload;
+ int af;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP IP-exit creation requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_TcpInternetStartMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ start = (const struct GNUNET_EXIT_TcpInternetStartMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_TcpInternetStartMessage);
+ if ( (NULL == state) ||
+ (NULL != state->serv) ||
+ (NULL != state->heap_node) )
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (start->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ af = (int) ntohl (start->af);
+ state->ri.remote_address.af = af;
+ switch (af)
+ {
+ case AF_INET:
+ if (pkt_len < sizeof (struct in_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv4_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v4 = (const struct in_addr*) &start[1];
+ payload = &v4[1];
+ pkt_len -= sizeof (struct in_addr);
+ state->ri.remote_address.address.ipv4 = *v4;
+ break;
+ case AF_INET6:
+ if (pkt_len < sizeof (struct in6_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv6_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v6 = (const struct in6_addr*) &start[1];
+ payload = &v6[1];
+ pkt_len -= sizeof (struct in6_addr);
+ state->ri.remote_address.address.ipv6 = *v6;
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for starting TCP stream to %s:%u\n",
+ GNUNET_i2s (sender),
+ inet_ntop (af,
+ &state->ri.remote_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) ntohs (start->tcp_header.destination_port));
+ }
+ state->ri.remote_address.proto = IPPROTO_TCP;
+ state->ri.remote_address.port = ntohs (start->tcp_header.destination_port);
+ setup_state_record (state);
+ send_tcp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &start->tcp_header,
+ payload, pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Process a request to forward TCP data on an established
+ * connection via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_tcp_data (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpDataMessage *data;
+ uint16_t pkt_len = ntohs (message->size);
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP data requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_TcpDataMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ data = (const struct GNUNET_EXIT_TcpDataMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_TcpDataMessage);
+ if ( (NULL == state) ||
+ (NULL == state->heap_node) )
+ {
+ /* connection should have been up! */
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP DATA requests dropped (no session)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ if (data->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_break_op (ntohl (data->reserved) == 0);
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received additional %u bytes of data from %s for TCP stream to %s:%u\n",
+ pkt_len,
+ GNUNET_i2s (sender),
+ inet_ntop (state->ri.remote_address.af,
+ &state->ri.remote_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) state->ri.remote_address.port);
+ }
+
+ send_tcp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &data->tcp_header,
+ &data[1], pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Send an ICMP packet via the TUN interface.
+ *
+ * @param destination_address IP to use for the ICMP packet's destination
+ * @param source_address IP to use for the ICMP packet's source
+ * @param icmp_header ICMP header to send
+ * @param payload payload of the ICMP packet (does NOT include ICMP header)
+ * @param payload_length number of bytes of data in payload
+ */
+static void
+send_icmp_packet_via_tun (const struct SocketAddress *destination_address,
+ const struct SocketAddress *source_address,
+ const struct GNUNET_TUN_IcmpHeader *icmp_header,
+ const void *payload, size_t payload_length)
+{
+ size_t len;
+ struct GNUNET_TUN_IcmpHeader *icmp;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP packets sent via TUN"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending packet with %u bytes ICMP payload via TUN\n",
+ (unsigned int) payload_length);
+ len = sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader);
+ switch (destination_address->af)
+ {
+ case AF_INET:
+ len += sizeof (struct GNUNET_TUN_IPv4Header);
+ break;
+ case AF_INET6:
+ len += sizeof (struct GNUNET_TUN_IPv6Header);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ len += sizeof (struct GNUNET_TUN_IcmpHeader);
+ len += payload_length;
+ if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ {
+ char buf[len];
+ struct GNUNET_MessageHeader *hdr;
+ struct GNUNET_TUN_Layer2PacketHeader *tun;
+
+ hdr= (struct GNUNET_MessageHeader *) buf;
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (len);
+ tun = (struct GNUNET_TUN_Layer2PacketHeader*) &hdr[1];
+ tun->flags = htons (0);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ {
+ struct GNUNET_TUN_IPv4Header * ipv4 = (struct GNUNET_TUN_IPv4Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_ICMP,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + payload_length,
+ &source_address->address.ipv4,
+ &destination_address->address.ipv4);
+ icmp = (struct GNUNET_TUN_IcmpHeader*) &ipv4[1];
+ }
+ break;
+ case AF_INET6:
+ {
+ struct GNUNET_TUN_IPv6Header * ipv6 = (struct GNUNET_TUN_IPv6Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_ICMPV6,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + payload_length,
+ &source_address->address.ipv6,
+ &destination_address->address.ipv6);
+ icmp = (struct GNUNET_TUN_IcmpHeader*) &ipv6[1];
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ *icmp = *icmp_header;
+ memcpy (&icmp[1],
+ payload,
+ payload_length);
+ GNUNET_TUN_calculate_icmp_checksum (icmp,
+ payload,
+ payload_length);
+ (void) GNUNET_HELPER_send (helper_handle,
+ (const struct GNUNET_MessageHeader*) buf,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMPv4 error
+ * response on the given tunnel.
+ *
+ * @param state tunnel information
+ * @param ipp IPv6 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv4_payload (struct TunnelState *state,
+ struct GNUNET_TUN_IPv4Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv4_header (ipp,
+ state->ri.remote_address.proto,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &state->ri.remote_address.address.ipv4,
+ &state->ri.local_address.address.ipv4);
+ udp->source_port = htons (state->ri.remote_address.port);
+ udp->destination_port = htons (state->ri.local_address.port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMPv6 error
+ * response on the given tunnel.
+ *
+ * @param state tunnel information
+ * @param ipp IPv6 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv6_payload (struct TunnelState *state,
+ struct GNUNET_TUN_IPv6Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv6_header (ipp,
+ state->ri.remote_address.proto,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &state->ri.remote_address.address.ipv6,
+ &state->ri.local_address.address.ipv6);
+ udp->source_port = htons (state->ri.remote_address.port);
+ udp->destination_port = htons (state->ri.local_address.port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * Process a request to forward ICMP data to the Internet via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_icmp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_IcmpInternetMessage *msg;
+ uint16_t pkt_len = ntohs (message->size);
+ const struct in_addr *v4;
+ const struct in6_addr *v6;
+ const void *payload;
+ char buf[sizeof (struct GNUNET_TUN_IPv6Header) + 8];
+ int af;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP IP-exit requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_IcmpInternetMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg = (const struct GNUNET_EXIT_IcmpInternetMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_IcmpInternetMessage);
+
+ af = (int) ntohl (msg->af);
+ if ( (NULL != state->heap_node) &&
+ (af != state->ri.remote_address.af) )
+ {
+ /* other peer switched AF on this tunnel; not allowed */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ switch (af)
+ {
+ case AF_INET:
+ if (pkt_len < sizeof (struct in_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv4_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v4 = (const struct in_addr*) &msg[1];
+ payload = &v4[1];
+ pkt_len -= sizeof (struct in_addr);
+ state->ri.remote_address.address.ipv4 = *v4;
+ if (NULL == state->heap_node)
+ {
+ state->ri.remote_address.af = af;
+ state->ri.remote_address.proto = IPPROTO_ICMP;
+ setup_state_record (state);
+ }
+ /* check that ICMP type is something we want to support
+ and possibly make up payload! */
+ switch (msg->icmp_header.type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ /* make up payload */
+ {
+ struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) buf;
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ pkt_len = sizeof (struct GNUNET_TUN_IPv4Header) + 8;
+ make_up_icmpv4_payload (state,
+ ipp,
+ udp);
+ payload = ipp;
+ }
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET */
+ break;
+ case AF_INET6:
+ if (pkt_len < sizeof (struct in6_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv6_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v6 = (const struct in6_addr*) &msg[1];
+ payload = &v6[1];
+ pkt_len -= sizeof (struct in6_addr);
+ state->ri.remote_address.address.ipv6 = *v6;
+ if (NULL == state->heap_node)
+ {
+ state->ri.remote_address.af = af;
+ state->ri.remote_address.proto = IPPROTO_ICMPV6;
+ setup_state_record (state);
+ }
+ /* check that ICMP type is something we want to support
+ and possibly make up payload! */
+ switch (msg->icmp_header.type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ break;
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ /* make up payload */
+ {
+ struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) buf;
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ pkt_len = sizeof (struct GNUNET_TUN_IPv6Header) + 8;
+ make_up_icmpv6_payload (state,
+ ipp,
+ udp);
+ payload = ipp;
+ }
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET6 */
+ break;
+ default:
+ /* bad AF */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received ICMP data from %s for forwarding to %s\n",
+ GNUNET_i2s (sender),
+ inet_ntop (af,
+ &state->ri.remote_address.address,
+ buf, sizeof (buf)));
+ }
+ send_icmp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &msg->icmp_header,
+ payload, pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Setup ICMP payload for ICMP error messages. Called
+ * for both IPv4 and IPv6 addresses.
+ *
+ * @param state context for creating the IP Packet
+ * @param buf where to create the payload, has at least
+ * sizeof (struct GNUNET_TUN_IPv6Header) + 8 bytes
+ * @return number of bytes of payload we created in buf
+ */
+static uint16_t
+make_up_icmp_service_payload (struct TunnelState *state,
+ char *buf)
+{
+ switch (state->serv->address.af)
+ {
+ case AF_INET:
+ {
+ struct GNUNET_TUN_IPv4Header *ipv4;
+ struct GNUNET_TUN_UdpHeader *udp;
+
+ ipv4 = (struct GNUNET_TUN_IPv4Header *)buf;
+ udp = (struct GNUNET_TUN_UdpHeader *) &ipv4[1];
+ make_up_icmpv4_payload (state,
+ ipv4,
+ udp);
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ return sizeof (struct GNUNET_TUN_IPv4Header) + 8;
+ }
+ break;
+ case AF_INET6:
+ {
+ struct GNUNET_TUN_IPv6Header *ipv6;
+ struct GNUNET_TUN_UdpHeader *udp;
+
+ ipv6 = (struct GNUNET_TUN_IPv6Header *)buf;
+ udp = (struct GNUNET_TUN_UdpHeader *) &ipv6[1];
+ make_up_icmpv6_payload (state,
+ ipv6,
+ udp);
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ return sizeof (struct GNUNET_TUN_IPv6Header) + 8;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ }
+ return 0;
+}
+
+
+/**
+ * Process a request via mesh to send ICMP data to a service
+ * offered by this system.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_icmp_service (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_IcmpServiceMessage *msg;
+ uint16_t pkt_len = ntohs (message->size);
+ struct GNUNET_TUN_IcmpHeader icmp;
+ char buf[sizeof (struct GNUNET_TUN_IPv6Header) + 8];
+ const void *payload;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP service requests received via mesh"),
+ 1, GNUNET_NO);
+ /* check that we got at least a valid header */
+ if (pkt_len < sizeof (struct GNUNET_EXIT_IcmpServiceMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg = (const struct GNUNET_EXIT_IcmpServiceMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_IcmpServiceMessage);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for forwarding to ICMP service %s\n",
+ GNUNET_i2s (sender),
+ GNUNET_h2s (&msg->service_descriptor));
+ if (NULL == state->serv)
+ {
+ /* first packet to service must not be ICMP (cannot determine service!) */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ icmp = msg->icmp_header;
+ payload = &msg[1];
+ state->ri.remote_address = state->serv->address;
+ setup_state_record (state);
+
+ /* check that ICMP type is something we want to support,
+ perform ICMP PT if needed ans possibly make up payload */
+ switch (msg->af)
+ {
+ case AF_INET:
+ switch (msg->icmp_header.type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ if (state->serv->address.af == AF_INET6)
+ icmp.type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY;
+ break;
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ if (state->serv->address.af == AF_INET6)
+ icmp.type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ if (state->serv->address.af == AF_INET6)
+ icmp.type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE;
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ if (state->serv->address.af == AF_INET6)
+ icmp.type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED;
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ if (state->serv->address.af == AF_INET6)
+ {
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"),
+ 1, GNUNET_NO);
+ return GNUNET_OK;
+ }
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end of AF_INET */
+ break;
+ case AF_INET6:
+ switch (msg->icmp_header.type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ if (state->serv->address.af == AF_INET)
+ icmp.type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY;
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ if (state->serv->address.af == AF_INET)
+ icmp.type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ if (state->serv->address.af == AF_INET)
+ icmp.type = GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE;
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ if (state->serv->address.af == AF_INET)
+ icmp.type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED;
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ if (state->serv->address.af == AF_INET)
+ {
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"),
+ 1, GNUNET_NO);
+ return GNUNET_OK;
+ }
+ if (0 != pkt_len)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ payload = buf;
+ pkt_len = make_up_icmp_service_payload (state, buf);
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end of AF_INET6 */
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ send_icmp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &icmp,
+ payload, pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Send a UDP packet via the TUN interface.
+ *
+ * @param destination_address IP and port to use for the UDP packet's destination
+ * @param source_address IP and port to use for the UDP packet's source
+ * @param payload payload of the UDP packet (does NOT include UDP header)
+ * @param payload_length number of bytes of data in payload
+ */
+static void
+send_udp_packet_via_tun (const struct SocketAddress *destination_address,
+ const struct SocketAddress *source_address,
+ const void *payload, size_t payload_length)
+{
+ size_t len;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# UDP packets sent via TUN"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending packet with %u bytes UDP payload via TUN\n",
+ (unsigned int) payload_length);
+ len = sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ len += sizeof (struct GNUNET_TUN_IPv4Header);
+ break;
+ case AF_INET6:
+ len += sizeof (struct GNUNET_TUN_IPv6Header);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ len += sizeof (struct GNUNET_TUN_UdpHeader);
+ len += payload_length;
+ if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ {
+ char buf[len];
+ struct GNUNET_MessageHeader *hdr;
+ struct GNUNET_TUN_Layer2PacketHeader *tun;
+
+ hdr= (struct GNUNET_MessageHeader *) buf;
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (len);
+ tun = (struct GNUNET_TUN_Layer2PacketHeader*) &hdr[1];
+ tun->flags = htons (0);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ {
+ struct GNUNET_TUN_IPv4Header * ipv4 = (struct GNUNET_TUN_IPv4Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV4);
+ prepare_ipv4_packet (payload, payload_length,
+ IPPROTO_UDP,
+ NULL,
+ source_address,
+ destination_address,
+ ipv4);
+ }
+ break;
+ case AF_INET6:
+ {
+ struct GNUNET_TUN_IPv6Header * ipv6 = (struct GNUNET_TUN_IPv6Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV6);
+ prepare_ipv6_packet (payload, payload_length,
+ IPPROTO_UDP,
+ NULL,
+ source_address,
+ destination_address,
+ ipv6);
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ (void) GNUNET_HELPER_send (helper_handle,
+ (const struct GNUNET_MessageHeader*) buf,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+}
+
+
+/**
+ * Process a request to forward UDP data to the Internet via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_udp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_UdpInternetMessage *msg;
+ uint16_t pkt_len = ntohs (message->size);
+ const struct in_addr *v4;
+ const struct in6_addr *v6;
+ const void *payload;
+ int af;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# UDP IP-exit requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_UdpInternetMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg = (const struct GNUNET_EXIT_UdpInternetMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_UdpInternetMessage);
+ af = (int) ntohl (msg->af);
+ state->ri.remote_address.af = af;
+ switch (af)
+ {
+ case AF_INET:
+ if (pkt_len < sizeof (struct in_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv4_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v4 = (const struct in_addr*) &msg[1];
+ payload = &v4[1];
+ pkt_len -= sizeof (struct in_addr);
+ state->ri.remote_address.address.ipv4 = *v4;
+ break;
+ case AF_INET6:
+ if (pkt_len < sizeof (struct in6_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv6_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v6 = (const struct in6_addr*) &msg[1];
+ payload = &v6[1];
+ pkt_len -= sizeof (struct in6_addr);
+ state->ri.remote_address.address.ipv6 = *v6;
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for forwarding to UDP %s:%u\n",
+ GNUNET_i2s (sender),
+ inet_ntop (af,
+ &state->ri.remote_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) ntohs (msg->destination_port));
+ }
+ state->ri.remote_address.proto = IPPROTO_UDP;
+ state->ri.remote_address.port = msg->destination_port;
+ if (NULL == state->heap_node)
+ setup_state_record (state);
+ if (0 != ntohs (msg->source_port))
+ state->ri.local_address.port = msg->source_port;
+ send_udp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ payload, pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Process a request via mesh to send a request to a UDP service
+ * offered by this system.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_udp_service (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_UdpServiceMessage *msg;
+ uint16_t pkt_len = ntohs (message->size);
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# UDP service requests received via mesh"),
+ 1, GNUNET_NO);
+ /* check that we got at least a valid header */
+ if (pkt_len < sizeof (struct GNUNET_EXIT_UdpServiceMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg = (const struct GNUNET_EXIT_UdpServiceMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_UdpServiceMessage);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for forwarding to UDP service %s on port %u\n",
+ GNUNET_i2s (sender),
+ GNUNET_h2s (&msg->service_descriptor),
+ (unsigned int) ntohs (msg->destination_port));
+ if (NULL == (state->serv = find_service (udp_services, &msg->service_descriptor,
+ ntohs (msg->destination_port))))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("No service found for %s on port %d!\n"),
+ "UDP",
+ ntohs (msg->destination_port));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# UDP requests dropped (no such service)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ state->ri.remote_address = state->serv->address;
+ setup_state_record (state);
+ if (0 != ntohs (msg->source_port))
+ state->ri.local_address.port = msg->source_port;
+ send_udp_packet_via_tun (&state->ri.remote_address,
+ &state->ri.local_address,
+ &msg[1], pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Callback from GNUNET_MESH for new tunnels.
+ *
+ * @param cls closure
+ * @param tunnel new handle to the tunnel
+ * @param initiator peer that started the tunnel
+ * @param atsi performance information for the tunnel
+ * @return initial tunnel context for the tunnel
+ */
+static void *
+new_tunnel (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ const struct GNUNET_PeerIdentity *initiator GNUNET_UNUSED,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *s = GNUNET_malloc (sizeof (struct TunnelState));
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Inbound MESH tunnels created"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received inbound tunnel from `%s'\n",
+ GNUNET_i2s (initiator));
+ s->tunnel = tunnel;
+ return s;
+}
+
+
+/**
+ * Function called by mesh whenever an inbound tunnel is destroyed.
+ * Should clean up any associated state.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end (henceforth invalid)
+ * @param tunnel_ctx place where local state associated
+ * with the tunnel is stored
+ */
+static void
+clean_tunnel (void *cls GNUNET_UNUSED, const struct GNUNET_MESH_Tunnel *tunnel,
+ void *tunnel_ctx)
+{
+ struct TunnelState *s = tunnel_ctx;
+ struct TunnelMessageQueue *tnq;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel destroyed\n");
+ while (NULL != (tnq = s->head))
+ {
+ GNUNET_CONTAINER_DLL_remove (s->head,
+ s->tail,
+ tnq);
+ GNUNET_free (tnq);
+ }
+ if (s->heap_node != NULL)
+ {
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (connections_map,
+ &s->state_key,
+ s));
+ GNUNET_CONTAINER_heap_remove_node (s->heap_node);
+ s->heap_node = NULL;
+ }
+ if (NULL != s->th)
+ {
+ GNUNET_MESH_notify_transmit_ready_cancel (s->th);
+ s->th = NULL;
+ }
+ GNUNET_free (s);
+}
+
+
+/**
+ * Function that frees everything from a hashmap
+ *
+ * @param cls unused
+ * @param hash key
+ * @param value value to free
+ */
+static int
+free_iterate (void *cls GNUNET_UNUSED,
+ const GNUNET_HashCode * hash GNUNET_UNUSED, void *value)
+{
+ GNUNET_free (value);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function scheduled as very last function, cleans up after us
+ */
+static void
+cleanup (void *cls GNUNET_UNUSED,
+ const struct GNUNET_SCHEDULER_TaskContext *tskctx)
+{
+ unsigned int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Exit service is shutting down now\n");
+ if (helper_handle != NULL)
+ {
+ GNUNET_HELPER_stop (helper_handle);
+ helper_handle = NULL;
+ }
+ if (mesh_handle != NULL)
+ {
+ GNUNET_MESH_disconnect (mesh_handle);
+ mesh_handle = NULL;
+ }
+ if (NULL != connections_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (connections_map, &free_iterate, NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (connections_map);
+ connections_map = NULL;
+ }
+ if (NULL != connections_heap)
+ {
+ GNUNET_CONTAINER_heap_destroy (connections_heap);
+ connections_heap = NULL;
+ }
+ if (NULL != tcp_services)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (tcp_services, &free_service_record, NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (tcp_services);
+ tcp_services = NULL;
+ }
+ if (NULL != udp_services)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (udp_services, &free_service_record, NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (udp_services);
+ udp_services = NULL;
+ }
+ if (stats != NULL)
+ {
+ GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
+ stats = NULL;
+ }
+ for (i=0;i<8;i++)
+ GNUNET_free_non_null (exit_argv[i]);
+}
+
+
+/**
+ * Add services to the service map.
+ *
+ * @param proto IPPROTO_TCP or IPPROTO_UDP
+ * @param cpy copy of the service descriptor (can be mutilated)
+ * @param name DNS name of the service
+ */
+static void
+add_services (int proto,
+ char *cpy,
+ const char *name)
+{
+ char *redirect;
+ char *hostname;
+ char *hostport;
+ struct LocalService *serv;
+
+ for (redirect = strtok (cpy, " "); redirect != NULL;
+ redirect = strtok (NULL, " "))
+ {
+ if (NULL == (hostname = strstr (redirect, ":")))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "option `%s' for domain `%s' is not formatted correctly!\n",
+ redirect,
+ name);
+ continue;
+ }
+ hostname[0] = '\0';
+ hostname++;
+ if (NULL == (hostport = strstr (hostname, ":")))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "option `%s' for domain `%s' is not formatted correctly!\n",
+ redirect,
+ name);
+ continue;
+ }
+ hostport[0] = '\0';
+ hostport++;
+
+ int local_port = atoi (redirect);
+ int remote_port = atoi (hostport);
+
+ if (!((local_port > 0) && (local_port < 65536)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "`%s' is not a valid port number (for domain `%s')!", redirect,
+ name);
+ continue;
+ }
+ if (!((remote_port > 0) && (remote_port < 65536)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "`%s' is not a valid port number (for domain `%s')!", hostport,
+ name);
+ continue;
+ }
+
+ serv = GNUNET_malloc (sizeof (struct LocalService));
+ serv->my_port = (uint16_t) local_port;
+ serv->address.port = remote_port;
+ if (0 == strcmp ("localhost4", hostname))
+ {
+ const char *ip4addr = exit_argv[4];
+
+ serv->address.af = AF_INET;
+ GNUNET_assert (1 != inet_pton (AF_INET, ip4addr, &serv->address.address.ipv4));
+ }
+ else if (0 == strcmp ("localhost6", hostname))
+ {
+ const char *ip6addr = exit_argv[2];
+
+ serv->address.af = AF_INET6;
+ GNUNET_assert (1 == inet_pton (AF_INET6, ip6addr, &serv->address.address.ipv6));
+ }
+ else
+ {
+ struct addrinfo *res;
+ int ret;
+
+ ret = getaddrinfo (hostname, NULL, NULL, &res);
+ if ( (ret != 0) || (res == NULL) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("No addresses found for hostname `%s' of service `%s'!\n"),
+ hostname,
+ name);
+ GNUNET_free (serv);
+ continue;
+ }
+
+ serv->address.af = res->ai_family;
+ switch (res->ai_family)
+ {
+ case AF_INET:
+ if (! ipv4_enabled)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Service `%s' configured for IPv4, but IPv4 is disabled!\n"),
+ name);
+ freeaddrinfo (res);
+ GNUNET_free (serv);
+ continue;
+ }
+ serv->address.address.ipv4 = ((struct sockaddr_in *) res->ai_addr)->sin_addr;
+ break;
+ case AF_INET6:
+ if (! ipv6_enabled)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Service `%s' configured for IPv4, but IPv4 is disabled!\n"),
+ name);
+ freeaddrinfo (res);
+ GNUNET_free (serv);
+ continue;
+ }
+ serv->address.address.ipv6 = ((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
+ break;
+ default:
+ freeaddrinfo (res);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("No IP addresses found for hostname `%s' of service `%s'!\n"),
+ hostname,
+ name);
+ GNUNET_free (serv);
+ continue;
+ }
+ freeaddrinfo (res);
+ }
+ store_service ((IPPROTO_UDP == proto) ? udp_services : tcp_services,
+ name,
+ local_port,
+ serv);
+ }
+}
+
+
+/**
+ * Reads the configuration servicecfg and populates udp_services
+ *
+ * @param cls unused
+ * @param section name of section in config, equal to hostname
+ */
+static void
+read_service_conf (void *cls GNUNET_UNUSED, const char *section)
+{
+ char *cpy;
+
+ if ((strlen (section) < 8) ||
+ (0 != strcmp (".gnunet.", section + (strlen (section) - 8))))
+ return;
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, section, "UDP_REDIRECTS",
+ &cpy))
+ {
+ add_services (IPPROTO_UDP, cpy, section);
+ GNUNET_free (cpy);
+ }
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, section, "TCP_REDIRECTS",
+ &cpy))
+ {
+ add_services (IPPROTO_TCP, cpy, section);
+ GNUNET_free (cpy);
+ }
+}
+
+
+/**
+ * Test if the given AF is supported by this system.
+ *
+ * @param af to test
+ * @return GNUNET_OK if the AF is supported
+ */
+static int
+test_af (int af)
+{
+ int s;
+
+ s = socket (af, SOCK_STREAM, 0);
+ if (-1 == s)
+ {
+ if (EAFNOSUPPORT == errno)
+ return GNUNET_NO;
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "socket");
+ return GNUNET_SYSERR;
+ }
+ close (s);
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg_ configuration
+ */
+static void
+run (void *cls, char *const *args GNUNET_UNUSED,
+ const char *cfgfile GNUNET_UNUSED,
+ const struct GNUNET_CONFIGURATION_Handle *cfg_)
+{
+ static struct GNUNET_MESH_MessageHandler handlers[] = {
+ {&receive_icmp_service, GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_SERVICE, 0},
+ {&receive_icmp_remote, GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_INTERNET, 0},
+ {&receive_udp_service, GNUNET_MESSAGE_TYPE_VPN_UDP_TO_SERVICE, 0},
+ {&receive_udp_remote, GNUNET_MESSAGE_TYPE_VPN_UDP_TO_INTERNET, 0},
+ {&receive_tcp_service, GNUNET_MESSAGE_TYPE_VPN_TCP_TO_SERVICE_START, 0},
+ {&receive_tcp_remote, GNUNET_MESSAGE_TYPE_VPN_TCP_TO_INTERNET_START, 0},
+ {&receive_tcp_data, GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_EXIT, 0},
+ {NULL, 0, 0}
+ };
+
+ static GNUNET_MESH_ApplicationType apptypes[] = {
+ GNUNET_APPLICATION_TYPE_END,
+ GNUNET_APPLICATION_TYPE_END,
+ GNUNET_APPLICATION_TYPE_END
+ };
+ unsigned int app_idx;
+ char *exit_ifname;
+ char *tun_ifname;
+ char *ipv6addr;
+ char *ipv6prefix_s;
+ char *ipv4addr;
+ char *ipv4mask;
+
+ if (GNUNET_YES !=
+ GNUNET_OS_check_helper_binary ("gnunet-helper-exit"))
+ {
+ fprintf (stderr,
+ "`%s' is not SUID, refusing to run.\n",
+ "gnunet-helper-exit");
+ global_ret = 1;
+ return;
+ }
+ cfg = cfg_;
+ stats = GNUNET_STATISTICS_create ("exit", cfg);
+ ipv4_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "EXIT_IPV4");
+ ipv6_exit = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "EXIT_IPV6");
+ ipv4_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV4");
+ ipv6_enabled = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_IPV6");
+
+ if ( (ipv4_exit || ipv4_enabled) &&
+ GNUNET_OK != test_af (AF_INET))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("This system does not support IPv4, will disable IPv4 functions despite them being enabled in the configuration\n"));
+ ipv4_exit = GNUNET_NO;
+ ipv4_enabled = GNUNET_NO;
+ }
+ if ( (ipv6_exit || ipv6_enabled) &&
+ GNUNET_OK != test_af (AF_INET6))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("This system does not support IPv6, will disable IPv6 functions despite them being enabled in the configuration\n"));
+ ipv6_exit = GNUNET_NO;
+ ipv6_enabled = GNUNET_NO;
+ }
+ if (ipv4_exit && (! ipv4_enabled))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Cannot enable IPv4 exit but disable IPv4 on TUN interface, will use ENABLE_IPv4=YES\n"));
+ ipv4_enabled = GNUNET_YES;
+ }
+ if (ipv6_exit && (! ipv6_enabled))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Cannot enable IPv6 exit but disable IPv6 on TUN interface, will use ENABLE_IPv6=YES\n"));
+ ipv6_enabled = GNUNET_YES;
+ }
+ if (! (ipv4_enabled || ipv6_enabled))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("No useful service enabled. Exiting.\n"));
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ app_idx = 0;
+ if (GNUNET_YES == ipv4_exit)
+ {
+ apptypes[app_idx] = GNUNET_APPLICATION_TYPE_IPV4_GATEWAY;
+ app_idx++;
+ }
+ if (GNUNET_YES == ipv6_exit)
+ {
+ apptypes[app_idx] = GNUNET_APPLICATION_TYPE_IPV6_GATEWAY;
+ app_idx++;
+ }
+
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, "exit", "MAX_CONNECTIONS",
+ &max_connections))
+ max_connections = 1024;
+ exit_argv[0] = GNUNET_strdup ("exit-gnunet");
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "TUN_IFNAME", &tun_ifname))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No entry 'TUN_IFNAME' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[1] = tun_ifname;
+ if (ipv4_enabled)
+ {
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "EXIT_IFNAME", &exit_ifname))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No entry 'EXIT_IFNAME' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[2] = exit_ifname;
+ }
+ else
+ {
+ exit_argv[2] = GNUNET_strdup ("%");
+ }
+
+
+ if (GNUNET_YES == ipv6_enabled)
+ {
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR",
+ &ipv6addr) ||
+ (1 != inet_pton (AF_INET6, ipv6addr, &exit_ipv6addr))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry 'IPV6ADDR' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[3] = ipv6addr;
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX",
+ &ipv6prefix_s))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No entry 'IPV6PREFIX' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[4] = ipv6prefix_s;
+ if ( (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, "exit",
+ "IPV6PREFIX",
+ &ipv6prefix)) ||
+ (ipv6prefix >= 127) )
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+ else
+ {
+ /* IPv6 explicitly disabled */
+ exit_argv[3] = GNUNET_strdup ("-");
+ exit_argv[4] = GNUNET_strdup ("-");
+ }
+ if (GNUNET_YES == ipv4_enabled)
+ {
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR",
+ &ipv4addr) ||
+ (1 != inet_pton (AF_INET, ipv4addr, &exit_ipv4addr))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry for 'IPV4ADDR' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[5] = ipv4addr;
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK",
+ &ipv4mask) ||
+ (1 != inet_pton (AF_INET, ipv4mask, &exit_ipv4mask))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry 'IPV4MASK' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ exit_argv[6] = ipv4mask;
+ }
+ else
+ {
+ /* IPv4 explicitly disabled */
+ exit_argv[5] = GNUNET_strdup ("-");
+ exit_argv[6] = GNUNET_strdup ("-");
+ }
+ exit_argv[7] = NULL;
+
+ udp_services = GNUNET_CONTAINER_multihashmap_create (65536);
+ tcp_services = GNUNET_CONTAINER_multihashmap_create (65536);
+ GNUNET_CONFIGURATION_iterate_sections (cfg, &read_service_conf, NULL);
+
+ connections_map = GNUNET_CONTAINER_multihashmap_create (65536);
+ connections_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ mesh_handle
+ = GNUNET_MESH_connect (cfg, 42 /* queue size */, NULL,
+ &new_tunnel,
+ &clean_tunnel, handlers,
+ apptypes);
+ if (NULL == mesh_handle)
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ helper_handle = GNUNET_HELPER_start ("gnunet-helper-exit",
+ exit_argv,
+ &message_token, NULL);
+}
+
+
+/**
+ * The main function
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ return (GNUNET_OK ==
+ GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-exit",
+ gettext_noop
+ ("Daemon to run to provide an IP exit node for the VPN"),
+ options, &run, NULL)) ? global_ret : 1;
+}
+
+
+/* end of gnunet-daemon-exit.c */
diff --git a/src/exit/gnunet-helper-exit.c b/src/exit/gnunet-helper-exit.c
new file mode 100644
index 0000000..573bb7a
--- /dev/null
+++ b/src/exit/gnunet-helper-exit.c
@@ -0,0 +1,767 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010, 2011, 2012 Christian Grothoff
+
+ GNUnet 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, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file exit/gnunet-helper-exit.c
+ *
+ * @brief the helper for exit nodes. Opens a virtual
+ * network-interface, sends data received on the if to stdout, sends
+ * data received on stdin to the interface. The code also enables
+ * IPv4/IPv6 forwarding and NAT on the current system (the latter on
+ * an interface specified on the command-line); these changes to the
+ * network configuration are NOT automatically undone when the program
+ * is stopped (this is because we cannot be sure that some other
+ * application didn't enable them before or after us; also, these
+ * changes should be mostly harmless as it simply turns the system
+ * into a router).
+ *
+ * @author Philipp Tölke
+ * @author Christian Grothoff
+ *
+ * The following list of people have reviewed this code and considered
+ * it safe since the last modification (if you reviewed it, please
+ * have your name added to the list):
+ *
+ * - Philipp Tölke
+ */
+#include "platform.h"
+#include <linux/if_tun.h>
+
+/**
+ * Need 'struct GNUNET_MessageHeader'.
+ */
+#include "gnunet_common.h"
+
+/**
+ * Need VPN message types.
+ */
+#include "gnunet_protocols.h"
+
+/**
+ * Should we print (interesting|debug) messages that can happen during
+ * normal operation?
+ */
+#define DEBUG GNUNET_NO
+
+/**
+ * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ */
+#define MAX_SIZE 65536
+
+/**
+ * Path to 'sysctl' binary.
+ */
+static const char *sbin_sysctl;
+
+/**
+ * Path to 'iptables' binary.
+ */
+static const char *sbin_iptables;
+
+
+#ifndef _LINUX_IN6_H
+/**
+ * This is in linux/include/net/ipv6.h, but not always exported...
+ */
+struct in6_ifreq
+{
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+#endif
+
+
+
+/**
+ * Run the given command and wait for it to complete.
+ *
+ * @param file name of the binary to run
+ * @param cmd command line arguments (as given to 'execv')
+ * @return 0 on success, 1 on any error
+ */
+static int
+fork_and_exec (const char *file,
+ char *const cmd[])
+{
+ int status;
+ pid_t pid;
+ pid_t ret;
+
+ pid = fork ();
+ if (-1 == pid)
+ {
+ fprintf (stderr,
+ "fork failed: %s\n",
+ strerror (errno));
+ return 1;
+ }
+ if (0 == pid)
+ {
+ /* we are the child process */
+ /* close stdin/stdout to not cause interference
+ with the helper's main protocol! */
+ (void) close (0);
+ (void) close (1);
+ (void) execv (file, cmd);
+ /* can only get here on error */
+ fprintf (stderr,
+ "exec `%s' failed: %s\n",
+ file,
+ strerror (errno));
+ _exit (1);
+ }
+ /* keep running waitpid as long as the only error we get is 'EINTR' */
+ while ( (-1 == (ret = waitpid (pid, &status, 0))) &&
+ (errno == EINTR) );
+ if (-1 == ret)
+ {
+ fprintf (stderr,
+ "waitpid failed: %s\n",
+ strerror (errno));
+ return 1;
+ }
+ if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
+ return 1;
+ /* child process completed and returned success, we're happy */
+ return 0;
+}
+
+
+/**
+ * Creates a tun-interface called dev;
+ *
+ * @param dev is asumed to point to a char[IFNAMSIZ]
+ * if *dev == '\\0', uses the name supplied by the kernel;
+ * @return the fd to the tun or -1 on error
+ */
+static int
+init_tun (char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ if (NULL == dev)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
+ {
+ fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
+ strerror (errno));
+ return -1;
+ }
+
+ if (fd >= FD_SETSIZE)
+ {
+ fprintf (stderr, "File descriptor to large: %d", fd);
+ (void) close (fd);
+ return -1;
+ }
+
+ memset (&ifr, 0, sizeof (ifr));
+ ifr.ifr_flags = IFF_TUN;
+
+ if ('\0' != *dev)
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+ if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
+ {
+ fprintf (stderr,
+ "Error with ioctl on `%s': %s\n", "/dev/net/tun",
+ strerror (errno));
+ (void) close (fd);
+ return -1;
+ }
+ strcpy (dev, ifr.ifr_name);
+ return fd;
+}
+
+
+/**
+ * @brief Sets the IPv6-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv6-Address
+ * @param prefix_len the length of the network-prefix
+ */
+static void
+set_address6 (const char *dev, const char *address, unsigned long prefix_len)
+{
+ struct ifreq ifr;
+ struct sockaddr_in6 sa6;
+ int fd;
+ struct in6_ifreq ifr6;
+
+ /*
+ * parse the new address
+ */
+ memset (&sa6, 0, sizeof (struct sockaddr_in6));
+ sa6.sin6_family = AF_INET6;
+ if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+ strerror (errno));
+ exit (1);
+ }
+
+ if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
+ {
+ fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ /*
+ * Get the index of the if
+ */
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+ if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ memset (&ifr6, 0, sizeof (struct in6_ifreq));
+ ifr6.ifr6_addr = sa6.sin6_addr;
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+
+ /*
+ * Set the address
+ */
+ if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Get the flags
+ */
+ if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Add the UP and RUNNING flags
+ */
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ if (0 != close (fd))
+ {
+ fprintf (stderr, "close failed: %s\n", strerror (errno));
+ exit (1);
+ }
+}
+
+
+/**
+ * @brief Sets the IPv4-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv4-Address
+ * @param mask the netmask
+ */
+static void
+set_address4 (const char *dev, const char *address, const char *mask)
+{
+ int fd;
+ struct sockaddr_in *addr;
+ struct ifreq ifr;
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ addr = (struct sockaddr_in *) &(ifr.ifr_addr);
+ addr->sin_family = AF_INET;
+
+ /*
+ * Parse the address
+ */
+ if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+ strerror (errno));
+ exit (1);
+ }
+
+ if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
+ {
+ fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+ /*
+ * Set the address
+ */
+ if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Parse the netmask
+ */
+ addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
+ if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Set the netmask
+ */
+ if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Get the flags
+ */
+ if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Add the UP and RUNNING flags
+ */
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ if (0 != close (fd))
+ {
+ fprintf (stderr, "close failed: %s\n", strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+}
+
+
+/**
+ * Start forwarding to and from the tunnel.
+ *
+ * @param fd_tun tunnel FD
+ */
+static void
+run (int fd_tun)
+{
+ /*
+ * The buffer filled by reading from fd_tun
+ */
+ unsigned char buftun[MAX_SIZE];
+ ssize_t buftun_size = 0;
+ unsigned char *buftun_read = NULL;
+
+ /*
+ * The buffer filled by reading from stdin
+ */
+ unsigned char bufin[MAX_SIZE];
+ ssize_t bufin_size = 0;
+ size_t bufin_rpos = 0;
+ unsigned char *bufin_read = NULL;
+
+ fd_set fds_w;
+ fd_set fds_r;
+
+ /* read refers to reading from fd_tun, writing to stdout */
+ int read_open = 1;
+
+ /* write refers to reading from stdin, writing to fd_tun */
+ int write_open = 1;
+
+ while ((1 == read_open) || (1 == write_open))
+ {
+ FD_ZERO (&fds_w);
+ FD_ZERO (&fds_r);
+
+ /*
+ * We are supposed to read and the buffer is empty
+ * -> select on read from tun
+ */
+ if (read_open && (0 == buftun_size))
+ FD_SET (fd_tun, &fds_r);
+
+ /*
+ * We are supposed to read and the buffer is not empty
+ * -> select on write to stdout
+ */
+ if (read_open && (0 != buftun_size))
+ FD_SET (1, &fds_w);
+
+ /*
+ * We are supposed to write and the buffer is empty
+ * -> select on read from stdin
+ */
+ if (write_open && (NULL == bufin_read))
+ FD_SET (0, &fds_r);
+
+ /*
+ * We are supposed to write and the buffer is not empty
+ * -> select on write to tun
+ */
+ if (write_open && (NULL != bufin_read))
+ FD_SET (fd_tun, &fds_w);
+
+ int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
+
+ if (-1 == r)
+ {
+ if (EINTR == errno)
+ continue;
+ fprintf (stderr, "select failed: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ if (r > 0)
+ {
+ if (FD_ISSET (fd_tun, &fds_r))
+ {
+ buftun_size =
+ read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
+ MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
+ if (-1 == buftun_size)
+ {
+ fprintf (stderr, "read-error: %s\n", strerror (errno));
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else if (0 == buftun_size)
+ {
+#if DEBUG
+ fprintf (stderr, "EOF on tun\n");
+#endif
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else
+ {
+ buftun_read = buftun;
+ struct GNUNET_MessageHeader *hdr =
+ (struct GNUNET_MessageHeader *) buftun;
+ buftun_size += sizeof (struct GNUNET_MessageHeader);
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (buftun_size);
+ }
+ }
+ else if (FD_ISSET (1, &fds_w))
+ {
+ ssize_t written = write (1, buftun_read, buftun_size);
+
+ if (-1 == written)
+ {
+#if !DEBUG
+ if (errno != EPIPE)
+#endif
+ fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else if (0 == written)
+ {
+ fprintf (stderr, "write returned 0!?\n");
+ exit (1);
+ }
+ else
+ {
+ buftun_size -= written;
+ buftun_read += written;
+ }
+ }
+
+ if (FD_ISSET (0, &fds_r))
+ {
+ bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
+ if (-1 == bufin_size)
+ {
+ fprintf (stderr, "read-error: %s\n", strerror (errno));
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else if (0 == bufin_size)
+ {
+#if DEBUG
+ fprintf (stderr, "EOF on stdin\n");
+#endif
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else
+ {
+ struct GNUNET_MessageHeader *hdr;
+
+PROCESS_BUFFER:
+ bufin_rpos += bufin_size;
+ if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
+ continue;
+ hdr = (struct GNUNET_MessageHeader *) bufin;
+ if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
+ {
+ fprintf (stderr, "protocol violation!\n");
+ exit (1);
+ }
+ if (ntohs (hdr->size) > bufin_rpos)
+ continue;
+ bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
+ bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
+ bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
+ }
+ }
+ else if (FD_ISSET (fd_tun, &fds_w))
+ {
+ ssize_t written = write (fd_tun, bufin_read, bufin_size);
+
+ if (-1 == written)
+ {
+ fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else if (0 == written)
+ {
+ fprintf (stderr, "write returned 0!?\n");
+ exit (1);
+ }
+ else
+ {
+ bufin_size -= written;
+ bufin_read += written;
+ if (0 == bufin_size)
+ {
+ memmove (bufin, bufin_read, bufin_rpos);
+ bufin_read = NULL; /* start reading again */
+ bufin_size = 0;
+ goto PROCESS_BUFFER;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Open VPN tunnel interface.
+ *
+ * @param argc must be 6
+ * @param argv 0: binary name ("gnunet-helper-exit")
+ * 1: tunnel interface name ("gnunet-exit")
+ * 2: IPv4 "physical" interface name ("eth0"), or "%" to not do IPv4 NAT
+ * 3: IPv6 address ("::1"), or "-" to skip IPv6
+ * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"]
+ * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4
+ * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"]
+ */
+int
+main (int argc, char **argv)
+{
+ char dev[IFNAMSIZ];
+ int fd_tun;
+ int global_ret;
+
+ if (7 != argc)
+ {
+ fprintf (stderr, "Fatal: must supply 6 arguments!\n");
+ return 1;
+ }
+ if ( (0 == strcmp (argv[3], "-")) &&
+ (0 == strcmp (argv[5], "-")) )
+ {
+ fprintf (stderr, "Fatal: disabling both IPv4 and IPv6 makes no sense.\n");
+ return 1;
+ }
+ if (0 == access ("/sbin/iptables", X_OK))
+ sbin_iptables = "/sbin/iptables";
+ else if (0 == access ("/usr/sbin/iptables", X_OK))
+ sbin_iptables = "/usr/sbin/iptables";
+ else
+ {
+ fprintf (stderr,
+ "Fatal: executable iptables not found in approved directories: %s\n",
+ strerror (errno));
+ return 1;
+ }
+ if (0 == access ("/sbin/sysctl", X_OK))
+ sbin_sysctl = "/sbin/sysctl";
+ else if (0 == access ("/usr/sbin/sysctl", X_OK))
+ sbin_sysctl = "/usr/sbin/sysctl";
+ else
+ {
+ fprintf (stderr,
+ "Fatal: executable sysctl not found in approved directories: %s\n",
+ strerror (errno));
+ return 1;
+ }
+
+ strncpy (dev, argv[1], IFNAMSIZ);
+ dev[IFNAMSIZ - 1] = '\0';
+
+ if (-1 == (fd_tun = init_tun (dev)))
+ {
+ fprintf (stderr,
+ "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
+ dev,
+ argv[3],
+ argv[4],
+ argv[5],
+ argv[6]);
+ return 1;
+ }
+
+ if (0 != strcmp (argv[3], "-"))
+ {
+ {
+ const char *address = argv[3];
+ long prefix_len = atol (argv[4]);
+
+ if ((prefix_len < 1) || (prefix_len > 127))
+ {
+ fprintf (stderr, "Fatal: prefix_len out of range\n");
+ return 1;
+ }
+ set_address6 (dev, address, prefix_len);
+ }
+ {
+ char *const sysctl_args[] =
+ {
+ "sysctl", "-w", "net.ipv6.conf.all.forwarding=1", NULL
+ };
+ if (0 != fork_and_exec (sbin_sysctl,
+ sysctl_args))
+ {
+ fprintf (stderr,
+ "Failed to enable IPv6 forwarding. Will continue anyway.\n");
+ }
+ }
+ }
+
+ if (0 != strcmp (argv[5], "-"))
+ {
+ {
+ const char *address = argv[5];
+ const char *mask = argv[6];
+
+ set_address4 (dev, address, mask);
+ }
+ {
+ char *const sysctl_args[] =
+ {
+ "sysctl", "-w", "net.ipv4.ip_forward=1", NULL
+ };
+ if (0 != fork_and_exec (sbin_sysctl,
+ sysctl_args))
+ {
+ fprintf (stderr,
+ "Failed to enable IPv4 forwarding. Will continue anyway.\n");
+ }
+ }
+ if (0 != strcmp (argv[2], "%"))
+ {
+ char *const iptables_args[] =
+ {
+ "iptables", "-t", "nat", "-A", "POSTROUTING", "-o", argv[2], "-j", "MASQUERADE", NULL
+ };
+ if (0 != fork_and_exec (sbin_iptables,
+ iptables_args))
+ {
+ fprintf (stderr,
+ "Failed to enable IPv4 masquerading (NAT). Will continue anyway.\n");
+ }
+ }
+ }
+
+ uid_t uid = getuid ();
+#ifdef HAVE_SETRESUID
+ if (0 != setresuid (uid, uid, uid))
+ {
+ fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
+ global_ret = 2;
+ goto cleanup;
+ }
+#else
+ if (0 != (setuid (uid) | seteuid (uid)))
+ {
+ fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
+ global_ret = 2;
+ goto cleanup;
+ }
+#endif
+
+ if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
+ {
+ fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
+ strerror (errno));
+ /* no exit, we might as well die with SIGPIPE should it ever happen */
+ }
+ run (fd_tun);
+ global_ret = 0;
+ cleanup:
+ close (fd_tun);
+ return global_ret;
+}
+
+/* end of gnunet-helper-exit.c */