diff options
author | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
---|---|---|
committer | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
commit | 2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch) | |
tree | 394774c0f735199b57d51a2d3840356317853fe1 /src/core |
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/core')
35 files changed, 11189 insertions, 0 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am new file mode 100644 index 0000000..ad9bddc --- /dev/null +++ b/src/core/Makefile.am @@ -0,0 +1,137 @@ +INCLUDES = -I$(top_srcdir)/src/include + +pkgcfgdir= $(pkgdatadir)/config.d/ + +pkgcfg_DATA = \ + core.conf + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + + +lib_LTLIBRARIES = \ + libgnunetcore.la + +libgnunetcore_la_SOURCES = \ + core_api.c core.h \ + core_api_iterate_peers.c +libgnunetcore_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) +libgnunetcore_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-service-core \ + gnunet-core-list-connections + +gnunet_service_core_SOURCES = \ + gnunet-service-core.c gnunet-service-core.h \ + gnunet-service-core_clients.c gnunet-service-core_clients.h \ + gnunet-service-core_neighbours.c gnunet-service-core_neighbours.h \ + gnunet-service-core_kx.c gnunet-service-core_kx.h \ + gnunet-service-core_sessions.c gnunet-service-core_sessions.h \ + gnunet-service-core_typemap.c gnunet-service-core_typemap.h +gnunet_service_core_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) -lz + + +gnunet_core_list_connections_SOURCES = \ + gnunet-core-list-connections.c +gnunet_core_list_connections_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la +gnunet_core_list_connections_DEPENDENCIES = \ + libgnunetcore.la + +check_PROGRAMS = \ + test_core_api_start_only \ + test_core_api \ + test_core_api_reliability \ + test_core_quota_compliance_symmetric \ + test_core_quota_compliance_asymmetric_send_limited \ + test_core_quota_compliance_asymmetric_recv_limited \ + test_core_api_send_to_self + +if ENABLE_TEST_RUN +TESTS = $(check_PROGRAMS) +endif + +test_core_api_SOURCES = \ + test_core_api.c +test_core_api_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_reliability_SOURCES = \ + test_core_api_reliability.c +test_core_api_reliability_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_send_to_self_SOURCES = \ + test_core_api_send_to_self.c +test_core_api_send_to_self_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_start_only_SOURCES = \ + test_core_api_start_only.c +test_core_api_start_only_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_quota_compliance_symmetric_SOURCES = \ + test_core_quota_compliance.c +test_core_quota_compliance_symmetric_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +test_core_quota_compliance_asymmetric_send_limited_SOURCES = \ + test_core_quota_compliance.c +test_core_quota_compliance_asymmetric_send_limited_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +test_core_quota_compliance_asymmetric_recv_limited_SOURCES = \ + test_core_quota_compliance.c +test_core_quota_compliance_asymmetric_recv_limited_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +EXTRA_DIST = \ + test_core_defaults.conf \ + test_core_api_data.conf \ + test_core_api_peer1.conf \ + test_core_api_peer2.conf \ + test_core_api_send_to_self.conf \ + test_core_quota_asymmetric_recv_limited_peer1.conf \ + test_core_quota_asymmetric_recv_limited_peer2.conf \ + test_core_quota_asymmetric_send_limit_peer1.conf \ + test_core_quota_asymmetric_send_limit_peer2.conf \ + test_core_quota_peer1.conf \ + test_core_quota_peer2.conf diff --git a/src/core/Makefile.in b/src/core/Makefile.in new file mode 100644 index 0000000..42177b4 --- /dev/null +++ b/src/core/Makefile.in @@ -0,0 +1,1081 @@ +# 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-service-core$(EXEEXT) \ + gnunet-core-list-connections$(EXEEXT) +check_PROGRAMS = test_core_api_start_only$(EXEEXT) \ + test_core_api$(EXEEXT) test_core_api_reliability$(EXEEXT) \ + test_core_quota_compliance_symmetric$(EXEEXT) \ + test_core_quota_compliance_asymmetric_send_limited$(EXEEXT) \ + test_core_quota_compliance_asymmetric_recv_limited$(EXEEXT) \ + test_core_api_send_to_self$(EXEEXT) +subdir = src/core +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/core.conf.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 = core.conf +CONFIG_CLEAN_VPATH_FILES = +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' +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(pkgcfgdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgnunetcore_la_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgnunetcore_la_OBJECTS = core_api.lo core_api_iterate_peers.lo +libgnunetcore_la_OBJECTS = $(am_libgnunetcore_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libgnunetcore_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgnunetcore_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_gnunet_core_list_connections_OBJECTS = \ + gnunet-core-list-connections.$(OBJEXT) +gnunet_core_list_connections_OBJECTS = \ + $(am_gnunet_core_list_connections_OBJECTS) +am_gnunet_service_core_OBJECTS = gnunet-service-core.$(OBJEXT) \ + gnunet-service-core_clients.$(OBJEXT) \ + gnunet-service-core_neighbours.$(OBJEXT) \ + gnunet-service-core_kx.$(OBJEXT) \ + gnunet-service-core_sessions.$(OBJEXT) \ + gnunet-service-core_typemap.$(OBJEXT) +gnunet_service_core_OBJECTS = $(am_gnunet_service_core_OBJECTS) +gnunet_service_core_DEPENDENCIES = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(am__DEPENDENCIES_1) +am_test_core_api_OBJECTS = test_core_api.$(OBJEXT) +test_core_api_OBJECTS = $(am_test_core_api_OBJECTS) +test_core_api_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_core_api_reliability_OBJECTS = \ + test_core_api_reliability.$(OBJEXT) +test_core_api_reliability_OBJECTS = \ + $(am_test_core_api_reliability_OBJECTS) +test_core_api_reliability_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_core_api_send_to_self_OBJECTS = \ + test_core_api_send_to_self.$(OBJEXT) +test_core_api_send_to_self_OBJECTS = \ + $(am_test_core_api_send_to_self_OBJECTS) +test_core_api_send_to_self_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_core_api_start_only_OBJECTS = \ + test_core_api_start_only.$(OBJEXT) +test_core_api_start_only_OBJECTS = \ + $(am_test_core_api_start_only_OBJECTS) +test_core_api_start_only_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_core_quota_compliance_asymmetric_recv_limited_OBJECTS = \ + test_core_quota_compliance.$(OBJEXT) +test_core_quota_compliance_asymmetric_recv_limited_OBJECTS = $(am_test_core_quota_compliance_asymmetric_recv_limited_OBJECTS) +test_core_quota_compliance_asymmetric_recv_limited_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la +am_test_core_quota_compliance_asymmetric_send_limited_OBJECTS = \ + test_core_quota_compliance.$(OBJEXT) +test_core_quota_compliance_asymmetric_send_limited_OBJECTS = $(am_test_core_quota_compliance_asymmetric_send_limited_OBJECTS) +test_core_quota_compliance_asymmetric_send_limited_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la +am_test_core_quota_compliance_symmetric_OBJECTS = \ + test_core_quota_compliance.$(OBJEXT) +test_core_quota_compliance_symmetric_OBJECTS = \ + $(am_test_core_quota_compliance_symmetric_OBJECTS) +test_core_quota_compliance_symmetric_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la +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 = $(libgnunetcore_la_SOURCES) \ + $(gnunet_core_list_connections_SOURCES) \ + $(gnunet_service_core_SOURCES) $(test_core_api_SOURCES) \ + $(test_core_api_reliability_SOURCES) \ + $(test_core_api_send_to_self_SOURCES) \ + $(test_core_api_start_only_SOURCES) \ + $(test_core_quota_compliance_asymmetric_recv_limited_SOURCES) \ + $(test_core_quota_compliance_asymmetric_send_limited_SOURCES) \ + $(test_core_quota_compliance_symmetric_SOURCES) +DIST_SOURCES = $(libgnunetcore_la_SOURCES) \ + $(gnunet_core_list_connections_SOURCES) \ + $(gnunet_service_core_SOURCES) $(test_core_api_SOURCES) \ + $(test_core_api_reliability_SOURCES) \ + $(test_core_api_send_to_self_SOURCES) \ + $(test_core_api_start_only_SOURCES) \ + $(test_core_quota_compliance_asymmetric_recv_limited_SOURCES) \ + $(test_core_quota_compliance_asymmetric_send_limited_SOURCES) \ + $(test_core_quota_compliance_symmetric_SOURCES) +DATA = $(pkgcfg_DATA) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +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 +pkgcfgdir = $(pkgdatadir)/config.d/ +pkgcfg_DATA = \ + core.conf + +@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0 +@USE_COVERAGE_TRUE@XLIB = -lgcov +lib_LTLIBRARIES = \ + libgnunetcore.la + +libgnunetcore_la_SOURCES = \ + core_api.c core.h \ + core_api_iterate_peers.c + +libgnunetcore_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) + +libgnunetcore_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +gnunet_service_core_SOURCES = \ + gnunet-service-core.c gnunet-service-core.h \ + gnunet-service-core_clients.c gnunet-service-core_clients.h \ + gnunet-service-core_neighbours.c gnunet-service-core_neighbours.h \ + gnunet-service-core_kx.c gnunet-service-core_kx.h \ + gnunet-service-core_sessions.c gnunet-service-core_sessions.h \ + gnunet-service-core_typemap.c gnunet-service-core_typemap.h + +gnunet_service_core_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) -lz + +gnunet_core_list_connections_SOURCES = \ + gnunet-core-list-connections.c + +gnunet_core_list_connections_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la + +gnunet_core_list_connections_DEPENDENCIES = \ + libgnunetcore.la + +@ENABLE_TEST_RUN_TRUE@TESTS = $(check_PROGRAMS) +test_core_api_SOURCES = \ + test_core_api.c + +test_core_api_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_reliability_SOURCES = \ + test_core_api_reliability.c + +test_core_api_reliability_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_send_to_self_SOURCES = \ + test_core_api_send_to_self.c + +test_core_api_send_to_self_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_api_start_only_SOURCES = \ + test_core_api_start_only.c + +test_core_api_start_only_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_core_quota_compliance_symmetric_SOURCES = \ + test_core_quota_compliance.c + +test_core_quota_compliance_symmetric_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +test_core_quota_compliance_asymmetric_send_limited_SOURCES = \ + test_core_quota_compliance.c + +test_core_quota_compliance_asymmetric_send_limited_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +test_core_quota_compliance_asymmetric_recv_limited_SOURCES = \ + test_core_quota_compliance.c + +test_core_quota_compliance_asymmetric_recv_limited_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + +EXTRA_DIST = \ + test_core_defaults.conf \ + test_core_api_data.conf \ + test_core_api_peer1.conf \ + test_core_api_peer2.conf \ + test_core_api_send_to_self.conf \ + test_core_quota_asymmetric_recv_limited_peer1.conf \ + test_core_quota_asymmetric_recv_limited_peer2.conf \ + test_core_quota_asymmetric_send_limit_peer1.conf \ + test_core_quota_asymmetric_send_limit_peer2.conf \ + test_core_quota_peer1.conf \ + test_core_quota_peer2.conf + +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/core/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/core/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): +core.conf: $(top_builddir)/config.status $(srcdir)/core.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libgnunetcore.la: $(libgnunetcore_la_OBJECTS) $(libgnunetcore_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgnunetcore_la_LINK) -rpath $(libdir) $(libgnunetcore_la_OBJECTS) $(libgnunetcore_la_LIBADD) $(LIBS) +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 + +clean-checkPROGRAMS: + @list='$(check_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-core-list-connections$(EXEEXT): $(gnunet_core_list_connections_OBJECTS) $(gnunet_core_list_connections_DEPENDENCIES) + @rm -f gnunet-core-list-connections$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_core_list_connections_OBJECTS) $(gnunet_core_list_connections_LDADD) $(LIBS) +gnunet-service-core$(EXEEXT): $(gnunet_service_core_OBJECTS) $(gnunet_service_core_DEPENDENCIES) + @rm -f gnunet-service-core$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_service_core_OBJECTS) $(gnunet_service_core_LDADD) $(LIBS) +test_core_api$(EXEEXT): $(test_core_api_OBJECTS) $(test_core_api_DEPENDENCIES) + @rm -f test_core_api$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_api_OBJECTS) $(test_core_api_LDADD) $(LIBS) +test_core_api_reliability$(EXEEXT): $(test_core_api_reliability_OBJECTS) $(test_core_api_reliability_DEPENDENCIES) + @rm -f test_core_api_reliability$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_api_reliability_OBJECTS) $(test_core_api_reliability_LDADD) $(LIBS) +test_core_api_send_to_self$(EXEEXT): $(test_core_api_send_to_self_OBJECTS) $(test_core_api_send_to_self_DEPENDENCIES) + @rm -f test_core_api_send_to_self$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_api_send_to_self_OBJECTS) $(test_core_api_send_to_self_LDADD) $(LIBS) +test_core_api_start_only$(EXEEXT): $(test_core_api_start_only_OBJECTS) $(test_core_api_start_only_DEPENDENCIES) + @rm -f test_core_api_start_only$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_api_start_only_OBJECTS) $(test_core_api_start_only_LDADD) $(LIBS) +test_core_quota_compliance_asymmetric_recv_limited$(EXEEXT): $(test_core_quota_compliance_asymmetric_recv_limited_OBJECTS) $(test_core_quota_compliance_asymmetric_recv_limited_DEPENDENCIES) + @rm -f test_core_quota_compliance_asymmetric_recv_limited$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_quota_compliance_asymmetric_recv_limited_OBJECTS) $(test_core_quota_compliance_asymmetric_recv_limited_LDADD) $(LIBS) +test_core_quota_compliance_asymmetric_send_limited$(EXEEXT): $(test_core_quota_compliance_asymmetric_send_limited_OBJECTS) $(test_core_quota_compliance_asymmetric_send_limited_DEPENDENCIES) + @rm -f test_core_quota_compliance_asymmetric_send_limited$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_quota_compliance_asymmetric_send_limited_OBJECTS) $(test_core_quota_compliance_asymmetric_send_limited_LDADD) $(LIBS) +test_core_quota_compliance_symmetric$(EXEEXT): $(test_core_quota_compliance_symmetric_OBJECTS) $(test_core_quota_compliance_symmetric_DEPENDENCIES) + @rm -f test_core_quota_compliance_symmetric$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_core_quota_compliance_symmetric_OBJECTS) $(test_core_quota_compliance_symmetric_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_api.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/core_api_iterate_peers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-core-list-connections.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core_clients.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core_kx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core_neighbours.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core_sessions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-core_typemap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_core_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_core_api_reliability.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_core_api_send_to_self.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_core_api_start_only.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_core_quota_compliance.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-pkgcfgDATA: $(pkgcfg_DATA) + @$(NORMAL_INSTALL) + test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)" + @list='$(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-pkgcfgDATA: + @$(NORMAL_UNINSTALL) + @list='$(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 + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(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-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES 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-pkgcfgDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +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-libLTLIBRARIES \ + uninstall-pkgcfgDATA + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES 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-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgcfgDATA 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-libLTLIBRARIES \ + uninstall-pkgcfgDATA + + +# 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/core/core.conf.in b/src/core/core.conf.in new file mode 100644 index 0000000..84e2df9 --- /dev/null +++ b/src/core/core.conf.in @@ -0,0 +1,22 @@ +[core] +AUTOSTART = YES +@UNIXONLY@ PORT = 2092 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-core +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = /tmp/gnunet-service-core.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES +# DISABLE_SOCKET_FORWARDING = NO +# DEBUG = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = diff --git a/src/core/core.h b/src/core/core.h new file mode 100644 index 0000000..4942ad0 --- /dev/null +++ b/src/core/core.h @@ -0,0 +1,380 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/core.h + * @brief common internal definitions for core service + * @author Christian Grothoff + */ +#ifndef CORE_H +#define CORE_H + +#include "gnunet_bandwidth_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_time_lib.h" + +/** + * General core debugging. + */ +#define DEBUG_CORE GNUNET_EXTRA_LOGGING + +/** + * Definition of bits in the InitMessage's options field that specify + * which events this client cares about. Note that inbound messages + * for handlers that were specifically registered are always + * transmitted to the client. + */ +#define GNUNET_CORE_OPTION_NOTHING 0 +#define GNUNET_CORE_OPTION_SEND_STATUS_CHANGE 4 +#define GNUNET_CORE_OPTION_SEND_FULL_INBOUND 8 +#define GNUNET_CORE_OPTION_SEND_HDR_INBOUND 16 +#define GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND 32 +#define GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND 64 + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message transmitted core clients to gnunet-service-core + * to start the interaction. This header is followed by + * uint16_t type values specifying which messages this + * client is interested in. + */ +struct InitMessage +{ + + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT. + */ + struct GNUNET_MessageHeader header; + + /** + * Options, see GNUNET_CORE_OPTION_ values. + */ + uint32_t options GNUNET_PACKED; + +}; + + +/** + * Message transmitted by the gnunet-service-core process + * to its clients in response to an INIT message. + */ +struct InitReplyMessage +{ + + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Public key of the local peer. + */ + struct GNUNET_PeerIdentity my_identity; + +}; + + +/** + * Message sent by the service to clients to notify them + * about a peer connecting. + */ +struct ConnectNotifyMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT + */ + struct GNUNET_MessageHeader header; + + /** + * Number of ATS key-value pairs that follow this struct + * (excluding the 0-terminator). + */ + uint32_t ats_count GNUNET_PACKED; + + /** + * Identity of the connecting peer. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message sent by the service to clients to notify them + * about a peer changing status. + */ +struct PeerStatusNotifyMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_PEER_STATUS + */ + struct GNUNET_MessageHeader header; + + /** + * Number of ATS key-value pairs that follow this struct + * (excluding the 0-terminator). + */ + uint32_t ats_count GNUNET_PACKED; + + /** + * When the peer would time out (unless we see activity) + */ + struct GNUNET_TIME_AbsoluteNBO timeout; + + /** + * Available bandwidth from the peer. + */ + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in; + + /** + * Available bandwidth to the peer. + */ + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out; + + /** + * Identity of the peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * First of the ATS information blocks (we must have at least + * one due to the 0-termination requirement). + */ + struct GNUNET_ATS_Information ats; + +}; + + +/** + * Message sent by the service to clients to notify them + * about a peer disconnecting. + */ +struct DisconnectNotifyMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT. + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Identity of the connecting peer. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message sent by the service to clients to notify them about + * messages being received or transmitted. This overall message is + * followed by the real message, or just the header of the real + * message (depending on the client's preferences). The receiver can + * tell if he got the full message or only a partial message by + * looking at the size field in the header of NotifyTrafficMessage and + * checking it with the size field in the message that follows. + */ +struct NotifyTrafficMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND + * or GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND. + */ + struct GNUNET_MessageHeader header; + + /** + * Number of ATS key-value pairs that follow this struct + * (excluding the 0-terminator). + */ + uint32_t ats_count GNUNET_PACKED; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + + /** + * First of the ATS information blocks (we must have at least + * one due to the 0-termination requirement). + */ + struct GNUNET_ATS_Information ats; + +}; + + +/** + * Client notifying core about the maximum-priority + * message it has in the queue for a particular target. + */ +struct SendMessageRequest +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * How important is this message? + */ + uint32_t priority GNUNET_PACKED; + + /** + * By what time would the sender really like to see this + * message transmitted? + */ + struct GNUNET_TIME_AbsoluteNBO deadline; + + /** + * Identity of the intended target. + */ + struct GNUNET_PeerIdentity peer; + + /** + * How large is the client's message queue for this peer? + */ + uint32_t queue_size GNUNET_PACKED; + + /** + * How large is the message? + */ + uint16_t size GNUNET_PACKED; + + /** + * Counter for this peer to match SMRs to replies. + */ + uint16_t smr_id GNUNET_PACKED; + +}; + + +/** + * Core notifying client that it is allowed to now + * transmit a message to the given target + * (response to GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST). + */ +struct SendMessageReady +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_SEND_READY + */ + struct GNUNET_MessageHeader header; + + /** + * How many bytes are allowed for transmission? + * Guaranteed to be at least as big as the requested size, + * or ZERO if the request is rejected (will timeout, + * peer disconnected, queue full, etc.). + */ + uint16_t size GNUNET_PACKED; + + /** + * smr_id from the request. + */ + uint16_t smr_id GNUNET_PACKED; + + /** + * Identity of the intended target. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Client asking core to transmit a particular message to a particular + * target (response to GNUNET_MESSAGE_TYPE_CORE_SEND_READY). + */ +struct SendMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_SEND + */ + struct GNUNET_MessageHeader header; + + /** + * How important is this message? + */ + uint32_t priority GNUNET_PACKED; + + /** + * By what time would the sender really like to see this + * message transmitted? + */ + struct GNUNET_TIME_AbsoluteNBO deadline; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + + /** + * GNUNET_YES if corking is allowed, GNUNET_NO if not. + */ + uint32_t cork GNUNET_PACKED; + + /** + * Always 0. + */ + uint64_t reserved GNUNET_PACKED; + +}; + + +/** + * Client asking core to connect to a particular target. There is no + * response from the core to this type of request (however, if an + * actual connection is created or destroyed, be it because of this + * type request or not, the core generally needs to notify the + * clients). + */ +struct ConnectMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_REQUEST_CONNECT or + * GNUNET_MESSAGE_TYPE_REQUEST_DISCONNECT. + */ + struct GNUNET_MessageHeader header; + + /** + * For alignment. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Identity of the other peer. + */ + struct GNUNET_PeerIdentity peer; + +}; +GNUNET_NETWORK_STRUCT_END +#endif +/* end of core.h */ diff --git a/src/core/core_api.c b/src/core/core_api.c new file mode 100644 index 0000000..66df134 --- /dev/null +++ b/src/core/core_api.c @@ -0,0 +1,1493 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/core_api.c + * @brief core service; this is the main API for encrypted P2P + * communications + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_core_service.h" +#include "core.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "core-api",__VA_ARGS__) + +/** + * Information we track for each peer. + */ +struct PeerRecord +{ + + /** + * We generally do NOT keep peer records in a DLL; this + * DLL is only used IF this peer's 'pending_head' message + * is ready for transmission. + */ + struct PeerRecord *prev; + + /** + * We generally do NOT keep peer records in a DLL; this + * DLL is only used IF this peer's 'pending_head' message + * is ready for transmission. + */ + struct PeerRecord *next; + + /** + * Peer the record is about. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Corresponding core handle. + */ + struct GNUNET_CORE_Handle *ch; + + /** + * Head of doubly-linked list of pending requests. + * Requests are sorted by deadline *except* for HEAD, + * which is only modified upon transmission to core. + */ + struct GNUNET_CORE_TransmitHandle *pending_head; + + /** + * Tail of doubly-linked list of pending requests. + */ + struct GNUNET_CORE_TransmitHandle *pending_tail; + + /** + * ID of timeout task for the 'pending_head' handle + * which is the one with the smallest timeout. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * ID of task to run 'next_request_transmission'. + */ + GNUNET_SCHEDULER_TaskIdentifier ntr_task; + + /** + * Current size of the queue of pending requests. + */ + unsigned int queue_size; + + /** + * SendMessageRequest ID generator for this peer. + */ + uint16_t smr_id_gen; + +}; + + +/** + * Type of function called upon completion. + * + * @param cls closure + * @param success GNUNET_OK on success (which for request_connect + * ONLY means that we transmitted the connect request to CORE, + * it does not mean that we are actually now connected!); + * GNUNET_NO on timeout, + * GNUNET_SYSERR if core was shut down + */ +typedef void (*GNUNET_CORE_ControlContinuation) (void *cls, int success); + + +/** + * Entry in a doubly-linked list of control messages to be transmitted + * to the core service. Control messages include traffic allocation, + * connection requests and of course our initial 'init' request. + * + * The actual message is allocated at the end of this struct. + */ +struct ControlMessage +{ + /** + * This is a doubly-linked list. + */ + struct ControlMessage *next; + + /** + * This is a doubly-linked list. + */ + struct ControlMessage *prev; + + /** + * Function to run after transmission failed/succeeded. + */ + GNUNET_CORE_ControlContinuation cont; + + /** + * Closure for 'cont'. + */ + void *cont_cls; + + /** + * Transmit handle (if one is associated with this ControlMessage), or NULL. + */ + struct GNUNET_CORE_TransmitHandle *th; +}; + + + +/** + * Context for the core service connection. + */ +struct GNUNET_CORE_Handle +{ + + /** + * Configuration we're using. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Closure for the various callbacks. + */ + void *cls; + + /** + * Function to call once we've handshaked with the core service. + */ + GNUNET_CORE_StartupCallback init; + + /** + * Function to call whenever we're notified about a peer connecting. + */ + GNUNET_CORE_ConnectEventHandler connects; + + /** + * Function to call whenever we're notified about a peer disconnecting. + */ + GNUNET_CORE_DisconnectEventHandler disconnects; + + /** + * Function to call whenever we receive an inbound message. + */ + GNUNET_CORE_MessageCallback inbound_notify; + + /** + * Function to call whenever we receive an outbound message. + */ + GNUNET_CORE_MessageCallback outbound_notify; + + /** + * Function handlers for messages of particular type. + */ + const struct GNUNET_CORE_MessageHandler *handlers; + + /** + * Our connection to the service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Handle for our current transmission request. + */ + struct GNUNET_CLIENT_TransmitHandle *cth; + + /** + * Head of doubly-linked list of pending requests. + */ + struct ControlMessage *control_pending_head; + + /** + * Tail of doubly-linked list of pending requests. + */ + struct ControlMessage *control_pending_tail; + + /** + * Head of doubly-linked list of peers that are core-approved + * to send their next message. + */ + struct PeerRecord *ready_peer_head; + + /** + * Tail of doubly-linked list of peers that are core-approved + * to send their next message. + */ + struct PeerRecord *ready_peer_tail; + + /** + * Hash map listing all of the peers that we are currently + * connected to. + */ + struct GNUNET_CONTAINER_MultiHashMap *peers; + + /** + * Identity of this peer. + */ + struct GNUNET_PeerIdentity me; + + /** + * ID of reconnect task (if any). + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Current delay we use for re-trying to connect to core. + */ + struct GNUNET_TIME_Relative retry_backoff; + + /** + * Number of messages we are allowed to queue per target. + */ + unsigned int queue_size; + + /** + * Number of entries in the handlers array. + */ + unsigned int hcnt; + + /** + * For inbound notifications without a specific handler, do + * we expect to only receive headers? + */ + int inbound_hdr_only; + + /** + * For outbound notifications without a specific handler, do + * we expect to only receive headers? + */ + int outbound_hdr_only; + + /** + * Are we currently disconnected and hence unable to forward + * requests? + */ + int currently_down; + +}; + + +/** + * Handle for a transmission request. + */ +struct GNUNET_CORE_TransmitHandle +{ + + /** + * We keep active transmit handles in a doubly-linked list. + */ + struct GNUNET_CORE_TransmitHandle *next; + + /** + * We keep active transmit handles in a doubly-linked list. + */ + struct GNUNET_CORE_TransmitHandle *prev; + + /** + * Corresponding peer record. + */ + struct PeerRecord *peer; + + /** + * Corresponding SEND_REQUEST message. Only non-NULL + * while SEND_REQUEST message is pending. + */ + struct ControlMessage *cm; + + /** + * Function that will be called to get the actual request + * (once we are ready to transmit this request to the core). + * The function will be called with a NULL buffer to signal + * timeout. + */ + GNUNET_CONNECTION_TransmitReadyNotify get_message; + + /** + * Closure for get_message. + */ + void *get_message_cls; + + /** + * Timeout for this handle. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * How important is this message? + */ + uint32_t priority; + + /** + * Size of this request. + */ + uint16_t msize; + + /** + * Send message request ID for this request. + */ + uint16_t smr_id; + + /** + * Is corking allowed? + */ + int cork; + +}; + + +/** + * Our current client connection went down. Clean it up + * and try to reconnect! + * + * @param h our handle to the core service + */ +static void +reconnect (struct GNUNET_CORE_Handle *h); + + +/** + * Task schedule to try to re-connect to core. + * + * @param cls the 'struct GNUNET_CORE_Handle' + * @param tc task context + */ +static void +reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CORE_Handle *h = cls; + + h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service after delay\n"); +#endif + reconnect (h); +} + + +/** + * Notify clients about disconnect and free + * the entry for connected peer. + * + * @param cls the 'struct GNUNET_CORE_Handle*' + * @param key the peer identity (not used) + * @param value the 'struct PeerRecord' to free. + * @return GNUNET_YES (continue) + */ +static int +disconnect_and_free_peer_entry (void *cls, const GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_CORE_Handle *h = cls; + struct GNUNET_CORE_TransmitHandle *th; + struct PeerRecord *pr = value; + + if (pr->timeout_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (pr->timeout_task); + pr->timeout_task = GNUNET_SCHEDULER_NO_TASK; + } + if (pr->ntr_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (pr->ntr_task); + pr->ntr_task = GNUNET_SCHEDULER_NO_TASK; + } + if ((pr->prev != NULL) || (pr->next != NULL) || (h->ready_peer_head == pr)) + GNUNET_CONTAINER_DLL_remove (h->ready_peer_head, h->ready_peer_tail, pr); + if (h->disconnects != NULL) + h->disconnects (h->cls, &pr->peer); + /* all requests should have been cancelled, clean up anyway, just in case */ + GNUNET_break (pr->queue_size == 0); + while (NULL != (th = pr->pending_head)) + { + GNUNET_break (0); + GNUNET_CONTAINER_DLL_remove (pr->pending_head, pr->pending_tail, th); + pr->queue_size--; + if (th->cm != NULL) + th->cm->th = NULL; + GNUNET_free (th); + } + /* done with 'voluntary' cleanups, now on to normal freeing */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (h->peers, key, pr)); + GNUNET_assert (pr->pending_head == NULL); + GNUNET_assert (pr->pending_tail == NULL); + GNUNET_assert (pr->ch == h); + GNUNET_assert (pr->queue_size == 0); + GNUNET_assert (pr->timeout_task == GNUNET_SCHEDULER_NO_TASK); + GNUNET_assert (pr->ntr_task == GNUNET_SCHEDULER_NO_TASK); + GNUNET_free (pr); + return GNUNET_YES; +} + + +/** + * Close down any existing connection to the CORE service and + * try re-establishing it later. + * + * @param h our handle + */ +static void +reconnect_later (struct GNUNET_CORE_Handle *h) +{ + struct ControlMessage *cm; + struct PeerRecord *pr; + + GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + if (NULL != h->cth) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth); + h->cth = NULL; + } + if (h->client != NULL) + { + GNUNET_CLIENT_disconnect (h->client, GNUNET_NO); + h->client = NULL; + } + h->currently_down = GNUNET_YES; + GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_task, h); + while (NULL != (cm = h->control_pending_head)) + { + GNUNET_CONTAINER_DLL_remove (h->control_pending_head, + h->control_pending_tail, cm); + if (cm->th != NULL) + cm->th->cm = NULL; + if (cm->cont != NULL) + cm->cont (cm->cont_cls, GNUNET_NO); + GNUNET_free (cm); + } + GNUNET_CONTAINER_multihashmap_iterate (h->peers, + &disconnect_and_free_peer_entry, h); + while (NULL != (pr = h->ready_peer_head)) + GNUNET_CONTAINER_DLL_remove (h->ready_peer_head, h->ready_peer_tail, pr); + GNUNET_assert (h->control_pending_head == NULL); + h->retry_backoff = + GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS, h->retry_backoff); + h->retry_backoff = GNUNET_TIME_relative_multiply (h->retry_backoff, 2); +} + + +/** + * Check the list of pending requests, send the next + * one to the core. + * + * @param h core handle + * @param ignore_currently_down transmit message even if not initialized? + */ +static void +trigger_next_request (struct GNUNET_CORE_Handle *h, int ignore_currently_down); + + +/** + * The given request hit its timeout. Remove from the + * doubly-linked list and call the respective continuation. + * + * @param cls the transmit handle of the request that timed out + * @param tc context, can be NULL (!) + */ +static void +transmission_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Send a control message to the peer asking for transmission + * of the message in the given peer record. + * + * @param pr peer to request transmission to + */ +static void +request_next_transmission (struct PeerRecord *pr) +{ + struct GNUNET_CORE_Handle *h = pr->ch; + struct ControlMessage *cm; + struct SendMessageRequest *smr; + struct GNUNET_CORE_TransmitHandle *th; + + if (pr->timeout_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (pr->timeout_task); + pr->timeout_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL == (th = pr->pending_head)) + { + trigger_next_request (h, GNUNET_NO); + return; + } + if (th->cm != NULL) + return; /* already done */ + GNUNET_assert (pr->prev == NULL); + GNUNET_assert (pr->next == NULL); + pr->timeout_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (th->timeout), &transmission_timeout, pr); + cm = GNUNET_malloc (sizeof (struct ControlMessage) + + sizeof (struct SendMessageRequest)); + th->cm = cm; + cm->th = th; + smr = (struct SendMessageRequest *) &cm[1]; + smr->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST); + smr->header.size = htons (sizeof (struct SendMessageRequest)); + smr->priority = htonl (th->priority); + smr->deadline = GNUNET_TIME_absolute_hton (th->timeout); + smr->peer = pr->peer; + smr->queue_size = htonl (pr->queue_size); + smr->size = htons (th->msize); + smr->smr_id = htons (th->smr_id = pr->smr_id_gen++); + GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head, + h->control_pending_tail, cm); +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Adding SEND REQUEST for peer `%s' to message queue\n", + GNUNET_i2s (&pr->peer)); +#endif + trigger_next_request (h, GNUNET_NO); +} + + +/** + * The given request hit its timeout. Remove from the + * doubly-linked list and call the respective continuation. + * + * @param cls the transmit handle of the request that timed out + * @param tc context, can be NULL (!) + */ +static void +transmission_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerRecord *pr = cls; + struct GNUNET_CORE_Handle *h = pr->ch; + struct GNUNET_CORE_TransmitHandle *th; + + pr->timeout_task = GNUNET_SCHEDULER_NO_TASK; + th = pr->pending_head; + GNUNET_CONTAINER_DLL_remove (pr->pending_head, pr->pending_tail, th); + pr->queue_size--; + if ((pr->prev != NULL) || (pr->next != NULL) || (pr == h->ready_peer_head)) + { + /* the request that was 'approved' by core was + * canceled before it could be transmitted; remove + * us from the 'ready' list */ + GNUNET_CONTAINER_DLL_remove (h->ready_peer_head, h->ready_peer_tail, pr); + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Signalling timeout of request for transmission to CORE service\n"); +#endif + request_next_transmission (pr); + GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL)); + GNUNET_free (th); +} + + +/** + * Transmit the next message to the core service. + */ +static size_t +transmit_message (void *cls, size_t size, void *buf) +{ + struct GNUNET_CORE_Handle *h = cls; + struct ControlMessage *cm; + struct GNUNET_CORE_TransmitHandle *th; + struct PeerRecord *pr; + struct SendMessage *sm; + const struct GNUNET_MessageHeader *hdr; + uint16_t msize; + size_t ret; + + GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + h->cth = NULL; + if (buf == NULL) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmission failed, initiating reconnect\n"); +#endif + reconnect_later (h); + return 0; + } + /* first check for control messages */ + if (NULL != (cm = h->control_pending_head)) + { + hdr = (const struct GNUNET_MessageHeader *) &cm[1]; + msize = ntohs (hdr->size); + if (size < msize) + { + trigger_next_request (h, GNUNET_NO); + return 0; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting control message with %u bytes of type %u to core.\n", + (unsigned int) msize, (unsigned int) ntohs (hdr->type)); +#endif + memcpy (buf, hdr, msize); + GNUNET_CONTAINER_DLL_remove (h->control_pending_head, + h->control_pending_tail, cm); + if (cm->th != NULL) + cm->th->cm = NULL; + if (NULL != cm->cont) + cm->cont (cm->cont_cls, GNUNET_OK); + GNUNET_free (cm); + trigger_next_request (h, GNUNET_NO); + return msize; + } + /* now check for 'ready' P2P messages */ + if (NULL != (pr = h->ready_peer_head)) + { + GNUNET_assert (pr->pending_head != NULL); + th = pr->pending_head; + if (size < th->msize + sizeof (struct SendMessage)) + { + trigger_next_request (h, GNUNET_NO); + return 0; + } + GNUNET_CONTAINER_DLL_remove (h->ready_peer_head, h->ready_peer_tail, pr); + GNUNET_CONTAINER_DLL_remove (pr->pending_head, pr->pending_tail, th); + pr->queue_size--; + if (pr->timeout_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (pr->timeout_task); + pr->timeout_task = GNUNET_SCHEDULER_NO_TASK; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting SEND request to `%s' with %u bytes.\n", + GNUNET_i2s (&pr->peer), (unsigned int) th->msize); +#endif + sm = (struct SendMessage *) buf; + sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND); + sm->priority = htonl (th->priority); + sm->deadline = GNUNET_TIME_absolute_hton (th->timeout); + sm->peer = pr->peer; + sm->cork = htonl ((uint32_t) th->cork); + sm->reserved = htonl (0); + ret = + th->get_message (th->get_message_cls, + size - sizeof (struct SendMessage), &sm[1]); + +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting SEND request to `%s' yielded %u bytes.\n", + GNUNET_i2s (&pr->peer), ret); +#endif + GNUNET_free (th); + if (0 == ret) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Size of clients message to peer %s is 0!\n", + GNUNET_i2s (&pr->peer)); +#endif + /* client decided to send nothing! */ + request_next_transmission (pr); + return 0; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Produced SEND message to core with %u bytes payload\n", + (unsigned int) ret); +#endif + GNUNET_assert (ret >= sizeof (struct GNUNET_MessageHeader)); + if (ret + sizeof (struct SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + request_next_transmission (pr); + return 0; + } + ret += sizeof (struct SendMessage); + sm->header.size = htons (ret); + GNUNET_assert (ret <= size); + request_next_transmission (pr); + return ret; + } + return 0; +} + + +/** + * Check the list of pending requests, send the next + * one to the core. + * + * @param h core handle + * @param ignore_currently_down transmit message even if not initialized? + */ +static void +trigger_next_request (struct GNUNET_CORE_Handle *h, int ignore_currently_down) +{ + uint16_t msize; + + if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO)) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Core connection down, not processing queue\n"); +#endif + return; + } + if (NULL != h->cth) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n"); +#endif + return; + } + if (h->control_pending_head != NULL) + msize = + ntohs (((struct GNUNET_MessageHeader *) &h-> + control_pending_head[1])->size); + else if (h->ready_peer_head != NULL) + msize = + h->ready_peer_head->pending_head->msize + sizeof (struct SendMessage); + else + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Request queue empty, not processing queue\n"); +#endif + return; /* no pending message */ + } + h->cth = + GNUNET_CLIENT_notify_transmit_ready (h->client, msize, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, &transmit_message, h); +} + + +/** + * Handler for notification messages received from the core. + * + * @param cls our "struct GNUNET_CORE_Handle" + * @param msg the message received from the core service + */ +static void +main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CORE_Handle *h = cls; + const struct InitReplyMessage *m; + const struct ConnectNotifyMessage *cnm; + const struct DisconnectNotifyMessage *dnm; + const struct NotifyTrafficMessage *ntm; + const struct GNUNET_MessageHeader *em; + const struct SendMessageReady *smr; + const struct GNUNET_CORE_MessageHandler *mh; + const struct GNUNET_ATS_Information *ats; + GNUNET_CORE_StartupCallback init; + struct PeerRecord *pr; + struct GNUNET_CORE_TransmitHandle *th; + unsigned int hpos; + int trigger; + uint16_t msize; + uint16_t et; + uint32_t ats_count; + + if (msg == NULL) + { + LOG (GNUNET_ERROR_TYPE_INFO, + _ + ("Client was disconnected from core service, trying to reconnect.\n")); + reconnect_later (h); + return; + } + msize = ntohs (msg->size); +#if DEBUG_CORE > 2 + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing message of type %u and size %u from core service\n", + ntohs (msg->type), msize); +#endif + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY: + if (ntohs (msg->size) != sizeof (struct InitReplyMessage)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + m = (const struct InitReplyMessage *) msg; + GNUNET_break (0 == ntohl (m->reserved)); + /* start our message processing loop */ + if (GNUNET_YES == h->currently_down) + { + h->currently_down = GNUNET_NO; + trigger_next_request (h, GNUNET_NO); + } + h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + h->me = m->my_identity; + if (NULL != (init = h->init)) + { + /* mark so we don't call init on reconnect */ + h->init = NULL; +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connected to core service of peer `%s'.\n", + GNUNET_i2s (&h->me)); +#endif + init (h->cls, h, &h->me); + } + else + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Successfully reconnected to core service.\n"); +#endif + } + /* fake 'connect to self' */ + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &h->me.hashPubKey); + GNUNET_assert (pr == NULL); + pr = GNUNET_malloc (sizeof (struct PeerRecord)); + pr->peer = h->me; + pr->ch = h; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (h->peers, + &h->me.hashPubKey, pr, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + if (NULL != h->connects) + h->connects (h->cls, &h->me, NULL, 0); + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT: + if (msize < sizeof (struct ConnectNotifyMessage)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + cnm = (const struct ConnectNotifyMessage *) msg; + ats_count = ntohl (cnm->ats_count); + if (msize != + sizeof (struct ConnectNotifyMessage) + + ats_count * sizeof (struct GNUNET_ATS_Information)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received notification about connection from `%s'.\n", + GNUNET_i2s (&cnm->peer)); +#endif + if (0 == memcmp (&h->me, &cnm->peer, sizeof (struct GNUNET_PeerIdentity))) + { + /* connect to self!? */ + GNUNET_break (0); + return; + } + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &cnm->peer.hashPubKey); + if (pr != NULL) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + pr = GNUNET_malloc (sizeof (struct PeerRecord)); + pr->peer = cnm->peer; + pr->ch = h; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (h->peers, + &cnm->peer.hashPubKey, pr, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + ats = (const struct GNUNET_ATS_Information *) &cnm[1]; + if (NULL != h->connects) + h->connects (h->cls, &cnm->peer, ats, ats_count); + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT: + if (msize != sizeof (struct DisconnectNotifyMessage)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + dnm = (const struct DisconnectNotifyMessage *) msg; + if (0 == memcmp (&h->me, &dnm->peer, sizeof (struct GNUNET_PeerIdentity))) + { + /* connection to self!? */ + GNUNET_break (0); + return; + } + GNUNET_break (0 == ntohl (dnm->reserved)); +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received notification about disconnect from `%s'.\n", + GNUNET_i2s (&dnm->peer)); +#endif + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &dnm->peer.hashPubKey); + if (pr == NULL) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + trigger = ((pr->prev != NULL) || (pr->next != NULL) || + (h->ready_peer_head == pr)); + disconnect_and_free_peer_entry (h, &dnm->peer.hashPubKey, pr); + if (trigger) + trigger_next_request (h, GNUNET_NO); + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND: + if (msize < sizeof (struct NotifyTrafficMessage)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + ntm = (const struct NotifyTrafficMessage *) msg; + + ats_count = ntohl (ntm->ats_count); + if ((msize < + sizeof (struct NotifyTrafficMessage) + + ats_count * sizeof (struct GNUNET_ATS_Information) + + sizeof (struct GNUNET_MessageHeader)) || + (GNUNET_ATS_ARRAY_TERMINATOR != ntohl ((&ntm->ats)[ats_count].type))) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + em = (const struct GNUNET_MessageHeader *) &(&ntm->ats)[ats_count + 1]; +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u and size %u from peer `%4s'\n", + ntohs (em->type), ntohs (em->size), GNUNET_i2s (&ntm->peer)); +#endif + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &ntm->peer.hashPubKey); + if (pr == NULL) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + if ((GNUNET_NO == h->inbound_hdr_only) && + (msize != + ntohs (em->size) + sizeof (struct NotifyTrafficMessage) + + +ats_count * sizeof (struct GNUNET_ATS_Information))) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + et = ntohs (em->type); + for (hpos = 0; hpos < h->hcnt; hpos++) + { + mh = &h->handlers[hpos]; + if (mh->type != et) + continue; + if ((mh->expected_size != ntohs (em->size)) && (mh->expected_size != 0)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected message size %u for message of type %u from peer `%4s'\n", + htons (em->size), mh->type, GNUNET_i2s (&ntm->peer)); + GNUNET_break_op (0); + continue; + } + if (GNUNET_OK != + h->handlers[hpos].callback (h->cls, &ntm->peer, em, &ntm->ats, + ats_count)) + { + /* error in processing, do not process other messages! */ + break; + } + } + if (NULL != h->inbound_notify) + h->inbound_notify (h->cls, &ntm->peer, em, &ntm->ats, ats_count); + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND: + if (msize < sizeof (struct NotifyTrafficMessage)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + ntm = (const struct NotifyTrafficMessage *) msg; + if (0 == memcmp (&h->me, &ntm->peer, sizeof (struct GNUNET_PeerIdentity))) + { + /* self-change!? */ + GNUNET_break (0); + return; + } + ats_count = ntohl (ntm->ats_count); + if ((msize < + sizeof (struct NotifyTrafficMessage) + + ats_count * sizeof (struct GNUNET_ATS_Information) + + sizeof (struct GNUNET_MessageHeader)) || + (GNUNET_ATS_ARRAY_TERMINATOR != ntohl ((&ntm->ats)[ats_count].type))) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + em = (const struct GNUNET_MessageHeader *) &(&ntm->ats)[ats_count + 1]; + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &ntm->peer.hashPubKey); + if (pr == NULL) + { + GNUNET_break (0); + reconnect_later (h); + return; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received notification about transmission to `%s'.\n", + GNUNET_i2s (&ntm->peer)); +#endif + if ((GNUNET_NO == h->outbound_hdr_only) && + (msize != + ntohs (em->size) + sizeof (struct NotifyTrafficMessage) + + ats_count * sizeof (struct GNUNET_ATS_Information))) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + if (NULL == h->outbound_notify) + { + GNUNET_break (0); + break; + } + h->outbound_notify (h->cls, &ntm->peer, em, &ntm->ats, ats_count); + break; + case GNUNET_MESSAGE_TYPE_CORE_SEND_READY: + if (msize != sizeof (struct SendMessageReady)) + { + GNUNET_break (0); + reconnect_later (h); + return; + } + smr = (const struct SendMessageReady *) msg; + pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &smr->peer.hashPubKey); + if (pr == NULL) + { + GNUNET_break (0); + reconnect_later (h); + return; + } +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received notification about transmission readiness to `%s'.\n", + GNUNET_i2s (&smr->peer)); +#endif + if (pr->pending_head == NULL) + { + /* request must have been cancelled between the original request + * and the response from core, ignore core's readiness */ + break; + } + + th = pr->pending_head; + if (ntohs (smr->smr_id) != th->smr_id) + { + /* READY message is for expired or cancelled message, + * ignore! (we should have already sent another request) */ + break; + } + if ((pr->prev != NULL) || (pr->next != NULL) || (h->ready_peer_head == pr)) + { + /* we should not already be on the ready list... */ + GNUNET_break (0); + reconnect_later (h); + return; + } + GNUNET_CONTAINER_DLL_insert (h->ready_peer_head, h->ready_peer_tail, pr); + trigger_next_request (h, GNUNET_NO); + break; + default: + reconnect_later (h); + return; + } + GNUNET_CLIENT_receive (h->client, &main_notify_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Task executed once we are done transmitting the INIT message. + * Starts our 'receive' loop. + * + * @param cls the 'struct GNUNET_CORE_Handle' + * @param success were we successful + */ +static void +init_done_task (void *cls, int success) +{ + struct GNUNET_CORE_Handle *h = cls; + + if (success == GNUNET_SYSERR) + return; /* shutdown */ + if (success == GNUNET_NO) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Failed to exchange INIT with core, retrying\n"); +#endif + if (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK) + reconnect_later (h); + return; + } + GNUNET_CLIENT_receive (h->client, &main_notify_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Our current client connection went down. Clean it up + * and try to reconnect! + * + * @param h our handle to the core service + */ +static void +reconnect (struct GNUNET_CORE_Handle *h) +{ + struct ControlMessage *cm; + struct InitMessage *init; + uint32_t opt; + uint16_t msize; + uint16_t *ts; + unsigned int hpos; + +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting to CORE service\n"); +#endif + GNUNET_assert (h->client == NULL); + GNUNET_assert (h->currently_down == GNUNET_YES); + h->client = GNUNET_CLIENT_connect ("core", h->cfg); + if (h->client == NULL) + { + reconnect_later (h); + return; + } + msize = h->hcnt * sizeof (uint16_t) + sizeof (struct InitMessage); + cm = GNUNET_malloc (sizeof (struct ControlMessage) + msize); + cm->cont = &init_done_task; + cm->cont_cls = h; + init = (struct InitMessage *) &cm[1]; + init->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT); + init->header.size = htons (msize); + opt = 0; + if (h->inbound_notify != NULL) + { + if (h->inbound_hdr_only) + opt |= GNUNET_CORE_OPTION_SEND_HDR_INBOUND; + else + opt |= GNUNET_CORE_OPTION_SEND_FULL_INBOUND; + } + if (h->outbound_notify != NULL) + { + if (h->outbound_hdr_only) + opt |= GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND; + else + opt |= GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND; + } + init->options = htonl (opt); + ts = (uint16_t *) & init[1]; + for (hpos = 0; hpos < h->hcnt; hpos++) + ts[hpos] = htons (h->handlers[hpos].type); + GNUNET_CONTAINER_DLL_insert (h->control_pending_head, h->control_pending_tail, + cm); + trigger_next_request (h, GNUNET_YES); +} + + + +/** + * Connect to the core service. Note that the connection may + * complete (or fail) asynchronously. + * + * @param cfg configuration to use + * @param queue_size size of the per-peer message queue + * @param cls closure for the various callbacks that follow (including handlers in the handlers array) + * @param init callback to call on timeout or once we have successfully + * connected to the core service; note that timeout is only meaningful if init is not NULL + * @param connects function to call on peer connect, can be NULL + * @param disconnects function to call on peer disconnect / timeout, can be NULL + * @param inbound_notify function to call for all inbound messages, can be NULL + * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message; + * can be used to improve efficiency, ignored if inbound_notify is NULLL + * @param outbound_notify function to call for all outbound messages, can be NULL + * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message + * can be used to improve efficiency, ignored if outbound_notify is NULLL + * @param handlers callbacks for messages we care about, NULL-terminated + * @return handle to the core service (only useful for disconnect until 'init' is called); + * NULL on error (in this case, init is never called) + */ +struct GNUNET_CORE_Handle * +GNUNET_CORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int queue_size, void *cls, + GNUNET_CORE_StartupCallback init, + GNUNET_CORE_ConnectEventHandler connects, + GNUNET_CORE_DisconnectEventHandler disconnects, + GNUNET_CORE_MessageCallback inbound_notify, + int inbound_hdr_only, + GNUNET_CORE_MessageCallback outbound_notify, + int outbound_hdr_only, + const struct GNUNET_CORE_MessageHandler *handlers) +{ + struct GNUNET_CORE_Handle *h; + + h = GNUNET_malloc (sizeof (struct GNUNET_CORE_Handle)); + h->cfg = cfg; + h->queue_size = queue_size; + h->cls = cls; + h->init = init; + h->connects = connects; + h->disconnects = disconnects; + h->inbound_notify = inbound_notify; + h->outbound_notify = outbound_notify; + h->inbound_hdr_only = inbound_hdr_only; + h->outbound_hdr_only = outbound_hdr_only; + h->handlers = handlers; + h->hcnt = 0; + h->currently_down = GNUNET_YES; + h->peers = GNUNET_CONTAINER_multihashmap_create (128); + h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + if (NULL != handlers) + while (handlers[h->hcnt].callback != NULL) + h->hcnt++; + GNUNET_assert (h->hcnt < + (GNUNET_SERVER_MAX_MESSAGE_SIZE - + sizeof (struct InitMessage)) / sizeof (uint16_t)); +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to CORE service\n"); +#endif + reconnect (h); + return h; +} + + +/** + * Disconnect from the core service. This function can only + * be called *after* all pending 'GNUNET_CORE_notify_transmit_ready' + * requests have been explicitly canceled. + * + * @param handle connection to core to disconnect + */ +void +GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle) +{ + struct ControlMessage *cm; + +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from CORE service\n"); +#endif + if (handle->cth != NULL) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth); + handle->cth = NULL; + } + while (NULL != (cm = handle->control_pending_head)) + { + GNUNET_CONTAINER_DLL_remove (handle->control_pending_head, + handle->control_pending_tail, cm); + if (cm->th != NULL) + cm->th->cm = NULL; + if (cm->cont != NULL) + cm->cont (cm->cont_cls, GNUNET_SYSERR); + GNUNET_free (cm); + } + if (handle->client != NULL) + { + GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO); + handle->client = NULL; + } + GNUNET_CONTAINER_multihashmap_iterate (handle->peers, + &disconnect_and_free_peer_entry, + handle); + if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (handle->reconnect_task); + handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_CONTAINER_multihashmap_destroy (handle->peers); + handle->peers = NULL; + GNUNET_break (handle->ready_peer_head == NULL); + GNUNET_free (handle); +} + + +/** + * Task that calls 'request_next_transmission'. + * + * @param cls the 'struct PeerRecord*' + * @param tc scheduler context + */ +static void +run_request_next_transmission (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerRecord *pr = cls; + + pr->ntr_task = GNUNET_SCHEDULER_NO_TASK; + request_next_transmission (pr); +} + + +/** + * Ask the core to call "notify" once it is ready to transmit the + * given number of bytes to the specified "target". Must only be + * called after a connection to the respective peer has been + * established (and the client has been informed about this). + * + * @param handle connection to core service + * @param cork is corking allowed for this transmission? + * @param priority how important is the message? + * @param maxdelay how long can the message wait? + * @param target who should receive the message, + * use NULL for this peer (loopback) + * @param notify_size how many bytes of buffer space does notify want? + * @param notify function to call when buffer space is available + * @param notify_cls closure for notify + * @return non-NULL if the notify callback was queued, + * NULL if we can not even queue the request (insufficient + * memory); if NULL is returned, "notify" will NOT be called. + */ +struct GNUNET_CORE_TransmitHandle * +GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle, int cork, + uint32_t priority, + struct GNUNET_TIME_Relative maxdelay, + const struct GNUNET_PeerIdentity *target, + size_t notify_size, + GNUNET_CONNECTION_TransmitReadyNotify notify, + void *notify_cls) +{ + struct PeerRecord *pr; + struct GNUNET_CORE_TransmitHandle *th; + struct GNUNET_CORE_TransmitHandle *pos; + struct GNUNET_CORE_TransmitHandle *prev; + struct GNUNET_CORE_TransmitHandle *minp; + + pr = GNUNET_CONTAINER_multihashmap_get (handle->peers, &target->hashPubKey); + if (NULL == pr) + { + /* attempt to send to peer that is not connected */ + LOG (GNUNET_ERROR_TYPE_WARNING, + "Attempting to send to peer `%s' from peer `%s', but not connected!\n", + GNUNET_i2s (target), GNUNET_h2s (&handle->me.hashPubKey)); + GNUNET_break (0); + return NULL; + } + GNUNET_assert (notify_size + sizeof (struct SendMessage) < + GNUNET_SERVER_MAX_MESSAGE_SIZE); + th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle)); + th->peer = pr; + GNUNET_assert (NULL != notify); + th->get_message = notify; + th->get_message_cls = notify_cls; + th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay); + th->priority = priority; + th->msize = notify_size; + th->cork = cork; + /* bound queue size */ + if (pr->queue_size == handle->queue_size) + { + /* find lowest-priority entry, but skip the head of the list */ + minp = pr->pending_head->next; + prev = minp; + while (prev != NULL) + { + if (prev->priority < minp->priority) + minp = prev; + prev = prev->next; + } + if (minp == NULL) + { + GNUNET_break (handle->queue_size != 0); + GNUNET_break (pr->queue_size == 1); + GNUNET_free (th); +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dropping transmission request: cannot drop queue head and limit is one\n"); +#endif + return NULL; + } + if (priority <= minp->priority) + { +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dropping transmission request: priority too low\n"); +#endif + GNUNET_free (th); + return NULL; /* priority too low */ + } + GNUNET_CONTAINER_DLL_remove (pr->pending_head, pr->pending_tail, minp); + pr->queue_size--; + GNUNET_assert (0 == minp->get_message (minp->get_message_cls, 0, NULL)); + GNUNET_free (minp); + } + + /* Order entries by deadline, but SKIP 'HEAD' (as we may have transmitted + * that request already or might even already be approved to transmit that + * message to core) */ + pos = pr->pending_head; + if (pos != NULL) + pos = pos->next; /* skip head */ + + /* insertion sort */ + prev = pos; + while ((pos != NULL) && (pos->timeout.abs_value < th->timeout.abs_value)) + { + prev = pos; + pos = pos->next; + } + GNUNET_CONTAINER_DLL_insert_after (pr->pending_head, pr->pending_tail, prev, + th); + pr->queue_size++; + /* was the request queue previously empty? */ +#if DEBUG_CORE + LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmission request added to queue\n"); +#endif + if ((pr->pending_head == th) && (pr->ntr_task == GNUNET_SCHEDULER_NO_TASK) && + (pr->next == NULL) && (pr->prev == NULL) && + (handle->ready_peer_head != pr)) + pr->ntr_task = + GNUNET_SCHEDULER_add_now (&run_request_next_transmission, pr); + return th; +} + + +/** + * Cancel the specified transmission-ready notification. + * + * @param th handle that was returned by "notify_transmit_ready". + */ +void +GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle *th) +{ + struct PeerRecord *pr = th->peer; + struct GNUNET_CORE_Handle *h = pr->ch; + int was_head; + + was_head = (pr->pending_head == th); + GNUNET_CONTAINER_DLL_remove (pr->pending_head, pr->pending_tail, th); + pr->queue_size--; + if (th->cm != NULL) + { + /* we're currently in the control queue, remove */ + GNUNET_CONTAINER_DLL_remove (h->control_pending_head, + h->control_pending_tail, th->cm); + GNUNET_free (th->cm); + } + GNUNET_free (th); + if (was_head) + { + if ((pr->prev != NULL) || (pr->next != NULL) || (pr == h->ready_peer_head)) + { + /* the request that was 'approved' by core was + * canceled before it could be transmitted; remove + * us from the 'ready' list */ + GNUNET_CONTAINER_DLL_remove (h->ready_peer_head, h->ready_peer_tail, pr); + } + request_next_transmission (pr); + } +} + + +/* end of core_api.c */ diff --git a/src/core/core_api_iterate_peers.c b/src/core/core_api_iterate_peers.c new file mode 100644 index 0000000..7b28842 --- /dev/null +++ b/src/core/core_api_iterate_peers.c @@ -0,0 +1,247 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/core_api_iterate_peers.c + * @brief implementation of the peer_iterate function + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_core_service.h" +#include "core.h" + + +struct GNUNET_CORE_RequestContext +{ + /** + * Our connection to the service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Handle for transmitting a request. + */ + struct GNUNET_CLIENT_TransmitHandle *th; + + /** + * Function called with the peer. + */ + GNUNET_CORE_ConnectEventHandler peer_cb; + + /** + * Peer to check for. + */ + struct GNUNET_PeerIdentity *peer; + + /** + * Closure for peer_cb. + */ + void *cb_cls; + +}; + + +/** + * Receive reply from core service with information about a peer. + * + * @param cls our 'struct GNUNET_CORE_RequestContext *' + * @param msg NULL on error or last entry + */ +static void +receive_info (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CORE_RequestContext *request_context = cls; + const struct ConnectNotifyMessage *connect_message; + uint32_t ats_count; + uint16_t msize; + + /* Handle last message or error case, disconnect and clean up */ + if ((msg == NULL) || + ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END) && + (ntohs (msg->size) == sizeof (struct GNUNET_MessageHeader)))) + { + if (request_context->peer_cb != NULL) + request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0); + GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO); + GNUNET_free (request_context); + return; + } + + msize = ntohs (msg->size); + /* Handle incorrect message type or size, disconnect and clean up */ + if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT) || + (msize < sizeof (struct ConnectNotifyMessage))) + { + GNUNET_break (0); + if (request_context->peer_cb != NULL) + request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0); + GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO); + GNUNET_free (request_context); + return; + } + connect_message = (const struct ConnectNotifyMessage *) msg; + ats_count = ntohl (connect_message->ats_count); + if (msize != + sizeof (struct ConnectNotifyMessage) + + ats_count * sizeof (struct GNUNET_ATS_Information)) + { + GNUNET_break (0); + if (request_context->peer_cb != NULL) + request_context->peer_cb (request_context->cb_cls, NULL, NULL, 0); + GNUNET_CLIENT_disconnect (request_context->client, GNUNET_NO); + GNUNET_free (request_context); + return; + } + /* Normal case */ + if (request_context->peer_cb != NULL) + request_context->peer_cb (request_context->cb_cls, &connect_message->peer, + (const struct GNUNET_ATS_Information *) + &connect_message[1], ats_count); + GNUNET_CLIENT_receive (request_context->client, &receive_info, + request_context, GNUNET_TIME_UNIT_FOREVER_REL); +} + +/** + * Function called to notify a client about the socket + * begin ready to queue more data. "buf" will be + * NULL and "size" zero if the socket was closed for + * writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_request (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg; + struct GNUNET_PeerIdentity *peer = cls; + int msize; + + if (peer == NULL) + msize = sizeof (struct GNUNET_MessageHeader); + else + msize = + sizeof (struct GNUNET_MessageHeader) + + sizeof (struct GNUNET_PeerIdentity); + + if ((size < msize) || (buf == NULL)) + return 0; + + msg = (struct GNUNET_MessageHeader *) buf; + msg->size = htons (msize); + if (peer != NULL) + { + msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED); + memcpy (&msg[1], peer, sizeof (struct GNUNET_PeerIdentity)); + } + else + msg->type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS); + + return msize; +} + +/** + * Iterate over all currently connected peers. + * Calls peer_cb with each connected peer, and then + * once with NULL to indicate that all peers have + * been handled. + * + * @param cfg configuration to use + * @param peer the specific peer to check for + * @param peer_cb function to call with the peer information + * @param cb_cls closure for peer_cb + * + * @return GNUNET_OK if iterating, GNUNET_SYSERR on error + */ +int +GNUNET_CORE_is_peer_connected (const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_PeerIdentity *peer, + GNUNET_CORE_ConnectEventHandler peer_cb, + void *cb_cls) +{ + struct GNUNET_CORE_RequestContext *request_context; + struct GNUNET_CLIENT_Connection *client; + + client = GNUNET_CLIENT_connect ("core", cfg); + if (client == NULL) + return GNUNET_SYSERR; + GNUNET_assert (peer != NULL); + request_context = GNUNET_malloc (sizeof (struct GNUNET_CORE_RequestContext)); + request_context->client = client; + request_context->peer_cb = peer_cb; + request_context->cb_cls = cb_cls; + request_context->peer = peer; + + request_context->th = + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader) + + + sizeof (struct GNUNET_PeerIdentity), + GNUNET_TIME_relative_get_forever (), + GNUNET_YES, &transmit_request, peer); + GNUNET_assert (request_context->th != NULL); + GNUNET_CLIENT_receive (client, &receive_info, request_context, + GNUNET_TIME_relative_get_forever ()); + return GNUNET_OK; +} + +/** + * Iterate over all currently connected peers. + * Calls peer_cb with each connected peer, and then + * once with NULL to indicate that all peers have + * been handled. + * + * @param cfg configuration to use + * @param peer_cb function to call with the peer information + * @param cb_cls closure for peer_cb + * + * @return GNUNET_OK if iterating, GNUNET_SYSERR on error + */ +int +GNUNET_CORE_iterate_peers (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_CORE_ConnectEventHandler peer_cb, + void *cb_cls) +{ + struct GNUNET_CORE_RequestContext *request_context; + struct GNUNET_CLIENT_Connection *client; + + client = GNUNET_CLIENT_connect ("core", cfg); + if (client == NULL) + return GNUNET_SYSERR; + request_context = GNUNET_malloc (sizeof (struct GNUNET_CORE_RequestContext)); + request_context->client = client; + request_context->peer_cb = peer_cb; + request_context->cb_cls = cb_cls; + + request_context->th = + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_relative_get_forever (), + GNUNET_YES, &transmit_request, NULL); + + GNUNET_CLIENT_receive (client, &receive_info, request_context, + GNUNET_TIME_relative_get_forever ()); + return GNUNET_OK; +} + +/* end of core_api_iterate_peers.c */ diff --git a/src/core/gnunet-core-list-connections.c b/src/core/gnunet-core-list-connections.c new file mode 100644 index 0000000..fcd0765 --- /dev/null +++ b/src/core/gnunet-core-list-connections.c @@ -0,0 +1,207 @@ +/* + This file is part of GNUnet. + (C) 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-core-list-connections.c + * @brief Print information about other known _connected_ peers. + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_core_service.h" +#include "gnunet_program_lib.h" + +#define VERBOSE 0 +static int no_resolve; + +#if VERBOSE +static unsigned int peer_count; +#endif + +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +struct AddressStringList +{ + /** + * Pointer to previous element. + */ + struct AddressStringList *prev; + + /** + * Pointer to next element. + */ + struct AddressStringList *next; + + /** + * Address as string. + */ + char *address_string; +}; + +struct PrintContext +{ + struct GNUNET_PeerIdentity peer; + struct AddressStringList *address_list_head; + struct AddressStringList *address_list_tail; +}; + + +static void +dump_pc (struct PrintContext *pc) +{ + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + struct AddressStringList *address; + + GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc); + printf (_("Peer `%s'\n"), (const char *) &enc); + while (NULL != (address = pc->address_list_head)) + { + printf ("\t%s\n", address->address_string); + GNUNET_free (address->address_string); + GNUNET_CONTAINER_DLL_remove (pc->address_list_head, pc->address_list_tail, + address); + GNUNET_free (address); + } + + printf ("\n"); + + GNUNET_free (pc); +} + + +/** + * Function to call with a human-readable format of an address + * + * @param cls closure + * @param peer peer this update is about + * @param address NULL on error, otherwise 0-terminated printable UTF-8 string + */ +static void +process_resolved_address (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address) +{ + struct PrintContext *pc = cls; + +// struct AddressStringList *new_address; + + if (address == NULL) + { + dump_pc (pc); + return; + } + + /* This does exactly the same as gnunet-transport -i ! */ + /* + * new_address = GNUNET_malloc (sizeof (struct AddressStringList)); + * #if VERBOSE + * FPRINTF (stderr, "Received address %s\n", address); + * #endif + * + * new_address->address_string = GNUNET_strdup ("FIXME"); + * GNUNET_CONTAINER_DLL_insert (pc->address_list_head, pc->address_list_tail, + * new_address); + */ +} + + +/** + * Callback for retrieving a list of connected peers. + */ +static void +connected_peer_callback (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PrintContext *pc; + + if (peer != NULL) /* Not yet finished */ + { +#if VERBOSE + FPRINTF (stderr, "Learned about peer %s\n", GNUNET_i2s (peer)); + peer_count++; +#endif + pc = GNUNET_malloc (sizeof (struct PrintContext)); + pc->peer = *peer; + GNUNET_TRANSPORT_peer_get_active_addresses (cfg, peer, GNUNET_YES, + GNUNET_TIME_UNIT_MINUTES, + &process_resolved_address, pc); + } +#if VERBOSE + else + { + FPRINTF (stderr, "Counted %u total connected peers.\n", peer_count); + } +#endif +} + + +/** + * 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 c configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + + cfg = c; + if (args[0] != NULL) + { + FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]); + return; + } + + GNUNET_CORE_iterate_peers (cfg, &connected_peer_callback, NULL); + +} + + +/** + * The main function to obtain peer information. + * + * @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[] = { + {'n', "numeric", NULL, + gettext_noop ("don't resolve host names"), + 0, &GNUNET_GETOPT_set_one, &no_resolve}, + GNUNET_GETOPT_OPTION_END + }; + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, argv, "gnunet-list-connections", + gettext_noop + ("Print information about connected peers."), + options, &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-core-list-connections.c */ diff --git a/src/core/gnunet-service-core.c b/src/core/gnunet-service-core.c new file mode 100644 index 0000000..2eb71c2 --- /dev/null +++ b/src/core/gnunet-service-core.c @@ -0,0 +1,120 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core.c + * @brief high-level P2P messaging + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_clients.h" +#include "gnunet-service-core_kx.h" +#include "gnunet-service-core_neighbours.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet-service-core_typemap.h" + + +/** + * Our identity. + */ +struct GNUNET_PeerIdentity GSC_my_identity; + +/** + * Our configuration. + */ +const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; + +/** + * For creating statistics. + */ +struct GNUNET_STATISTICS_Handle *GSC_stats; + + +/** + * Last task run during shutdown. Disconnects us from + * the transport. + */ +static void +cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n"); +#endif + GSC_CLIENTS_done (); + GSC_NEIGHBOURS_done (); + GSC_SESSIONS_done (); + GSC_KX_done (); + GSC_TYPEMAP_done (); + if (GSC_stats != NULL) + { + GNUNET_STATISTICS_destroy (GSC_stats, GNUNET_NO); + GSC_stats = NULL; + } + GSC_cfg = NULL; +} + + +/** + * Initiate core service. + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + GSC_cfg = c; + GSC_stats = GNUNET_STATISTICS_create ("core", GSC_cfg); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleaning_task, + NULL); + GSC_TYPEMAP_init (); + if ((GNUNET_OK != GSC_KX_init ()) || (GNUNET_OK != GSC_NEIGHBOURS_init ())) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + GSC_SESSIONS_init (); + GSC_CLIENTS_init (server); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Core service of `%4s' ready.\n"), + GNUNET_i2s (&GSC_my_identity)); +} + + + +/** + * The main function for the transport service. + * + * @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) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "core", GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-core.c */ diff --git a/src/core/gnunet-service-core.h b/src/core/gnunet-service-core.h new file mode 100644 index 0000000..e48ad8b --- /dev/null +++ b/src/core/gnunet-service-core.h @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core.h + * @brief Globals for gnunet-service-core + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_H +#define GNUNET_SERVICE_CORE_H + +#include "gnunet_statistics_service.h" +#include "core.h" + +/** + * Opaque handle to a client. + */ +struct GSC_Client; + + +/** + * Record kept for each request for transmission issued by a + * client that is still pending. (This struct is used by + * both the 'CLIENTS' and 'SESSIONS' subsystems.) + */ +struct GSC_ClientActiveRequest +{ + + /** + * Active requests are kept in a doubly-linked list of + * the respective target peer. + */ + struct GSC_ClientActiveRequest *next; + + /** + * Active requests are kept in a doubly-linked list of + * the respective target peer. + */ + struct GSC_ClientActiveRequest *prev; + + /** + * Which peer is the message going to be for? + */ + struct GNUNET_PeerIdentity target; + + /** + * Handle to the client. + */ + struct GSC_Client *client_handle; + + /** + * By what time would the client want to see this message out? + */ + struct GNUNET_TIME_Absolute deadline; + + /** + * How important is this request. + */ + uint32_t priority; + + /** + * Has this request been solicited yet? + */ + int was_solicited; + + /** + * How many bytes does the client intend to send? + */ + uint16_t msize; + + /** + * Unique request ID (in big endian). + */ + uint16_t smr_id; + +}; + + +/** + * Our configuration. + */ +extern const struct GNUNET_CONFIGURATION_Handle *GSC_cfg; + +/** + * For creating statistics. + */ +extern struct GNUNET_STATISTICS_Handle *GSC_stats; + +/** + * Our identity. + */ +extern struct GNUNET_PeerIdentity GSC_my_identity; + + +#endif diff --git a/src/core/gnunet-service-core_clients.c b/src/core/gnunet-service-core_clients.c new file mode 100644 index 0000000..4098b45 --- /dev/null +++ b/src/core/gnunet-service-core_clients.c @@ -0,0 +1,887 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_clients.c + * @brief code for managing interactions with clients of core service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_clients.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet-service-core_typemap.h" +#include "core.h" + + +/** + * How many messages do we queue up at most for optional + * notifications to a client? (this can cause notifications + * about outgoing messages to be dropped). + */ +#define MAX_NOTIFY_QUEUE 1024 + + +/** + * Data structure for each client connected to the core service. + */ +struct GSC_Client +{ + /** + * Clients are kept in a linked list. + */ + struct GSC_Client *next; + + /** + * Clients are kept in a linked list. + */ + struct GSC_Client *prev; + + /** + * Handle for the client with the server API. + */ + struct GNUNET_SERVER_Client *client_handle; + + /** + * Array of the types of messages this peer cares + * about (with "tcnt" entries). Allocated as part + * of this client struct, do not free! + */ + const uint16_t *types; + + /** + * Map of peer identities to active transmission requests of this + * client to the peer (of type 'struct GSC_ClientActiveRequest'). + */ + struct GNUNET_CONTAINER_MultiHashMap *requests; + + /** + * Map containing all peers that this client knows we're connected to. + */ + struct GNUNET_CONTAINER_MultiHashMap *connectmap; + + /** + * Options for messages this client cares about, + * see GNUNET_CORE_OPTION_ values. + */ + uint32_t options; + + /** + * Number of types of incoming messages this client + * specifically cares about. Size of the "types" array. + */ + unsigned int tcnt; + +}; + + +/** + * Head of linked list of our clients. + */ +static struct GSC_Client *client_head; + +/** + * Tail of linked list of our clients. + */ +static struct GSC_Client *client_tail; + +/** + * Context for notifications we need to send to our clients. + */ +static struct GNUNET_SERVER_NotificationContext *notifier; + +/** + * Tokenizer for messages received from clients. + */ +static struct GNUNET_SERVER_MessageStreamTokenizer *client_mst; + + +/** + * Lookup our client struct given the server's client handle. + * + * @param client server client handle to look up + * @return our client handle for the client + */ +static struct GSC_Client * +find_client (struct GNUNET_SERVER_Client *client) +{ + struct GSC_Client *c; + + c = client_head; + while ((c != NULL) && (c->client_handle != client)) + c = c->next; + return c; +} + + +/** + * Send a message to one of our clients. + * + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +static void +send_to_client (struct GSC_Client *client, + const struct GNUNET_MessageHeader *msg, int can_drop) +{ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing to send %u bytes of message of type %u to client.\n", + (unsigned int) ntohs (msg->size), + (unsigned int) ntohs (msg->type)); +#endif + GNUNET_SERVER_notification_context_unicast (notifier, client->client_handle, + msg, can_drop); +} + + +/** + * Send a message to one of our clients. + * + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +void +GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *msg, + int can_drop) +{ + struct GSC_Client *c; + + c = find_client (client); + if (NULL == c) + { + GNUNET_break (0); + return; + } + send_to_client (c, msg, can_drop); +} + + +/** + * Test if the client is interested in messages of the given type. + * + * @param type message type + * @param c client to test + * @return GNUNET_YES if 'c' is interested, GNUNET_NO if not. + */ +static int +type_match (uint16_t type, struct GSC_Client *c) +{ + unsigned int i; + + if (c->tcnt == 0) + return GNUNET_YES; /* peer without handlers matches ALL */ + for (i = 0; i < c->tcnt; i++) + if (type == c->types[i]) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Send a message to all of our current clients that have the right + * options set. + * + * @param sender origin of the message (used to check that this peer is + * known to be connected to the respective client) + * @param msg message to multicast + * @param can_drop can this message be discarded if the queue is too long + * @param options mask to use + * @param type type of the embedded message, 0 for none + */ +static void +send_to_all_clients (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg, int can_drop, + int options, uint16_t type) +{ + struct GSC_Client *c; + + for (c = client_head; c != NULL; c = c->next) + { + if ((0 == (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) && + (GNUNET_YES == type_match (type, c))) + continue; /* not the full message, but we'd like the full one! */ + if ((0 == (c->options & options)) && (GNUNET_YES != type_match (type, c))) + continue; /* neither options nor type match permit the message */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to client interested in messages of type %u.\n", + (unsigned int) type); +#endif + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &sender->hashPubKey)); + send_to_client (c, msg, can_drop); + } +} + + +/** + * Handle CORE_INIT request. + * + * @param cls unused + * @param client new client that sent INIT + * @param message the 'struct InitMessage' (presumably) + */ +static void +handle_client_init (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct InitMessage *im; + struct InitReplyMessage irm; + struct GSC_Client *c; + uint16_t msize; + const uint16_t *types; + uint16_t *wtypes; + unsigned int i; + + /* check that we don't have an entry already */ + c = find_client (client); + if (NULL != c) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msize = ntohs (message->size); + if (msize < sizeof (struct InitMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_SERVER_notification_context_add (notifier, client); + im = (const struct InitMessage *) message; + types = (const uint16_t *) &im[1]; + msize -= sizeof (struct InitMessage); + c = GNUNET_malloc (sizeof (struct GSC_Client) + msize); + c->client_handle = client; + c->tcnt = msize / sizeof (uint16_t); + c->options = ntohl (im->options); + c->types = (const uint16_t *) &c[1]; + c->connectmap = GNUNET_CONTAINER_multihashmap_create (16); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (c->connectmap, + &GSC_my_identity.hashPubKey, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + wtypes = (uint16_t *) & c[1]; + for (i = 0; i < c->tcnt; i++) + wtypes[i] = ntohs (types[i]); + GSC_TYPEMAP_add (wtypes, c->tcnt); + GNUNET_CONTAINER_DLL_insert (client_head, client_tail, c); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client connecting to core service is interested in %u message types\n", + (unsigned int) c->tcnt); +#endif + /* send init reply message */ + irm.header.size = htons (sizeof (struct InitReplyMessage)); + irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY); + irm.reserved = htonl (0); + irm.my_identity = GSC_my_identity; + send_to_client (c, &irm.header, GNUNET_NO); + GSC_SESSIONS_notify_client_about_sessions (c); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle CORE_SEND_REQUEST message. + * + * @param cls unused + * @param client new client that sent CORE_SEND_REQUEST + * @param message the 'struct SendMessageRequest' (presumably) + */ +static void +handle_client_send_request (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct SendMessageRequest *req; + struct GSC_Client *c; + struct GSC_ClientActiveRequest *car; + int is_loopback; + + req = (const struct SendMessageRequest *) message; + c = find_client (client); + if (c == NULL) + { + /* client did not send INIT first! */ + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (c->requests == NULL) + c->requests = GNUNET_CONTAINER_multihashmap_create (16); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client asked for transmission to `%s'\n", + GNUNET_i2s (&req->peer)); +#endif + is_loopback = + (0 == + memcmp (&req->peer, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))); + if ((!is_loopback) && + (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &req->peer.hashPubKey))) + { + /* neighbour must have disconnected since request was issued, + * ignore (client will realize it once it processes the + * disconnect notification) */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# send requests dropped (disconnected)"), 1, + GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + car = GNUNET_CONTAINER_multihashmap_get (c->requests, &req->peer.hashPubKey); + if (car == NULL) + { + /* create new entry */ + car = GNUNET_malloc (sizeof (struct GSC_ClientActiveRequest)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (c->requests, + &req->peer.hashPubKey, + car, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + car->client_handle = c; + } + else + { + GSC_SESSIONS_dequeue_request (car); + } + car->target = req->peer; + car->deadline = GNUNET_TIME_absolute_ntoh (req->deadline); + car->priority = ntohl (req->priority); + car->msize = ntohs (req->size); + car->smr_id = req->smr_id; + car->was_solicited = GNUNET_NO; + if (is_loopback) + { + /* loopback, satisfy immediately */ + GSC_CLIENTS_solicit_request (car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GSC_SESSIONS_queue_request (car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Closure for the 'client_tokenizer_callback'. + */ +struct TokenizerContext +{ + + /** + * Active request handle for the message. + */ + struct GSC_ClientActiveRequest *car; + + /** + * Is corking allowed (set only once we have the real message). + */ + int cork; + +}; + + +/** + * Handle CORE_SEND request. + * + * @param cls unused + * @param client the client issuing the request + * @param message the "struct SendMessage" + */ +static void +handle_client_send (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct SendMessage *sm; + struct GSC_Client *c; + struct TokenizerContext tc; + uint16_t msize; + + msize = ntohs (message->size); + if (msize < + sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + sm = (const struct SendMessage *) message; + msize -= sizeof (struct SendMessage); + GNUNET_break (0 == ntohl (sm->reserved)); + c = find_client (client); + if (c == NULL) + { + /* client did not send INIT first! */ + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + tc.car = + GNUNET_CONTAINER_multihashmap_get (c->requests, &sm->peer.hashPubKey); + if (NULL == tc.car) + { + /* Must have been that we first approved the request, then got disconnected + * (which triggered removal of the 'car') and now the client gives us a message + * just *before* the client learns about the disconnect. Theoretically, we + * might also now be *again* connected. So this can happen (but should be + * rare). If it does happen, the message is discarded. */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# messages discarded (session disconnected)"), + 1, GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->requests, + &sm->peer.hashPubKey, + tc.car)); + tc.cork = ntohl (sm->cork); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client asked for transmission of %u bytes to `%s' %s\n", msize, + GNUNET_i2s (&sm->peer), tc.cork ? "now" : ""); +#endif + GNUNET_SERVER_mst_receive (client_mst, &tc, (const char *) &sm[1], msize, + GNUNET_YES, GNUNET_NO); + if (0 != + memcmp (&tc.car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + GSC_SESSIONS_dequeue_request (tc.car); + GNUNET_free (tc.car); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Functions with this signature are called whenever a complete + * message is received by the tokenizer. Used by the 'client_mst' for + * dispatching messages from clients to either the SESSION subsystem + * or other CLIENT (for loopback). + * + * @param cls closure + * @param client reservation request ('struct GSC_ClientActiveRequest') + * @param message the actual message + */ +static void +client_tokenizer_callback (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + struct TokenizerContext *tc = client; + struct GSC_ClientActiveRequest *car = tc->car; + + if (0 == + memcmp (&car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering message of type %u to myself\n", + ntohs (message->type)); +#endif + GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message, + ntohs (message->size), + GNUNET_CORE_OPTION_SEND_FULL_INBOUND | + GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND); + GSC_CLIENTS_deliver_message (&GSC_my_identity, NULL, 0, message, + sizeof (struct GNUNET_MessageHeader), + GNUNET_CORE_OPTION_SEND_HDR_INBOUND | + GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND); + } + else + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering message of type %u to %s\n", ntohs (message->type), + GNUNET_i2s (&car->target)); +#endif + GSC_SESSIONS_transmit (car, message, tc->cork); + } +} + + +/** + * Free client request records. + * + * @param cls NULL + * @param key identity of peer for which this is an active request + * @param value the 'struct GSC_ClientActiveRequest' to free + * @return GNUNET_YES (continue iteration) + */ +static int +destroy_active_client_request (void *cls, const GNUNET_HashCode * key, + void *value) +{ + struct GSC_ClientActiveRequest *car = value; + + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (car-> + client_handle->requests, + &car->target.hashPubKey, + car)); + GSC_SESSIONS_dequeue_request (car); + GNUNET_free (car); + return GNUNET_YES; +} + + +/** + * A client disconnected, clean up. + * + * @param cls closure + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct GSC_Client *c; + + if (client == NULL) + return; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client %p has disconnected from core service.\n", client); +#endif + c = find_client (client); + if (c == NULL) + return; /* client never sent INIT */ + GNUNET_CONTAINER_DLL_remove (client_head, client_tail, c); + if (c->requests != NULL) + { + GNUNET_CONTAINER_multihashmap_iterate (c->requests, + &destroy_active_client_request, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (c->requests); + } + GNUNET_CONTAINER_multihashmap_destroy (c->connectmap); + c->connectmap = NULL; + GSC_TYPEMAP_remove (c->types, c->tcnt); + GNUNET_free (c); +} + + +/** + * Tell a client that we are ready to receive the message. + * + * @param car request that is now ready; the responsibility + * for the handle remains shared between CLIENTS + * and SESSIONS after this call. + */ +void +GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car) +{ + struct GSC_Client *c; + struct SendMessageReady smr; + + c = car->client_handle; + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_contains (c->connectmap, + &car->target.hashPubKey)) + { + /* connection has gone down since, drop request */ + GNUNET_assert (0 != + memcmp (&car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))); + GSC_SESSIONS_dequeue_request (car); + GSC_CLIENTS_reject_request (car); + return; + } + smr.header.size = htons (sizeof (struct SendMessageReady)); + smr.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND_READY); + smr.size = htons (car->msize); + smr.smr_id = car->smr_id; + smr.peer = car->target; + send_to_client (c, &smr.header, GNUNET_NO); +} + + +/** + * Tell a client that we will never be ready to receive the + * given message in time (disconnect or timeout). + * + * @param car request that now permanently failed; the + * responsibility for the handle is now returned + * to CLIENTS (SESSIONS is done with it). + */ +void +GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car) +{ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (car-> + client_handle->requests, + &car->target.hashPubKey, + car)); + GNUNET_free (car); +} + + +/** + * Notify a particular client about a change to existing connection to + * one of our neighbours (check if the client is interested). Called + * from 'GSC_SESSIONS_notify_client_about_sessions'. + * + * @param client client to notify + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client, + const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new) +{ + struct ConnectNotifyMessage *cnm; + size_t size; + char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1]; + struct GNUNET_ATS_Information *a; + struct DisconnectNotifyMessage dcm; + int old_match; + int new_match; + + old_match = GSC_TYPEMAP_test_match (tmap_old, client->types, client->tcnt); + new_match = GSC_TYPEMAP_test_match (tmap_new, client->types, client->tcnt); + if (old_match == new_match) + { + GNUNET_assert (old_match == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + return; /* no change */ + } + if (old_match == GNUNET_NO) + { + /* send connect */ + GNUNET_assert (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (client->connectmap, + &neighbour->hashPubKey, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + size = + sizeof (struct ConnectNotifyMessage) + + (atsi_count) * sizeof (struct GNUNET_ATS_Information); + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + /* recovery strategy: throw away performance data */ + atsi_count = 0; + size = sizeof (struct ConnectNotifyMessage); + } + cnm = (struct ConnectNotifyMessage *) buf; + cnm->header.size = htons (size); + cnm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + cnm->ats_count = htonl (atsi_count); + a = (struct GNUNET_ATS_Information *) &cnm[1]; + memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message to client.\n", + "NOTIFY_CONNECT"); +#endif + cnm->peer = *neighbour; + send_to_client (client, &cnm->header, GNUNET_NO); + } + else + { + /* send disconnect */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (client->connectmap, + &neighbour->hashPubKey)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (client->connectmap, + &neighbour->hashPubKey, + NULL)); + dcm.header.size = htons (sizeof (struct DisconnectNotifyMessage)); + dcm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT); + dcm.reserved = htonl (0); + dcm.peer = *neighbour; + send_to_client (client, &dcm.header, GNUNET_NO); + } +} + + +/** + * Notify all clients about a change to existing session. + * Called from SESSIONS whenever there is a change in sessions + * or types processed by the respective peer. + * + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new) +{ + struct GSC_Client *c; + + for (c = client_head; c != NULL; c = c->next) + GSC_CLIENTS_notify_client_about_neighbour (c, neighbour, atsi, atsi_count, + tmap_old, tmap_new); +} + + +/** + * Deliver P2P message to interested clients. Caller must have checked + * that the sending peer actually lists the given message type as one + * of its types. + * + * @param sender peer who sent us the message + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param msg the message + * @param msize number of bytes to transmit + * @param options options for checking which clients should + * receive the message + */ +void +GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count, + const struct GNUNET_MessageHeader *msg, + uint16_t msize, int options) +{ + size_t size = + msize + sizeof (struct NotifyTrafficMessage) + + atsi_count * sizeof (struct GNUNET_ATS_Information); + char buf[size]; + struct NotifyTrafficMessage *ntm; + struct GNUNET_ATS_Information *a; + + if (0 == options) + { + GNUNET_snprintf (buf, sizeof (buf), + gettext_noop ("# bytes of messages of type %u received"), + (unsigned int) ntohs (msg->type)); + GNUNET_STATISTICS_update (GSC_stats, buf, msize, GNUNET_NO); + } + if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + /* recovery strategy: throw performance data away... */ + atsi_count = 0; + size = msize + sizeof (struct NotifyTrafficMessage); + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service passes message from `%4s' of type %u to client.\n", + GNUNET_i2s (sender), (unsigned int) ntohs (msg->type)); +#endif + GSC_SESSIONS_add_to_typemap (sender, ntohs (msg->type)); + ntm = (struct NotifyTrafficMessage *) buf; + ntm->header.size = htons (size); + ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND); + ntm->ats_count = htonl (atsi_count); + ntm->peer = *sender; + a = &ntm->ats; + memcpy (a, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count); + a[atsi_count].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR); + a[atsi_count].value = htonl (0); + memcpy (&a[atsi_count + 1], msg, msize); + send_to_all_clients (sender, &ntm->header, GNUNET_YES, options, + ntohs (msg->type)); +} + + +/** + * Initialize clients subsystem. + * + * @param server handle to server clients connect to + */ +void +GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_client_init, NULL, + GNUNET_MESSAGE_TYPE_CORE_INIT, 0}, + {&GSC_SESSIONS_handle_client_iterate_peers, NULL, + GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS, + sizeof (struct GNUNET_MessageHeader)}, + {&GSC_SESSIONS_handle_client_have_peer, NULL, + GNUNET_MESSAGE_TYPE_CORE_PEER_CONNECTED, + sizeof (struct GNUNET_MessageHeader) + + sizeof (struct GNUNET_PeerIdentity)}, + {&handle_client_send_request, NULL, + GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST, + sizeof (struct SendMessageRequest)}, + {&handle_client_send, NULL, + GNUNET_MESSAGE_TYPE_CORE_SEND, 0}, + {NULL, NULL, 0, 0} + }; + + /* setup notification */ + client_mst = GNUNET_SERVER_mst_create (&client_tokenizer_callback, NULL); + notifier = + GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE); + GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * Shutdown clients subsystem. + */ +void +GSC_CLIENTS_done () +{ + struct GSC_Client *c; + + while (NULL != (c = client_head)) + handle_client_disconnect (NULL, c->client_handle); + if (NULL != notifier) + { + GNUNET_SERVER_notification_context_destroy (notifier); + notifier = NULL; + } + GNUNET_SERVER_mst_destroy (client_mst); + client_mst = NULL; +} + +/* end of gnunet-service-core_clients.c */ diff --git a/src/core/gnunet-service-core_clients.h b/src/core/gnunet-service-core_clients.h new file mode 100644 index 0000000..bdad20d --- /dev/null +++ b/src/core/gnunet-service-core_clients.h @@ -0,0 +1,150 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_clients.h + * @brief code for managing interactions with clients of core service + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_CLIENTS_H +#define GNUNET_SERVICE_CORE_CLIENTS_H + +#include "gnunet_util_lib.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_typemap.h" + + +/** + * Send a message to one of our clients. + * + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +void +GSC_CLIENTS_send_to_client (struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *msg, + int can_drop); + + +/** + * Notify a particular client about a change to existing connection to + * one of our neighbours (check if the client is interested). Called + * from 'GSC_SESSIONS_notify_client_about_sessions'. + * + * @param client client to notify + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_client_about_neighbour (struct GSC_Client *client, + const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new); + + +/** + * Notify all clients about a change to existing session. + * Called from SESSIONS whenever there is a change in sessions + * or types processed by the respective peer. + * + * @param neighbour identity of the neighbour that changed status + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param tmap_old previous type map for the neighbour, NULL for disconnect + * @param tmap_new updated type map for the neighbour, NULL for disconnect + */ +void +GSC_CLIENTS_notify_clients_about_neighbour (const struct GNUNET_PeerIdentity + *neighbour, + const struct GNUNET_ATS_Information + *atsi, unsigned int atsi_count, + const struct GSC_TypeMap *tmap_old, + const struct GSC_TypeMap *tmap_new); + + +/** + * Deliver P2P message to interested clients. Caller must have checked + * that the sending peer actually lists the given message type as one + * of its types. + * + * @param sender peer who sent us the message + * @param atsi performance information about neighbour + * @param atsi_count number of entries in 'ats' array + * @param msg the message + * @param msize number of bytes to transmit + * @param options options for checking which clients should + * receive the message + */ +void +GSC_CLIENTS_deliver_message (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count, + const struct GNUNET_MessageHeader *msg, + uint16_t msize, int options); + + +/** + * Tell a client that we are ready to receive the message. + * + * @param car request that is now ready; the responsibility + * for the handle remains shared between CLIENTS + * and SESSIONS after this call. + */ +void +GSC_CLIENTS_solicit_request (struct GSC_ClientActiveRequest *car); + + +/** + * Tell a client that we will never be ready to receive the + * given message in time (disconnect or timeout). + * + * @param car request that now permanently failed; the + * responsibility for the handle is now returned + * to CLIENTS (SESSIONS is done with it). + */ +void +GSC_CLIENTS_reject_request (struct GSC_ClientActiveRequest *car); + + +/** + * Initialize clients subsystem. + * + * @param server handle to server clients connect to + */ +void +GSC_CLIENTS_init (struct GNUNET_SERVER_Handle *server); + + +/** + * Shutdown clients subsystem. + */ +void +GSC_CLIENTS_done (void); + +#endif +/* end of gnunet-service-core_clients.h */ diff --git a/src/core/gnunet-service-core_kx.c b/src/core/gnunet-service-core_kx.c new file mode 100644 index 0000000..c2acc6b --- /dev/null +++ b/src/core/gnunet-service-core_kx.c @@ -0,0 +1,1579 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_kx.c + * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet-service-core_kx.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_clients.h" +#include "gnunet-service-core_neighbours.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet_statistics_service.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_constants.h" +#include "gnunet_signatures.h" +#include "gnunet_protocols.h" +#include "core.h" + +/** + * How long do we wait for SET_KEY confirmation initially? + */ +#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (MAX_SET_KEY_DELAY, 1) + +/** + * What is the minimum frequency for a PING message? + */ +#define MIN_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * What is the maximum age of a message for us to consider processing + * it? Note that this looks at the timestamp used by the other peer, + * so clock skew between machines does come into play here. So this + * should be picked high enough so that a little bit of clock skew + * does not prevent peers from connecting to us. + */ +#define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS + +/** + * What is the maximum delay for a SET_KEY message? + */ +#define MAX_SET_KEY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * We're sending an (encrypted) PING to the other peer to check if he + * can decrypt. The other peer should respond with a PONG with the + * same content, except this time encrypted with the receiver's key. + */ +struct PingMessage +{ + /** + * Message type is CORE_PING. + */ + struct GNUNET_MessageHeader header; + + /** + * Seed for the IV + */ + uint32_t iv_seed GNUNET_PACKED; + + /** + * Intended target of the PING, used primarily to check + * that decryption actually worked. + */ + struct GNUNET_PeerIdentity target; + + /** + * Random number chosen to make reply harder. + */ + uint32_t challenge GNUNET_PACKED; +}; + + +/** + * Response to a PING. Includes data from the original PING. + */ +struct PongMessage +{ + /** + * Message type is CORE_PONG. + */ + struct GNUNET_MessageHeader header; + + /** + * Seed for the IV + */ + uint32_t iv_seed GNUNET_PACKED; + + /** + * Random number to make faking the reply harder. Must be + * first field after header (this is where we start to encrypt!). + */ + uint32_t challenge GNUNET_PACKED; + + /** + * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'. + */ + struct GNUNET_BANDWIDTH_Value32NBO reserved; + + /** + * Intended target of the PING, used primarily to check + * that decryption actually worked. + */ + struct GNUNET_PeerIdentity target; +}; + + +/** + * Message transmitted to set (or update) a session key. + */ +struct SetKeyMessage +{ + + /** + * Message type is either CORE_SET_KEY. + */ + struct GNUNET_MessageHeader header; + + /** + * Status of the sender (should be in "enum PeerStateMachine"), nbo. + */ + int32_t sender_status GNUNET_PACKED; + + /** + * Purpose of the signature, will be + * GNUNET_SIGNATURE_PURPOSE_SET_KEY. + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * At what time was this key created? + */ + struct GNUNET_TIME_AbsoluteNBO creation_time; + + /** + * The encrypted session key. + */ + struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; + + /** + * Who is the intended recipient? + */ + struct GNUNET_PeerIdentity target; + + /** + * Signature of the stuff above (starting at purpose). + */ + struct GNUNET_CRYPTO_RsaSignature signature; + +}; + + +/** + * Encapsulation for encrypted messages exchanged between + * peers. Followed by the actual encrypted data. + */ +struct EncryptedMessage +{ + /** + * Message type is either CORE_ENCRYPTED_MESSAGE. + */ + struct GNUNET_MessageHeader header; + + /** + * Random value used for IV generation. + */ + uint32_t iv_seed GNUNET_PACKED; + + /** + * MAC of the encrypted message (starting at 'sequence_number'), + * used to verify message integrity. Everything after this value + * (excluding this value itself) will be encrypted and authenticated. + * ENCRYPTED_HEADER_SIZE must be set to the offset of the *next* field. + */ + GNUNET_HashCode hmac; + + /** + * Sequence number, in network byte order. This field + * must be the first encrypted/decrypted field + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'. + */ + struct GNUNET_BANDWIDTH_Value32NBO reserved; + + /** + * Timestamp. Used to prevent reply of ancient messages + * (recent messages are caught with the sequence number). + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + +}; +GNUNET_NETWORK_STRUCT_END +/** + * Number of bytes (at the beginning) of "struct EncryptedMessage" + * that are NOT encrypted. + */ +#define ENCRYPTED_HEADER_SIZE (offsetof(struct EncryptedMessage, sequence_number)) + + +/** + * State machine for our P2P encryption handshake. Everyone starts in + * "DOWN", if we receive the other peer's key (other peer initiated) + * we start in state RECEIVED (since we will immediately send our + * own); otherwise we start in SENT. If we get back a PONG from + * within either state, we move up to CONFIRMED (the PONG will always + * be sent back encrypted with the key we sent to the other peer). + */ +enum KxStateMachine +{ + /** + * No handshake yet. + */ + KX_STATE_DOWN, + + /** + * We've sent our session key. + */ + KX_STATE_KEY_SENT, + + /** + * We've received the other peers session key. + */ + KX_STATE_KEY_RECEIVED, + + /** + * The other peer has confirmed our session key with a message + * encrypted with his session key (which we got). Key exchange + * is done. + */ + KX_STATE_UP +}; + + +/** + * Information about the status of a key exchange with another peer. + */ +struct GSC_KeyExchangeInfo +{ + /** + * Identity of the peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * SetKeyMessage to transmit (initialized the first + * time our status goes past 'KX_STATE_KEY_SENT'). + */ + struct SetKeyMessage skm; + + /** + * PING message we transmit to the other peer. + */ + struct PingMessage ping; + + /** + * SetKeyMessage we received and did not process yet. + */ + struct SetKeyMessage *skm_received; + + /** + * PING message we received from the other peer and + * did not process yet (or NULL). + */ + struct PingMessage *ping_received; + + /** + * PONG message we received from the other peer and + * did not process yet (or NULL). + */ + struct PongMessage *pong_received; + + /** + * Encrypted message we received from the other peer and + * did not process yet (or NULL). + */ + struct EncryptedMessage *emsg_received; + + /** + * Non-NULL if we are currently looking up HELLOs for this peer. + * for this peer. + */ + struct GNUNET_PEERINFO_IteratorContext *pitr; + + /** + * Public key of the neighbour, NULL if we don't have it yet. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; + + /** + * We received a PONG message before we got the "public_key" + * (or the SET_KEY). We keep it here until we have a key + * to decrypt it. NULL if no PONG is pending. + */ + struct PongMessage *pending_pong; + + /** + * Key we use to encrypt our messages for the other peer + * (initialized by us when we do the handshake). + */ + struct GNUNET_CRYPTO_AesSessionKey encrypt_key; + + /** + * Key we use to decrypt messages from the other peer + * (given to us by the other peer during the handshake). + */ + struct GNUNET_CRYPTO_AesSessionKey decrypt_key; + + /** + * At what time did we generate our encryption key? + */ + struct GNUNET_TIME_Absolute encrypt_key_created; + + /** + * At what time did the other peer generate the decryption key? + */ + struct GNUNET_TIME_Absolute decrypt_key_created; + + /** + * When should the session time out (if there are no PONGs)? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * At what frequency are we currently re-trying SET_KEY messages? + */ + struct GNUNET_TIME_Relative set_key_retry_frequency; + + /** + * ID of task used for re-trying SET_KEY and PING message. + */ + GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task; + + /** + * ID of task used for sending keep-alive pings. + */ + GNUNET_SCHEDULER_TaskIdentifier keep_alive_task; + + /** + * Bit map indicating which of the 32 sequence numbers before the last + * were received (good for accepting out-of-order packets and + * estimating reliability of the connection) + */ + unsigned int last_packets_bitmap; + + /** + * last sequence number received on this connection (highest) + */ + uint32_t last_sequence_number_received; + + /** + * last sequence number transmitted + */ + uint32_t last_sequence_number_sent; + + /** + * What was our PING challenge number (for this peer)? + */ + uint32_t ping_challenge; + + /** + * What is our connection status? + */ + enum KxStateMachine status; + +}; + + + +/** + * Handle to peerinfo service. + */ +static struct GNUNET_PEERINFO_Handle *peerinfo; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Our message stream tokenizer (for encrypted payload). + */ +static struct GNUNET_SERVER_MessageStreamTokenizer *mst; + + + +/** + * Derive an authentication key from "set key" information + */ +static void +derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + struct GNUNET_TIME_Absolute creation_time) +{ + static const char ctx[] = "authentication key"; + struct GNUNET_TIME_AbsoluteNBO ctbe; + + + ctbe = GNUNET_TIME_absolute_hton (creation_time); + GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key, + sizeof (skey->key), &ctbe, sizeof (ctbe), ctx, + sizeof (ctx), NULL); +} + + +/** + * Derive an IV from packet information + */ +static void +derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + const struct GNUNET_PeerIdentity *identity) +{ + static const char ctx[] = "initialization vector"; + + GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), + &identity->hashPubKey.bits, + sizeof (identity->hashPubKey.bits), ctx, + sizeof (ctx), NULL); +} + +/** + * Derive an IV from pong packet information + */ +static void +derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, + const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, + uint32_t challenge, const struct GNUNET_PeerIdentity *identity) +{ + static const char ctx[] = "pong initialization vector"; + + GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), + &identity->hashPubKey.bits, + sizeof (identity->hashPubKey.bits), &challenge, + sizeof (challenge), ctx, sizeof (ctx), NULL); +} + + +/** + * Encrypt size bytes from in and write the result to out. Use the + * key for outbound traffic of the given neighbour. + * + * @param kx key information context + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_encrypt (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_CRYPTO_AesInitializationVector *iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + GNUNET_assert (size == + GNUNET_CRYPTO_aes_encrypt (in, (uint16_t) size, + &kx->encrypt_key, iv, out)); + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes encrypted"), size, + GNUNET_NO); +#if DEBUG_CORE > 2 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted %u bytes for `%4s' using key %u, IV %u\n", + (unsigned int) size, GNUNET_i2s (&kx->peer), + (unsigned int) kx->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, + sizeof + (iv))); +#endif + return GNUNET_OK; +} + + + + +/** + * Decrypt size bytes from in and write the result to out. Use the + * key for inbound traffic of the given neighbour. This function does + * NOT do any integrity-checks on the result. + * + * @param kx key information context + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_decrypt (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_CRYPTO_AesInitializationVector *iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (size != + GNUNET_CRYPTO_aes_decrypt (in, (uint16_t) size, &kx->decrypt_key, iv, + out)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes decrypted"), size, + GNUNET_NO); +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted %u bytes from `%4s' using key %u, IV %u\n", + (unsigned int) size, GNUNET_i2s (&kx->peer), + (unsigned int) kx->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, + sizeof + (*iv))); +#endif + return GNUNET_OK; +} + + +/** + * Send our key (and encrypted PING) to the other peer. + * + * @param kx key exchange context + */ +static void +send_key (struct GSC_KeyExchangeInfo *kx); + + +/** + * Task that will retry "send_key" if our previous attempt failed. + * + * @param cls our 'struct GSC_KeyExchangeInfo' + * @param tc scheduler context + */ +static void +set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GSC_KeyExchangeInfo *kx = cls; + + kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; + kx->set_key_retry_frequency = + GNUNET_TIME_relative_multiply (kx->set_key_retry_frequency, 2); + send_key (kx); +} + + +/** + * PEERINFO is giving us a HELLO for a peer. Add the public key to + * the neighbour's struct and continue with the key exchange. Or, if + * we did not get a HELLO, just do nothing. + * + * @param cls the 'struct GSC_KeyExchangeInfo' to retry sending the key for + * @param peer the peer for which this is the HELLO + * @param hello HELLO message of that peer + * @param err_msg NULL if successful, otherwise contains error message + */ +static void +process_hello (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, const char *err_msg) +{ + struct GSC_KeyExchangeInfo *kx = cls; + struct SetKeyMessage *skm; + + if (err_msg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Error in communication with PEERINFO service\n")); + kx->pitr = NULL; + kx->retry_set_key_task = + GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, + &set_key_retry_task, kx); + return; + } + if (peer == NULL) + { + kx->pitr = NULL; + if (kx->public_key != NULL) + return; /* done here */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n", + GNUNET_i2s (&kx->peer)); +#endif + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# Delayed connecting due to lack of public key"), + 1, GNUNET_NO); + kx->retry_set_key_task = + GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, + &set_key_retry_task, kx); + return; + } + if (kx->public_key != NULL) + { + /* already have public key, why are we here? */ + GNUNET_break (0); + return; + } + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->retry_set_key_task); + kx->public_key = + GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + if (GNUNET_OK != GNUNET_HELLO_get_key (hello, kx->public_key)) + { + GNUNET_break (0); + GNUNET_free (kx->public_key); + kx->public_key = NULL; + return; + } + send_key (kx); + if (NULL != kx->skm_received) + { + skm = kx->skm_received; + kx->skm_received = NULL; + GSC_KX_handle_set_key (kx, &skm->header); + GNUNET_free (skm); + } +} + + +/** + * Start the key exchange with the given peer. + * + * @param pid identity of the peer to do a key exchange with + * @return key exchange information context + */ +struct GSC_KeyExchangeInfo * +GSC_KX_start (const struct GNUNET_PeerIdentity *pid) +{ + struct GSC_KeyExchangeInfo *kx; + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating key exchange with `%s'\n", + GNUNET_i2s (pid)); +#endif + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# key exchanges initiated"), 1, + GNUNET_NO); + kx = GNUNET_malloc (sizeof (struct GSC_KeyExchangeInfo)); + kx->peer = *pid; + kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; + kx->pitr = + GNUNET_PEERINFO_iterate (peerinfo, pid, + GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ , + &process_hello, kx); + return kx; +} + + +/** + * Stop key exchange with the given peer. Clean up key material. + * + * @param kx key exchange to stop + */ +void +GSC_KX_stop (struct GSC_KeyExchangeInfo *kx) +{ + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# key exchanges stopped"), + 1, GNUNET_NO); + if (kx->pitr != NULL) + { + GNUNET_PEERINFO_iterate_cancel (kx->pitr); + kx->pitr = NULL; + } + if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); + kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; + } + if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (kx->keep_alive_task); + kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_free_non_null (kx->skm_received); + GNUNET_free_non_null (kx->ping_received); + GNUNET_free_non_null (kx->pong_received); + GNUNET_free_non_null (kx->emsg_received); + GNUNET_free_non_null (kx->public_key); + GNUNET_free (kx); +} + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param kx key exchange status for the corresponding peer + * @param msg the set key message we received + */ +void +GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg) +{ + const struct SetKeyMessage *m; + struct GNUNET_TIME_Absolute t; + struct GNUNET_CRYPTO_AesSessionKey k; + struct PingMessage *ping; + struct PongMessage *pong; + enum KxStateMachine sender_status; + uint16_t size; + + size = ntohs (msg->size); + if (size != sizeof (struct SetKeyMessage)) + { + GNUNET_break_op (0); + return; + } + m = (const struct SetKeyMessage *) msg; + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# session keys received"), + 1, GNUNET_NO); + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", "SET_KEY", + GNUNET_i2s (&kx->peer)); +#endif + if (kx->public_key == NULL) + { + GNUNET_free_non_null (kx->skm_received); + kx->skm_received = (struct SetKeyMessage *) GNUNET_copy_message (msg); + return; + } + if (0 != + memcmp (&m->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("`%s' is for `%s', not for me. Ignoring.\n"), "SET_KEY", + GNUNET_i2s (&m->target)); + return; + } + if ((ntohl (m->purpose.size) != + sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + + sizeof (struct GNUNET_PeerIdentity)) || + (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY, &m->purpose, + &m->signature, kx->public_key))) + { + /* invalid signature */ + GNUNET_break_op (0); + return; + } + t = GNUNET_TIME_absolute_ntoh (m->creation_time); + if (((kx->status == KX_STATE_KEY_RECEIVED) || (kx->status == KX_STATE_UP)) && + (t.abs_value < kx->decrypt_key_created.abs_value)) + { + /* this could rarely happen due to massive re-ordering of + * messages on the network level, but is most likely either + * a bug or some adversary messing with us. Report. */ + GNUNET_break_op (0); + return; + } + if ((GNUNET_CRYPTO_rsa_decrypt + (my_private_key, &m->encrypted_key, &k, + sizeof (struct GNUNET_CRYPTO_AesSessionKey)) != + sizeof (struct GNUNET_CRYPTO_AesSessionKey)) || + (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k))) + { + /* failed to decrypt !? */ + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# SET_KEY messages decrypted"), 1, + GNUNET_NO); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received SET_KEY from `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + kx->decrypt_key = k; + if (kx->decrypt_key_created.abs_value != t.abs_value) + { + /* fresh key, reset sequence numbers */ + kx->last_sequence_number_received = 0; + kx->last_packets_bitmap = 0; + kx->decrypt_key_created = t; + } + sender_status = (enum KxStateMachine) ntohl (m->sender_status); + + switch (kx->status) + { + case KX_STATE_DOWN: + kx->status = KX_STATE_KEY_RECEIVED; + /* we're not up, so we are already doing 'send_key' */ + break; + case KX_STATE_KEY_SENT: + kx->status = KX_STATE_KEY_RECEIVED; + /* we're not up, so we are already doing 'send_key' */ + break; + case KX_STATE_KEY_RECEIVED: + /* we're not up, so we are already doing 'send_key' */ + break; + case KX_STATE_UP: + if ((sender_status == KX_STATE_DOWN) || + (sender_status == KX_STATE_KEY_SENT)) + send_key (kx); /* we are up, but other peer is not! */ + break; + default: + GNUNET_break (0); + break; + } + if (kx->ping_received != NULL) + { + ping = kx->ping_received; + kx->ping_received = NULL; + GSC_KX_handle_ping (kx, &ping->header); + GNUNET_free (ping); + } + if (kx->pong_received != NULL) + { + pong = kx->pong_received; + kx->pong_received = NULL; + GSC_KX_handle_pong (kx, &pong->header); + GNUNET_free (pong); + } +} + + +/** + * We received a PING message. Validate and transmit + * a PONG message. + * + * @param kx key exchange status for the corresponding peer + * @param msg the encrypted PING message itself + */ +void +GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg) +{ + const struct PingMessage *m; + struct PingMessage t; + struct PongMessage tx; + struct PongMessage tp; + struct GNUNET_CRYPTO_AesInitializationVector iv; + uint16_t msize; + + msize = ntohs (msg->size); + if (msize != sizeof (struct PingMessage)) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PING messages received"), 1, + GNUNET_NO); + if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP)) + { + /* defer */ + GNUNET_free_non_null (kx->ping_received); + kx->ping_received = (struct PingMessage *) GNUNET_copy_message (msg); + return; + } + m = (const struct PingMessage *) msg; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", "PING", + GNUNET_i2s (&kx->peer)); +#endif + derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); + if (GNUNET_OK != + do_decrypt (kx, &iv, &m->target, &t.target, + sizeof (struct PingMessage) - ((void *) &m->target - + (void *) m))) + { + GNUNET_break_op (0); + return; + } + if (0 != + memcmp (&t.target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + char sender[9]; + char peer[9]; + + GNUNET_snprintf (sender, sizeof (sender), "%8s", GNUNET_i2s (&kx->peer)); + GNUNET_snprintf (peer, sizeof (peer), "%8s", GNUNET_i2s (&t.target)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Received PING from `%s' for different identity: I am `%s', PONG identity: `%s'\n"), + sender, GNUNET_i2s (&GSC_my_identity), peer); + GNUNET_break_op (0); + return; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PING from `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + /* construct PONG */ + tx.reserved = GNUNET_BANDWIDTH_VALUE_MAX; + tx.challenge = t.challenge; + tx.target = t.target; + tp.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); + tp.header.size = htons (sizeof (struct PongMessage)); + tp.iv_seed = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + derive_pong_iv (&iv, &kx->encrypt_key, tp.iv_seed, t.challenge, &kx->peer); + do_encrypt (kx, &iv, &tx.challenge, &tp.challenge, + sizeof (struct PongMessage) - ((void *) &tp.challenge - + (void *) &tp)); + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# PONG messages created"), + 1, GNUNET_NO); + GSC_NEIGHBOURS_transmit (&kx->peer, &tp.header, + GNUNET_TIME_UNIT_FOREVER_REL /* FIXME: timeout */ ); +} + + +/** + * Create a fresh SET KEY message for transmission to the other peer. + * Also creates a new key. + * + * @param kx key exchange context to create SET KEY message for + */ +static void +setup_fresh_setkey (struct GSC_KeyExchangeInfo *kx) +{ + struct SetKeyMessage *skm; + + GNUNET_CRYPTO_aes_create_session_key (&kx->encrypt_key); + kx->encrypt_key_created = GNUNET_TIME_absolute_get (); + skm = &kx->skm; + skm->header.size = htons (sizeof (struct SetKeyMessage)); + skm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY); + skm->purpose.size = + htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + + sizeof (struct GNUNET_PeerIdentity)); + skm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY); + skm->creation_time = GNUNET_TIME_absolute_hton (kx->encrypt_key_created); + skm->target = kx->peer; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_encrypt (&kx->encrypt_key, + sizeof (struct + GNUNET_CRYPTO_AesSessionKey), + kx->public_key, + &skm->encrypted_key)); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (my_private_key, &skm->purpose, + &skm->signature)); +} + + +/** + * Create a fresh PING message for transmission to the other peer. + * + * @param kx key exchange context to create PING for + */ +static void +setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) +{ + struct PingMessage pp; + struct PingMessage *pm; + struct GNUNET_CRYPTO_AesInitializationVector iv; + + pm = &kx->ping; + pm->header.size = htons (sizeof (struct PingMessage)); + pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); + pm->iv_seed = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, &kx->peer); + pp.challenge = kx->ping_challenge; + pp.target = kx->peer; + do_encrypt (kx, &iv, &pp.target, &pm->target, + sizeof (struct PingMessage) - ((void *) &pm->target - + (void *) pm)); +} + + +/** + * Task triggered when a neighbour entry is about to time out + * (and we should prevent this by sending a PING). + * + * @param cls the 'struct GSC_KeyExchangeInfo' + * @param tc scheduler context (not used) + */ +static void +send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GSC_KeyExchangeInfo *kx = cls; + struct GNUNET_TIME_Relative retry; + struct GNUNET_TIME_Relative left; + + kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK; + left = GNUNET_TIME_absolute_get_remaining (kx->timeout); + if (left.rel_value == 0) + { + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# sessions terminated by timeout"), + 1, GNUNET_NO); + GSC_SESSIONS_end (&kx->peer); + kx->status = KX_STATE_DOWN; + return; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending KEEPALIVE to `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# keepalive messages sent"), 1, + GNUNET_NO); + setup_fresh_ping (kx); + GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header, + kx->set_key_retry_frequency); + retry = + GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2), + MIN_PING_FREQUENCY); + kx->keep_alive_task = + GNUNET_SCHEDULER_add_delayed (retry, &send_keep_alive, kx); +} + + +/** + * We've seen a valid message from the other peer. + * Update the time when the session would time out + * and delay sending our keep alive message further. + * + * @param kx key exchange where we saw activity + */ +static void +update_timeout (struct GSC_KeyExchangeInfo *kx) +{ + kx->timeout = + GNUNET_TIME_relative_to_absolute + (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (kx->keep_alive_task); + kx->keep_alive_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + 2), &send_keep_alive, kx); +} + + +/** + * We received a PONG message. Validate and update our status. + * + * @param kx key exchange context for the the PONG + * @param msg the encrypted PONG message itself + */ +void +GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg) +{ + const struct PongMessage *m; + struct PongMessage t; + struct EncryptedMessage *emsg; + struct GNUNET_CRYPTO_AesInitializationVector iv; + uint16_t msize; + + msize = ntohs (msg->size); + if (msize != sizeof (struct PongMessage)) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PONG messages received"), 1, + GNUNET_NO); + if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP)) + { + if (kx->status == KX_STATE_KEY_SENT) + { + GNUNET_free_non_null (kx->pong_received); + kx->pong_received = (struct PongMessage *) GNUNET_copy_message (msg); + } + return; + } + m = (const struct PongMessage *) msg; +#if DEBUG_HANDSHAKE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' response from `%4s'.\n", "PONG", + GNUNET_i2s (&kx->peer)); +#endif + /* mark as garbage, just to be sure */ + memset (&t, 255, sizeof (t)); + derive_pong_iv (&iv, &kx->decrypt_key, m->iv_seed, kx->ping_challenge, + &GSC_my_identity); + if (GNUNET_OK != + do_decrypt (kx, &iv, &m->challenge, &t.challenge, + sizeof (struct PongMessage) - ((void *) &m->challenge - + (void *) m))) + { + GNUNET_break_op (0); + return; + } + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PONG messages decrypted"), 1, + GNUNET_NO); + if ((0 != memcmp (&t.target, &kx->peer, sizeof (struct GNUNET_PeerIdentity))) + || (kx->ping_challenge != t.challenge)) + { + /* PONG malformed */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malformed `%s' wanted sender `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&kx->peer), + (unsigned int) kx->ping_challenge); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malformed `%s' received from `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge); +#endif + return; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PONG from `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + switch (kx->status) + { + case KX_STATE_DOWN: + GNUNET_break (0); /* should be impossible */ + return; + case KX_STATE_KEY_SENT: + GNUNET_break (0); /* should be impossible */ + return; + case KX_STATE_KEY_RECEIVED: + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# session keys confirmed via PONG"), 1, + GNUNET_NO); + kx->status = KX_STATE_UP; + GSC_SESSIONS_create (&kx->peer, kx); + if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task) + { + GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); + kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_assert (kx->keep_alive_task == GNUNET_SCHEDULER_NO_TASK); + if (kx->emsg_received != NULL) + { + emsg = kx->emsg_received; + kx->emsg_received = NULL; + GSC_KX_handle_encrypted_message (kx, &emsg->header, NULL, + 0 /* FIXME: ATSI */ ); + GNUNET_free (emsg); + } + update_timeout (kx); + break; + case KX_STATE_UP: + update_timeout (kx); + break; + default: + GNUNET_break (0); + break; + } +} + + +/** + * Send our key (and encrypted PING) to the other peer. + * + * @param kx key exchange context + */ +static void +send_key (struct GSC_KeyExchangeInfo *kx) +{ + GNUNET_assert (kx->retry_set_key_task == GNUNET_SCHEDULER_NO_TASK); + if (KX_STATE_UP == kx->status) + return; /* nothing to do */ + if (kx->public_key == NULL) + { + /* lookup public key, then try again */ +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to obtain public key for `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + kx->pitr = + GNUNET_PEERINFO_iterate (peerinfo, &kx->peer, + GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ , + &process_hello, kx); + return; + } + + /* update status */ + switch (kx->status) + { + case KX_STATE_DOWN: + kx->status = KX_STATE_KEY_SENT; + /* setup SET KEY message */ + setup_fresh_setkey (kx); + setup_fresh_ping (kx); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# SET_KEY and PING messages created"), 1, + GNUNET_NO); + break; + case KX_STATE_KEY_SENT: + break; + case KX_STATE_KEY_RECEIVED: + break; + case KX_STATE_UP: + GNUNET_break (0); + return; + default: + GNUNET_break (0); + return; + } + + /* always update sender status in SET KEY message */ + kx->skm.sender_status = htonl ((int32_t) kx->status); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SET_KEY and PING to `%s'\n", + GNUNET_i2s (&kx->peer)); +#endif + GSC_NEIGHBOURS_transmit (&kx->peer, &kx->skm.header, + kx->set_key_retry_frequency); + GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header, + kx->set_key_retry_frequency); + kx->retry_set_key_task = + GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, + &set_key_retry_task, kx); +} + + +/** + * Encrypt and transmit a message with the given payload. + * + * @param kx key exchange context + * @param payload payload of the message + * @param payload_size number of bytes in 'payload' + */ +void +GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, + const void *payload, size_t payload_size) +{ + size_t used = payload_size + sizeof (struct EncryptedMessage); + char pbuf[used]; /* plaintext */ + char cbuf[used]; /* ciphertext */ + struct EncryptedMessage *em; /* encrypted message */ + struct EncryptedMessage *ph; /* plaintext header */ + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_AuthKey auth_key; + + ph = (struct EncryptedMessage *) pbuf; + ph->iv_seed = + htonl (GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); + ph->sequence_number = htonl (++kx->last_sequence_number_sent); + ph->reserved = GNUNET_BANDWIDTH_VALUE_MAX; + ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + memcpy (&ph[1], payload, payload_size); + + em = (struct EncryptedMessage *) cbuf; + em->header.size = htons (used); + em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); + em->iv_seed = ph->iv_seed; + derive_iv (&iv, &kx->encrypt_key, ph->iv_seed, &kx->peer); + GNUNET_assert (GNUNET_OK == + do_encrypt (kx, &iv, &ph->sequence_number, + &em->sequence_number, + used - ENCRYPTED_HEADER_SIZE)); +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted %u bytes for %s\n", + used - ENCRYPTED_HEADER_SIZE, GNUNET_i2s (&kx->peer)); +#endif + derive_auth_key (&auth_key, &kx->encrypt_key, ph->iv_seed, + kx->encrypt_key_created); + GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number, + used - ENCRYPTED_HEADER_SIZE, &em->hmac); + GSC_NEIGHBOURS_transmit (&kx->peer, &em->header, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Closure for 'deliver_message' + */ +struct DeliverMessageContext +{ + + /** + * Performance information for the connection. + */ + const struct GNUNET_ATS_Information *atsi; + + /** + * Sender of the message. + */ + const struct GNUNET_PeerIdentity *peer; + + /** + * Number of entries in 'atsi' array. + */ + uint32_t atsi_count; +}; + + +/** + * We received an encrypted message. Decrypt, validate and + * pass on to the appropriate clients. + * + * @param kx key exchange context for encrypting the message + * @param msg encrypted message + * @param atsi performance data + * @param atsi_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_ATS_Information *atsi, + uint32_t atsi_count) +{ + const struct EncryptedMessage *m; + struct EncryptedMessage *pt; /* plaintext */ + GNUNET_HashCode ph; + uint32_t snum; + struct GNUNET_TIME_Absolute t; + struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_AuthKey auth_key; + struct DeliverMessageContext dmc; + uint16_t size = ntohs (msg->size); + char buf[size]; + + if (size < + sizeof (struct EncryptedMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return; + } + m = (const struct EncryptedMessage *) msg; + if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP)) + { + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# failed to decrypt message (no session key)"), + 1, GNUNET_NO); + return; + } + if (kx->status == KX_STATE_KEY_RECEIVED) + { + /* defer */ + GNUNET_free_non_null (kx->ping_received); + kx->emsg_received = (struct EncryptedMessage *) GNUNET_copy_message (msg); + return; + } + /* validate hash */ + derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed, + kx->decrypt_key_created); + GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number, + size - ENCRYPTED_HEADER_SIZE, &ph); + if (0 != memcmp (&ph, &m->hmac, sizeof (GNUNET_HashCode))) + { + /* checksum failed */ + GNUNET_break_op (0); + return; + } + derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); + /* decrypt */ + if (GNUNET_OK != + do_decrypt (kx, &iv, &m->sequence_number, &buf[ENCRYPTED_HEADER_SIZE], + size - ENCRYPTED_HEADER_SIZE)) + return; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypted %u bytes from %s\n", + size - ENCRYPTED_HEADER_SIZE, GNUNET_i2s (&kx->peer)); +#endif + pt = (struct EncryptedMessage *) buf; + + /* validate sequence number */ + snum = ntohl (pt->sequence_number); + if (kx->last_sequence_number_received == snum) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received duplicate message, ignoring.\n"); + /* duplicate, ignore */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# bytes dropped (duplicates)"), + size, GNUNET_NO); + return; + } + if ((kx->last_sequence_number_received > snum) && + (kx->last_sequence_number_received - snum > 32)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received ancient out of sequence message, ignoring.\n"); + /* ancient out of sequence, ignore */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# bytes dropped (out of sequence)"), size, + GNUNET_NO); + return; + } + if (kx->last_sequence_number_received > snum) + { + unsigned int rotbit = 1 << (kx->last_sequence_number_received - snum - 1); + + if ((kx->last_packets_bitmap & rotbit) != 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received duplicate message, ignoring.\n"); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# bytes dropped (duplicates)"), + size, GNUNET_NO); + /* duplicate, ignore */ + return; + } + kx->last_packets_bitmap |= rotbit; + } + if (kx->last_sequence_number_received < snum) + { + unsigned int shift = (snum - kx->last_sequence_number_received); + + if (shift >= 8 * sizeof (kx->last_packets_bitmap)) + kx->last_packets_bitmap = 0; + else + kx->last_packets_bitmap <<= shift; + kx->last_sequence_number_received = snum; + } + + /* check timestamp */ + t = GNUNET_TIME_absolute_ntoh (pt->timestamp); + if (GNUNET_TIME_absolute_get_duration (t).rel_value > + MAX_MESSAGE_AGE.rel_value) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Message received far too old (%llu ms). Content ignored.\n"), + GNUNET_TIME_absolute_get_duration (t).rel_value); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# bytes dropped (ancient message)"), size, + GNUNET_NO); + return; + } + + /* process decrypted message(s) */ + update_timeout (kx); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# bytes of payload decrypted"), + size - sizeof (struct EncryptedMessage), GNUNET_NO); + dmc.atsi = atsi; + dmc.atsi_count = atsi_count; + dmc.peer = &kx->peer; + if (GNUNET_OK != + GNUNET_SERVER_mst_receive (mst, &dmc, + &buf[sizeof (struct EncryptedMessage)], + size - sizeof (struct EncryptedMessage), + GNUNET_YES, GNUNET_NO)) + GNUNET_break_op (0); +} + + +/** + * Deliver P2P message to interested clients. + * Invokes send twice, once for clients that want the full message, and once + * for clients that only want the header + * + * @param cls always NULL + * @param client who sent us the message (struct GSC_KeyExchangeInfo) + * @param m the message + */ +static void +deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m) +{ + struct DeliverMessageContext *dmc = client; + + switch (ntohs (m->type)) + { + case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: + case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: + GSC_SESSIONS_set_typemap (dmc->peer, m); + return; + default: + GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m, + ntohs (m->size), + GNUNET_CORE_OPTION_SEND_FULL_INBOUND); + GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m, + sizeof (struct GNUNET_MessageHeader), + GNUNET_CORE_OPTION_SEND_HDR_INBOUND); + } +} + + +/** + * Initialize KX subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int +GSC_KX_init () +{ + char *keyfile; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY", + &keyfile)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); + return GNUNET_SYSERR; + } + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Core service could not access hostkey. Exiting.\n")); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), + &GSC_my_identity.hashPubKey); + peerinfo = GNUNET_PEERINFO_connect (GSC_cfg); + if (NULL == peerinfo) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not access PEERINFO service. Exiting.\n")); + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; + return GNUNET_SYSERR; + } + mst = GNUNET_SERVER_mst_create (&deliver_message, NULL); + return GNUNET_OK; +} + + +/** + * Shutdown KX subsystem. + */ +void +GSC_KX_done () +{ + if (my_private_key != NULL) + { + GNUNET_CRYPTO_rsa_key_free (my_private_key); + my_private_key = NULL; + } + if (peerinfo != NULL) + { + GNUNET_PEERINFO_disconnect (peerinfo); + peerinfo = NULL; + } + if (mst != NULL) + { + GNUNET_SERVER_mst_destroy (mst); + mst = NULL; + } +} + +/* end of gnunet-service-core_kx.c */ diff --git a/src/core/gnunet-service-core_kx.h b/src/core/gnunet-service-core_kx.h new file mode 100644 index 0000000..5ecd2c1 --- /dev/null +++ b/src/core/gnunet-service-core_kx.h @@ -0,0 +1,137 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_kx.h + * @brief code for managing the key exchange (SET_KEY, PING, PONG) with other peers + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_KX_H +#define GNUNET_SERVICE_CORE_KX_H + +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" + + +/** + * Information about the status of a key exchange with another peer. + */ +struct GSC_KeyExchangeInfo; + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param kx key exchange status for the corresponding peer + * @param msg the set key message we received + */ +void +GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg); + + +/** + * We received a PING message. Validate and transmit + * a PONG message. + * + * @param kx key exchange status for the corresponding peer + * @param msg the encrypted PING message itself + */ +void +GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg); + + +/** + * We received a PONG message. Validate and update our status. + * + * @param kx key exchange status for the corresponding peer + * @param msg the encrypted PONG message itself + */ +void +GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg); + + +/** + * Encrypt and transmit a message with the given payload. + * + * @param kx key exchange context + * @param payload payload of the message + * @param payload_size number of bytes in 'payload' + */ +void +GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, + const void *payload, size_t payload_size); + + +/** + * We received an encrypted message. Decrypt, validate and + * pass on to the appropriate clients. + * + * @param kx key exchange information context + * @param msg encrypted message + * @param atsi performance data + * @param atsi_count number of entries in ats (excluding 0-termination) + */ +void +GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, + const struct GNUNET_MessageHeader *msg, + const struct GNUNET_ATS_Information *atsi, + uint32_t atsi_count); + + +/** + * Start the key exchange with the given peer. + * + * @param pid identity of the peer to do a key exchange with + * @return key exchange information context + */ +struct GSC_KeyExchangeInfo * +GSC_KX_start (const struct GNUNET_PeerIdentity *pid); + + +/** + * Stop key exchange with the given peer. Clean up key material. + * + * @param kx key exchange to stop + */ +void +GSC_KX_stop (struct GSC_KeyExchangeInfo *kx); + + +/** + * Initialize KX subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int +GSC_KX_init (void); + + +/** + * Shutdown KX subsystem. + */ +void +GSC_KX_done (void); + +#endif +/* end of gnunet-service-core_kx.h */ diff --git a/src/core/gnunet-service-core_neighbours.c b/src/core/gnunet-service-core_neighbours.c new file mode 100644 index 0000000..c4db40a --- /dev/null +++ b/src/core/gnunet-service-core_neighbours.c @@ -0,0 +1,528 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_neighbours.c + * @brief code for managing low-level 'plaintext' connections with transport (key exchange may or may not be done yet) + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_neighbours.h" +#include "gnunet-service-core_kx.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet_constants.h" + + +/** + * Message ready for transmission via transport service. This struct + * is followed by the actual content of the message. + */ +struct NeighbourMessageEntry +{ + + /** + * We keep messages in a doubly linked list. + */ + struct NeighbourMessageEntry *next; + + /** + * We keep messages in a doubly linked list. + */ + struct NeighbourMessageEntry *prev; + + /** + * By when are we supposed to transmit this message? + */ + struct GNUNET_TIME_Absolute deadline; + + /** + * How long is the message? (number of bytes following the "struct + * MessageEntry", but not including the size of "struct + * MessageEntry" itself!) + */ + size_t size; + +}; + + +/** + * Data kept per transport-connected peer. + */ +struct Neighbour +{ + + /** + * Head of the batched message queue (already ordered, transmit + * starting with the head). + */ + struct NeighbourMessageEntry *message_head; + + /** + * Tail of the batched message queue (already ordered, append new + * messages to tail). + */ + struct NeighbourMessageEntry *message_tail; + + /** + * Handle for pending requests for transmission to this peer + * with the transport service. NULL if no request is pending. + */ + struct GNUNET_TRANSPORT_TransmitHandle *th; + + /** + * Information about the key exchange with the other peer. + */ + struct GSC_KeyExchangeInfo *kxinfo; + + /** + * Identity of the other peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * ID of task used for re-trying plaintext scheduling. + */ + GNUNET_SCHEDULER_TaskIdentifier retry_plaintext_task; + +}; + + +/** + * Map of peer identities to 'struct Neighbour'. + */ +static struct GNUNET_CONTAINER_MultiHashMap *neighbours; + +/** + * Transport service. + */ +static struct GNUNET_TRANSPORT_Handle *transport; + + +/** + * Find the entry for the given neighbour. + * + * @param peer identity of the neighbour + * @return NULL if we are not connected, otherwise the + * neighbour's entry. + */ +static struct Neighbour * +find_neighbour (const struct GNUNET_PeerIdentity *peer) +{ + return GNUNET_CONTAINER_multihashmap_get (neighbours, &peer->hashPubKey); +} + + +/** + * Free the given entry for the neighbour. + * + * @param n neighbour to free + */ +static void +free_neighbour (struct Neighbour *n) +{ + struct NeighbourMessageEntry *m; + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying neighbour entry for peer `%4s'\n", + GNUNET_i2s (&n->peer)); +#endif + while (NULL != (m = n->message_head)) + { + GNUNET_CONTAINER_DLL_remove (n->message_head, n->message_tail, m); + GNUNET_free (m); + } + if (NULL != n->th) + { + GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th); + n->th = NULL; + } + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# sessions terminated by transport disconnect"), + 1, GNUNET_NO); + GSC_SESSIONS_end (&n->peer); + if (NULL != n->kxinfo) + { + GSC_KX_stop (n->kxinfo); + n->kxinfo = NULL; + } + if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (n->retry_plaintext_task); + n->retry_plaintext_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_remove (neighbours, + &n->peer.hashPubKey, n)); + GNUNET_STATISTICS_set (GSC_stats, + gettext_noop ("# neighbour entries allocated"), + GNUNET_CONTAINER_multihashmap_size (neighbours), + GNUNET_NO); + GNUNET_free (n); +} + + +/** + * Check if we have encrypted messages for the specified neighbour + * pending, and if so, check with the transport about sending them + * out. + * + * @param n neighbour to check. + */ +static void +process_queue (struct Neighbour *n); + + +/** + * Function called when the transport service is ready to receive a + * message for the respective peer + * + * @param cls neighbour to use message from + * @param size number of bytes we can transmit + * @param buf where to copy the message + * @return number of bytes transmitted + */ +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + struct Neighbour *n = cls; + struct NeighbourMessageEntry *m; + size_t ret; + char *cbuf; + + n->th = NULL; + m = n->message_head; + if (m == NULL) + { + GNUNET_break (0); + return 0; + } + GNUNET_CONTAINER_DLL_remove (n->message_head, n->message_tail, m); + if (buf == NULL) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission of message of type %u and size %u failed\n", + (unsigned int) + ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), + (unsigned int) m->size); +#endif + GNUNET_free (m); + process_queue (n); + return 0; + } + cbuf = buf; + GNUNET_assert (size >= m->size); + memcpy (cbuf, &m[1], m->size); + ret = m->size; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Copied message of type %u and size %u into transport buffer for `%4s'\n", + (unsigned int) + ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), + (unsigned int) ret, GNUNET_i2s (&n->peer)); +#endif + GNUNET_free (m); + process_queue (n); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# encrypted bytes given to transport"), ret, + GNUNET_NO); + return ret; +} + + +/** + * Check if we have messages for the specified neighbour pending, and + * if so, check with the transport about sending them out. + * + * @param n neighbour to check. + */ +static void +process_queue (struct Neighbour *n) +{ + struct NeighbourMessageEntry *m; + + if (n->th != NULL) + return; /* request already pending */ + m = n->message_head; + if (m == NULL) + { + /* notify sessions that the queue is empty and more messages + * could thus be queued now */ + GSC_SESSIONS_solicit (&n->peer); + return; + } +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n", + (unsigned int) m->size, GNUNET_i2s (&n->peer), + (unsigned long long) + GNUNET_TIME_absolute_get_remaining (m->deadline).rel_value); +#endif + n->th = + GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer, m->size, 0, + GNUNET_TIME_absolute_get_remaining + (m->deadline), &transmit_ready, + n); + if (n->th != NULL) + return; + /* message request too large or duplicate request */ + GNUNET_break (0); + /* discard encrypted message */ + GNUNET_CONTAINER_DLL_remove (n->message_head, n->message_tail, m); + GNUNET_free (m); + process_queue (n); +} + + + +/** + * Function called by transport to notify us that + * a peer connected to us (on the network level). + * + * @param cls closure + * @param peer the peer that connected + * @param atsi performance data + * @param atsi_count number of entries in ats (excluding 0-termination) + */ +static void +handle_transport_notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + uint32_t atsi_count) +{ + struct Neighbour *n; + + if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + return; + } + n = find_neighbour (peer); + if (n != NULL) + { + /* duplicate connect notification!? */ + GNUNET_break (0); + return; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received connection from `%4s'.\n", + GNUNET_i2s (peer)); +#endif + n = GNUNET_malloc (sizeof (struct Neighbour)); + n->peer = *peer; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (neighbours, + &n->peer.hashPubKey, n, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_STATISTICS_set (GSC_stats, + gettext_noop ("# neighbour entries allocated"), + GNUNET_CONTAINER_multihashmap_size (neighbours), + GNUNET_NO); + n->kxinfo = GSC_KX_start (peer); +} + + +/** + * Function called by transport telling us that a peer + * disconnected. + * + * @param cls closure + * @param peer the peer that disconnected + */ +static void +handle_transport_notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer) +{ + struct Neighbour *n; + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%4s' disconnected from us; received notification from transport.\n", + GNUNET_i2s (peer)); +#endif + n = find_neighbour (peer); + if (n == NULL) + { + GNUNET_break (0); + return; + } + free_neighbour (n); +} + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param peer (claimed) identity of the other peer + * @param message the message + * @param atsi performance data + * @param atsi_count number of entries in ats (excluding 0-termination) + */ +static void +handle_transport_receive (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + uint32_t atsi_count) +{ + struct Neighbour *n; + uint16_t type; + +#if DEBUG_CORE > 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u from `%4s', demultiplexing.\n", + (unsigned int) ntohs (message->type), GNUNET_i2s (peer)); +#endif + if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + return; + } + n = find_neighbour (peer); + if (n == NULL) + { + /* received message from peer that is not connected!? */ + GNUNET_break (0); + return; + } + type = ntohs (message->type); + switch (type) + { + case GNUNET_MESSAGE_TYPE_CORE_SET_KEY: + GSC_KX_handle_set_key (n->kxinfo, message); + break; + case GNUNET_MESSAGE_TYPE_CORE_PING: + GSC_KX_handle_ping (n->kxinfo, message); + break; + case GNUNET_MESSAGE_TYPE_CORE_PONG: + GSC_KX_handle_pong (n->kxinfo, message); + break; + case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE: + GSC_KX_handle_encrypted_message (n->kxinfo, message, atsi, atsi_count); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Unsupported message of type %u (%u bytes) received from peer `%s'\n"), + (unsigned int) type, (unsigned int) ntohs (message->size), + GNUNET_i2s (peer)); + return; + } +} + + +/** + * Transmit the given message to the given target. + * + * @param target peer that should receive the message (must be connected) + * @param msg message to transmit + * @param timeout by when should the transmission be done? + */ +void +GSC_NEIGHBOURS_transmit (const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout) +{ + struct NeighbourMessageEntry *me; + struct Neighbour *n; + size_t msize; + + n = find_neighbour (target); + if (NULL == n) + { + GNUNET_break (0); + return; + } + msize = ntohs (msg->size); + me = GNUNET_malloc (sizeof (struct NeighbourMessageEntry) + msize); + me->deadline = GNUNET_TIME_relative_to_absolute (timeout); + me->size = msize; + memcpy (&me[1], msg, msize); + GNUNET_CONTAINER_DLL_insert_tail (n->message_head, n->message_tail, me); + process_queue (n); +} + + +/** + * Initialize neighbours subsystem. + */ +int +GSC_NEIGHBOURS_init () +{ + neighbours = GNUNET_CONTAINER_multihashmap_create (128); + transport = + GNUNET_TRANSPORT_connect (GSC_cfg, &GSC_my_identity, NULL, + &handle_transport_receive, + &handle_transport_notify_connect, + &handle_transport_notify_disconnect); + if (NULL == transport) + { + GNUNET_CONTAINER_multihashmap_destroy (neighbours); + neighbours = NULL; + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Wrapper around 'free_neighbour'. + * + * @param cls unused + * @param key peer identity + * @param value the 'struct Neighbour' to free + * @return GNUNET_OK (continue to iterate) + */ +static int +free_neighbour_helper (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct Neighbour *n = value; + + /* transport should have 'disconnected' all neighbours... */ + GNUNET_break (0); + free_neighbour (n); + return GNUNET_OK; +} + + +/** + * Shutdown neighbours subsystem. + */ +void +GSC_NEIGHBOURS_done () +{ + if (NULL == transport) + return; + GNUNET_TRANSPORT_disconnect (transport); + transport = NULL; + GNUNET_CONTAINER_multihashmap_iterate (neighbours, &free_neighbour_helper, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (neighbours); + neighbours = NULL; +} + +/* end of gnunet-service-core_neighbours.c */ diff --git a/src/core/gnunet-service-core_neighbours.h b/src/core/gnunet-service-core_neighbours.h new file mode 100644 index 0000000..d613c46 --- /dev/null +++ b/src/core/gnunet-service-core_neighbours.h @@ -0,0 +1,63 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_neighbours.h + * @brief code for managing low-level 'plaintext' connections with transport (key exchange may or may not be done yet) + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_NEIGHBOURS_H +#define GNUNET_SERVICE_CORE_NEIGHBOURS_H + +#include "gnunet_util_lib.h" + +/** + * Transmit the given message to the given target. Note that a + * non-control messages should only be transmitted after a + * 'GSC_SESSION_solicit' call was made (that call is always invoked + * when the message queue is empty). Outbound quotas and memory + * bounds will then be enfoced (as GSC_SESSION_solicit is only called + * if sufficient banwdith is available). + * + * @param target peer that should receive the message (must be connected) + * @param msg message to transmit + * @param timeout by when should the transmission be done? + */ +void +GSC_NEIGHBOURS_transmit (const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout); + + +/** + * Initialize neighbours subsystem. + */ +int +GSC_NEIGHBOURS_init (void); + + +/** + * Shutdown neighbours subsystem. + */ +void +GSC_NEIGHBOURS_done (void); + + +#endif diff --git a/src/core/gnunet-service-core_sessions.c b/src/core/gnunet-service-core_sessions.c new file mode 100644 index 0000000..1f697cf --- /dev/null +++ b/src/core/gnunet-service-core_sessions.c @@ -0,0 +1,826 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_sessions.c + * @brief code for managing of 'encrypted' sessions (key exchange done) + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_neighbours.h" +#include "gnunet-service-core_kx.h" +#include "gnunet-service-core_typemap.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet-service-core_clients.h" +#include "gnunet_constants.h" + +/** + * How often do we transmit our typemap? + */ +#define TYPEMAP_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + + +/** + * Message ready for encryption. This struct is followed by the + * actual content of the message. + */ +struct SessionMessageEntry +{ + + /** + * We keep messages in a doubly linked list. + */ + struct SessionMessageEntry *next; + + /** + * We keep messages in a doubly linked list. + */ + struct SessionMessageEntry *prev; + + /** + * Deadline for transmission, 1s after we received it (if we + * are not corking), otherwise "now". Note that this message + * does NOT expire past its deadline. + */ + struct GNUNET_TIME_Absolute deadline; + + /** + * How long is the message? (number of bytes following the "struct + * MessageEntry", but not including the size of "struct + * MessageEntry" itself!) + */ + size_t size; + +}; + + +/** + * Data kept per session. + */ +struct Session +{ + /** + * Identity of the other peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Head of list of requests from clients for transmission to + * this peer. + */ + struct GSC_ClientActiveRequest *active_client_request_head; + + /** + * Tail of list of requests from clients for transmission to + * this peer. + */ + struct GSC_ClientActiveRequest *active_client_request_tail; + + /** + * Head of list of messages ready for encryption. + */ + struct SessionMessageEntry *sme_head; + + /** + * Tail of list of messages ready for encryption. + */ + struct SessionMessageEntry *sme_tail; + + /** + * Information about the key exchange with the other peer. + */ + struct GSC_KeyExchangeInfo *kxinfo; + + /** + * Current type map for this peer. + */ + struct GSC_TypeMap *tmap; + + /** + * At what time did we initially establish this session? + * (currently unused, should be integrated with ATS in the + * future...). + */ + struct GNUNET_TIME_Absolute time_established; + + /** + * Task to transmit corked messages with a delay. + */ + GNUNET_SCHEDULER_TaskIdentifier cork_task; + + /** + * Task to transmit our type map. + */ + GNUNET_SCHEDULER_TaskIdentifier typemap_task; + + /** + * Is the neighbour queue empty and thus ready for us + * to transmit an encrypted message? + */ + int ready_to_transmit; + +}; + + +/** + * Map of peer identities to 'struct Session'. + */ +static struct GNUNET_CONTAINER_MultiHashMap *sessions; + + +/** + * Find the session for the given peer. + * + * @param peer identity of the peer + * @return NULL if we are not connected, otherwise the + * session handle + */ +static struct Session * +find_session (const struct GNUNET_PeerIdentity *peer) +{ + return GNUNET_CONTAINER_multihashmap_get (sessions, &peer->hashPubKey); +} + + +/** + * End the session with the given peer (we are no longer + * connected). + * + * @param pid identity of peer to kill session with + */ +void +GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid) +{ + struct Session *session; + struct GSC_ClientActiveRequest *car; + struct SessionMessageEntry *sme; + + session = find_session (pid); + if (NULL == session) + return; +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying session for peer `%4s'\n", + GNUNET_i2s (&session->peer)); +#endif + if (GNUNET_SCHEDULER_NO_TASK != session->cork_task) + { + GNUNET_SCHEDULER_cancel (session->cork_task); + session->cork_task = GNUNET_SCHEDULER_NO_TASK; + } + while (NULL != (car = session->active_client_request_head)) + { + GNUNET_CONTAINER_DLL_remove (session->active_client_request_head, + session->active_client_request_tail, car); + GSC_CLIENTS_reject_request (car); + } + while (NULL != (sme = session->sme_head)) + { + GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme); + GNUNET_free (sme); + } + GNUNET_SCHEDULER_cancel (session->typemap_task); + GSC_CLIENTS_notify_clients_about_neighbour (&session->peer, NULL, + 0 /* FIXME: ATSI */ , + session->tmap, NULL); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (sessions, + &session-> + peer.hashPubKey, + session)); + GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# entries in session map"), + GNUNET_CONTAINER_multihashmap_size (sessions), + GNUNET_NO); + GSC_TYPEMAP_destroy (session->tmap); + session->tmap = NULL; + GNUNET_free (session); +} + + +/** + * Transmit our current typemap message to the other peer. + * (Done periodically in case an update got lost). + * + * @param cls the 'struct Session*' + * @param tc unused + */ +static void +transmit_typemap_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Session *session = cls; + struct GNUNET_MessageHeader *hdr; + struct GNUNET_TIME_Relative delay; + + delay = TYPEMAP_FREQUENCY; + /* randomize a bit to avoid spont. sync */ + delay.rel_value += + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000); + session->typemap_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_typemap_task, session); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# type map refreshes sent"), 1, + GNUNET_NO); + hdr = GSC_TYPEMAP_compute_type_map_message (); + GSC_KX_encrypt_and_transmit (session->kxinfo, hdr, ntohs (hdr->size)); + GNUNET_free (hdr); +} + + +/** + * Create a session, a key exchange was just completed. + * + * @param peer peer that is now connected + * @param kx key exchange that completed + */ +void +GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer, + struct GSC_KeyExchangeInfo *kx) +{ + struct Session *session; + +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating session for peer `%4s'\n", + GNUNET_i2s (peer)); +#endif + session = GNUNET_malloc (sizeof (struct Session)); + session->tmap = GSC_TYPEMAP_create (); + session->peer = *peer; + session->kxinfo = kx; + session->time_established = GNUNET_TIME_absolute_get (); + session->typemap_task = + GNUNET_SCHEDULER_add_now (&transmit_typemap_task, session); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (sessions, &peer->hashPubKey, + session, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# entries in session map"), + GNUNET_CONTAINER_multihashmap_size (sessions), + GNUNET_NO); + GSC_CLIENTS_notify_clients_about_neighbour (peer, NULL, 0 /* FIXME: ATSI */ , + NULL, session->tmap); +} + + +/** + * Notify the given client about the session (client is new). + * + * @param cls the 'struct GSC_Client' + * @param key peer identity + * @param value the 'struct Session' + * @return GNUNET_OK (continue to iterate) + */ +static int +notify_client_about_session (void *cls, const GNUNET_HashCode * key, + void *value) +{ + struct GSC_Client *client = cls; + struct Session *session = value; + + GSC_CLIENTS_notify_client_about_neighbour (client, &session->peer, NULL, 0, /* FIXME: ATS!? */ + NULL, /* old TMAP: none */ + session->tmap); + return GNUNET_OK; +} + + +/** + * We have a new client, notify it about all current sessions. + * + * @param client the new client + */ +void +GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client) +{ + /* notify new client about existing sessions */ + GNUNET_CONTAINER_multihashmap_iterate (sessions, ¬ify_client_about_session, + client); +} + + +/** + * Try to perform a transmission on the given session. Will solicit + * additional messages if the 'sme' queue is not full enough. + * + * @param session session to transmit messages from + */ +static void +try_transmission (struct Session *session); + + +/** + * Queue a request from a client for transmission to a particular peer. + * + * @param car request to queue; this handle is then shared between + * the caller (CLIENTS subsystem) and SESSIONS and must not + * be released by either until either 'GNUNET_SESSIONS_dequeue', + * 'GNUNET_SESSIONS_transmit' or 'GNUNET_CLIENTS_failed' + * have been invoked on it + */ +void +GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car) +{ + struct Session *session; + + session = find_session (&car->target); + if (session == NULL) + { +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dropped client request for transmission (am disconnected)\n"); +#endif + GNUNET_break (0); /* should have been rejected earlier */ + GSC_CLIENTS_reject_request (car); + return; + } + if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) + { + GNUNET_break (0); + GSC_CLIENTS_reject_request (car); + return; + } +#if DEBUG_CORE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received client transmission request. queueing\n"); +#endif + GNUNET_CONTAINER_DLL_insert (session->active_client_request_head, + session->active_client_request_tail, car); + try_transmission (session); +} + + +/** + * Dequeue a request from a client from transmission to a particular peer. + * + * @param car request to dequeue; this handle will then be 'owned' by + * the caller (CLIENTS sysbsystem) + */ +void +GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car) +{ + struct Session *s; + + if (0 == + memcmp (&car->target, &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) + return; + s = find_session (&car->target); + GNUNET_assert (NULL != s); + GNUNET_CONTAINER_DLL_remove (s->active_client_request_head, + s->active_client_request_tail, car); +} + + +/** + * Discard all expired active transmission requests from clients. + * + * @param session session to clean up + */ +static void +discard_expired_requests (struct Session *session) +{ + struct GSC_ClientActiveRequest *pos; + struct GSC_ClientActiveRequest *nxt; + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + pos = NULL; + nxt = session->active_client_request_head; + while (NULL != nxt) + { + pos = nxt; + nxt = pos->next; + if ((pos->deadline.abs_value < now.abs_value) && + (GNUNET_YES != pos->was_solicited)) + { + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop + ("# messages discarded (expired prior to transmission)"), + 1, GNUNET_NO); + GNUNET_CONTAINER_DLL_remove (session->active_client_request_head, + session->active_client_request_tail, pos); + GSC_CLIENTS_reject_request (pos); + } + } +} + + +/** + * Solicit messages for transmission. + * + * @param session session to solict messages for + */ +static void +solicit_messages (struct Session *session) +{ + struct GSC_ClientActiveRequest *car; + struct GSC_ClientActiveRequest *nxt; + size_t so_size; + + discard_expired_requests (session); + so_size = 0; + nxt = session->active_client_request_head; + while (NULL != (car = nxt)) + { + nxt = car->next; + if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) + break; + so_size += car->msize; + if (car->was_solicited == GNUNET_YES) + continue; + car->was_solicited = GNUNET_YES; + GSC_CLIENTS_solicit_request (car); + } +} + + +/** + * Some messages were delayed (corked), but the timeout has now expired. + * Send them now. + * + * @param cls 'struct Session' with the messages to transmit now + * @param tc scheduler context (unused) + */ +static void +pop_cork_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Session *session = cls; + + session->cork_task = GNUNET_SCHEDULER_NO_TASK; + try_transmission (session); +} + + +/** + * Try to perform a transmission on the given session. Will solicit + * additional messages if the 'sme' queue is not full enough. + * + * @param session session to transmit messages from + */ +static void +try_transmission (struct Session *session) +{ + struct SessionMessageEntry *pos; + size_t msize; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute min_deadline; + + if (GNUNET_YES != session->ready_to_transmit) + return; + msize = 0; + min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS; + /* check 'ready' messages */ + pos = session->sme_head; + while ((NULL != pos) && + (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)) + { + GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE); + msize += pos->size; + min_deadline = GNUNET_TIME_absolute_min (min_deadline, pos->deadline); + pos = pos->next; + } + now = GNUNET_TIME_absolute_get (); + if ((msize == 0) || + ((msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) && + (min_deadline.abs_value > now.abs_value))) + { + /* not enough ready yet, try to solicit more */ + solicit_messages (session); + if (msize > 0) + { + /* if there is data to send, just not yet, make sure we do transmit + * it once the deadline is reached */ + if (session->cork_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (session->cork_task); + session->cork_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (min_deadline), &pop_cork_task, + session); + } + return; + } + /* create plaintext buffer of all messages, encrypt and transmit */ + { + static unsigned long long total_bytes; + static unsigned int total_msgs; + char pbuf[msize]; /* plaintext */ + size_t used; + + used = 0; + while ((NULL != (pos = session->sme_head)) && (used + pos->size <= msize)) + { + memcpy (&pbuf[used], &pos[1], pos->size); + used += pos->size; + GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, pos); + GNUNET_free (pos); + } + /* compute average payload size */ + total_bytes += used; + total_msgs++; + if (0 == total_msgs) + { + /* 2^32 messages, wrap around... */ + total_msgs = 1; + total_bytes = used; + } + GNUNET_STATISTICS_set (GSC_stats, "# avg payload per encrypted message", + total_bytes / total_msgs, GNUNET_NO); + /* now actually transmit... */ + session->ready_to_transmit = GNUNET_NO; + GSC_KX_encrypt_and_transmit (session->kxinfo, pbuf, used); + } +} + + +/** + * Send a message to the neighbour now. + * + * @param cls the message + * @param key neighbour's identity + * @param value 'struct Neighbour' of the target + * @return always GNUNET_OK + */ +static int +do_send_message (void *cls, const GNUNET_HashCode * key, void *value) +{ + const struct GNUNET_MessageHeader *hdr = cls; + struct Session *session = value; + struct SessionMessageEntry *m; + uint16_t size; + + size = ntohs (hdr->size); + m = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size); + memcpy (&m[1], hdr, size); + m->size = size; + GNUNET_CONTAINER_DLL_insert (session->sme_head, session->sme_tail, m); + try_transmission (session); + return GNUNET_OK; +} + + +/** + * Broadcast a message to all neighbours. + * + * @param msg message to transmit + */ +void +GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg) +{ + if (NULL == sessions) + return; + GNUNET_CONTAINER_multihashmap_iterate (sessions, &do_send_message, + (void *) msg); +} + + +/** + * Traffic is being solicited for the given peer. This means that the + * message queue on the transport-level (NEIGHBOURS subsystem) is now + * empty and it is now OK to transmit another (non-control) message. + * + * @param pid identity of peer ready to receive data + */ +void +GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid) +{ + struct Session *session; + + session = find_session (pid); + if (NULL == session) + return; + session->ready_to_transmit = GNUNET_YES; + try_transmission (session); +} + + +/** + * Transmit a message to a particular peer. + * + * @param car original request that was queued and then solicited; + * this handle will now be 'owned' by the SESSIONS subsystem + * @param msg message to transmit + * @param cork is corking allowed? + */ +void +GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car, + const struct GNUNET_MessageHeader *msg, int cork) +{ + struct Session *session; + struct SessionMessageEntry *sme; + size_t msize; + + session = find_session (&car->target); + if (NULL == session) + return; + msize = ntohs (msg->size); + sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + msize); + memcpy (&sme[1], msg, msize); + sme->size = msize; + if (GNUNET_YES == cork) + sme->deadline = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY); + GNUNET_CONTAINER_DLL_insert_tail (session->sme_head, session->sme_tail, sme); + try_transmission (session); +} + + +/** + * Helper function for GSC_SESSIONS_handle_client_iterate_peers. + * + * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies + * @param key identity of the connected peer + * @param value the 'struct Neighbour' for the peer + * @return GNUNET_OK (continue to iterate) + */ +#include "core.h" +static int +queue_connect_message (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct GNUNET_SERVER_TransmitContext *tc = cls; + struct Session *session = value; + struct ConnectNotifyMessage cnm; + + /* FIXME: code duplication with clients... */ + cnm.header.size = htons (sizeof (struct ConnectNotifyMessage)); + cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + // FIXME: full ats... + cnm.ats_count = htonl (0); + cnm.peer = session->peer; + GNUNET_SERVER_transmit_context_append_message (tc, &cnm.header); + return GNUNET_OK; +} + + +/** + * Handle CORE_ITERATE_PEERS request. For this request type, the client + * does not have to have transmitted an INIT request. All current peers + * are returned, regardless of which message types they accept. + * + * @param cls unused + * @param client client sending the iteration request + * @param message iteration request message + */ +void +GSC_SESSIONS_handle_client_iterate_peers (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader + *message) +{ + struct GNUNET_MessageHeader done_msg; + struct GNUNET_SERVER_TransmitContext *tc; + + tc = GNUNET_SERVER_transmit_context_create (client); + GNUNET_CONTAINER_multihashmap_iterate (sessions, &queue_connect_message, tc); + done_msg.size = htons (sizeof (struct GNUNET_MessageHeader)); + done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END); + GNUNET_SERVER_transmit_context_append_message (tc, &done_msg); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Handle CORE_PEER_CONNECTED request. Notify client about connection + * to the given neighbour. For this request type, the client does not + * have to have transmitted an INIT request. All current peers are + * returned, regardless of which message types they accept. + * + * @param cls unused + * @param client client sending the iteration request + * @param message iteration request message + */ +void +GSC_SESSIONS_handle_client_have_peer (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader + *message) +{ + struct GNUNET_MessageHeader done_msg; + struct GNUNET_SERVER_TransmitContext *tc; + const struct GNUNET_PeerIdentity *peer; + + peer = (const struct GNUNET_PeerIdentity *) &message[1]; // YUCK! + tc = GNUNET_SERVER_transmit_context_create (client); + GNUNET_CONTAINER_multihashmap_get_multiple (sessions, &peer->hashPubKey, + &queue_connect_message, tc); + done_msg.size = htons (sizeof (struct GNUNET_MessageHeader)); + done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END); + GNUNET_SERVER_transmit_context_append_message (tc, &done_msg); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * We've received a typemap message from a peer, update ours. + * Notifies clients about the session. + * + * @param peer peer this is about + * @param msg typemap update message + */ +void +GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *msg) +{ + struct Session *session; + struct GSC_TypeMap *nmap; + + nmap = GSC_TYPEMAP_get_from_message (msg); + if (NULL == nmap) + return; /* malformed */ + session = find_session (peer); + if (NULL == session) + { + GNUNET_break (0); + return; + } + GSC_CLIENTS_notify_clients_about_neighbour (peer, NULL, 0, /* FIXME: ATS */ + session->tmap, nmap); + GSC_TYPEMAP_destroy (session->tmap); + session->tmap = nmap; +} + + +/** + * The given peer send a message of the specified type. Make sure the + * respective bit is set in its type-map and that clients are notified + * about the session. + * + * @param peer peer this is about + * @param type type of the message + */ +void +GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer, + uint16_t type) +{ + struct Session *session; + struct GSC_TypeMap *nmap; + + if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity))) + return; + session = find_session (peer); + GNUNET_assert (NULL != session); + if (GNUNET_YES == GSC_TYPEMAP_test_match (session->tmap, &type, 1)) + return; /* already in it */ + nmap = GSC_TYPEMAP_extend (session->tmap, &type, 1); + GSC_CLIENTS_notify_clients_about_neighbour (peer, NULL, 0, /* FIXME: ATS */ + session->tmap, nmap); + GSC_TYPEMAP_destroy (session->tmap); + session->tmap = nmap; +} + + +/** + * Initialize sessions subsystem. + */ +void +GSC_SESSIONS_init () +{ + sessions = GNUNET_CONTAINER_multihashmap_create (128); +} + + +/** + * Helper function for GSC_SESSIONS_handle_client_iterate_peers. + * + * @param cls NULL + * @param key identity of the connected peer + * @param value the 'struct Session' for the peer + * @return GNUNET_OK (continue to iterate) + */ +static int +free_session_helper (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct Session *session = value; + + GSC_SESSIONS_end (&session->peer); + return GNUNET_OK; +} + + +/** + * Shutdown sessions subsystem. + */ +void +GSC_SESSIONS_done () +{ + GNUNET_CONTAINER_multihashmap_iterate (sessions, &free_session_helper, NULL); + GNUNET_CONTAINER_multihashmap_destroy (sessions); + sessions = NULL; +} + +/* end of gnunet-service-core_sessions.c */ diff --git a/src/core/gnunet-service-core_sessions.h b/src/core/gnunet-service-core_sessions.h new file mode 100644 index 0000000..e09cf50 --- /dev/null +++ b/src/core/gnunet-service-core_sessions.h @@ -0,0 +1,192 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_neighbours.h + * @brief code for managing of 'encrypted' sessions (key exchange done) + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_SESSIONS_H +#define GNUNET_SERVICE_CORE_SESSIONS_H + +#include "gnunet-service-core.h" +#include "gnunet-service-core_kx.h" + + +/** + * Create a session, a key exchange was just completed. + * + * @param peer peer that is now connected + * @param kx key exchange that completed + */ +void +GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer, + struct GSC_KeyExchangeInfo *kx); + + +/** + * End the session with the given peer (we are no longer + * connected). + * + * @param pid identity of peer to kill session with + */ +void +GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid); + + +/** + * Traffic is being solicited for the given peer. This means that the + * message queue on the transport-level (NEIGHBOURS subsystem) is now + * empty and it is now OK to transmit another (non-control) message. + * + * @param pid identity of peer ready to receive data + */ +void +GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid); + + +/** + * Queue a request from a client for transmission to a particular peer. + * + * @param car request to queue; this handle is then shared between + * the caller (CLIENTS subsystem) and SESSIONS and must not + * be released by either until either 'GNUNET_SESSIONS_dequeue', + * or 'GNUNET_CLIENTS_failed' + * have been invoked on it + */ +void +GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car); + + +/** + * Dequeue a request from a client from transmission to a particular peer. + * + * @param car request to dequeue; this handle will then be 'owned' by + * the caller (CLIENTS sysbsystem) + */ +void +GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car); + + +/** + * Transmit a message to a particular peer. + * + * @param car original request that was queued and then solicited, + * ownership does not change (dequeue will be called soon). + * @param msg message to transmit + * @param cork is corking allowed? + */ +void +GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car, + const struct GNUNET_MessageHeader *msg, int cork); + + +/** + * Broadcast a message to all neighbours. + * + * @param msg message to transmit + */ +void +GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg); + + +/** + * We have a new client, notify it about all current sessions. + * + * @param client the new client + */ +void +GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client); + +/** + * We've received a typemap message from a peer, update ours. + * Notifies clients about the session. + * + * @param peer peer this is about + * @param msg typemap update message + */ +void +GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *msg); + + +/** + * The given peer send a message of the specified type. Make sure the + * respective bit is set in its type-map and that clients are notified + * about the session. + * + * @param peer peer this is about + * @param type type of the message + */ +void +GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer, + uint16_t type); + + +/** + * Handle CORE_ITERATE_PEERS request. For this request type, the client + * does not have to have transmitted an INIT request. All current peers + * are returned, regardless of which message types they accept. + * + * @param cls unused + * @param client client sending the iteration request + * @param message iteration request message + */ +void +GSC_SESSIONS_handle_client_iterate_peers (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader + *message); + + +/** + * Handle CORE_PEER_CONNECTED request. Notify client about connection + * to the given neighbour. For this request type, the client does not + * have to have transmitted an INIT request. All current peers are + * returned, regardless of which message types they accept. + * + * @param cls unused + * @param client client sending the iteration request + * @param message iteration request message + */ +void +GSC_SESSIONS_handle_client_have_peer (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader + *message); + + + +/** + * Initialize sessions subsystem. + */ +void +GSC_SESSIONS_init (void); + + +/** + * Shutdown sessions subsystem. + */ +void +GSC_SESSIONS_done (void); + + + +#endif diff --git a/src/core/gnunet-service-core_typemap.c b/src/core/gnunet-service-core_typemap.c new file mode 100644 index 0000000..d2dab5d --- /dev/null +++ b/src/core/gnunet-service-core_typemap.c @@ -0,0 +1,297 @@ +/* + This file is part of GNUnet. + (C) 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_typemap.c + * @brief management of map that specifies which message types this peer supports + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet-service-core.h" +#include "gnunet-service-core_sessions.h" +#include "gnunet-service-core_typemap.h" +#include <zlib.h> + + +/** + * A type map describing which messages a given neighbour is able + * to process. + */ +struct GSC_TypeMap +{ + uint32_t bits[(UINT16_MAX + 1) / 32]; +}; + +/** + * Bitmap of message types this peer is able to handle. + */ +static struct GSC_TypeMap my_type_map; + +/** + * Counters for message types this peer is able to handle. + */ +static uint8_t map_counters[UINT16_MAX + 1]; + + +/** + * Compute a type map message for this peer. + * + * @return this peers current type map message. + */ +struct GNUNET_MessageHeader * +GSC_TYPEMAP_compute_type_map_message () +{ + char *tmp; + uLongf dlen; + struct GNUNET_MessageHeader *hdr; + +#ifdef compressBound + dlen = compressBound (sizeof (my_type_map)); +#else + dlen = sizeof (my_type_map) + (sizeof (my_type_map) / 100) + 20; + /* documentation says 100.1% oldSize + 12 bytes, but we + * should be able to overshoot by more to be safe */ +#endif + hdr = GNUNET_malloc (dlen + sizeof (struct GNUNET_MessageHeader)); + tmp = (char *) &hdr[1]; + if ((Z_OK != + compress2 ((Bytef *) tmp, &dlen, (const Bytef *) &my_type_map, + sizeof (my_type_map), 9)) || (dlen >= sizeof (my_type_map))) + { + dlen = sizeof (my_type_map); + memcpy (tmp, &my_type_map, sizeof (my_type_map)); + hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP); + } + else + { + hdr->type = htons (GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP); + } + hdr->size = htons ((uint16_t) dlen + sizeof (struct GNUNET_MessageHeader)); + return hdr; +} + + +/** + * Extract a type map from a TYPE_MAP message. + * + * @param msg a type map message + * @return NULL on error + */ +struct GSC_TypeMap * +GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg) +{ + struct GSC_TypeMap *ret; + uint16_t size; + uLongf dlen; + + size = ntohs (msg->size); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# type maps received"), + 1, GNUNET_NO); + if (size != sizeof (struct GSC_TypeMap)) + { + GNUNET_break_op (0); + return NULL; + } + ret = GNUNET_malloc (sizeof (struct GSC_TypeMap)); + memcpy (ret, &msg[1], sizeof (struct GSC_TypeMap)); + return ret; + case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: + GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# type maps received"), + 1, GNUNET_NO); + ret = GNUNET_malloc (sizeof (struct GSC_TypeMap)); + dlen = sizeof (struct GSC_TypeMap); + if ((Z_OK != + uncompress ((Bytef *) ret, &dlen, (const Bytef *) &msg[1], + (uLong) size)) || (dlen != sizeof (struct GSC_TypeMap))) + { + GNUNET_break_op (0); + GNUNET_free (ret); + return NULL; + } + return ret; + default: + GNUNET_break (0); + return NULL; + } +} + + +/** + * Send my type map to all connected peers (it got changed). + */ +static void +broadcast_my_type_map () +{ + struct GNUNET_MessageHeader *hdr; + + hdr = GSC_TYPEMAP_compute_type_map_message (); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# updates to my type map"), 1, + GNUNET_NO); + GSC_SESSIONS_broadcast (hdr); + GNUNET_free (hdr); +} + + +/** + * Add a set of types to our type map. + */ +void +GSC_TYPEMAP_add (const uint16_t * types, unsigned int tlen) +{ + unsigned int i; + int changed; + + changed = GNUNET_NO; + for (i = 0; i < tlen; i++) + { + if (0 == map_counters[types[i]]++) + { + my_type_map.bits[types[i] / 32] |= (1 << (types[i] % 32)); + changed = GNUNET_YES; + } + } + if (GNUNET_YES == changed) + broadcast_my_type_map (); +} + + +/** + * Remove a set of types from our type map. + */ +void +GSC_TYPEMAP_remove (const uint16_t * types, unsigned int tlen) +{ + unsigned int i; + int changed; + + changed = GNUNET_NO; + for (i = 0; i < tlen; i++) + { + if (0 == --map_counters[types[i]]) + { + my_type_map.bits[types[i] / 32] &= ~(1 << (types[i] % 32)); + changed = GNUNET_YES; + } + } + if (GNUNET_YES == changed) + broadcast_my_type_map (); +} + + +/** + * Test if any of the types from the types array is in the + * given type map. + * + * @param tmap map to test + * @param types array of types + * @param tcnt number of entries in types + * @return GNUNET_YES if a type is in the map, GNUNET_NO if not + */ +int +GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap, const uint16_t * types, + unsigned int tcnt) +{ + unsigned int i; + + if (NULL == tmap) + return GNUNET_NO; + if (0 == tcnt) + return GNUNET_YES; /* matches all */ + for (i = 0; i < tcnt; i++) + if (0 != (tmap->bits[types[i] / 32] & (1 << (types[i] % 32)))) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Add additional types to a given typemap. + * + * @param tmap map to extend (not changed) + * @param types array of types to add + * @param tcnt number of entries in types + * @return updated type map (fresh copy) + */ +struct GSC_TypeMap * +GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap, const uint16_t * types, + unsigned int tcnt) +{ + struct GSC_TypeMap *ret; + unsigned int i; + + ret = GNUNET_malloc (sizeof (struct GSC_TypeMap)); + if (NULL != tmap) + memcpy (ret, tmap, sizeof (struct GSC_TypeMap)); + for (i = 0; i < tcnt; i++) + ret->bits[types[i] / 32] |= (1 << (types[i] % 32)); + return ret; +} + + +/** + * Create an empty type map. + * + * @return an empty type map + */ +struct GSC_TypeMap * +GSC_TYPEMAP_create () +{ + return GNUNET_malloc (sizeof (struct GSC_TypeMap)); +} + + +/** + * Free the given type map. + * + * @param tmap a type map + */ +void +GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap) +{ + GNUNET_free (tmap); +} + + +/** + * Initialize typemap subsystem. + */ +void +GSC_TYPEMAP_init () +{ + /* nothing to do */ +} + + +/** + * Shutdown typemap subsystem. + */ +void +GSC_TYPEMAP_done () +{ + /* nothing to do */ +} + +/* end of gnunet-service-core_typemap.c */ diff --git a/src/core/gnunet-service-core_typemap.h b/src/core/gnunet-service-core_typemap.h new file mode 100644 index 0000000..aee4573 --- /dev/null +++ b/src/core/gnunet-service-core_typemap.h @@ -0,0 +1,129 @@ +/* + This file is part of GNUnet. + (C) 2011 Christian Grothoff (and other contributing authors) + + 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 core/gnunet-service-core_typemap.h + * @brief management of map that specifies which message types this peer supports + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_CORE_TYPEMAP_H +#define GNUNET_SERVICE_CORE_TYPEMAP_H + +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" + +/** + * Map specifying which message types a peer supports. + */ +struct GSC_TypeMap; + + +/** + * Add a set of types to our type map. + */ +void +GSC_TYPEMAP_add (const uint16_t * types, unsigned int tlen); + + +/** + * Remove a set of types from our type map. + */ +void +GSC_TYPEMAP_remove (const uint16_t * types, unsigned int tlen); + + +/** + * Compute a type map message for this peer. + * + * @return this peers current type map message. + */ +struct GNUNET_MessageHeader * +GSC_TYPEMAP_compute_type_map_message (void); + + +/** + * Extract a type map from a TYPE_MAP message. + * + * @param msg a type map message + * @return NULL on error + */ +struct GSC_TypeMap * +GSC_TYPEMAP_get_from_message (const struct GNUNET_MessageHeader *msg); + + +/** + * Test if any of the types from the types array is in the + * given type map. + * + * @param tmap map to test + * @param types array of types + * @param tcnt number of entries in types + * @return GNUNET_YES if a type is in the map, GNUNET_NO if not + */ +int +GSC_TYPEMAP_test_match (const struct GSC_TypeMap *tmap, const uint16_t * types, + unsigned int tcnt); + + +/** + * Add additional types to a given typemap. + * + * @param tmap map to extend (not changed) + * @param types array of types to add + * @param tcnt number of entries in types + * @return updated type map (fresh copy) + */ +struct GSC_TypeMap * +GSC_TYPEMAP_extend (const struct GSC_TypeMap *tmap, const uint16_t * types, + unsigned int tcnt); + +/** + * Create an empty type map. + * + * @return an empty type map + */ +struct GSC_TypeMap * +GSC_TYPEMAP_create (void); + + +/** + * Free the given type map. + * + * @param tmap a type map + */ +void +GSC_TYPEMAP_destroy (struct GSC_TypeMap *tmap); + + +/** + * Initialize typemap subsystem. + */ +void +GSC_TYPEMAP_init (void); + + +/** + * Shutdown typemap subsystem. + */ +void +GSC_TYPEMAP_done (void); + +#endif +/* end of gnunet-service-core_typemap.h */ diff --git a/src/core/test_core_api.c b/src/core/test_core_api.c new file mode 100644 index 0000000..271c2ce --- /dev/null +++ b/src/core/test_core_api.c @@ -0,0 +1,412 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/test_core_api.c + * @brief testcase for core_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +#define MTYPE 12345 + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_PeerIdentity id; + struct GNUNET_TRANSPORT_Handle *th; + struct GNUNET_TRANSPORT_GetHelloHandle *ghh; + struct GNUNET_MessageHeader *hello; + int connect_status; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static GNUNET_SCHEDULER_TaskIdentifier err_task; + +static GNUNET_SCHEDULER_TaskIdentifier con_task; + +static int ok; + +#if VERBOSE +#define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + + +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received (my) `%s' from transport service\n", "HELLO"); + GNUNET_assert (message != NULL); + if ((p == &p1) && (p2.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL); + if ((p == &p2) && (p1.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, message, NULL, NULL); +} + + +static void +terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (ok == 6); + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + p1.ghh = NULL; + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + p2.ghh = NULL; + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; + if (GNUNET_SCHEDULER_NO_TASK != con_task) + { + GNUNET_SCHEDULER_cancel (con_task); + con_task = GNUNET_SCHEDULER_NO_TASK; + } + ok = 0; +} + + +static void +terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + FPRINTF (stderr, "ENDING ANGRILY %u\n", ok); +#endif + GNUNET_break (0); + if (NULL != p1.ch) + { + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + } + if (NULL != p2.ch) + { + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + } + if (p1.th != NULL) + { + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + } + if (p2.th != NULL) + { + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != con_task) + { + GNUNET_SCHEDULER_cancel (con_task); + con_task = GNUNET_SCHEDULER_NO_TASK; + } + ok = 42; +} + + +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + struct PeerContext *p = cls; + struct GNUNET_MessageHeader *m; + + GNUNET_assert (ok == 4); + OKPP; + GNUNET_assert (p == &p1); + GNUNET_assert (buf != NULL); + m = (struct GNUNET_MessageHeader *) buf; + m->type = htons (MTYPE); + m->size = htons (sizeof (struct GNUNET_MessageHeader)); + return sizeof (struct GNUNET_MessageHeader); +} + + +static void +connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; + GNUNET_assert (pc->connect_status == 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted connection established to peer `%4s'\n", + GNUNET_i2s (peer)); + if (GNUNET_SCHEDULER_NO_TASK != con_task) + { + GNUNET_SCHEDULER_cancel (con_task); + con_task = GNUNET_SCHEDULER_NO_TASK; + } + pc->connect_status = 1; + if (pc == &p1) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking core (1) for transmission to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + if (NULL == + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_YES, 0, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 145), + &p2.id, + sizeof (struct GNUNET_MessageHeader), + &transmit_ready, &p1)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "RECEIVED NULL when asking core (1) for transmission to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + } + } +} + + +static void +disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; + pc->connect_status = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted connection to `%4s' cut\n", + GNUNET_i2s (peer)); +} + + +static int +inbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other)); + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core notifies about outbound data for `%4s'.\n", + GNUNET_i2s (other)); + return GNUNET_OK; +} + + + +static int +process_mtype (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving message from `%4s'.\n", + GNUNET_i2s (peer)); + GNUNET_assert (ok == 5); + OKPP; + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task, NULL); + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, 0, 0} +}; + + +static void +connect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + con_task = GNUNET_SCHEDULER_NO_TASK; + return; + } + con_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &connect_task, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking transport (1) to connect to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + GNUNET_TRANSPORT_try_connect (p1.th, &p2.id); +} + + +static void +init_notify (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core connection to `%4s' established\n", + GNUNET_i2s (my_identity)); + GNUNET_assert (server != NULL); + p->id = *my_identity; + p->ch = server; + if (cls == &p1) + { + GNUNET_assert (ok == 2); + OKPP; + /* connect p2 */ + p2.ch = + GNUNET_CORE_connect (p2.cfg, 1, &p2, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (ok == 3); + OKPP; + GNUNET_assert (cls == &p2); + con_task = GNUNET_SCHEDULER_add_now (&connect_task, NULL); + } +} + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p, NULL, NULL, NULL); + GNUNET_assert (p->th != NULL); + p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p); +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + setup_peer (&p1, "test_core_api_peer1.conf"); + setup_peer (&p2, "test_core_api_peer2.conf"); + err_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 300), + &terminate_task_error, NULL); + p1.ch = + GNUNET_CORE_connect (p1.cfg, 1, &p1, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + +static int +check () +{ + char *const argv[] = { "test-core-api", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-core-api", "nohelp", options, &run, &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-core-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2"); + + return ret; +} + +/* end of test_core_api.c */ diff --git a/src/core/test_core_api_data.conf b/src/core/test_core_api_data.conf new file mode 100644 index 0000000..30eea2d --- /dev/null +++ b/src/core/test_core_api_data.conf @@ -0,0 +1,15 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +DEFAULTCONFIG = test_core_api_data.conf + +[arm] +DEFAULTSERVICES = topology hostlist + +[ats] +WAN_QUOTA_IN = 64 kiB +WAN_QUOTA_OUT = 64 kiB + +[core] +PORT = 2092 +UNIXPATH = /tmp/gnunet-service-core.sock + diff --git a/src/core/test_core_api_peer1.conf b/src/core/test_core_api_peer1.conf new file mode 100644 index 0000000..662318a --- /dev/null +++ b/src/core/test_core_api_peer1.conf @@ -0,0 +1,30 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-peer-1/ +DEFAULTCONFIG = test_core_api_peer1.conf + +[transport-tcp] +PORT = 12468 + +[arm] +PORT = 12466 +UNIXPATH = /tmp/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12467 + +[resolver] +PORT = 12464 +UNIXPATH = /tmp/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12469 +UNIXPATH = /tmp/gnunet-p1-service-peerinfo.sock + +[transport] +PORT = 12465 +UNIXPATH = /tmp/gnunet-p1-service-transport.sock + +[ats] +PORT = 12471 +UNIXPATH = /tmp/gnunet-p1-service-ats.sock diff --git a/src/core/test_core_api_peer2.conf b/src/core/test_core_api_peer2.conf new file mode 100644 index 0000000..2e848b8 --- /dev/null +++ b/src/core/test_core_api_peer2.conf @@ -0,0 +1,35 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-peer-2/ +DEFAULTCONFIG = test_core_api_peer2.conf + +[transport-tcp] +PORT = 22468 + +[arm] +PORT = 22466 +UNIXPATH = /tmp/gnunet-p2-service-arm.sock + +[statistics] +PORT = 22467 +UNIXPATH = /tmp/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 22464 +UNIXPATH = /tmp/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 22469 +UNIXPATH = /tmp/gnunet-p2-service-peerinfo.sock + +[transport] +PORT = 22465 +UNIXPATH = /tmp/gnunet-p2-service-transport.sock + +[core] +PORT = 22470 +UNIXPATH = /tmp/gnunet-p2-service-core.sock + +[ats] +PORT = 22471 +UNIXPATH = /tmp/gnunet-p2-service-ats.sock diff --git a/src/core/test_core_api_reliability.c b/src/core/test_core_api_reliability.c new file mode 100644 index 0000000..645b27e --- /dev/null +++ b/src/core/test_core_api_reliability.c @@ -0,0 +1,533 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/test_core_api_reliability.c + * @brief testcase for core_api.c focusing on reliable transmission (with TCP) + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" +#include <gauger.h> + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +/** + * Note that this value must not significantly exceed + * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise + * messages may be dropped even for a reliable transport. + */ +#define TOTAL_MSGS (600 * 10) + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 6000) + +/** + * What delay do we request from the core service for transmission? + */ +#define FAST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +#define MTYPE 12345 + + +static unsigned long long total_bytes; + +static struct GNUNET_TIME_Absolute start_time; + +static GNUNET_SCHEDULER_TaskIdentifier err_task; + +static GNUNET_SCHEDULER_TaskIdentifier connect_task; + + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_PeerIdentity id; + struct GNUNET_TRANSPORT_Handle *th; + struct GNUNET_MessageHeader *hello; + struct GNUNET_TRANSPORT_GetHelloHandle *ghh; + int connect_status; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static int ok; + +static int32_t tr_n; + + +#if VERBOSE +#define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + +struct TestMessage +{ + struct GNUNET_MessageHeader header; + uint32_t num; +}; + + +static unsigned int +get_size (unsigned int iter) +{ + unsigned int ret; + + if (iter < 60000) + return iter + sizeof (struct TestMessage); + ret = (iter * iter * iter); + return sizeof (struct TestMessage) + (ret % 60000); +} + +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message); + +static void +terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned long long delta; + + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + if (connect_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (connect_task); + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; + delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value; + FPRINTF (stderr, "\nThroughput was %llu kb/s\n", + total_bytes * 1000 / 1024 / delta); + GAUGER ("CORE", "Core throughput/s", total_bytes * 1000 / 1024 / delta, + "kb/s"); + ok = 0; +} + + +static void +terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_break (0); + if (p1.ch != NULL) + { + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + } + if (p2.ch != NULL) + { + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + } + if (connect_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (connect_task); + if (p1.th != NULL) + { + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + } + if (p2.th != NULL) + { + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; + } + ok = 42; +} + + +static void +try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + connect_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, + NULL); + GNUNET_TRANSPORT_try_connect (p1.th, &p2.id); +} + +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + char *cbuf = buf; + struct TestMessage hdr; + unsigned int s; + unsigned int ret; + + GNUNET_assert (size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE); + if (buf == NULL) + { + if (p1.ch != NULL) + GNUNET_break (NULL != + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + FAST_TIMEOUT, &p2.id, + get_size (tr_n), + &transmit_ready, &p1)); + return 0; + } + GNUNET_assert (tr_n < TOTAL_MSGS); + ret = 0; + s = get_size (tr_n); + GNUNET_assert (size >= s); + GNUNET_assert (buf != NULL); + cbuf = buf; + do + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message %u of size %u at offset %u\n", tr_n, s, ret); +#endif + hdr.header.size = htons (s); + hdr.header.type = htons (MTYPE); + hdr.num = htonl (tr_n); + memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage)); + ret += sizeof (struct TestMessage); + memset (&cbuf[ret], tr_n, s - sizeof (struct TestMessage)); + ret += s - sizeof (struct TestMessage); + tr_n++; + s = get_size (tr_n); + if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16)) + break; /* sometimes pack buffer full, sometimes not */ + } + while (size - ret >= s); + GNUNET_SCHEDULER_cancel (err_task); + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returning total message block of size %u\n", ret); + total_bytes += ret; + return ret; +} + + + +static void +connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; + GNUNET_assert (pc->connect_status == 0); + pc->connect_status = 1; + if (pc == &p1) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted connection established to peer `%4s'\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking core (1) for transmission to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + GNUNET_SCHEDULER_cancel (err_task); + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + start_time = GNUNET_TIME_absolute_get (); + GNUNET_break (NULL != + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + TIMEOUT, &p2.id, + get_size (0), + &transmit_ready, &p1)); + } +} + + +static void +disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; + pc->connect_status = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted connection to `%4s' cut\n", + GNUNET_i2s (peer)); +} + + +static int +inbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other)); +#endif + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core notifies about outbound data for `%4s'.\n", + GNUNET_i2s (other)); +#endif + return GNUNET_OK; +} + + +static size_t +transmit_ready (void *cls, size_t size, void *buf); + +static int +process_mtype (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + static int n; + unsigned int s; + const struct TestMessage *hdr; + + hdr = (const struct TestMessage *) message; + s = get_size (n); + if (MTYPE != ntohs (message->type)) + return GNUNET_SYSERR; + if (ntohs (message->size) != s) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected message %u of size %u, got %u bytes of message %u\n", + n, s, ntohs (message->size), ntohl (hdr->num)); + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, NULL); + return GNUNET_SYSERR; + } + if (ntohl (hdr->num) != n) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected message %u of size %u, got %u bytes of message %u\n", + n, s, ntohs (message->size), ntohl (hdr->num)); + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, NULL); + return GNUNET_SYSERR; + } +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n", + ntohl (hdr->num), ntohs (message->size)); +#endif + n++; + if (0 == (n % (TOTAL_MSGS / 100))) + FPRINTF (stderr, "%s", "."); + if (n == TOTAL_MSGS) + { + GNUNET_SCHEDULER_cancel (err_task); + GNUNET_SCHEDULER_add_now (&terminate_task, NULL); + } + else + { + if (n == tr_n) + GNUNET_break (NULL != + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + FAST_TIMEOUT, &p2.id, + get_size (tr_n), + &transmit_ready, &p1)); + } + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {&process_mtype, MTYPE, 0}, + {NULL, 0, 0} +}; + + + +static void +init_notify (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to CORE service of `%4s' established\n", + GNUNET_i2s (my_identity)); + GNUNET_assert (server != NULL); + p->id = *my_identity; + p->ch = server; + if (cls == &p1) + { + GNUNET_assert (ok == 2); + OKPP; + /* connect p2 */ + GNUNET_CORE_connect (p2.cfg, 1, &p2, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (ok == 3); + OKPP; + GNUNET_assert (cls == &p2); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking transport (1) to connect to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + connect_task = GNUNET_SCHEDULER_add_now (&try_connect, NULL); + } +} + + +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received (my) `%s' from transport service\n", "HELLO"); + GNUNET_assert (message != NULL); + p->hello = GNUNET_malloc (ntohs (message->size)); + memcpy (p->hello, message, ntohs (message->size)); + if ((p == &p1) && (p2.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL); + if ((p == &p2) && (p1.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, message, NULL, NULL); + + if ((p == &p1) && (p2.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello, NULL, NULL); + if ((p == &p2) && (p1.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello, NULL, NULL); +} + + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p, NULL, NULL, NULL); + GNUNET_assert (p->th != NULL); + p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p); +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + setup_peer (&p1, "test_core_api_peer1.conf"); + setup_peer (&p2, "test_core_api_peer2.conf"); + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + GNUNET_CORE_connect (p1.cfg, 1, &p1, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + +static int +check () +{ + char *const argv[] = { "test-core-api-reliability", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-core-api-reliability", "nohelp", options, &run, + &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-core-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2"); + + return ret; +} + +/* end of test_core_api_reliability.c */ diff --git a/src/core/test_core_api_send_to_self.c b/src/core/test_core_api_send_to_self.c new file mode 100644 index 0000000..4fa73d9 --- /dev/null +++ b/src/core/test_core_api_send_to_self.c @@ -0,0 +1,242 @@ +/* + This file is part of GNUnet. + (C) 2010 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 core/test_core_api_send_to_self.c + * @brief + * @author Philipp Toelke + */ +#include <platform.h> +#include <gnunet_common.h> +#include <gnunet_program_lib.h> +#include <gnunet_protocols.h> +#include <gnunet_core_service.h> +#include <gnunet_constants.h> + +/** + * Final status code. + */ +static int ret; + +/** + * Handle to the cleanup task. + */ +GNUNET_SCHEDULER_TaskIdentifier die_task; + +static struct GNUNET_PeerIdentity myself; + +/** + * Configuration to load for the new peer. + */ +struct GNUNET_CONFIGURATION_Handle *core_cfg; + +/** + * The handle to core + */ +struct GNUNET_CORE_Handle *core; + +/** + * Handle to gnunet-service-arm. + */ +struct GNUNET_OS_Process *arm_proc; + +/** + * Function scheduled as very last function, cleans up after us + */ +static void +cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tskctx) +{ + die_task = GNUNET_SCHEDULER_NO_TASK; + + if (core != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting core.\n"); + GNUNET_CORE_disconnect (core); + core = NULL; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peer\n"); + if (0 != GNUNET_OS_process_kill (arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + + if (GNUNET_OS_process_wait (arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (arm_proc)); + GNUNET_OS_process_close (arm_proc); + arm_proc = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); +} + +static int +receive (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, unsigned int atsi_count) +{ + if (die_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message from peer %s\n", + GNUNET_i2s (other)); + GNUNET_SCHEDULER_add_now (&cleanup, NULL); + ret = 0; + return GNUNET_OK; +} + +static size_t +send_message (void *cls, size_t size, void *buf) +{ + if (size == 0 || buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not send; got 0 buffer\n"); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending!\n"); + struct GNUNET_MessageHeader *hdr = buf; + + hdr->size = htons (sizeof (struct GNUNET_MessageHeader)); + hdr->type = htons (GNUNET_MESSAGE_TYPE_DUMMY); + return ntohs (hdr->size); +} + +static void +init (void *cls, struct GNUNET_CORE_Handle *core, + const struct GNUNET_PeerIdentity *my_identity) +{ + if (core == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could NOT connect to CORE;\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Correctly connected to CORE; we are the peer %s.\n", + GNUNET_i2s (my_identity)); + memcpy (&myself, my_identity, sizeof (struct GNUNET_PeerIdentity)); +} + +static void +connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to peer %s.\n", + GNUNET_i2s (peer)); + if (0 == memcmp (peer, &myself, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected to myself; sending message!\n"); + GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 0, + GNUNET_TIME_UNIT_FOREVER_REL, peer, + sizeof (struct GNUNET_MessageHeader), + send_message, NULL); + } +} + + +/** + * 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, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + const static struct GNUNET_CORE_MessageHandler handlers[] = { + {&receive, GNUNET_MESSAGE_TYPE_DUMMY, 0}, + {NULL, 0, 0} + }; + + core_cfg = GNUNET_CONFIGURATION_create (); + + arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", "test_core_api_peer1.conf", NULL); + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_load (core_cfg, + "test_core_api_peer1.conf")); + + core = + GNUNET_CORE_connect (core_cfg, 42, NULL, &init, &connect_cb, NULL, NULL, + 0, NULL, 0, handlers); + + die_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 300), &cleanup, + cls); +} + + +static int +check () +{ + char *const argv[] = { "test-core-api-send-to-self", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + ret = 1; + + return (GNUNET_OK == + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test_core_api_send_to_self", + gettext_noop ("help text"), options, &run, + NULL)) ? ret : 1; +} + +/** + * The main function to obtain template from gnunetd. + * + * @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 *argv[]) +{ + GNUNET_log_setup ("test-core-api-send-to-self", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); + return ret; +} + +/* end of test_core_api_send_to_self.c */ diff --git a/src/core/test_core_api_send_to_self.conf b/src/core/test_core_api_send_to_self.conf new file mode 100644 index 0000000..6737726 --- /dev/null +++ b/src/core/test_core_api_send_to_self.conf @@ -0,0 +1,32 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = ~/.gnunet/ +DEFAULTCONFIG = test_core_api_send_to_self.conf + +[arm] +PORT = 2425 +DEFAULTSERVICES = core test-sts + +[core] +PORT = 24512 +UNIXPATH = /tmp/gnunet-service-core.sock + +[ats] +WAN_QUOTA_IN = 104857600 +WAN_QUOTA_OUT = 104757600 +PORT = 24571 +UNIXPATH = /tmp/gnunet-p1-service-ats.sock + +[test-sts] +AUTOSTART = YES +PORT = 9252 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = test_core_api_send_to_self +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +TOTAL_QUOTA_IN = 65536 +TOTAL_QUOTA_OUT = 65536 +UNIXPATH = /tmp/gnunet-service-sts.sock + diff --git a/src/core/test_core_api_start_only.c b/src/core/test_core_api_start_only.c new file mode 100644 index 0000000..2eca575 --- /dev/null +++ b/src/core/test_core_api_start_only.c @@ -0,0 +1,262 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + 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 transport/test_core_api_start_only.c + * @brief testcase for core_api.c that only starts two peers, + * connects to the core service and shuts down again + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" + +#define VERBOSE GNUNET_NO + +#define TIMEOUT 5 + +#define START_ARM GNUNET_YES + +#define MTYPE 12345 + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_PeerIdentity id; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static GNUNET_SCHEDULER_TaskIdentifier timeout_task_id; + +static int ok; + +#if VERBOSE +#define OKPP do { ok++; FPRINTF (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + + + +static void +connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ +} + + +static void +disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer) +{ +} + + +static int +inbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {NULL, 0, 0} +}; + + +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + ok = 0; +} + + + + +static void +init_notify (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct PeerContext *p = cls; + + GNUNET_assert (server != NULL); + GNUNET_assert (p->ch == server); + if (cls == &p1) + { + /* connect p2 */ + p2.ch = + GNUNET_CORE_connect (p2.cfg, 1, &p2, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (cls == &p2); + GNUNET_SCHEDULER_cancel (timeout_task_id); + GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); + } +} + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); +} + + +static void +timeout_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + FPRINTF (stderr, "%s", "Timeout.\n"); + if (p1.ch != NULL) + { + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + } + if (p2.ch != NULL) + { + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + } + ok = 42; +} + + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + setup_peer (&p1, "test_core_api_peer1.conf"); + setup_peer (&p2, "test_core_api_peer2.conf"); + timeout_task_id = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, TIMEOUT), + &timeout_task, NULL); + p1.ch = + GNUNET_CORE_connect (p1.cfg, 1, &p1, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peer\n"); +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static int +check () +{ + char *const argv[] = { "test-core-api-start-only", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2"); + + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-core-api-start-only", "nohelp", options, &run, &ok); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test finished\n"); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-core-api-start-only", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2"); + return ret; +} + +/* end of test_core_api_start_only.c */ diff --git a/src/core/test_core_defaults.conf b/src/core/test_core_defaults.conf new file mode 100644 index 0000000..098c0ba --- /dev/null +++ b/src/core/test_core_defaults.conf @@ -0,0 +1,59 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core/ +DEFAULTCONFIG = test_core_defaults.conf + +[arm] +DEFAULTSERVICES = + +[nat] +DISABLEV6 = YES +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 +USE_LOCALADDR = NO + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +PORT = 12470 +UNIXPATH = /tmp/gnunet-p1-service-core.sock + +[transport-tcp] +BINDTO = 127.0.0.1 + +[testing] +WEAKRANDOM = YES + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[dv] +AUTOSTART = NO + +[chat] +AUTOSTART = NO + +[gns] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO diff --git a/src/core/test_core_quota_asymmetric_recv_limited_peer1.conf b/src/core/test_core_quota_asymmetric_recv_limited_peer1.conf new file mode 100644 index 0000000..5501fb8 --- /dev/null +++ b/src/core/test_core_quota_asymmetric_recv_limited_peer1.conf @@ -0,0 +1,39 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-asym-recv-lim-peer-1/ +DEFAULTCONFIG = test_core_quota_asymmetric_recv_limited_peer1.conf + +[transport-tcp] +PORT = 12488 + +[arm] +PORT = 12486 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-arm.sock + +[statistics] +PORT = 12487 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-statistics.sock + +[resolver] +PORT = 12484 +UNIXPATH = /tmp/gnunet-core-asym-recv-1-service-resolver.sock + +[peerinfo] +PORT = 12489 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-peerinfo.sock + +[transport] +PORT = 12485 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-transport.sock + +[ats] +PORT = 12491 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-ats.sock +WAN_QUOTA_IN = 1 MB +WAN_QUOTA_OUT = 1 MB + +[core] +PORT = 12490 +UNIXPATH = /tmp/gnunet-core-asym-recv-p1-service-core.sock +DEBUG = NO + diff --git a/src/core/test_core_quota_asymmetric_recv_limited_peer2.conf b/src/core/test_core_quota_asymmetric_recv_limited_peer2.conf new file mode 100644 index 0000000..0c89523 --- /dev/null +++ b/src/core/test_core_quota_asymmetric_recv_limited_peer2.conf @@ -0,0 +1,39 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-asym-recv-lim-peer-2/ +DEFAULTCONFIG = test_core_quota_asymmetric_recv_limited_peer2.conf + +[transport-tcp] +PORT = 22488 + +[arm] +PORT = 22486 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-arm.sock + +[statistics] +PORT = 22487 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-statistics.sock + +[resolver] +PORT = 22484 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-resolver.sock + +[peerinfo] +PORT = 22489 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-peerinfo.sock + +[transport] +PORT = 22485 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-transport.sock + +[core] +PORT = 22490 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-core.sock +DEBUG = NO + +[ats] +PORT = 22491 +UNIXPATH = /tmp/gnunet-core-asym-recv-p2-service-ats.sock +WAN_QUOTA_IN = 10 kiB +WAN_QUOTA_OUT = 10 kiB + diff --git a/src/core/test_core_quota_asymmetric_send_limit_peer1.conf b/src/core/test_core_quota_asymmetric_send_limit_peer1.conf new file mode 100644 index 0000000..568cf6b --- /dev/null +++ b/src/core/test_core_quota_asymmetric_send_limit_peer1.conf @@ -0,0 +1,38 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-asym-send-lim-peer-1/ +DEFAULTCONFIG = test_core_quota_asymmetric_send_limit_peer1.conf + +[transport-tcp] +PORT = 12488 + +[arm] +PORT = 12486 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-arm.sock + +[statistics] +PORT = 12487 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-statistics.sock + +[resolver] +PORT = 12484 +UNIXPATH = /tmp/gnunet-core-asym-send-1-service-resolver.sock + +[peerinfo] +PORT = 12489 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-peerinfo.sock + +[transport] +PORT = 12485 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-transport.sock + +[ats] +WAN_QUOTA_IN = 10240 +WAN_QUOTA_OUT = 10240 +PORT = 12491 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-ats.sock + +[core] +PORT = 12490 +UNIXPATH = /tmp/gnunet-core-asym-send-p1-service-core.sock +DEBUG = NO diff --git a/src/core/test_core_quota_asymmetric_send_limit_peer2.conf b/src/core/test_core_quota_asymmetric_send_limit_peer2.conf new file mode 100644 index 0000000..5474ce9 --- /dev/null +++ b/src/core/test_core_quota_asymmetric_send_limit_peer2.conf @@ -0,0 +1,38 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-asym-send-lim-peer-2/ +DEFAULTCONFIG = test_core_quota_asymmetric_send_limit_peer2.conf + +[transport-tcp] +PORT = 22488 + +[arm] +PORT = 22486 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-arm.sock + +[statistics] +PORT = 22487 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-statistics.sock + +[resolver] +PORT = 22484 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-resolver.sock + +[peerinfo] +PORT = 22489 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-peerinfo.sock + +[transport] +PORT = 22485 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-transport.sock + +[core] +PORT = 22490 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-core.sock +DEBUG = NO + +[ats] +PORT = 22491 +UNIXPATH = /tmp/gnunet-core-asym-send-p2-service-ats.sock +WAN_QUOTA_IN = 1 MB +WAN_QUOTA_OUT = 1 MB diff --git a/src/core/test_core_quota_compliance.c b/src/core/test_core_quota_compliance.c new file mode 100644 index 0000000..7c16531 --- /dev/null +++ b/src/core/test_core_quota_compliance.c @@ -0,0 +1,750 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + 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 core/test_core_quota_compliance.c + * @brief testcase for core_api.c focusing quota compliance on core level + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_statistics_service.h" + +#define VERBOSE GNUNET_NO + +#define SYMMETRIC 0 +#define ASYMMETRIC_SEND_LIMITED 1 +#define ASYMMETRIC_RECV_LIMITED 2 + +#define START_ARM GNUNET_YES + +/** + * Note that this value must not significantly exceed + * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise + * messages may be dropped even for a reliable transport. + */ +#define TOTAL_MSGS (60000 * 10) + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) + +/** + * What delay do we request from the core service for transmission? + */ +#define FAST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 150) + +#define MTYPE 12345 +#define MESSAGESIZE 1024 +#define MEASUREMENT_LENGTH GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +static unsigned long long total_bytes_sent; +static unsigned long long total_bytes_recv; + +static struct GNUNET_TIME_Absolute start_time; + +static GNUNET_SCHEDULER_TaskIdentifier err_task; + +static GNUNET_SCHEDULER_TaskIdentifier measure_task; + +static GNUNET_SCHEDULER_TaskIdentifier connect_task; + + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_CORE_TransmitHandle *nth; + struct GNUNET_PeerIdentity id; + struct GNUNET_TRANSPORT_Handle *th; + struct GNUNET_MessageHeader *hello; + struct GNUNET_STATISTICS_Handle *stats; + struct GNUNET_TRANSPORT_GetHelloHandle *ghh; + int connect_status; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; +static struct PeerContext p2; + +static unsigned long long current_quota_p1_in; +static unsigned long long current_quota_p1_out; +static unsigned long long current_quota_p2_in; +static unsigned long long current_quota_p2_out; + +static int ok; +static int test; +static int32_t tr_n; + +static int running; + + +#if VERBOSE +#define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + +struct TestMessage +{ + struct GNUNET_MessageHeader header; + uint32_t num; +}; + +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message); + +static void +terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CORE_Handle *ch; + + err_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + if (p1.nth != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (p1.nth); + p1.nth = NULL; + } + if (connect_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (connect_task); + ch = p1.ch; + p1.ch = NULL; + GNUNET_CORE_disconnect (ch); + ch = p2.ch; + p2.ch = NULL; + GNUNET_CORE_disconnect (ch); + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; +} + + +static void +terminate_task_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + err_task = GNUNET_SCHEDULER_NO_TASK; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Testcase failed!\n"); + //GNUNET_break (0); + if (p1.nth != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (p1.nth); + p1.nth = NULL; + } + if (measure_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (measure_task); + if (connect_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (connect_task); + + GNUNET_TRANSPORT_get_hello_cancel (p1.ghh); + GNUNET_TRANSPORT_get_hello_cancel (p2.ghh); + + GNUNET_CORE_disconnect (p1.ch); + p1.ch = NULL; + GNUNET_CORE_disconnect (p2.ch); + p2.ch = NULL; + GNUNET_TRANSPORT_disconnect (p1.th); + p1.th = NULL; + GNUNET_TRANSPORT_disconnect (p2.th); + p2.th = NULL; + ok = 42; +} + + +static void +try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + connect_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, + NULL); + GNUNET_TRANSPORT_try_connect (p1.th, &p2.id); + GNUNET_TRANSPORT_try_connect (p2.th, &p1.id); +} + +/** + * Callback function to process statistic values. + * + * @param cls closure + * @param subsystem name of subsystem that created the statistic + * @param name the name of the datum + * @param value the current value + * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not + * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration + */ +static int +print_stat (void *cls, const char *subsystem, const char *name, uint64_t value, + int is_persistent) +{ + if (cls == &p1) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer1 %50s = %12llu\n", name, + (unsigned long long) value); + if (cls == &p2) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer2 %50s = %12llu\n", name, + (unsigned long long) value); + return GNUNET_OK; +} + +static void +measurement_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned long long delta; + unsigned long long throughput_out; + unsigned long long throughput_in; + unsigned long long max_quota_in; + unsigned long long max_quota_out; + unsigned long long quota_delta; + enum GNUNET_ErrorType kind = GNUNET_ERROR_TYPE_DEBUG; + + measure_task = GNUNET_SCHEDULER_NO_TASK; + FPRINTF (stdout, "%s", "\n"); + running = GNUNET_NO; + + delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value; + + throughput_out = total_bytes_sent * 1000 / delta; /* convert to bytes/s */ + throughput_in = total_bytes_recv * 1000 / delta; /* convert to bytes/s */ + + max_quota_in = GNUNET_MIN (current_quota_p1_in, current_quota_p2_in); + max_quota_out = GNUNET_MIN (current_quota_p1_out, current_quota_p2_out); + if (max_quota_out < max_quota_in) + quota_delta = max_quota_in / 5; + else + quota_delta = max_quota_out / 5; + + if ((throughput_out > (max_quota_out + quota_delta)) || + (throughput_in > (max_quota_in + quota_delta))) + ok = 1; + else + ok = 0; + GNUNET_STATISTICS_get (p1.stats, "core", "# discarded CORE_SEND requests", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1); + + GNUNET_STATISTICS_get (p1.stats, "core", + "# discarded CORE_SEND request bytes", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1); + GNUNET_STATISTICS_get (p1.stats, "core", + "# discarded lower priority CORE_SEND requests", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, NULL); + GNUNET_STATISTICS_get (p1.stats, "core", + "# discarded lower priority CORE_SEND request bytes", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1); + GNUNET_STATISTICS_get (p2.stats, "core", "# discarded CORE_SEND requests", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2); + + GNUNET_STATISTICS_get (p2.stats, "core", + "# discarded CORE_SEND request bytes", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2); + GNUNET_STATISTICS_get (p2.stats, "core", + "# discarded lower priority CORE_SEND requests", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2); + GNUNET_STATISTICS_get (p2.stats, "core", + "# discarded lower priority CORE_SEND request bytes", + GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2); + + if (ok != 0) + kind = GNUNET_ERROR_TYPE_ERROR; + switch (test) + { + case SYMMETRIC: + GNUNET_log (kind, "Core quota compliance test with symmetric quotas: %s\n", + (ok != 0) ? "PASSED" : "FAILED"); + break; + case ASYMMETRIC_SEND_LIMITED: + GNUNET_log (kind, + "Core quota compliance test with limited sender quota: %s\n", + (ok != 0) ? "PASSED" : "FAILED"); + break; + case ASYMMETRIC_RECV_LIMITED: + GNUNET_log (kind, + "Core quota compliance test with limited receiver quota: %s\n", + (ok != 0) ? "PASSED" : "FAILED"); + break; + }; + GNUNET_log (kind, "Peer 1 send rate: %llu b/s (%llu bytes in %llu ms)\n", + throughput_out, total_bytes_sent, delta); + GNUNET_log (kind, "Peer 1 send quota: %llu b/s\n", current_quota_p1_out); + GNUNET_log (kind, "Peer 2 receive rate: %llu b/s (%llu bytes in %llu ms)\n", + throughput_in, total_bytes_recv, delta); + GNUNET_log (kind, "Peer 2 receive quota: %llu b/s\n", current_quota_p2_in); +/* + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. inbound quota allowed: %llu b/s\n",max_quota_in ); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. outbound quota allowed: %llu b/s\n",max_quota_out); +*/ + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task, NULL); + +} + +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + char *cbuf = buf; + struct TestMessage hdr; + unsigned int ret; + + p1.nth = NULL; + GNUNET_assert (size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE); + if (buf == NULL) + { + if ((p1.ch != NULL) && (p1.connect_status == 1)) + GNUNET_break (NULL != + (p1.nth = + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + FAST_TIMEOUT, &p2.id, + MESSAGESIZE, + &transmit_ready, &p1))); + return 0; + } + GNUNET_assert (tr_n < TOTAL_MSGS); + ret = 0; + GNUNET_assert (size >= MESSAGESIZE); + GNUNET_assert (buf != NULL); + cbuf = buf; + do + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message %u of size %u at offset %u\n", tr_n, + MESSAGESIZE, ret); + hdr.header.size = htons (MESSAGESIZE); + hdr.header.type = htons (MTYPE); + hdr.num = htonl (tr_n); + memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage)); + ret += sizeof (struct TestMessage); + memset (&cbuf[ret], tr_n, MESSAGESIZE - sizeof (struct TestMessage)); + ret += MESSAGESIZE - sizeof (struct TestMessage); + tr_n++; + if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16)) + break; /* sometimes pack buffer full, sometimes not */ + } + while (size - ret >= MESSAGESIZE); + GNUNET_SCHEDULER_cancel (err_task); + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + + total_bytes_sent += ret; + return ret; +} + + + +static void +connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; /* loopback */ + GNUNET_assert (pc->connect_status == 0); + pc->connect_status = 1; + if (pc == &p1) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted connection established to peer `%4s'\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking core (1) for transmission to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + if (err_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (err_task); + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + start_time = GNUNET_TIME_absolute_get (); + running = GNUNET_YES; + measure_task = + GNUNET_SCHEDULER_add_delayed (MEASUREMENT_LENGTH, &measurement_stop, + NULL); + + GNUNET_break (NULL != + (p1.nth = + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + TIMEOUT, &p2.id, + MESSAGESIZE, + &transmit_ready, &p1))); + } +} + + +static void +disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct PeerContext *pc = cls; + + if (0 == memcmp (&pc->id, peer, sizeof (struct GNUNET_PeerIdentity))) + return; /* loopback */ + pc->connect_status = 0; + if (GNUNET_SCHEDULER_NO_TASK != measure_task) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Measurement aborted due to disconnect!\n"); + GNUNET_SCHEDULER_cancel (measure_task); + measure_task = GNUNET_SCHEDULER_NO_TASK; + } + if (pc->nth != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (pc->nth); + pc->nth = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted connection to `%4s' cut\n", + GNUNET_i2s (peer)); +} + + +static int +inbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core provides inbound data from `%4s' %llu.\n", + GNUNET_i2s (other), ntohs (message->size)); + total_bytes_recv += ntohs (message->size); + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core notifies about outbound data for `%4s'.\n", + GNUNET_i2s (other)); + return GNUNET_OK; +} + + +static size_t +transmit_ready (void *cls, size_t size, void *buf); + +static int +process_mtype (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + static int n; + const struct TestMessage *hdr; + + hdr = (const struct TestMessage *) message; + if (MTYPE != ntohs (message->type)) + return GNUNET_SYSERR; + if (ntohs (message->size) != MESSAGESIZE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected message %u of size %u, got %u bytes of message %u\n", + n, MESSAGESIZE, ntohs (message->size), ntohl (hdr->num)); + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, NULL); + return GNUNET_SYSERR; + } + if (ntohl (hdr->num) != n) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected message %u of size %u, got %u bytes of message %u\n", + n, MESSAGESIZE, ntohs (message->size), ntohl (hdr->num)); + GNUNET_SCHEDULER_cancel (err_task); + err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error, NULL); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n", + ntohl (hdr->num), ntohs (message->size)); + n++; + if (0 == (n % 10)) + FPRINTF (stderr, "%s", "."); + + + if (running == GNUNET_YES) + GNUNET_break (NULL != + GNUNET_CORE_notify_transmit_ready (p1.ch, GNUNET_NO, 0, + FAST_TIMEOUT, &p2.id, + MESSAGESIZE, + &transmit_ready, &p1)); + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {&process_mtype, MTYPE, 0}, + {NULL, 0, 0} +}; + + + +static void +init_notify (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to CORE service of `%4s' established\n", + GNUNET_i2s (my_identity)); + GNUNET_assert (server != NULL); + p->id = *my_identity; + GNUNET_assert (p->ch == server); + if (cls == &p1) + { + GNUNET_assert (ok == 2); + OKPP; + /* connect p2 */ + p2.ch = + GNUNET_CORE_connect (p2.cfg, 1, &p2, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (ok == 3); + OKPP; + GNUNET_assert (cls == &p2); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking core (1) to connect to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + connect_task = GNUNET_SCHEDULER_add_now (&try_connect, NULL); + } +} + + +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received (my) `%s' from transport service\n", "HELLO"); + GNUNET_assert (message != NULL); + p->hello = GNUNET_malloc (ntohs (message->size)); + memcpy (p->hello, message, ntohs (message->size)); + if ((p == &p1) && (p2.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, message, NULL, NULL); + if ((p == &p2) && (p1.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, message, NULL, NULL); + + if ((p == &p1) && (p2.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello, NULL, NULL); + if ((p == &p2) && (p1.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello, NULL, NULL); +} + + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + p->stats = GNUNET_STATISTICS_create ("core", p->cfg); + GNUNET_assert (p->stats != NULL); + p->th = GNUNET_TRANSPORT_connect (p->cfg, NULL, p, NULL, NULL, NULL); + GNUNET_assert (p->th != NULL); + p->ghh = GNUNET_TRANSPORT_get_hello (p->th, &process_hello, p); +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + err_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &terminate_task_error, NULL); + if (test == SYMMETRIC) + { + setup_peer (&p1, "test_core_quota_peer1.conf"); + setup_peer (&p2, "test_core_quota_peer2.conf"); + } + else if (test == ASYMMETRIC_SEND_LIMITED) + { + setup_peer (&p1, "test_core_quota_asymmetric_send_limit_peer1.conf"); + setup_peer (&p2, "test_core_quota_asymmetric_send_limit_peer2.conf"); + } + else if (test == ASYMMETRIC_RECV_LIMITED) + { + setup_peer (&p1, "test_core_quota_asymmetric_recv_limited_peer1.conf"); + setup_peer (&p2, "test_core_quota_asymmetric_recv_limited_peer2.conf"); + } + + GNUNET_assert (test != -1); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONFIGURATION_get_value_size (p1.cfg, "ATS", + "WAN_QUOTA_IN", + ¤t_quota_p1_in)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONFIGURATION_get_value_size (p2.cfg, "ATS", + "WAN_QUOTA_IN", + ¤t_quota_p2_in)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONFIGURATION_get_value_size (p1.cfg, "ATS", + "WAN_QUOTA_OUT", + ¤t_quota_p1_out)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONFIGURATION_get_value_size (p2.cfg, "ATS", + "WAN_QUOTA_OUT", + ¤t_quota_p2_out)); + + p1.ch = + GNUNET_CORE_connect (p1.cfg, 1, &p1, &init_notify, &connect_notify, + &disconnect_notify, &inbound_notify, GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + +static int +check () +{ + + + char *const argv[] = { "test-core-quota-compliance", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-core-quota-compliance", "nohelp", options, &run, + &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + test = -1; + if (strstr (argv[0], "_symmetric") != NULL) + { + test = SYMMETRIC; + } + else if (strstr (argv[0], "_asymmetric_send") != NULL) + { + test = ASYMMETRIC_SEND_LIMITED; + } + else if (strstr (argv[0], "_asymmetric_recv") != NULL) + { + test = ASYMMETRIC_RECV_LIMITED; + } + GNUNET_assert (test != -1); + if (test == SYMMETRIC) + { + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-quota-sym-peer-1/"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-quota-sym-peer-2/"); + } + else if (test == ASYMMETRIC_SEND_LIMITED) + { + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-send-lim-peer-1/"); + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-send-lim-peer-2/"); + } + else if (test == ASYMMETRIC_RECV_LIMITED) + { + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-recv-lim-peer-1/"); + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-recv-lim-peer-2/"); + } + + GNUNET_log_setup ("test-core-quota-compliance", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + if (test == SYMMETRIC) + { + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-quota-sym-peer-1/"); + GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-quota-sym-peer-2/"); + } + else if (test == ASYMMETRIC_SEND_LIMITED) + { + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-send-lim-peer-1/"); + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-send-lim-peer-2/"); + } + else if (test == ASYMMETRIC_RECV_LIMITED) + { + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-recv-lim-peer-1/"); + GNUNET_DISK_directory_remove + ("/tmp/test-gnunet-core-quota-asym-recv-lim-peer-2/"); + } + + + + return ret; +} + +/* end of test_core_api_reliability.c */ diff --git a/src/core/test_core_quota_peer1.conf b/src/core/test_core_quota_peer1.conf new file mode 100644 index 0000000..4ff2ee4 --- /dev/null +++ b/src/core/test_core_quota_peer1.conf @@ -0,0 +1,40 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-sym-peer-1/ +DEFAULTCONFIG = test_core_quota_peer1.conf + +[transport-tcp] +PORT = 12468 + +[arm] +PORT = 12476 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-arm.sock + +[statistics] +PORT = 12477 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-statistics.sock + +[resolver] +PORT = 12474 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-resolver.sock + +[peerinfo] +PORT = 12479 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-peerinfo.sock + +[transport] +PORT = 12475 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-transport.sock + +[ats] +WAN_QUOTA_IN = 10240 +WAN_QUOTA_OUT = 10240 + +[core] +PORT = 12480 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-core.sock +DEBUG = NO + +[ats] +PORT = 12481 +UNIXPATH = /tmp/gnunet-core-sym-p1-service-ats.sock diff --git a/src/core/test_core_quota_peer2.conf b/src/core/test_core_quota_peer2.conf new file mode 100644 index 0000000..61e03fb --- /dev/null +++ b/src/core/test_core_quota_peer2.conf @@ -0,0 +1,38 @@ +@INLINE@ test_core_defaults.conf +[PATHS] +SERVICEHOME = /tmp/test-gnunet-core-quota-sym-peer-2/ +DEFAULTCONFIG = test_core_quota_peer2.conf + +[transport-tcp] +PORT = 22478 + +[arm] +PORT = 22476 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-arm.sock + +[statistics] +PORT = 22477 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-statistics.sock + +[resolver] +PORT = 22474 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-resolver.sock + +[peerinfo] +PORT = 22479 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-peerinfo.sock + +[transport] +PORT = 22475 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-transport.sock + +[core] +PORT = 22480 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-core.sock +DEBUG = NO + +[ats] +PORT = 22482 +UNIXPATH = /tmp/gnunet-core-sym-p2-service-ats.sock +WAN_QUOTA_IN = 10 kiB +WAN_QUOTA_OUT = 10 kiB |