diff options
Diffstat (limited to 'src/exit')
-rw-r--r-- | src/exit/Makefile.am | 43 | ||||
-rw-r--r-- | src/exit/Makefile.in | 714 | ||||
-rw-r--r-- | src/exit/exit.conf | 47 | ||||
-rw-r--r-- | src/exit/exit.h | 313 | ||||
-rw-r--r-- | src/exit/gnunet-daemon-exit.c | 3240 | ||||
-rw-r--r-- | src/exit/gnunet-helper-exit.c | 767 |
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 */ |