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/dht |
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/dht')
38 files changed, 13513 insertions, 0 deletions
diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am new file mode 100644 index 0000000..93880c2 --- /dev/null +++ b/src/dht/Makefile.am @@ -0,0 +1,204 @@ +INCLUDES = -I$(top_srcdir)/src/include +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -liconv -lstdc++ -lcomdlg32 -lgdi32 +endif + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +pkgcfg_DATA = \ + dht.conf + +if HAVE_ZLIB + ZLIB_LNK = -lz +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +lib_LTLIBRARIES = \ + libgnunetdht.la + +libgnunetdht_la_SOURCES = \ + dht_api.c dht.h +libgnunetdht_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(XLIB) +libgnunetdht_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 1:0:1 + + +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_dht.la + +libgnunet_plugin_block_dht_la_SOURCES = \ + plugin_block_dht.c +libgnunet_plugin_block_dht_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_block_dht_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_block_dht_la_DEPENDENCIES = \ + $(top_builddir)/src/block/libgnunetblock.la + + + +bin_PROGRAMS = \ + gnunet-service-dht \ + gnunet-dht-get \ + gnunet-dht-put + +gnunet_service_dht_SOURCES = \ + gnunet-service-dht.c gnunet-service-dht.h \ + gnunet-service-dht_clients.c gnunet-service-dht_clients.h \ + gnunet-service-dht_datacache.c gnunet-service-dht_datacache.h \ + gnunet-service-dht_hello.c gnunet-service-dht_hello.h \ + gnunet-service-dht_nse.c gnunet-service-dht_nse.h \ + gnunet-service-dht_neighbours.c gnunet-service-dht_neighbours.h \ + gnunet-service-dht_routing.c gnunet-service-dht_routing.h +gnunet_service_dht_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/ats/libgnunetats.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/datacache/libgnunetdatacache.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + -lm + +gnunet_dht_get_SOURCES = \ + gnunet-dht-get.c +gnunet_dht_get_LDADD = \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la +gnunet_dht_get_DEPENDENCIES = \ + libgnunetdht.la + +gnunet_dht_put_SOURCES = \ + gnunet-dht-put.c +gnunet_dht_put_LDADD = \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la +gnunet_dht_put_DEPENDENCIES = \ + libgnunetdht.la + +check_PROGRAMS = \ + test_dht_api \ + test_dht_twopeer \ + test_dht_twopeer_put_get \ + test_dht_twopeer_get_put \ + test_dht_twopeer_path_tracking \ + test_dht_multipeer \ + test_dht_line \ + test_dht_2dtorus \ + test_dht_monitor + +if ENABLE_TEST_RUN +TESTS = test_dht_api $(check_SCRIPTS) \ + test_dht_twopeer \ + test_dht_twopeer_put_get \ + test_dht_twopeer_get_put \ + test_dht_twopeer_path_tracking \ + test_dht_multipeer \ + test_dht_line \ + test_dht_2dtorus \ + test_dht_monitor +endif + +test_dht_api_SOURCES = \ + test_dht_api.c +test_dht_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_api_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_twopeer_SOURCES = \ + test_dht_twopeer.c +test_dht_twopeer_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_twopeer_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_twopeer_put_get_SOURCES = \ + test_dht_twopeer_put_get.c +test_dht_twopeer_put_get_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_twopeer_get_put_SOURCES = \ + test_dht_twopeer_get_put.c +test_dht_twopeer_get_put_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_twopeer_path_tracking_SOURCES = \ + test_dht_twopeer_path_tracking.c +test_dht_twopeer_path_tracking_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_multipeer_SOURCES = \ + test_dht_multipeer.c +test_dht_multipeer_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_multipeer_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_2dtorus_SOURCES = \ + test_dht_topo.c +test_dht_2dtorus_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_2dtorus_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_line_SOURCES = \ + test_dht_topo.c +test_dht_line_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_line_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_monitor_SOURCES = test_dht_monitor.c +test_dht_monitor_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +test_dht_monitor_DEPENDENCIES = \ + libgnunetdht.la + +EXTRA_DIST = \ + $(check_SCRIPTS) \ + test_dht_api_data.conf \ + test_dht_api_peer1.conf \ + test_dht_twopeer_data.conf \ + test_dht_multipeer_data.conf \ + test_dht_2dtorus.conf \ + test_dht_line.conf \ + multipeer_topo.dat + +check_SCRIPTS = \ + test_dht_tools.sh diff --git a/src/dht/Makefile.in b/src/dht/Makefile.in new file mode 100644 index 0000000..97e9c59 --- /dev/null +++ b/src/dht/Makefile.in @@ -0,0 +1,1197 @@ +# 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-dht$(EXEEXT) gnunet-dht-get$(EXEEXT) \ + gnunet-dht-put$(EXEEXT) +check_PROGRAMS = test_dht_api$(EXEEXT) test_dht_twopeer$(EXEEXT) \ + test_dht_twopeer_put_get$(EXEEXT) \ + test_dht_twopeer_get_put$(EXEEXT) \ + test_dht_twopeer_path_tracking$(EXEEXT) \ + test_dht_multipeer$(EXEEXT) test_dht_line$(EXEEXT) \ + test_dht_2dtorus$(EXEEXT) test_dht_monitor$(EXEEXT) +@ENABLE_TEST_RUN_TRUE@TESTS = test_dht_api$(EXEEXT) $(check_SCRIPTS) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_twopeer$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_twopeer_put_get$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_twopeer_get_put$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_twopeer_path_tracking$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_multipeer$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_line$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_2dtorus$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_dht_monitor$(EXEEXT) +subdir = src/dht +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/dht.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 = dht.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)$(plugindir)" \ + "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(plugin_LTLIBRARIES) +am_libgnunet_plugin_block_dht_la_OBJECTS = plugin_block_dht.lo +libgnunet_plugin_block_dht_la_OBJECTS = \ + $(am_libgnunet_plugin_block_dht_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libgnunet_plugin_block_dht_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libgnunet_plugin_block_dht_la_LDFLAGS) $(LDFLAGS) -o $@ +am__DEPENDENCIES_1 = +libgnunetdht_la_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(am__DEPENDENCIES_1) +am_libgnunetdht_la_OBJECTS = dht_api.lo +libgnunetdht_la_OBJECTS = $(am_libgnunetdht_la_OBJECTS) +libgnunetdht_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgnunetdht_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_gnunet_dht_get_OBJECTS = gnunet-dht-get.$(OBJEXT) +gnunet_dht_get_OBJECTS = $(am_gnunet_dht_get_OBJECTS) +am_gnunet_dht_put_OBJECTS = gnunet-dht-put.$(OBJEXT) +gnunet_dht_put_OBJECTS = $(am_gnunet_dht_put_OBJECTS) +am_gnunet_service_dht_OBJECTS = gnunet-service-dht.$(OBJEXT) \ + gnunet-service-dht_clients.$(OBJEXT) \ + gnunet-service-dht_datacache.$(OBJEXT) \ + gnunet-service-dht_hello.$(OBJEXT) \ + gnunet-service-dht_nse.$(OBJEXT) \ + gnunet-service-dht_neighbours.$(OBJEXT) \ + gnunet-service-dht_routing.$(OBJEXT) +gnunet_service_dht_OBJECTS = $(am_gnunet_service_dht_OBJECTS) +gnunet_service_dht_DEPENDENCIES = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/ats/libgnunetats.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/datacache/libgnunetdatacache.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_dht_2dtorus_OBJECTS = test_dht_topo.$(OBJEXT) +test_dht_2dtorus_OBJECTS = $(am_test_dht_2dtorus_OBJECTS) +am_test_dht_api_OBJECTS = test_dht_api.$(OBJEXT) +test_dht_api_OBJECTS = $(am_test_dht_api_OBJECTS) +am_test_dht_line_OBJECTS = test_dht_topo.$(OBJEXT) +test_dht_line_OBJECTS = $(am_test_dht_line_OBJECTS) +am_test_dht_monitor_OBJECTS = test_dht_monitor.$(OBJEXT) +test_dht_monitor_OBJECTS = $(am_test_dht_monitor_OBJECTS) +am_test_dht_multipeer_OBJECTS = test_dht_multipeer.$(OBJEXT) +test_dht_multipeer_OBJECTS = $(am_test_dht_multipeer_OBJECTS) +am_test_dht_twopeer_OBJECTS = test_dht_twopeer.$(OBJEXT) +test_dht_twopeer_OBJECTS = $(am_test_dht_twopeer_OBJECTS) +am_test_dht_twopeer_get_put_OBJECTS = \ + test_dht_twopeer_get_put.$(OBJEXT) +test_dht_twopeer_get_put_OBJECTS = \ + $(am_test_dht_twopeer_get_put_OBJECTS) +test_dht_twopeer_get_put_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +am_test_dht_twopeer_path_tracking_OBJECTS = \ + test_dht_twopeer_path_tracking.$(OBJEXT) +test_dht_twopeer_path_tracking_OBJECTS = \ + $(am_test_dht_twopeer_path_tracking_OBJECTS) +test_dht_twopeer_path_tracking_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la +am_test_dht_twopeer_put_get_OBJECTS = \ + test_dht_twopeer_put_get.$(OBJEXT) +test_dht_twopeer_put_get_OBJECTS = \ + $(am_test_dht_twopeer_put_get_OBJECTS) +test_dht_twopeer_put_get_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.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 = $(libgnunet_plugin_block_dht_la_SOURCES) \ + $(libgnunetdht_la_SOURCES) $(gnunet_dht_get_SOURCES) \ + $(gnunet_dht_put_SOURCES) $(gnunet_service_dht_SOURCES) \ + $(test_dht_2dtorus_SOURCES) $(test_dht_api_SOURCES) \ + $(test_dht_line_SOURCES) $(test_dht_monitor_SOURCES) \ + $(test_dht_multipeer_SOURCES) $(test_dht_twopeer_SOURCES) \ + $(test_dht_twopeer_get_put_SOURCES) \ + $(test_dht_twopeer_path_tracking_SOURCES) \ + $(test_dht_twopeer_put_get_SOURCES) +DIST_SOURCES = $(libgnunet_plugin_block_dht_la_SOURCES) \ + $(libgnunetdht_la_SOURCES) $(gnunet_dht_get_SOURCES) \ + $(gnunet_dht_put_SOURCES) $(gnunet_service_dht_SOURCES) \ + $(test_dht_2dtorus_SOURCES) $(test_dht_api_SOURCES) \ + $(test_dht_line_SOURCES) $(test_dht_monitor_SOURCES) \ + $(test_dht_multipeer_SOURCES) $(test_dht_twopeer_SOURCES) \ + $(test_dht_twopeer_get_put_SOURCES) \ + $(test_dht_twopeer_path_tracking_SOURCES) \ + $(test_dht_twopeer_put_get_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 +@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -liconv -lstdc++ -lcomdlg32 -lgdi32 +plugindir = $(libdir)/gnunet +pkgcfgdir = $(pkgdatadir)/config.d/ +pkgcfg_DATA = \ + dht.conf + +@HAVE_ZLIB_TRUE@ZLIB_LNK = -lz +@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0 +@USE_COVERAGE_TRUE@XLIB = -lgcov +lib_LTLIBRARIES = \ + libgnunetdht.la + +libgnunetdht_la_SOURCES = \ + dht_api.c dht.h + +libgnunetdht_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(XLIB) + +libgnunetdht_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 1:0:1 + +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_dht.la + +libgnunet_plugin_block_dht_la_SOURCES = \ + plugin_block_dht.c + +libgnunet_plugin_block_dht_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunet_plugin_block_dht_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_plugin_block_dht_la_DEPENDENCIES = \ + $(top_builddir)/src/block/libgnunetblock.la + +gnunet_service_dht_SOURCES = \ + gnunet-service-dht.c gnunet-service-dht.h \ + gnunet-service-dht_clients.c gnunet-service-dht_clients.h \ + gnunet-service-dht_datacache.c gnunet-service-dht_datacache.h \ + gnunet-service-dht_hello.c gnunet-service-dht_hello.h \ + gnunet-service-dht_nse.c gnunet-service-dht_nse.h \ + gnunet-service-dht_neighbours.c gnunet-service-dht_neighbours.h \ + gnunet-service-dht_routing.c gnunet-service-dht_routing.h + +gnunet_service_dht_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/ats/libgnunetats.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/datacache/libgnunetdatacache.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + -lm + +gnunet_dht_get_SOURCES = \ + gnunet-dht-get.c + +gnunet_dht_get_LDADD = \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la + +gnunet_dht_get_DEPENDENCIES = \ + libgnunetdht.la + +gnunet_dht_put_SOURCES = \ + gnunet-dht-put.c + +gnunet_dht_put_LDADD = \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la + +gnunet_dht_put_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_api_SOURCES = \ + test_dht_api.c + +test_dht_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_api_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_twopeer_SOURCES = \ + test_dht_twopeer.c + +test_dht_twopeer_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_twopeer_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_twopeer_put_get_SOURCES = \ + test_dht_twopeer_put_get.c + +test_dht_twopeer_put_get_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_twopeer_get_put_SOURCES = \ + test_dht_twopeer_get_put.c + +test_dht_twopeer_get_put_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_twopeer_path_tracking_SOURCES = \ + test_dht_twopeer_path_tracking.c + +test_dht_twopeer_path_tracking_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_multipeer_SOURCES = \ + test_dht_multipeer.c + +test_dht_multipeer_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_multipeer_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_2dtorus_SOURCES = \ + test_dht_topo.c + +test_dht_2dtorus_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_2dtorus_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_line_SOURCES = \ + test_dht_topo.c + +test_dht_line_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_line_DEPENDENCIES = \ + libgnunetdht.la + +test_dht_monitor_SOURCES = test_dht_monitor.c +test_dht_monitor_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/dht/libgnunetdht.la + +test_dht_monitor_DEPENDENCIES = \ + libgnunetdht.la + +EXTRA_DIST = \ + $(check_SCRIPTS) \ + test_dht_api_data.conf \ + test_dht_api_peer1.conf \ + test_dht_twopeer_data.conf \ + test_dht_multipeer_data.conf \ + test_dht_2dtorus.conf \ + test_dht_line.conf \ + multipeer_topo.dat + +check_SCRIPTS = \ + test_dht_tools.sh + +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/dht/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/dht/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): +dht.conf: $(top_builddir)/config.status $(srcdir)/dht.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 +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)" + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || 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)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_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 +libgnunet_plugin_block_dht.la: $(libgnunet_plugin_block_dht_la_OBJECTS) $(libgnunet_plugin_block_dht_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgnunet_plugin_block_dht_la_LINK) -rpath $(plugindir) $(libgnunet_plugin_block_dht_la_OBJECTS) $(libgnunet_plugin_block_dht_la_LIBADD) $(LIBS) +libgnunetdht.la: $(libgnunetdht_la_OBJECTS) $(libgnunetdht_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgnunetdht_la_LINK) -rpath $(libdir) $(libgnunetdht_la_OBJECTS) $(libgnunetdht_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-dht-get$(EXEEXT): $(gnunet_dht_get_OBJECTS) $(gnunet_dht_get_DEPENDENCIES) + @rm -f gnunet-dht-get$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_dht_get_OBJECTS) $(gnunet_dht_get_LDADD) $(LIBS) +gnunet-dht-put$(EXEEXT): $(gnunet_dht_put_OBJECTS) $(gnunet_dht_put_DEPENDENCIES) + @rm -f gnunet-dht-put$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_dht_put_OBJECTS) $(gnunet_dht_put_LDADD) $(LIBS) +gnunet-service-dht$(EXEEXT): $(gnunet_service_dht_OBJECTS) $(gnunet_service_dht_DEPENDENCIES) + @rm -f gnunet-service-dht$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_service_dht_OBJECTS) $(gnunet_service_dht_LDADD) $(LIBS) +test_dht_2dtorus$(EXEEXT): $(test_dht_2dtorus_OBJECTS) $(test_dht_2dtorus_DEPENDENCIES) + @rm -f test_dht_2dtorus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_2dtorus_OBJECTS) $(test_dht_2dtorus_LDADD) $(LIBS) +test_dht_api$(EXEEXT): $(test_dht_api_OBJECTS) $(test_dht_api_DEPENDENCIES) + @rm -f test_dht_api$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_api_OBJECTS) $(test_dht_api_LDADD) $(LIBS) +test_dht_line$(EXEEXT): $(test_dht_line_OBJECTS) $(test_dht_line_DEPENDENCIES) + @rm -f test_dht_line$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_line_OBJECTS) $(test_dht_line_LDADD) $(LIBS) +test_dht_monitor$(EXEEXT): $(test_dht_monitor_OBJECTS) $(test_dht_monitor_DEPENDENCIES) + @rm -f test_dht_monitor$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_monitor_OBJECTS) $(test_dht_monitor_LDADD) $(LIBS) +test_dht_multipeer$(EXEEXT): $(test_dht_multipeer_OBJECTS) $(test_dht_multipeer_DEPENDENCIES) + @rm -f test_dht_multipeer$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_multipeer_OBJECTS) $(test_dht_multipeer_LDADD) $(LIBS) +test_dht_twopeer$(EXEEXT): $(test_dht_twopeer_OBJECTS) $(test_dht_twopeer_DEPENDENCIES) + @rm -f test_dht_twopeer$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_twopeer_OBJECTS) $(test_dht_twopeer_LDADD) $(LIBS) +test_dht_twopeer_get_put$(EXEEXT): $(test_dht_twopeer_get_put_OBJECTS) $(test_dht_twopeer_get_put_DEPENDENCIES) + @rm -f test_dht_twopeer_get_put$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_twopeer_get_put_OBJECTS) $(test_dht_twopeer_get_put_LDADD) $(LIBS) +test_dht_twopeer_path_tracking$(EXEEXT): $(test_dht_twopeer_path_tracking_OBJECTS) $(test_dht_twopeer_path_tracking_DEPENDENCIES) + @rm -f test_dht_twopeer_path_tracking$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_twopeer_path_tracking_OBJECTS) $(test_dht_twopeer_path_tracking_LDADD) $(LIBS) +test_dht_twopeer_put_get$(EXEEXT): $(test_dht_twopeer_put_get_OBJECTS) $(test_dht_twopeer_put_get_DEPENDENCIES) + @rm -f test_dht_twopeer_put_get$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_dht_twopeer_put_get_OBJECTS) $(test_dht_twopeer_put_get_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dht_api.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-dht-get.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-dht-put.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_clients.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_datacache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_hello.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_neighbours.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_nse.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-dht_routing.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_block_dht.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_monitor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_multipeer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_topo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_twopeer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_twopeer_get_put.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_twopeer_path_tracking.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dht_twopeer_put_get.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) $(check_SCRIPTS) + $(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)$(plugindir)" "$(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 clean-pluginLTLIBRARIES \ + 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-pluginLTLIBRARIES + +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 uninstall-pluginLTLIBRARIES + +.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 clean-pluginLTLIBRARIES \ + 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-pluginLTLIBRARIES 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 uninstall-pluginLTLIBRARIES + + +# 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/dht/dht.conf.in b/src/dht/dht.conf.in new file mode 100644 index 0000000..17c13e9 --- /dev/null +++ b/src/dht/dht.conf.in @@ -0,0 +1,39 @@ +[dht] +AUTOSTART = YES +@UNIXONLY@ PORT = 2095 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-dht +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +BUCKET_SIZE = 4 +UNIXPATH = /tmp/gnunet-service-dht.sock +# This could be relaxed... +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES +# DISABLE_SOCKET_FORWARDING = NO +# DEBUG = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = +# DO_FIND_PEER = +# STRICT_KADEMLIA = +# USE_MAX_HOPS = +# MAX_HOPS = +# REPUBLISH = YES +# REPLICATION_FREQUENCY = 60 +# STOP_ON_CLOSEST = +# STOP_FOUND = +# CONVERGE_MODIFIER = + + +[dhtcache] +DATABASE = sqlite +QUOTA = 1 MB + diff --git a/src/dht/dht.h b/src/dht/dht.h new file mode 100644 index 0000000..9894be8 --- /dev/null +++ b/src/dht/dht.h @@ -0,0 +1,261 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2009, 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. +*/ + +/** + * @author Christian Grothoff + * @author Nathan Evans + * @file dht/dht.h + */ + +#ifndef DHT_H +#define DHT_H + + +/** + * Size of the bloom filter the DHT uses to filter peers. + */ +#define DHT_BLOOM_SIZE 128 + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message which indicates the DHT should cancel outstanding + * requests and discard any state. + */ +struct GNUNET_DHT_ClientGetStopMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_GET_STOP + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Unique ID identifying this request + */ + uint64_t unique_id GNUNET_PACKED; + + /** + * Key of this request + */ + GNUNET_HashCode key; + +}; + + +/** + * DHT GET message sent from clients to service. Indicates that a GET + * request should be issued. + */ +struct GNUNET_DHT_ClientGetMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET + */ + struct GNUNET_MessageHeader header; + + /** + * Message options, actually an 'enum GNUNET_DHT_RouteOption' value. + */ + uint32_t options GNUNET_PACKED; + + /** + * Replication level for this message + */ + uint32_t desired_replication_level GNUNET_PACKED; + + /** + * The type for the data for the GET request; actually an 'enum + * GNUNET_BLOCK_Type'. + */ + uint32_t type; + + /** + * The key to search for + */ + GNUNET_HashCode key; + + /** + * Unique ID identifying this request, if 0 then + * the client will not expect a response + */ + uint64_t unique_id GNUNET_PACKED; + + /* Possibly followed by xquery, copied to end of this dealy do */ + +}; + + +/** + * Reply to a GET send from the service to a client. + */ +struct GNUNET_DHT_ClientResultMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT + */ + struct GNUNET_MessageHeader header; + + /** + * The type for the data. + */ + uint32_t type; + + /** + * Number of peers recorded in the outgoing path from source to the + * storgage location of this message. + */ + uint32_t put_path_length GNUNET_PACKED; + + /** + * The number of peer identities recorded from the storage location + * to this peer. + */ + uint32_t get_path_length GNUNET_PACKED; + + /** + * Unique ID of the matching GET request. + */ + uint64_t unique_id GNUNET_PACKED; + + /** + * When does this entry expire? + */ + struct GNUNET_TIME_AbsoluteNBO expiration; + + /** + * The key that was searched for + */ + GNUNET_HashCode key; + + /* put path, get path and actual data are copied to end of this dealy do */ + +}; + + +/** + * Message to insert data into the DHT, sent from clients to DHT service. + */ +struct GNUNET_DHT_ClientPutMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT + */ + struct GNUNET_MessageHeader header; + + /** + * The type of data to insert. + */ + uint32_t type GNUNET_PACKED; + + /** + * Message options, actually an 'enum GNUNET_DHT_RouteOption' value. + */ + uint32_t options GNUNET_PACKED; + + /** + * Replication level for this message + */ + uint32_t desired_replication_level GNUNET_PACKED; + + /** + * How long should this data persist? + */ + struct GNUNET_TIME_AbsoluteNBO expiration; + + /** + * The key to store the value under. + */ + GNUNET_HashCode key; + + /* DATA copied to end of this message */ + +}; + + +/** + * Message to monitor requests going through peer, clients <--> DHT service. + */ +struct GNUNET_DHT_MonitorMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_MONITOR_{GET, PUT, GET_RESP, PUT_RESP*} + * (*) not yet implemented, necessary for key randomization + */ + struct GNUNET_MessageHeader header; + + /** + * The type of data in the request. + */ + uint32_t type GNUNET_PACKED; + + /** + * Message options, actually an 'enum GNUNET_DHT_RouteOption' value. + */ + uint32_t options GNUNET_PACKED; + + /** + * Replication level for this message + */ + uint32_t desired_replication_level GNUNET_PACKED; + + /** + * Number of peers recorded in the outgoing path from source to the + * storgage location of this message. + */ + uint32_t put_path_length GNUNET_PACKED; + + /** + * The number of peer identities recorded from the storage location + * to this peer. + */ + uint32_t get_path_length GNUNET_PACKED; + + /** + * Unique ID for GET / GET responses. + */ + uint64_t unique_id GNUNET_PACKED; + + /** + * How long should this data persist? + */ + struct GNUNET_TIME_AbsoluteNBO expiration; + + /** + * The key to store the value under. + */ + GNUNET_HashCode key; + + /* put path (if tracked) */ + + /* get path (if tracked) */ + + /* Payload */ + +}; + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/dht/dht_api.c b/src/dht/dht_api.c new file mode 100644 index 0000000..3cb13b4 --- /dev/null +++ b/src/dht/dht_api.c @@ -0,0 +1,997 @@ +/* + 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 dht/dht_api.c + * @brief library to access the DHT service + * @author Christian Grothoff + * @author Nathan Evans + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_dht_service.h" +#include "dht.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "dht-api",__VA_ARGS__) + +/** + * Entry in our list of messages to be (re-)transmitted. + */ +struct PendingMessage +{ + /** + * This is a doubly-linked list. + */ + struct PendingMessage *prev; + + /** + * This is a doubly-linked list. + */ + struct PendingMessage *next; + + /** + * Message that is pending, allocated at the end + * of this struct. + */ + const struct GNUNET_MessageHeader *msg; + + /** + * Handle to the DHT API context. + */ + struct GNUNET_DHT_Handle *handle; + + /** + * Continuation to call when the request has been + * transmitted (for the first time) to the service; can be NULL. + */ + GNUNET_SCHEDULER_Task cont; + + /** + * Closure for 'cont'. + */ + void *cont_cls; + + /** + * Timeout task for this message + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * Unique ID for this request + */ + uint64_t unique_id; + + /** + * Free the saved message once sent, set to GNUNET_YES for messages + * that do not receive responses; GNUNET_NO if this pending message + * is aliased from a 'struct GNUNET_DHT_RouteHandle' and will be freed + * from there. + */ + int free_on_send; + + /** + * GNUNET_YES if this message is in our pending queue right now. + */ + int in_pending_queue; + +}; + + +/** + * Handle to a GET request + */ +struct GNUNET_DHT_GetHandle +{ + + /** + * Iterator to call on data receipt + */ + GNUNET_DHT_GetIterator iter; + + /** + * Closure for the iterator callback + */ + void *iter_cls; + + /** + * Main handle to this DHT api + */ + struct GNUNET_DHT_Handle *dht_handle; + + /** + * The actual message sent for this request, + * used for retransmitting requests on service + * failure/reconnect. Freed on route_stop. + */ + struct PendingMessage *message; + + /** + * Key that this get request is for + */ + GNUNET_HashCode key; + + /** + * Unique identifier for this request (for key collisions). + */ + uint64_t unique_id; + +}; + + +/** + * Handle to a monitoring request. + */ +struct GNUNET_DHT_MonitorHandle +{ + /** + * DLL. + */ + struct GNUNET_DHT_MonitorHandle *next; + + /** + * DLL. + */ + struct GNUNET_DHT_MonitorHandle *prev; + + /** + * Main handle to this DHT api. + */ + struct GNUNET_DHT_Handle *dht_handle; + + /** + * Type of block looked for. + */ + enum GNUNET_BLOCK_Type type; + + /** + * Key being looked for, NULL == all. + */ + GNUNET_HashCode *key; + + /** + * Callback for each received message of interest. + */ + GNUNET_DHT_MonitorCB cb; + + /** + * Closure for cb. + */ + void *cb_cls; + +}; + + +/** + * Connection to the DHT service. + */ +struct GNUNET_DHT_Handle +{ + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Socket (if available). + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Currently pending transmission request (or NULL). + */ + struct GNUNET_CLIENT_TransmitHandle *th; + + /** + * Head of linked list of messages we would like to transmit. + */ + struct PendingMessage *pending_head; + + /** + * Tail of linked list of messages we would like to transmit. + */ + struct PendingMessage *pending_tail; + + /** + * Head of linked list of messages we would like to monitor. + */ + struct GNUNET_DHT_MonitorHandle *monitor_head; + + /** + * Tail of linked list of messages we would like to monitor. + */ + struct GNUNET_DHT_MonitorHandle *monitor_tail; + + /** + * Hash map containing the current outstanding unique requests + * (values are of type 'struct GNUNET_DHT_RouteHandle'). + */ + struct GNUNET_CONTAINER_MultiHashMap *active_requests; + + /** + * Task for trying to reconnect. + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * How quickly should we retry? Used for exponential back-off on + * connect-errors. + */ + struct GNUNET_TIME_Relative retry_time; + + /** + * Generator for unique ids. + */ + uint64_t uid_gen; + + /** + * Did we start our receive loop yet? + */ + int in_receive; +}; + + +/** + * Handler for messages received from the DHT service + * a demultiplexer which handles numerous message types + * + */ +static void +service_message_handler (void *cls, const struct GNUNET_MessageHeader *msg); + + +/** + * Try to (re)connect to the DHT service. + * + * @return GNUNET_YES on success, GNUNET_NO on failure. + */ +static int +try_connect (struct GNUNET_DHT_Handle *handle) +{ + if (handle->client != NULL) + return GNUNET_OK; + handle->in_receive = GNUNET_NO; + handle->client = GNUNET_CLIENT_connect ("dht", handle->cfg); + if (handle->client == NULL) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Failed to connect to the DHT service!\n")); + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Add the request corresponding to the given route handle + * to the pending queue (if it is not already in there). + * + * @param cls the 'struct GNUNET_DHT_Handle*' + * @param key key for the request (not used) + * @param value the 'struct GNUNET_DHT_GetHandle*' + * @return GNUNET_YES (always) + */ +static int +add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct GNUNET_DHT_Handle *handle = cls; + struct GNUNET_DHT_GetHandle *rh = value; + + if (GNUNET_NO == rh->message->in_pending_queue) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Retransmitting request related to %s to DHT %p\n", GNUNET_h2s (key), + handle); + GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail, + rh->message); + rh->message->in_pending_queue = GNUNET_YES; + } + return GNUNET_YES; +} + + +/** + * Try to send messages from list of messages to send + * @param handle DHT_Handle + */ +static void +process_pending_messages (struct GNUNET_DHT_Handle *handle); + + +/** + * Try reconnecting to the dht service. + * + * @param cls GNUNET_DHT_Handle + * @param tc scheduler context + */ +static void +try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_DHT_Handle *handle = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with DHT %p\n", handle); + handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value) + handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY; + else + handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2); + if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value) + handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT; + handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + if (GNUNET_YES != try_connect (handle)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "dht reconnect failed(!)\n"); + return; + } + GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests, + &add_request_to_pending, handle); + process_pending_messages (handle); +} + + +/** + * Try reconnecting to the DHT service. + * + * @param handle handle to dht to (possibly) disconnect and reconnect + */ +static void +do_disconnect (struct GNUNET_DHT_Handle *handle) +{ + if (handle->client == NULL) + return; + GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + if (NULL != handle->th) + GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th); + handle->th = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from DHT service, will try to reconnect in %llu ms\n", + (unsigned long long) handle->retry_time.rel_value); + GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO); + handle->client = NULL; + handle->reconnect_task = + GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle); +} + + +/** + * Transmit the next pending message, called by notify_transmit_ready + */ +static size_t +transmit_pending (void *cls, size_t size, void *buf); + + +/** + * Try to send messages from list of messages to send + */ +static void +process_pending_messages (struct GNUNET_DHT_Handle *handle) +{ + struct PendingMessage *head; + + if (handle->client == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "process_pending_messages called, but client is null, reconnecting\n"); + do_disconnect (handle); + return; + } + if (handle->th != NULL) + return; + if (NULL == (head = handle->pending_head)) + return; + handle->th = + GNUNET_CLIENT_notify_transmit_ready (handle->client, + ntohs (head->msg->size), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_YES, &transmit_pending, + handle); + if (NULL != handle->th) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "notify_transmit_ready returned NULL, reconnecting\n"); + do_disconnect (handle); +} + + +/** + * Transmit the next pending message, called by notify_transmit_ready + */ +static size_t +transmit_pending (void *cls, size_t size, void *buf) +{ + struct GNUNET_DHT_Handle *handle = cls; + struct PendingMessage *head; + size_t tsize; + + handle->th = NULL; + if (buf == NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmission to DHT service failed! Reconnecting!\n"); + do_disconnect (handle); + return 0; + } + if (NULL == (head = handle->pending_head)) + return 0; + + tsize = ntohs (head->msg->size); + if (size < tsize) + { + process_pending_messages (handle); + return 0; + } + memcpy (buf, head->msg, tsize); + GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail, + head); + head->in_pending_queue = GNUNET_NO; + if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (head->timeout_task); + head->timeout_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != head->cont) + { + head->cont (head->cont_cls, NULL); + head->cont = NULL; + head->cont_cls = NULL; + } + if (GNUNET_YES == head->free_on_send) + GNUNET_free (head); + process_pending_messages (handle); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Forwarded request of %u bytes to DHT service\n", (unsigned int) tsize); + if (GNUNET_NO == handle->in_receive) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from DHT\n"); + handle->in_receive = GNUNET_YES; + GNUNET_CLIENT_receive (handle->client, &service_message_handler, handle, + GNUNET_TIME_UNIT_FOREVER_REL); + } + return tsize; +} + + +/** + * Process a given reply that might match the given + * request. + * + * @param cls the 'struct GNUNET_DHT_ClientResultMessage' + * @param key query of the request + * @param value the 'struct GNUNET_DHT_RouteHandle' of a request matching the same key + * @return GNUNET_YES to continue to iterate over all results, + * GNUNET_NO if the reply is malformed + */ +static int +process_reply (void *cls, const GNUNET_HashCode * key, void *value) +{ + const struct GNUNET_DHT_ClientResultMessage *dht_msg = cls; + struct GNUNET_DHT_GetHandle *get_handle = value; + const struct GNUNET_PeerIdentity *put_path; + const struct GNUNET_PeerIdentity *get_path; + uint32_t put_path_length; + uint32_t get_path_length; + size_t data_length; + size_t msize; + size_t meta_length; + const void *data; + + if (dht_msg->unique_id != get_handle->unique_id) + { + /* UID mismatch */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key), + dht_msg->unique_id, get_handle->unique_id); + return GNUNET_YES; + } + msize = ntohs (dht_msg->header.size); + put_path_length = ntohl (dht_msg->put_path_length); + get_path_length = ntohl (dht_msg->get_path_length); + meta_length = + sizeof (struct GNUNET_DHT_ClientResultMessage) + + sizeof (struct GNUNET_PeerIdentity) * (get_path_length + put_path_length); + if ((msize < meta_length) || + (get_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity)) || + (put_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + return GNUNET_NO; + } + data_length = msize - meta_length; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n", + (unsigned int) data_length, GNUNET_h2s (key)); + put_path = (const struct GNUNET_PeerIdentity *) &dht_msg[1]; + get_path = &put_path[put_path_length]; + data = &get_path[get_path_length]; + get_handle->iter (get_handle->iter_cls, + GNUNET_TIME_absolute_ntoh (dht_msg->expiration), key, + get_path, get_path_length, put_path, put_path_length, + ntohl (dht_msg->type), data_length, data); + return GNUNET_YES; +} + + +/** + * Process a monitoring message from the service. + * + * @param handle The DHT handle. + * @param msg Message from the service. + * + * @return GNUNET_OK if everything went fine, + * GNUNET_SYSERR if the message is malformed. + */ +static int +process_monitor_message (struct GNUNET_DHT_Handle *handle, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_DHT_MonitorMessage *m; + struct GNUNET_DHT_MonitorHandle *h; + size_t msize; + + if (ntohs (msg->type) < GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET || + ntohs (msg->type) > GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT) + return GNUNET_SYSERR; + msize = ntohs (msg->size); + if (msize < sizeof (struct GNUNET_DHT_MonitorMessage)) + return GNUNET_SYSERR; + + m = (struct GNUNET_DHT_MonitorMessage *) msg; + h = handle->monitor_head; + while (NULL != h) + { + if (h->type == ntohl(m->type) && + (NULL == h->key || + memcmp (h->key, &m->key, sizeof (GNUNET_HashCode)) == 0)) + { + struct GNUNET_PeerIdentity *path; + uint32_t getl; + uint32_t putl; + + path = (struct GNUNET_PeerIdentity *) &m[1]; + getl = ntohl (m->get_path_length); + putl = ntohl (m->put_path_length); + h->cb (h->cb_cls, ntohs(msg->type), + GNUNET_TIME_absolute_ntoh(m->expiration), + &m->key, + &path[getl], putl, path, getl, + ntohl (m->desired_replication_level), + ntohl (m->options), ntohl (m->type), + (void *) &path[getl + putl], + ntohs (msg->size) - + sizeof (struct GNUNET_DHT_MonitorMessage) - + sizeof (struct GNUNET_PeerIdentity) * (putl + getl)); + } + h = h->next; + } + + return GNUNET_OK; +} + +/** + * Handler for messages received from the DHT service + * a demultiplexer which handles numerous message types + * + * @param cls the 'struct GNUNET_DHT_Handle' + * @param msg the incoming message + */ +static void +service_message_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_DHT_Handle *handle = cls; + const struct GNUNET_DHT_ClientResultMessage *dht_msg; + + if (msg == NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Error receiving data from DHT service, reconnecting\n"); + do_disconnect (handle); + return; + } + if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT) + { + if (process_monitor_message (handle, msg) == GNUNET_OK) + { + GNUNET_CLIENT_receive (handle->client, &service_message_handler, handle, + GNUNET_TIME_UNIT_FOREVER_REL); + return; + } + GNUNET_break (0); + do_disconnect (handle); + return; + } + if (ntohs (msg->size) < sizeof (struct GNUNET_DHT_ClientResultMessage)) + { + GNUNET_break (0); + do_disconnect (handle); + return; + } + dht_msg = (const struct GNUNET_DHT_ClientResultMessage *) msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from DHT service %p\n", + GNUNET_h2s (&dht_msg->key), handle); + GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests, + &dht_msg->key, &process_reply, + (void *) dht_msg); + GNUNET_CLIENT_receive (handle->client, &service_message_handler, handle, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Initialize the connection with the DHT service. + * + * @param cfg configuration to use + * @param ht_len size of the internal hash table to use for + * processing multiple GET/FIND requests in parallel + * + * @return handle to the DHT service, or NULL on error + */ +struct GNUNET_DHT_Handle * +GNUNET_DHT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int ht_len) +{ + struct GNUNET_DHT_Handle *handle; + + handle = GNUNET_malloc (sizeof (struct GNUNET_DHT_Handle)); + handle->cfg = cfg; + handle->uid_gen = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len); + if (GNUNET_NO == try_connect (handle)) + { + GNUNET_DHT_disconnect (handle); + return NULL; + } + return handle; +} + + +/** + * Shutdown connection with the DHT service. + * + * @param handle handle of the DHT connection to stop + */ +void +GNUNET_DHT_disconnect (struct GNUNET_DHT_Handle *handle) +{ + struct PendingMessage *pm; + + GNUNET_assert (handle != NULL); + GNUNET_assert (0 == + GNUNET_CONTAINER_multihashmap_size (handle->active_requests)); + if (handle->th != NULL) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th); + handle->th = NULL; + } + while (NULL != (pm = handle->pending_head)) + { + GNUNET_assert (GNUNET_YES == pm->in_pending_queue); + GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail, + pm); + pm->in_pending_queue = GNUNET_NO; + GNUNET_assert (GNUNET_YES == pm->free_on_send); + if (GNUNET_SCHEDULER_NO_TASK != pm->timeout_task) + GNUNET_SCHEDULER_cancel (pm->timeout_task); + if (NULL != pm->cont) + pm->cont (pm->cont_cls, NULL); + GNUNET_free (pm); + } + if (handle->client != NULL) + { + GNUNET_CLIENT_disconnect (handle->client, GNUNET_YES); + handle->client = NULL; + } + if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (handle->reconnect_task); + GNUNET_CONTAINER_multihashmap_destroy (handle->active_requests); + GNUNET_free (handle); +} + + +/** + * Timeout for the transmission of a fire&forget-request. Clean it up. + * + * @param cls the 'struct PendingMessage' + * @param tc scheduler context + */ +static void +timeout_put_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PendingMessage *pending = cls; + struct GNUNET_DHT_Handle *handle; + + handle = pending->handle; + GNUNET_assert (GNUNET_YES == pending->in_pending_queue); + GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail, + pending); + pending->in_pending_queue = GNUNET_NO; + if (pending->cont != NULL) + pending->cont (pending->cont_cls, tc); + GNUNET_free (pending); +} + + +/** + * Perform a PUT operation storing data in the DHT. + * + * @param handle handle to DHT service + * @param key the key to store under + * @param desired_replication_level estimate of how many + * nearest peers this request should reach + * @param options routing options for this message + * @param type type of the value + * @param size number of bytes in data; must be less than 64k + * @param data the data to store + * @param exp desired expiration time for the value + * @param timeout how long to wait for transmission of this request + * @param cont continuation to call when done (transmitting request to service) + * @param cont_cls closure for cont + */ +void +GNUNET_DHT_put (struct GNUNET_DHT_Handle *handle, const GNUNET_HashCode * key, + uint32_t desired_replication_level, + enum GNUNET_DHT_RouteOption options, + enum GNUNET_BLOCK_Type type, size_t size, const char *data, + struct GNUNET_TIME_Absolute exp, + struct GNUNET_TIME_Relative timeout, GNUNET_SCHEDULER_Task cont, + void *cont_cls) +{ + struct GNUNET_DHT_ClientPutMessage *put_msg; + size_t msize; + struct PendingMessage *pending; + + msize = sizeof (struct GNUNET_DHT_ClientPutMessage) + size; + if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)) + { + GNUNET_break (0); + if (NULL != cont) + cont (cont_cls, NULL); + return; + } + pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize); + put_msg = (struct GNUNET_DHT_ClientPutMessage *) &pending[1]; + pending->msg = &put_msg->header; + pending->handle = handle; + pending->cont = cont; + pending->cont_cls = cont_cls; + pending->free_on_send = GNUNET_YES; + pending->timeout_task = + GNUNET_SCHEDULER_add_delayed (timeout, &timeout_put_request, pending); + put_msg->header.size = htons (msize); + put_msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT); + put_msg->type = htonl (type); + put_msg->options = htonl ((uint32_t) options); + put_msg->desired_replication_level = htonl (desired_replication_level); + put_msg->expiration = GNUNET_TIME_absolute_hton (exp); + put_msg->key = *key; + memcpy (&put_msg[1], data, size); + GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail, + pending); + pending->in_pending_queue = GNUNET_YES; + process_pending_messages (handle); +} + + +/** + * Perform an asynchronous GET operation on the DHT identified. See + * also "GNUNET_BLOCK_evaluate". + * + * @param handle handle to the DHT service + * @param timeout how long to wait for transmission of this request to the service + * @param type expected type of the response object + * @param key the key to look up + * @param desired_replication_level estimate of how many + nearest peers this request should reach + * @param options routing options for this message + * @param xquery extended query data (can be NULL, depending on type) + * @param xquery_size number of bytes in xquery + * @param iter function to call on each result + * @param iter_cls closure for iter + * @return handle to stop the async get + */ +struct GNUNET_DHT_GetHandle * +GNUNET_DHT_get_start (struct GNUNET_DHT_Handle *handle, + struct GNUNET_TIME_Relative timeout, + enum GNUNET_BLOCK_Type type, const GNUNET_HashCode * key, + uint32_t desired_replication_level, + enum GNUNET_DHT_RouteOption options, const void *xquery, + size_t xquery_size, GNUNET_DHT_GetIterator iter, + void *iter_cls) +{ + struct GNUNET_DHT_ClientGetMessage *get_msg; + struct GNUNET_DHT_GetHandle *get_handle; + size_t msize; + struct PendingMessage *pending; + + msize = sizeof (struct GNUNET_DHT_ClientGetMessage) + xquery_size; + if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (xquery_size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)) + { + GNUNET_break (0); + return NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending query for %s to DHT %p\n", + GNUNET_h2s (key), handle); + pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize); + get_msg = (struct GNUNET_DHT_ClientGetMessage *) &pending[1]; + pending->msg = &get_msg->header; + pending->handle = handle; + pending->free_on_send = GNUNET_NO; + get_msg->header.size = htons (msize); + get_msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET); + get_msg->options = htonl ((uint32_t) options); + get_msg->desired_replication_level = htonl (desired_replication_level); + get_msg->type = htonl (type); + get_msg->key = *key; + handle->uid_gen++; + get_msg->unique_id = handle->uid_gen; + memcpy (&get_msg[1], xquery, xquery_size); + GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail, + pending); + pending->in_pending_queue = GNUNET_YES; + get_handle = GNUNET_malloc (sizeof (struct GNUNET_DHT_GetHandle)); + get_handle->iter = iter; + get_handle->iter_cls = iter_cls; + get_handle->message = pending; + get_handle->unique_id = get_msg->unique_id; + GNUNET_CONTAINER_multihashmap_put (handle->active_requests, key, get_handle, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + process_pending_messages (handle); + return get_handle; +} + + +/** + * Stop async DHT-get. + * + * @param get_handle handle to the GET operation to stop + */ +void +GNUNET_DHT_get_stop (struct GNUNET_DHT_GetHandle *get_handle) +{ + struct GNUNET_DHT_Handle *handle; + const struct GNUNET_DHT_ClientGetMessage *get_msg; + struct GNUNET_DHT_ClientGetStopMessage *stop_msg; + struct PendingMessage *pending; + + handle = get_handle->message->handle; + get_msg = + (const struct GNUNET_DHT_ClientGetMessage *) get_handle->message->msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending STOP for %s to DHT via %p\n", + GNUNET_h2s (&get_msg->key), handle); + /* generate STOP */ + pending = + GNUNET_malloc (sizeof (struct PendingMessage) + + sizeof (struct GNUNET_DHT_ClientGetStopMessage)); + stop_msg = (struct GNUNET_DHT_ClientGetStopMessage *) &pending[1]; + pending->msg = &stop_msg->header; + pending->handle = handle; + pending->free_on_send = GNUNET_YES; + stop_msg->header.size = + htons (sizeof (struct GNUNET_DHT_ClientGetStopMessage)); + stop_msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP); + stop_msg->reserved = htonl (0); + stop_msg->unique_id = get_msg->unique_id; + stop_msg->key = get_msg->key; + GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail, + pending); + pending->in_pending_queue = GNUNET_YES; + + /* remove 'GET' from active status */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (handle->active_requests, + &get_msg->key, + get_handle)); + if (GNUNET_YES == get_handle->message->in_pending_queue) + { + GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail, + get_handle->message); + get_handle->message->in_pending_queue = GNUNET_NO; + } + GNUNET_free (get_handle->message); + GNUNET_free (get_handle); + + process_pending_messages (handle); +} + + +/** + * Start monitoring the local DHT service. + * + * @param handle Handle to the DHT service. + * @param type Type of blocks that are of interest. + * @param key Key of data of interest, NULL for all. + * @param cb Callback to process all monitored data. + * @param cb_cls Closure for cb. + * + * @return Handle to stop monitoring. + */ +struct GNUNET_DHT_MonitorHandle * +GNUNET_DHT_monitor_start (struct GNUNET_DHT_Handle *handle, + enum GNUNET_BLOCK_Type type, + const GNUNET_HashCode *key, + GNUNET_DHT_MonitorCB cb, + void *cb_cls) +{ + struct GNUNET_DHT_MonitorHandle *h; + struct GNUNET_DHT_MonitorMessage *m; + struct PendingMessage *pending; + + h = GNUNET_malloc (sizeof (struct GNUNET_DHT_MonitorHandle)); + GNUNET_CONTAINER_DLL_insert(handle->monitor_head, handle->monitor_tail, h); + + GNUNET_assert (NULL != cb); + h->cb = cb; + h->cb_cls = cb_cls; + h->type = type; + h->dht_handle = handle; + if (NULL != key) + { + h->key = GNUNET_malloc (sizeof(GNUNET_HashCode)); + memcpy (h->key, key, sizeof(GNUNET_HashCode)); + } + + pending = GNUNET_malloc (sizeof (struct GNUNET_DHT_MonitorMessage) + + sizeof (struct PendingMessage)); + m = (struct GNUNET_DHT_MonitorMessage *) &pending[1]; + pending->msg = &m->header; + pending->handle = handle; + pending->free_on_send = GNUNET_YES; + m->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET); + m->header.size = htons (sizeof (struct GNUNET_DHT_MonitorMessage)); + m->type = htonl(type); + if (NULL != key) + memcpy (&m->key, key, sizeof(GNUNET_HashCode)); + GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail, + pending); + pending->in_pending_queue = GNUNET_YES; + process_pending_messages (handle); + + return h; +} + + +/** + * Stop monitoring. + * + * @param handle The handle to the monitor request returned by monitor_start. + * + * On return get_handle will no longer be valid, caller must not use again!!! + */ +void +GNUNET_DHT_monitor_stop (struct GNUNET_DHT_MonitorHandle *handle) +{ + GNUNET_free_non_null (handle->key); + GNUNET_CONTAINER_DLL_remove (handle->dht_handle->monitor_head, + handle->dht_handle->monitor_tail, + handle); + GNUNET_free (handle); +} + + + +/* end of dht_api.c */ diff --git a/src/dht/gnunet-dht-get.c b/src/dht/gnunet-dht-get.c new file mode 100644 index 0000000..6ad4b30 --- /dev/null +++ b/src/dht/gnunet-dht-get.c @@ -0,0 +1,236 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2006, 2007, 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 dht/gnunet-dht-get.c + * @brief search for data in DHT + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_dht_service.h" + +/** + * The type of the query + */ +static unsigned int query_type; + +/** + * Desired replication level + */ +static unsigned int replication = 5; + +/** + * The key for the query + */ +static char *query_key; + +/** + * User supplied timeout value (in seconds) + */ +static unsigned long long timeout_request = 5; + +/** + * When this request should really die + */ +struct GNUNET_TIME_Absolute absolute_timeout; + +/** + * Be verbose + */ +static int verbose; + +/** + * Handle to the DHT + */ +static struct GNUNET_DHT_Handle *dht_handle; + +/** + * Global handle of the configuration + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle for the get request + */ +static struct GNUNET_DHT_GetHandle *get_handle; + +/** + * Count of results found + */ +static unsigned int result_count; + +/** + * Global status value + */ +static int ret; + + +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (dht_handle != NULL) + { + GNUNET_DHT_disconnect (dht_handle); + dht_handle = NULL; + } +} + + +static void +cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (get_handle != NULL) + { + GNUNET_DHT_get_stop (get_handle); + get_handle = NULL; + } + GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); +} + + +/** + * Iterator called on each result obtained for a DHT + * operation that expects a reply + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param get_path peers on reply path (or NULL if not recorded) + * @param get_path_length number of entries in get_path + * @param put_path peers on the PUT path (or NULL if not recorded) + * @param put_path_length number of entries in get_path + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + FPRINTF (stdout, "Result %d, type %d:\n%.*s\n", result_count, type, + (unsigned int) size, (char *) data); + result_count++; +} + + +/** + * 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) +{ + struct GNUNET_TIME_Relative timeout; + GNUNET_HashCode key; + + cfg = c; + + if (query_key == NULL) + { + if (verbose) + FPRINTF (stderr, "%s", "Must provide key for DHT GET!\n"); + ret = 1; + return; + } + + dht_handle = GNUNET_DHT_connect (cfg, 1); + + if (dht_handle == NULL) + { + if (verbose) + FPRINTF (stderr, "%s", "Couldn't connect to DHT service!\n"); + ret = 1; + return; + } + else if (verbose) + FPRINTF (stderr, "%s", "Connected to DHT service!\n"); + + if (query_type == GNUNET_BLOCK_TYPE_ANY) /* Type of data not set */ + query_type = GNUNET_BLOCK_TYPE_TEST; + + GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key); + + timeout = + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, timeout_request); + absolute_timeout = GNUNET_TIME_relative_to_absolute (timeout); + + if (verbose) + FPRINTF (stderr, "Issuing GET request for %s!\n", query_key); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining + (absolute_timeout), &cleanup_task, NULL); + get_handle = + GNUNET_DHT_get_start (dht_handle, timeout, query_type, &key, replication, + GNUNET_DHT_RO_NONE, NULL, 0, &get_result_iterator, + NULL); + +} + + +/** + * gnunet-dht-get command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'k', "key", "KEY", + gettext_noop ("the query key"), + 1, &GNUNET_GETOPT_set_string, &query_key}, + {'r', "replication", "LEVEL", + gettext_noop ("how many parallel requests (replicas) to create"), + 1, &GNUNET_GETOPT_set_uint, &replication}, + {'t', "type", "TYPE", + gettext_noop ("the type of data to look for"), + 1, &GNUNET_GETOPT_set_uint, &query_type}, + {'T', "timeout", "TIMEOUT", + gettext_noop ("how long to execute this query before giving up?"), + 1, &GNUNET_GETOPT_set_ulong, &timeout_request}, + {'V', "verbose", NULL, + gettext_noop ("be verbose (print progress information)"), + 0, &GNUNET_GETOPT_set_one, &verbose}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * Entry point for gnunet-dht-get + * + * @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_PROGRAM_run (argc, argv, "gnunet-dht-get", + gettext_noop + ("Issue a GET request to the GNUnet DHT, prints results."), + options, &run, NULL)) ? ret : 1; +} + +/* end of gnunet-dht-get.c */ diff --git a/src/dht/gnunet-dht-put.c b/src/dht/gnunet-dht-put.c new file mode 100644 index 0000000..ef5ae5e --- /dev/null +++ b/src/dht/gnunet-dht-put.c @@ -0,0 +1,207 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2006, 2007, 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 dht/gnunet-dht-put.c + * @brief search for data in DHT + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_dht_service.h" + +/** + * The type of the query + */ +static unsigned int query_type; + +/** + * The key for the query + */ +static char *query_key; + +/** + * User supplied timeout value + */ +static unsigned long long timeout_request = 5; + +/** + * User supplied expiration value + */ +static unsigned long long expiration_seconds = 3600; + +/** + * Desired replication level. + */ +static unsigned int replication = 5; + +/** + * Be verbose + */ +static int verbose; + +/** + * Handle to the DHT + */ +static struct GNUNET_DHT_Handle *dht_handle; + + +/** + * Global handle of the configuration + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Global status value + */ +static int ret; + +/** + * The data to insert into the dht + */ +static char *data; + +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (dht_handle != NULL) + { + GNUNET_DHT_disconnect (dht_handle); + dht_handle = NULL; + } +} + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +void +message_sent_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (verbose) + FPRINTF (stderr, "%s", _("PUT request sent!\n")); + GNUNET_SCHEDULER_add_now (&shutdown_task, 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 c configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + struct GNUNET_TIME_Relative timeout; + struct GNUNET_TIME_Absolute expiration; + GNUNET_HashCode key; + + cfg = c; + + if ((query_key == NULL) || (data == NULL)) + { + FPRINTF (stderr, "%s", _("Must provide KEY and DATA for DHT put!\n")); + ret = 1; + return; + } + + dht_handle = GNUNET_DHT_connect (cfg, 1); + if (dht_handle == NULL) + { + FPRINTF (stderr, _("Could not connect to %s service!\n"), "DHT"); + ret = 1; + return; + } + else if (verbose) + FPRINTF (stderr, _("Connected to %s service!\n"), "DHT"); + + if (query_type == GNUNET_BLOCK_TYPE_ANY) /* Type of data not set */ + query_type = GNUNET_BLOCK_TYPE_TEST; + + GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key); + + timeout = + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, timeout_request); + expiration = + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, + expiration_seconds)); + + if (verbose) + FPRINTF (stderr, _("Issuing put request for `%s' with data `%s'!\n"), + query_key, data); + GNUNET_DHT_put (dht_handle, &key, replication, GNUNET_DHT_RO_NONE, query_type, + strlen (data), data, expiration, timeout, &message_sent_cont, + NULL); + +} + + +/** + * gnunet-dht-put command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'d', "data", "DATA", + gettext_noop ("the data to insert under the key"), + 1, &GNUNET_GETOPT_set_string, &data}, + {'e', "expiration", "EXPIRATION", + gettext_noop ("how long to store this entry in the dht (in seconds)"), + 1, &GNUNET_GETOPT_set_ulong, &expiration_seconds}, + {'k', "key", "KEY", + gettext_noop ("the query key"), + 1, &GNUNET_GETOPT_set_string, &query_key}, + {'r', "replication", "LEVEL", + gettext_noop ("how many replicas to create"), + 1, &GNUNET_GETOPT_set_uint, &replication}, + {'t', "type", "TYPE", + gettext_noop ("the type to insert data as"), + 1, &GNUNET_GETOPT_set_uint, &query_type}, + {'T', "timeout", "TIMEOUT", + gettext_noop ("how long to execute this query before giving up?"), + 1, &GNUNET_GETOPT_set_ulong, &timeout_request}, + {'V', "verbose", NULL, + gettext_noop ("be verbose (print progress information)"), + 0, &GNUNET_GETOPT_set_one, &verbose}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * Entry point for gnunet-dht-put + * + * @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_PROGRAM_run (argc, argv, "gnunet-dht-put", + gettext_noop + ("Issue a PUT request to the GNUnet DHT insert DATA under KEY."), + options, &run, NULL)) ? ret : 1; +} + +/* end of gnunet-dht-put.c */ diff --git a/src/dht/gnunet-service-dht.c b/src/dht/gnunet-service-dht.c new file mode 100644 index 0000000..72575ac --- /dev/null +++ b/src/dht/gnunet-service-dht.c @@ -0,0 +1,190 @@ +/* + 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 dht/gnunet-service-dht.c + * @brief GNUnet DHT service + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_block_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_dht_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet-service-dht.h" +#include "gnunet-service-dht_clients.h" +#include "gnunet-service-dht_datacache.h" +#include "gnunet-service-dht_hello.h" +#include "gnunet-service-dht_neighbours.h" +#include "gnunet-service-dht_nse.h" +#include "gnunet-service-dht_routing.h" + + + +/** + * Handle for the statistics service. + */ +struct GNUNET_STATISTICS_Handle *GDS_stats; + +/** + * Our handle to the BLOCK library. + */ +struct GNUNET_BLOCK_Context *GDS_block_context; + +/** + * The configuration the DHT service is running with + */ +const struct GNUNET_CONFIGURATION_Handle *GDS_cfg; + +/** + * Our HELLO + */ +struct GNUNET_MessageHeader *GDS_my_hello; + +/** + * Handle to the transport service, for getting our hello + */ +struct GNUNET_TRANSPORT_Handle *GDS_transport_handle; + + +/** + * Handle to get our current HELLO. + */ +static struct GNUNET_TRANSPORT_GetHelloHandle *ghh; + + +/** + * Receive the HELLO from transport service, free current and replace + * if necessary. + * + * @param cls NULL + * @param message HELLO message of peer + */ +static void +process_hello (void *cls, const struct GNUNET_MessageHeader *message) +{ + GNUNET_assert (message != NULL); + GNUNET_free_non_null (GDS_my_hello); + GDS_my_hello = GNUNET_malloc (ntohs (message->size)); + memcpy (GDS_my_hello, message, ntohs (message->size)); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (NULL != ghh) + { + GNUNET_TRANSPORT_get_hello_cancel (ghh); + ghh = NULL; + } + if (GDS_transport_handle != NULL) + { + GNUNET_TRANSPORT_disconnect (GDS_transport_handle); + GDS_transport_handle = NULL; + } + GDS_NEIGHBOURS_done (); + GDS_DATACACHE_done (); + GDS_ROUTING_done (); + GDS_HELLO_done (); + GDS_NSE_done (); + if (GDS_block_context != NULL) + { + GNUNET_BLOCK_context_destroy (GDS_block_context); + GDS_block_context = NULL; + } + if (GDS_stats != NULL) + { + GNUNET_STATISTICS_destroy (GDS_stats, GNUNET_YES); + GDS_stats = NULL; + } + GNUNET_free_non_null (GDS_my_hello); + GDS_my_hello = NULL; +} + + +/** + * Process dht requests. + * + * @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) +{ + GDS_cfg = c; + GDS_block_context = GNUNET_BLOCK_context_create (GDS_cfg); + GDS_stats = GNUNET_STATISTICS_create ("dht", GDS_cfg); + GDS_ROUTING_init (); + GDS_NSE_init (); + GDS_DATACACHE_init (); + GDS_HELLO_init (); + GDS_CLIENTS_init (server); + if (GNUNET_OK != GDS_NEIGHBOURS_init ()) + { + shutdown_task (NULL, NULL); + return; + } + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, + NULL); + GDS_transport_handle = + GNUNET_TRANSPORT_connect (GDS_cfg, NULL, NULL, NULL, NULL, NULL); + if (GDS_transport_handle == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to connect to transport service!\n")); + return; + } + ghh = GNUNET_TRANSPORT_get_hello (GDS_transport_handle, &process_hello, NULL); +} + + +/** + * The main function for the dht 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) +{ + int ret; + + ret = + (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "dht", GNUNET_SERVICE_OPTION_NONE, &run, + NULL)) ? 0 : 1; + GDS_CLIENTS_done (); + return ret; +} + +/* end of gnunet-service-dht.c */ diff --git a/src/dht/gnunet-service-dht.h b/src/dht/gnunet-service-dht.h new file mode 100644 index 0000000..f06b24b --- /dev/null +++ b/src/dht/gnunet-service-dht.h @@ -0,0 +1,60 @@ +/* + 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 dht/gnunet-service-dht.h + * @brief GNUnet DHT globals + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_DHT_H +#define GNUNET_SERVICE_DHT_H + +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" + +#define DEBUG_DHT GNUNET_EXTRA_LOGGING + +/** + * Configuration we use. + */ +extern const struct GNUNET_CONFIGURATION_Handle *GDS_cfg; + +/** + * Our handle to the BLOCK library. + */ +extern struct GNUNET_BLOCK_Context *GDS_block_context; + +/** + * Handle for the statistics service. + */ +extern struct GNUNET_STATISTICS_Handle *GDS_stats; + +/** + * Our HELLO + */ +extern struct GNUNET_MessageHeader *GDS_my_hello; + +/** + * Handle to the transport service, for getting our hello + */ +extern struct GNUNET_TRANSPORT_Handle *GDS_transport_handle; + +#endif diff --git a/src/dht/gnunet-service-dht_clients.c b/src/dht/gnunet-service-dht_clients.c new file mode 100644 index 0000000..96fcd34 --- /dev/null +++ b/src/dht/gnunet-service-dht_clients.c @@ -0,0 +1,1164 @@ +/* + 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 dht/gnunet-service-dht_clients.c + * @brief GNUnet DHT service's client management code + * @author Christian Grothoff + * @author Nathan Evans + */ + +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_statistics_service.h" +#include "gnunet-service-dht.h" +#include "gnunet-service-dht_clients.h" +#include "gnunet-service-dht_datacache.h" +#include "gnunet-service-dht_neighbours.h" +#include "dht.h" + + +/** + * Linked list of messages to send to clients. + */ +struct PendingMessage +{ + /** + * Pointer to next item in the list + */ + struct PendingMessage *next; + + /** + * Pointer to previous item in the list + */ + struct PendingMessage *prev; + + /** + * Actual message to be sent, allocated at the end of the struct: + * // msg = (cast) &pm[1]; + * // memcpy (&pm[1], data, len); + */ + const struct GNUNET_MessageHeader *msg; + +}; + + +/** + * Struct containing information about a client, + * handle to connect to it, and any pending messages + * that need to be sent to it. + */ +struct ClientList +{ + /** + * Linked list of active clients + */ + struct ClientList *next; + + /** + * Linked list of active clients + */ + struct ClientList *prev; + + /** + * The handle to this client + */ + struct GNUNET_SERVER_Client *client_handle; + + /** + * Handle to the current transmission request, NULL + * if none pending. + */ + struct GNUNET_CONNECTION_TransmitHandle *transmit_handle; + + /** + * Linked list of pending messages for this client + */ + struct PendingMessage *pending_head; + + /** + * Tail of linked list of pending messages for this client + */ + struct PendingMessage *pending_tail; + +}; + + +/** + * Entry in the DHT routing table for a client's GET request. + */ +struct ClientQueryRecord +{ + + /** + * The key this request was about + */ + GNUNET_HashCode key; + + /** + * Client responsible for the request. + */ + struct ClientList *client; + + /** + * Extended query (see gnunet_block_lib.h), allocated at the end of this struct. + */ + const void *xquery; + + /** + * Replies we have already seen for this request. + */ + GNUNET_HashCode *seen_replies; + + /** + * Pointer to this nodes heap location in the retry-heap (for fast removal) + */ + struct GNUNET_CONTAINER_HeapNode *hnode; + + /** + * What's the delay between re-try operations that we currently use for this + * request? + */ + struct GNUNET_TIME_Relative retry_frequency; + + /** + * What's the next time we should re-try this request? + */ + struct GNUNET_TIME_Absolute retry_time; + + /** + * The unique identifier of this request + */ + uint64_t unique_id; + + /** + * Number of bytes in xquery. + */ + size_t xquery_size; + + /** + * Number of entries in 'seen_replies'. + */ + unsigned int seen_replies_count; + + /** + * Desired replication level + */ + uint32_t replication; + + /** + * Any message options for this request + */ + uint32_t msg_options; + + /** + * The type for the data for the GET request. + */ + enum GNUNET_BLOCK_Type type; + +}; + + +/** + * Struct containing paremeters of monitoring requests. + */ +struct ClientMonitorRecord +{ + + /** + * Next element in DLL. + */ + struct ClientMonitorRecord *next; + + /** + * Previous element in DLL. + */ + struct ClientMonitorRecord *prev; + + /** + * Type of blocks that are of interest + */ + enum GNUNET_BLOCK_Type type; + + /** + * Key of data of interest, NULL for all. + */ + GNUNET_HashCode *key; + + /** + * Client to notify of these requests. + */ + struct ClientList *client; +}; + + +/** + * List of active clients. + */ +static struct ClientList *client_head; + +/** + * List of active clients. + */ +static struct ClientList *client_tail; + +/** + * List of active monitoring requests. + */ +static struct ClientMonitorRecord *monitor_head; + +/** + * List of active monitoring requests. + */ +static struct ClientMonitorRecord *monitor_tail; + +/** + * Hashmap for fast key based lookup, maps keys to 'struct ClientQueryRecord' entries. + */ +static struct GNUNET_CONTAINER_MultiHashMap *forward_map; + +/** + * Heap with all of our client's request, sorted by retry time (earliest on top). + */ +static struct GNUNET_CONTAINER_Heap *retry_heap; + +/** + * Task that re-transmits requests (using retry_heap). + */ +static GNUNET_SCHEDULER_TaskIdentifier retry_task; + + +/** + * Find a client if it exists, add it otherwise. + * + * @param client the server handle to the client + * + * @return the client if found, a new client otherwise + */ +static struct ClientList * +find_active_client (struct GNUNET_SERVER_Client *client) +{ + struct ClientList *pos = client_head; + struct ClientList *ret; + + while (pos != NULL) + { + if (pos->client_handle == client) + return pos; + pos = pos->next; + } + ret = GNUNET_malloc (sizeof (struct ClientList)); + ret->client_handle = client; + GNUNET_CONTAINER_DLL_insert (client_head, client_tail, ret); + return ret; +} + + +/** + * Iterator over hash map entries that frees all entries + * associated with the given client. + * + * @param cls client to search for in source routes + * @param key current key code (ignored) + * @param value value in the hash map, a ClientQueryRecord + * @return GNUNET_YES (we should continue to iterate) + */ +static int +remove_client_records (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ClientList *client = cls; + struct ClientQueryRecord *record = value; + + if (record->client != client) + return GNUNET_YES; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Removing client %p's record for key %s\n", client, + GNUNET_h2s (key)); +#endif + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (forward_map, key, + record)); + if (NULL != record->hnode) + GNUNET_CONTAINER_heap_remove_node (record->hnode); + GNUNET_array_grow (record->seen_replies, record->seen_replies_count, 0); + GNUNET_free (record); + return GNUNET_YES; +} + + +/** + * Functions with this signature are called whenever a client + * is disconnected on the network level. + * + * @param cls closure (NULL for dht) + * @param client identification of the client; NULL + * for the last call when the server is destroyed + */ +static void +handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct ClientList *pos; + struct PendingMessage *reply; + struct ClientMonitorRecord *monitor; + +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Local client %p disconnects\n", client); +#endif + pos = find_active_client (client); + GNUNET_CONTAINER_DLL_remove (client_head, client_tail, pos); + if (pos->transmit_handle != NULL) + GNUNET_CONNECTION_notify_transmit_ready_cancel (pos->transmit_handle); + while (NULL != (reply = pos->pending_head)) + { + GNUNET_CONTAINER_DLL_remove (pos->pending_head, pos->pending_tail, reply); + GNUNET_free (reply); + } + monitor = monitor_head; + while (NULL != monitor) + { + if (monitor->client == pos) + { + struct ClientMonitorRecord *next; + + GNUNET_free_non_null (monitor->key); + next = monitor->next; + GNUNET_CONTAINER_DLL_remove (monitor_head, monitor_tail, monitor); + GNUNET_free (monitor); + monitor = next; + } + else + monitor = monitor->next; + } + GNUNET_CONTAINER_multihashmap_iterate (forward_map, &remove_client_records, + pos); + GNUNET_free (pos); +} + + +/** + * Route the given request via the DHT. This includes updating + * the bloom filter and retransmission times, building the P2P + * message and initiating the routing operation. + */ +static void +transmit_request (struct ClientQueryRecord *cqr) +{ + int32_t reply_bf_mutator; + struct GNUNET_CONTAINER_BloomFilter *reply_bf; + struct GNUNET_CONTAINER_BloomFilter *peer_bf; + + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# GET requests from clients injected"), 1, + GNUNET_NO); + reply_bf_mutator = + (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT32_MAX); + reply_bf = + GNUNET_BLOCK_construct_bloomfilter (reply_bf_mutator, cqr->seen_replies, + cqr->seen_replies_count); + peer_bf = + GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + GDS_NEIGHBOURS_handle_get (cqr->type, cqr->msg_options, cqr->replication, + 0 /* hop count */ , + &cqr->key, cqr->xquery, cqr->xquery_size, reply_bf, + reply_bf_mutator, peer_bf); + GNUNET_CONTAINER_bloomfilter_free (reply_bf); + GNUNET_CONTAINER_bloomfilter_free (peer_bf); + + /* exponential back-off for retries, max 1h */ + cqr->retry_frequency = + GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_HOURS, + GNUNET_TIME_relative_multiply + (cqr->retry_frequency, 2)); + cqr->retry_time = GNUNET_TIME_relative_to_absolute (cqr->retry_frequency); +} + + +/** + * Task that looks at the 'retry_heap' and transmits all of the requests + * on the heap that are ready for transmission. Then re-schedules + * itself (unless the heap is empty). + * + * @param cls unused + * @param tc scheduler context + */ +static void +transmit_next_request_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ClientQueryRecord *cqr; + struct GNUNET_TIME_Relative delay; + + retry_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + while (NULL != (cqr = GNUNET_CONTAINER_heap_remove_root (retry_heap))) + { + cqr->hnode = NULL; + delay = GNUNET_TIME_absolute_get_remaining (cqr->retry_time); + if (delay.rel_value > 0) + { + cqr->hnode = + GNUNET_CONTAINER_heap_insert (retry_heap, cqr, + cqr->retry_time.abs_value); + retry_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_next_request_task, + NULL); + return; + } + transmit_request (cqr); + cqr->hnode = + GNUNET_CONTAINER_heap_insert (retry_heap, cqr, + cqr->retry_time.abs_value); + } +} + + +/** + * Handler for PUT messages. + * + * @param cls closure for the service + * @param client the client we received this message from + * @param message the actual message received + */ +static void +handle_dht_local_put (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_DHT_ClientPutMessage *dht_msg; + struct GNUNET_CONTAINER_BloomFilter *peer_bf; + uint16_t size; + + size = ntohs (message->size); + if (size < sizeof (struct GNUNET_DHT_ClientPutMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# PUT requests received from clients"), 1, + GNUNET_NO); + dht_msg = (const struct GNUNET_DHT_ClientPutMessage *) message; + /* give to local clients */ +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling local PUT of %u-bytes for query %s\n", + size - sizeof (struct GNUNET_DHT_ClientPutMessage), + GNUNET_h2s (&dht_msg->key)); +#endif + GDS_CLIENTS_handle_reply (GNUNET_TIME_absolute_ntoh (dht_msg->expiration), + &dht_msg->key, 0, NULL, 0, NULL, + ntohl (dht_msg->type), + size - sizeof (struct GNUNET_DHT_ClientPutMessage), + &dht_msg[1]); + /* store locally */ + GDS_DATACACHE_handle_put (GNUNET_TIME_absolute_ntoh (dht_msg->expiration), + &dht_msg->key, 0, NULL, ntohl (dht_msg->type), + size - sizeof (struct GNUNET_DHT_ClientPutMessage), + &dht_msg[1]); + /* route to other peers */ + peer_bf = + GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + GDS_NEIGHBOURS_handle_put (ntohl (dht_msg->type), ntohl (dht_msg->options), + ntohl (dht_msg->desired_replication_level), + GNUNET_TIME_absolute_ntoh (dht_msg->expiration), + 0 /* hop count */ , + peer_bf, &dht_msg->key, 0, NULL, &dht_msg[1], + size - + sizeof (struct GNUNET_DHT_ClientPutMessage)); + GNUNET_CONTAINER_bloomfilter_free (peer_bf); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for any generic DHT messages, calls the appropriate handler + * depending on message type, sends confirmation if responses aren't otherwise + * expected. + * + * @param cls closure for the service + * @param client the client we received this message from + * @param message the actual message received + */ +static void +handle_dht_local_get (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_DHT_ClientGetMessage *get; + struct ClientQueryRecord *cqr; + size_t xquery_size; + const char *xquery; + uint16_t size; + + size = ntohs (message->size); + if (size < sizeof (struct GNUNET_DHT_ClientGetMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + xquery_size = size - sizeof (struct GNUNET_DHT_ClientGetMessage); + get = (const struct GNUNET_DHT_ClientGetMessage *) message; + xquery = (const char *) &get[1]; + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# GET requests received from clients"), 1, + GNUNET_NO); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request for %s from local client %p\n", + GNUNET_h2s (&get->key), client); +#endif + cqr = GNUNET_malloc (sizeof (struct ClientQueryRecord) + xquery_size); + cqr->key = get->key; + cqr->client = find_active_client (client); + cqr->xquery = (void *) &cqr[1]; + memcpy (&cqr[1], xquery, xquery_size); + cqr->hnode = GNUNET_CONTAINER_heap_insert (retry_heap, cqr, 0); + cqr->retry_frequency = GNUNET_TIME_UNIT_MILLISECONDS; + cqr->retry_time = GNUNET_TIME_absolute_get (); + cqr->unique_id = get->unique_id; + cqr->xquery_size = xquery_size; + cqr->replication = ntohl (get->desired_replication_level); + cqr->msg_options = ntohl (get->options); + cqr->type = ntohl (get->type); + GNUNET_CONTAINER_multihashmap_put (forward_map, &get->key, cqr, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + /* start remote requests */ + if (GNUNET_SCHEDULER_NO_TASK != retry_task) + GNUNET_SCHEDULER_cancel (retry_task); + retry_task = GNUNET_SCHEDULER_add_now (&transmit_next_request_task, NULL); + /* perform local lookup */ + GDS_DATACACHE_handle_get (&get->key, cqr->type, cqr->xquery, xquery_size, + NULL, 0); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Closure for 'remove_by_unique_id'. + */ +struct RemoveByUniqueIdContext +{ + /** + * Client that issued the removal request. + */ + struct ClientList *client; + + /** + * Unique ID of the request. + */ + uint64_t unique_id; +}; + + +/** + * Iterator over hash map entries that frees all entries + * that match the given client and unique ID. + * + * @param cls unique ID and client to search for in source routes + * @param key current key code + * @param value value in the hash map, a ClientQueryRecord + * @return GNUNET_YES (we should continue to iterate) + */ +static int +remove_by_unique_id (void *cls, const GNUNET_HashCode * key, void *value) +{ + const struct RemoveByUniqueIdContext *ctx = cls; + struct ClientQueryRecord *record = value; + + if (record->unique_id != ctx->unique_id) + return GNUNET_YES; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Removing client %p's record for key %s (by unique id)\n", + ctx->client->client_handle, GNUNET_h2s (key)); +#endif + return remove_client_records (ctx->client, key, record); +} + + +/** + * Handler for any generic DHT stop messages, calls the appropriate handler + * depending on message type (if processed locally) + * + * @param cls closure for the service + * @param client the client we received this message from + * @param message the actual message received + * + */ +static void +handle_dht_local_get_stop (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_DHT_ClientGetStopMessage *dht_stop_msg = + (const struct GNUNET_DHT_ClientGetStopMessage *) message; + struct RemoveByUniqueIdContext ctx; + + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# GET STOP requests received from clients"), 1, + GNUNET_NO); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p stopped request for key %s\n", + client, GNUNET_h2s (&dht_stop_msg->key)); +#endif + ctx.client = find_active_client (client); + ctx.unique_id = dht_stop_msg->unique_id; + GNUNET_CONTAINER_multihashmap_get_multiple (forward_map, &dht_stop_msg->key, + &remove_by_unique_id, &ctx); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for monitor messages + * + * @param cls closure for the service + * @param client the client we received this message from + * @param message the actual message received + * + */ +static void +handle_dht_local_monitor (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct ClientMonitorRecord *r; + const struct GNUNET_DHT_MonitorMessage *msg; + unsigned int i; + char *c; + + msg = (struct GNUNET_DHT_MonitorMessage *) message; + r = GNUNET_malloc (sizeof(struct ClientMonitorRecord)); + + r->client = find_active_client(client); + r->type = ntohl(msg->type); + c = (char *) &msg->key; + for (i = 0; i < sizeof (GNUNET_HashCode) && c[i] == 0; i++); + if (sizeof (GNUNET_HashCode) == i) + r->key = NULL; + else + { + r->key = GNUNET_malloc (sizeof (GNUNET_HashCode)); + memcpy (r->key, &msg->key, sizeof (GNUNET_HashCode)); + } + GNUNET_CONTAINER_DLL_insert (monitor_head, monitor_tail, r); + // FIXME add remove somewhere + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Task run to check for messages that need to be sent to a client. + * + * @param client a ClientList, containing the client and any messages to be sent to it + */ +static void +process_pending_messages (struct ClientList *client); + + +/** + * Callback called as a result of issuing a GNUNET_SERVER_notify_transmit_ready + * request. A ClientList is passed as closure, take the head of the list + * and copy it into buf, which has the result of sending the message to the + * client. + * + * @param cls closure to this call + * @param size maximum number of bytes available to send + * @param buf where to copy the actual message to + * + * @return the number of bytes actually copied, 0 indicates failure + */ +static size_t +send_reply_to_client (void *cls, size_t size, void *buf) +{ + struct ClientList *client = cls; + char *cbuf = buf; + struct PendingMessage *reply; + size_t off; + size_t msize; + + client->transmit_handle = NULL; + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client %p disconnected, pending messages will be discarded\n", + client->client_handle); +#endif + return 0; + } + off = 0; + while ((NULL != (reply = client->pending_head)) && + (size >= off + (msize = ntohs (reply->msg->size)))) + { + GNUNET_CONTAINER_DLL_remove (client->pending_head, client->pending_tail, + reply); + memcpy (&cbuf[off], reply->msg, msize); + GNUNET_free (reply); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u bytes to client %p\n", + msize, client->client_handle); +#endif + off += msize; + } + process_pending_messages (client); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted %u/%u bytes to client %p\n", + (unsigned int) off, (unsigned int) size, client->client_handle); +#endif + return off; +} + + +/** + * Task run to check for messages that need to be sent to a client. + * + * @param client a ClientList, containing the client and any messages to be sent to it + */ +static void +process_pending_messages (struct ClientList *client) +{ + if ((client->pending_head == NULL) || (client->transmit_handle != NULL)) + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not asking for transmission to %p now: %s\n", + client->client_handle, + client->pending_head == + NULL ? "no more messages" : "request already pending"); +#endif + return; + } +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking for transmission of %u bytes to client %p\n", + ntohs (client->pending_head->msg->size), client->client_handle); +#endif + client->transmit_handle = + GNUNET_SERVER_notify_transmit_ready (client->client_handle, + ntohs (client->pending_head-> + msg->size), + GNUNET_TIME_UNIT_FOREVER_REL, + &send_reply_to_client, client); +} + + +/** + * Add a PendingMessage to the clients list of messages to be sent + * + * @param client the active client to send the message to + * @param pending_message the actual message to send + */ +static void +add_pending_message (struct ClientList *client, + struct PendingMessage *pending_message) +{ + GNUNET_CONTAINER_DLL_insert_tail (client->pending_head, client->pending_tail, + pending_message); + process_pending_messages (client); +} + + +/** + * Closure for 'forward_reply' + */ +struct ForwardReplyContext +{ + + /** + * Actual message to send to matching clients. + */ + struct PendingMessage *pm; + + /** + * Embedded payload. + */ + const void *data; + + /** + * Type of the data. + */ + enum GNUNET_BLOCK_Type type; + + /** + * Number of bytes in data. + */ + size_t data_size; + + /** + * Do we need to copy 'pm' because it was already used? + */ + int do_copy; + +}; + + +/** + * Iterator over hash map entries that send a given reply to + * each of the matching clients. With some tricky recycling + * of the buffer. + * + * @param cls the 'struct ForwardReplyContext' + * @param key current key + * @param value value in the hash map, a ClientQueryRecord + * @return GNUNET_YES (we should continue to iterate), + * if the result is mal-formed, GNUNET_NO + */ +static int +forward_reply (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ForwardReplyContext *frc = cls; + struct ClientQueryRecord *record = value; + struct PendingMessage *pm; + struct GNUNET_DHT_ClientResultMessage *reply; + enum GNUNET_BLOCK_EvaluationResult eval; + int do_free; + GNUNET_HashCode ch; + unsigned int i; + + if ((record->type != GNUNET_BLOCK_TYPE_ANY) && (record->type != frc->type)) + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Record type missmatch, not passing request for key %s to local client\n", + GNUNET_h2s (key)); +#endif + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Key match, type mismatches in REPLY to CLIENT"), + 1, GNUNET_NO); + return GNUNET_YES; /* type mismatch */ + } + GNUNET_CRYPTO_hash (frc->data, frc->data_size, &ch); + for (i = 0; i < record->seen_replies_count; i++) + if (0 == memcmp (&record->seen_replies[i], &ch, sizeof (GNUNET_HashCode))) + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate reply, not passing request for key %s to local client\n", + GNUNET_h2s (key)); +#endif + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Duplicate REPLIES to CLIENT request dropped"), + 1, GNUNET_NO); + return GNUNET_YES; /* duplicate */ + } + eval = + GNUNET_BLOCK_evaluate (GDS_block_context, record->type, key, NULL, 0, + record->xquery, record->xquery_size, frc->data, + frc->data_size); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Evaluation result is %d for key %s for local client's query\n", + (int) eval, GNUNET_h2s (key)); +#endif + switch (eval) + { + case GNUNET_BLOCK_EVALUATION_OK_LAST: + do_free = GNUNET_YES; + break; + case GNUNET_BLOCK_EVALUATION_OK_MORE: + GNUNET_array_append (record->seen_replies, record->seen_replies_count, ch); + do_free = GNUNET_NO; + break; + case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: + /* should be impossible to encounter here */ + GNUNET_break (0); + return GNUNET_YES; + case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: + GNUNET_break_op (0); + return GNUNET_NO; + case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: + GNUNET_break (0); + return GNUNET_NO; + case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: + GNUNET_break (0); + return GNUNET_NO; + case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Unsupported block type (%u) in request!\n"), record->type); + return GNUNET_NO; + default: + GNUNET_break (0); + return GNUNET_NO; + } + if (GNUNET_NO == frc->do_copy) + { + /* first time, we can use the original data */ + pm = frc->pm; + frc->do_copy = GNUNET_YES; + } + else + { + /* two clients waiting for same reply, must copy for queueing */ + pm = GNUNET_malloc (sizeof (struct PendingMessage) + + ntohs (frc->pm->msg->size)); + memcpy (pm, frc->pm, + sizeof (struct PendingMessage) + ntohs (frc->pm->msg->size)); + pm->next = pm->prev = NULL; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# RESULTS queued for clients"), 1, + GNUNET_NO); + reply = (struct GNUNET_DHT_ClientResultMessage *) &pm[1]; + reply->unique_id = record->unique_id; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queueing reply to query %s for client %p\n", GNUNET_h2s (key), + record->client->client_handle); +#endif + add_pending_message (record->client, pm); + if (GNUNET_YES == do_free) + remove_client_records (record->client, key, record); + return GNUNET_YES; +} + + +/** + * Handle a reply we've received from another peer. If the reply + * matches any of our pending queries, forward it to the respective + * client(s). + * + * @param expiration when will the reply expire + * @param key the query this reply is for + * @param get_path_length number of peers in 'get_path' + * @param get_path path the reply took on get + * @param put_path_length number of peers in 'put_path' + * @param put_path path the reply took on put + * @param type type of the reply + * @param data_size number of bytes in 'data' + * @param data application payload data + */ +void +GDS_CLIENTS_handle_reply (struct GNUNET_TIME_Absolute expiration, + const GNUNET_HashCode * key, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + enum GNUNET_BLOCK_Type type, size_t data_size, + const void *data) +{ + struct ForwardReplyContext frc; + struct PendingMessage *pm; + struct GNUNET_DHT_ClientResultMessage *reply; + struct GNUNET_PeerIdentity *paths; + size_t msize; + + if (NULL == GNUNET_CONTAINER_multihashmap_get (forward_map, key)) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# REPLIES ignored for CLIENTS (no match)"), 1, + GNUNET_NO); + return; /* no matching request, fast exit! */ + } + msize = + sizeof (struct GNUNET_DHT_ClientResultMessage) + data_size + + (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity); + if (msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not pass reply to client, message too big!\n")); + return; + } + pm = (struct PendingMessage *) GNUNET_malloc (msize + + sizeof (struct PendingMessage)); + reply = (struct GNUNET_DHT_ClientResultMessage *) &pm[1]; + pm->msg = &reply->header; + reply->header.size = htons ((uint16_t) msize); + reply->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_RESULT); + reply->type = htonl (type); + reply->get_path_length = htonl (get_path_length); + reply->put_path_length = htonl (put_path_length); + reply->unique_id = 0; /* filled in later */ + reply->expiration = GNUNET_TIME_absolute_hton (expiration); + reply->key = *key; + paths = (struct GNUNET_PeerIdentity *) &reply[1]; + memcpy (paths, put_path, + sizeof (struct GNUNET_PeerIdentity) * put_path_length); + memcpy (&paths[put_path_length], get_path, + sizeof (struct GNUNET_PeerIdentity) * get_path_length); + memcpy (&paths[get_path_length + put_path_length], data, data_size); + frc.do_copy = GNUNET_NO; + frc.pm = pm; + frc.data = data; + frc.data_size = data_size; + frc.type = type; + GNUNET_CONTAINER_multihashmap_get_multiple (forward_map, key, &forward_reply, + &frc); + if (GNUNET_NO == frc.do_copy) + { + /* did not match any of the requests, free! */ + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# REPLIES ignored for CLIENTS (no match)"), 1, + GNUNET_NO); + GNUNET_free (pm); + } +} + + +/** + * Check if some client is monitoring messages of this type and notify + * him in that case. + * + * @param mtype Type of the DHT message. + * @param exp When will this value expire. + * @param key Key of the result/request. + * @param putl number of entries in get_path. + * @param put_path peers on the PUT path (or NULL if not recorded). + * @param getl number of entries in get_path. + * @param get_path Peers on reply path (or NULL if not recorded). + * @param desired_replication_level Desired replication level. + * @param type Type of the result/request. + * @param data Pointer to the result data. + * @param size Number of bytes in data. + */ +void +GDS_CLIENTS_process_monitor (uint16_t mtype, + const struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode *key, + uint32_t putl, + const struct GNUNET_PeerIdentity *put_path, + uint32_t getl, + const struct GNUNET_PeerIdentity *get_path, + uint32_t desired_replication_level, + enum GNUNET_BLOCK_Type type, + const struct GNUNET_MessageHeader *data, + uint16_t size) +{ + struct ClientMonitorRecord *m; + struct ClientList **cl; + unsigned int cl_size; + + cl = NULL; + cl_size = 0; + for (m = monitor_head; NULL != m; m = m->next) + { + if ((GNUNET_BLOCK_TYPE_ANY == m->type || m->type == type) && + (NULL == m->key || + memcmp (key, m->key, sizeof(GNUNET_HashCode)) == 0)) + { + struct PendingMessage *pm; + struct GNUNET_DHT_MonitorMessage *mmsg; + struct GNUNET_PeerIdentity *path; + size_t msize; + unsigned int i; + + /* Don't send duplicates */ + for (i = 0; i < cl_size; i++) + if (cl[i] == m->client) + break; + if (i < cl_size) + continue; + GNUNET_array_append (cl, cl_size, m->client); + + msize = size; + msize += (getl + putl) * sizeof (struct GNUNET_PeerIdentity); + msize += sizeof (struct GNUNET_DHT_MonitorMessage); + msize += sizeof (struct PendingMessage); + pm = (struct PendingMessage *) GNUNET_malloc (msize); + mmsg = (struct GNUNET_DHT_MonitorMessage *) &pm[1]; + pm->msg = (struct GNUNET_MessageHeader *) mmsg; + mmsg->header.size = htons (msize - sizeof (struct PendingMessage)); + mmsg->header.type = htons (mtype); + mmsg->expiration = GNUNET_TIME_absolute_hton(exp); + memcpy (&mmsg->key, key, sizeof (GNUNET_HashCode)); + mmsg->put_path_length = htonl(putl); + mmsg->get_path_length = htonl(getl); + mmsg->desired_replication_level = htonl (desired_replication_level); + path = (struct GNUNET_PeerIdentity *) &mmsg[1]; + if (putl > 0) + { + memcpy (path, put_path, putl * sizeof (struct GNUNET_PeerIdentity)); + path = &path[putl]; + } + if (getl > 0) + memcpy (path, get_path, getl * sizeof (struct GNUNET_PeerIdentity)); + if (size > 0) + memcpy (&path[getl], data, size); + add_pending_message (m->client, pm); + } + } + GNUNET_free_non_null (cl); +} + + +/** + * Initialize client subsystem. + * + * @param server the initialized server + */ +void +GDS_CLIENTS_init (struct GNUNET_SERVER_Handle *server) +{ + static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = { + {&handle_dht_local_put, NULL, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT, 0}, + {&handle_dht_local_get, NULL, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET, 0}, + {&handle_dht_local_get_stop, NULL, + GNUNET_MESSAGE_TYPE_DHT_CLIENT_GET_STOP, + sizeof (struct GNUNET_DHT_ClientGetStopMessage)}, + {&handle_dht_local_monitor, NULL, + GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET, + sizeof (struct GNUNET_DHT_MonitorMessage)}, + {NULL, NULL, 0, 0} + }; + forward_map = GNUNET_CONTAINER_multihashmap_create (1024); + retry_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + GNUNET_SERVER_add_handlers (server, plugin_handlers); + GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); +} + + +/** + * Shutdown client subsystem. + */ +void +GDS_CLIENTS_done () +{ + GNUNET_assert (client_head == NULL); + GNUNET_assert (client_tail == NULL); + if (GNUNET_SCHEDULER_NO_TASK != retry_task) + { + GNUNET_SCHEDULER_cancel (retry_task); + retry_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (retry_heap)); + GNUNET_CONTAINER_heap_destroy (retry_heap); + retry_heap = NULL; + GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (forward_map)); + GNUNET_CONTAINER_multihashmap_destroy (forward_map); + forward_map = NULL; +} + +/* end of gnunet-service-dht_clients.c */ diff --git a/src/dht/gnunet-service-dht_clients.h b/src/dht/gnunet-service-dht_clients.h new file mode 100644 index 0000000..a477456 --- /dev/null +++ b/src/dht/gnunet-service-dht_clients.h @@ -0,0 +1,103 @@ +/* + 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 dht/gnunet-service-dht_clients.h + * @brief GNUnet DHT service's client management code + * @author Christian Grothoff + * @author Nathan Evans + */ +#ifndef GNUNET_SERVICE_DHT_CLIENT_H +#define GNUNET_SERVICE_DHT_CLIENT_H + +#include "gnunet_util_lib.h" +#include "gnunet_block_lib.h" + +/** + * Handle a reply we've received from another peer. If the reply + * matches any of our pending queries, forward it to the respective + * client(s). + * + * @param expiration when will the reply expire + * @param key the query this reply is for + * @param get_path_length number of peers in 'get_path' + * @param get_path path the reply took on get + * @param put_path_length number of peers in 'put_path' + * @param put_path path the reply took on put + * @param type type of the reply + * @param data_size number of bytes in 'data' + * @param data application payload data + */ +void +GDS_CLIENTS_handle_reply (struct GNUNET_TIME_Absolute expiration, + const GNUNET_HashCode * key, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + enum GNUNET_BLOCK_Type type, size_t data_size, + const void *data); + + +/** + * Check if some client is monitoring messages of this type and notify + * him in that case. + * + * @param mtype Type of the DHT message. + * @param exp When will this value expire. + * @param key Key of the result/request. + * @param putl number of entries in get_path. + * @param put_path peers on the PUT path (or NULL if not recorded). + * @param getl number of entries in get_path. + * @param get_path Peers on reply path (or NULL if not recorded). + * @param desired_replication_level Desired replication level. + * @param type Type of the result/request. + * @param data Pointer to the result data. + * @param size Number of bytes in data. + */ +void +GDS_CLIENTS_process_monitor (uint16_t mtype, + const struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode *key, + uint32_t putl, + const struct GNUNET_PeerIdentity *put_path, + uint32_t getl, + const struct GNUNET_PeerIdentity *get_path, + uint32_t desired_replication_level, + enum GNUNET_BLOCK_Type type, + const struct GNUNET_MessageHeader *data, + uint16_t size); + +/** + * Initialize client subsystem. + * + * @param server the initialized server + */ +void +GDS_CLIENTS_init (struct GNUNET_SERVER_Handle *server); + + +/** + * Shutdown client subsystem. + */ +void +GDS_CLIENTS_done (void); + +#endif diff --git a/src/dht/gnunet-service-dht_datacache.c b/src/dht/gnunet-service-dht_datacache.c new file mode 100644 index 0000000..82cd067 --- /dev/null +++ b/src/dht/gnunet-service-dht_datacache.c @@ -0,0 +1,309 @@ +/* + 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 dht/gnunet-service-dht_datacache.c + * @brief GNUnet DHT service's datacache integration + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_datacache_lib.h" +#include "gnunet-service-dht_clients.h" +#include "gnunet-service-dht_datacache.h" +#include "gnunet-service-dht_routing.h" +#include "gnunet-service-dht.h" + + +/** + * Handle to the datacache service (for inserting/retrieving data) + */ +static struct GNUNET_DATACACHE_Handle *datacache; + + +/** + * Entry for inserting data into datacache from the DHT. + */ +struct DHTPutEntry +{ + /** + * Size of data. + */ + uint16_t data_size; + + /** + * Length of recorded path. + */ + uint16_t path_length; + + /* PATH ENTRIES */ + + /* PUT DATA */ + +}; + + +/** + * Handle a datum we've received from another peer. Cache if + * possible. + * + * @param expiration when will the reply expire + * @param key the query this reply is for + * @param put_path_length number of peers in 'put_path' + * @param put_path path the reply took on put + * @param type type of the reply + * @param data_size number of bytes in 'data' + * @param data application payload data + */ +void +GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute expiration, + const GNUNET_HashCode * key, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + enum GNUNET_BLOCK_Type type, size_t data_size, + const void *data) +{ + size_t plen = + data_size + put_path_length * sizeof (struct GNUNET_PeerIdentity) + + sizeof (struct DHTPutEntry); + char buf[plen]; + struct DHTPutEntry *pe; + struct GNUNET_PeerIdentity *pp; + + if (datacache == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("%s request received, but have no datacache!\n"), "PUT"); + return; + } + if (data_size >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + return; + } + /* Put size is actual data size plus struct overhead plus path length (if any) */ + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# ITEMS stored in datacache"), 1, + GNUNET_NO); + pe = (struct DHTPutEntry *) buf; + pe->data_size = htons (data_size); + pe->path_length = htons ((uint16_t) put_path_length); + pp = (struct GNUNET_PeerIdentity *) &pe[1]; + memcpy (pp, put_path, put_path_length * sizeof (struct GNUNET_PeerIdentity)); + memcpy (&pp[put_path_length], data, data_size); + (void) GNUNET_DATACACHE_put (datacache, key, plen, (const char *) pe, type, + expiration); +} + + +/** + * Context containing information about a GET request. + */ +struct GetRequestContext +{ + /** + * extended query (see gnunet_block_lib.h). + */ + const void *xquery; + + /** + * Bloomfilter to filter out duplicate replies (updated) + */ + struct GNUNET_CONTAINER_BloomFilter **reply_bf; + + /** + * The key this request was about + */ + GNUNET_HashCode key; + + /** + * Number of bytes in xquery. + */ + size_t xquery_size; + + /** + * Mutator value for the reply_bf, see gnunet_block_lib.h + */ + uint32_t reply_bf_mutator; + + /** + * Return value to give back. + */ + enum GNUNET_BLOCK_EvaluationResult eval; +}; + + +/** + * Iterator for local get request results, + * + * @param cls closure for iterator, a DatacacheGetContext + * @param exp when does this value expire? + * @param key the key this data is stored under + * @param size the size of the data identified by key + * @param data the actual data + * @param type the type of the data + * + * @return GNUNET_OK to continue iteration, anything else + * to stop iteration. + */ +static int +datacache_get_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, size_t size, + const char *data, enum GNUNET_BLOCK_Type type) +{ + struct GetRequestContext *ctx = cls; + const struct DHTPutEntry *pe; + const struct GNUNET_PeerIdentity *pp; + const char *rdata; + size_t rdata_size; + uint16_t put_path_length; + enum GNUNET_BLOCK_EvaluationResult eval; + + pe = (const struct DHTPutEntry *) data; + put_path_length = ntohs (pe->path_length); + rdata_size = ntohs (pe->data_size); + + if (size != + sizeof (struct DHTPutEntry) + rdata_size + + (put_path_length * sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + return GNUNET_OK; + } + pp = (const struct GNUNET_PeerIdentity *) &pe[1]; + rdata = (const char *) &pp[put_path_length]; + eval = + GNUNET_BLOCK_evaluate (GDS_block_context, type, key, ctx->reply_bf, + ctx->reply_bf_mutator, ctx->xquery, + ctx->xquery_size, rdata, rdata_size); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found reply for query %s in datacache, evaluation result is %d\n", + GNUNET_h2s (key), (int) eval); +#endif + ctx->eval = eval; + switch (eval) + { + case GNUNET_BLOCK_EVALUATION_OK_LAST: + case GNUNET_BLOCK_EVALUATION_OK_MORE: + /* forward to local clients */ + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Good RESULTS found in datacache"), 1, + GNUNET_NO); + GDS_CLIENTS_handle_reply (exp, key, 0, NULL, put_path_length, pp, type, + rdata_size, rdata); + /* forward to other peers */ + GDS_ROUTING_process (type, exp, key, put_path_length, pp, 0, NULL, rdata, + rdata_size); + break; + case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Duplicate RESULTS found in datacache"), 1, + GNUNET_NO); + break; + case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Invalid RESULTS found in datacache"), 1, + GNUNET_NO); + break; + case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: + GNUNET_break (0); + break; + case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: + GNUNET_break_op (0); + return GNUNET_SYSERR; + case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Unsupported RESULTS found in datacache"), 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Unsupported block type (%u) in local response!\n"), type); + break; + } + return (eval == GNUNET_BLOCK_EVALUATION_OK_LAST) ? GNUNET_NO : GNUNET_OK; +} + + +/** + * Handle a GET request we've received from another peer. + * + * @param key the query + * @param type requested data type + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf where the reply bf is (to be) stored, possibly updated, can be NULL + * @param reply_bf_mutator mutation value for reply_bf + * @return evaluation result for the local replies + */ +enum GNUNET_BLOCK_EvaluationResult +GDS_DATACACHE_handle_get (const GNUNET_HashCode * key, + enum GNUNET_BLOCK_Type type, const void *xquery, + size_t xquery_size, + struct GNUNET_CONTAINER_BloomFilter **reply_bf, + uint32_t reply_bf_mutator) +{ + struct GetRequestContext ctx; + + if (datacache == NULL) + return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# GET requests given to datacache"), + 1, GNUNET_NO); + ctx.eval = GNUNET_BLOCK_EVALUATION_REQUEST_VALID; + ctx.key = *key; + ctx.xquery = xquery; + ctx.xquery_size = xquery_size; + ctx.reply_bf = reply_bf; + ctx.reply_bf_mutator = reply_bf_mutator; + (void) GNUNET_DATACACHE_get (datacache, key, type, &datacache_get_iterator, + &ctx); + return ctx.eval; +} + + +/** + * Initialize datacache subsystem. + */ +void +GDS_DATACACHE_init () +{ + datacache = GNUNET_DATACACHE_create (GDS_cfg, "dhtcache"); +} + + +/** + * Shutdown datacache subsystem. + */ +void +GDS_DATACACHE_done () +{ + if (datacache != NULL) + { + GNUNET_DATACACHE_destroy (datacache); + datacache = NULL; + } +} + + +/* end of gnunet-service-dht_datacache.c */ diff --git a/src/dht/gnunet-service-dht_datacache.h b/src/dht/gnunet-service-dht_datacache.h new file mode 100644 index 0000000..926ad53 --- /dev/null +++ b/src/dht/gnunet-service-dht_datacache.h @@ -0,0 +1,86 @@ +/* + 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 dht/gnunet-service-dht_datacache.h + * @brief GNUnet DHT service's datacache integration + * @author Christian Grothoff + * @author Nathan Evans + */ +#ifndef GNUNET_SERVICE_DHT_DATACACHE_H +#define GNUNET_SERVICE_DHT_DATACACHE_H + +#include "gnunet_util_lib.h" +#include "gnunet_block_lib.h" + +/** + * Handle a datum we've received from another peer. Cache if + * possible. + * + * @param expiration when will the reply expire + * @param key the query this reply is for + * @param put_path_length number of peers in 'put_path' + * @param put_path path the reply took on put + * @param type type of the reply + * @param data_size number of bytes in 'data' + * @param data application payload data + */ +void +GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute expiration, + const GNUNET_HashCode * key, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + enum GNUNET_BLOCK_Type type, size_t data_size, + const void *data); + + +/** + * Handle a GET request we've received from another peer. + * + * @param key the query + * @param type requested data type + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf where the reply bf is (to be) stored, possibly updated!, can be NULL + * @param reply_bf_mutator mutation value for reply_bf + * @return evaluation result for the local replies + */ +enum GNUNET_BLOCK_EvaluationResult +GDS_DATACACHE_handle_get (const GNUNET_HashCode * key, + enum GNUNET_BLOCK_Type type, const void *xquery, + size_t xquery_size, + struct GNUNET_CONTAINER_BloomFilter **reply_bf, + uint32_t reply_bf_mutator); + + +/** + * Initialize datacache subsystem. + */ +void +GDS_DATACACHE_init (void); + + +/** + * Shutdown datacache subsystem. + */ +void +GDS_DATACACHE_done (void); + +#endif diff --git a/src/dht/gnunet-service-dht_hello.c b/src/dht/gnunet-service-dht_hello.c new file mode 100644 index 0000000..b9cc450 --- /dev/null +++ b/src/dht/gnunet-service-dht_hello.c @@ -0,0 +1,135 @@ +/* + 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 dht/gnunet-service-dht_hello.c + * @brief GNUnet DHT integration with peerinfo + * @author Christian Grothoff + * + * TODO: + * - consider adding mechanism to remove expired HELLOs + */ +#include "platform.h" +#include "gnunet-service-dht.h" +#include "gnunet-service-dht_hello.h" +#include "gnunet_peerinfo_service.h" + + +/** + * Handle for peerinfo notifications. + */ +static struct GNUNET_PEERINFO_NotifyContext *pnc; + +/** + * Hash map of peers to HELLOs. + */ +static struct GNUNET_CONTAINER_MultiHashMap *peer_to_hello; + + +/** + * Obtain a peer's HELLO if available + * + * @param peer peer to look for a HELLO from + * @return HELLO for the given peer + */ +const struct GNUNET_HELLO_Message * +GDS_HELLO_get (const struct GNUNET_PeerIdentity *peer) +{ + if (NULL == peer_to_hello) + return NULL; + return GNUNET_CONTAINER_multihashmap_get (peer_to_hello, &peer->hashPubKey); +} + + +/** + * Function called for each HELLO known to PEERINFO. + * + * @param cls closure + * @param peer id of the peer, NULL for last call + * @param hello hello message for the peer (can be NULL) + * @param err_msg error message (not used) + */ +static void +process_hello (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, const char *err_msg) +{ + struct GNUNET_TIME_Absolute ex; + struct GNUNET_HELLO_Message *hm; + + if (hello == NULL) + return; + ex = GNUNET_HELLO_get_last_expiration (hello); + if (GNUNET_TIME_absolute_get_remaining (ex).rel_value == 0) + return; + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# HELLOs obtained from peerinfo"), 1, + GNUNET_NO); + hm = GNUNET_CONTAINER_multihashmap_get (peer_to_hello, &peer->hashPubKey); + GNUNET_free_non_null (hm); + hm = GNUNET_malloc (GNUNET_HELLO_size (hello)); + memcpy (hm, hello, GNUNET_HELLO_size (hello)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONTAINER_multihashmap_put (peer_to_hello, + &peer->hashPubKey, hm, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE)); +} + + +/** + * Initialize HELLO subsystem. + */ +void +GDS_HELLO_init () +{ + pnc = GNUNET_PEERINFO_notify (GDS_cfg, &process_hello, NULL); + peer_to_hello = GNUNET_CONTAINER_multihashmap_create (256); +} + + +/** + * Free memory occopied by the HELLO. + */ +static int +free_hello (void *cls, const GNUNET_HashCode * key, void *hello) +{ + GNUNET_free (hello); + return GNUNET_OK; +} + + +/** + * Shutdown HELLO subsystem. + */ +void +GDS_HELLO_done () +{ + if (NULL != pnc) + { + GNUNET_PEERINFO_notify_cancel (pnc); + pnc = NULL; + } + if (NULL != peer_to_hello) + { + GNUNET_CONTAINER_multihashmap_iterate (peer_to_hello, &free_hello, NULL); + GNUNET_CONTAINER_multihashmap_destroy (peer_to_hello); + } +} + +/* end of gnunet-service-dht_hello.c */ diff --git a/src/dht/gnunet-service-dht_hello.h b/src/dht/gnunet-service-dht_hello.h new file mode 100644 index 0000000..04a6a49 --- /dev/null +++ b/src/dht/gnunet-service-dht_hello.h @@ -0,0 +1,55 @@ +/* + 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 dht/gnunet-service-dht_hello.h + * @brief GNUnet DHT integration with peerinfo + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_DHT_HELLO_H +#define GNUNET_SERVICE_DHT_HELLO_H + +#include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" + +/** + * Obtain a peer's HELLO if available + * + * @param peer peer to look for a HELLO from + * @return HELLO for the given peer + */ +const struct GNUNET_HELLO_Message * +GDS_HELLO_get (const struct GNUNET_PeerIdentity *peer); + + +/** + * Initialize HELLO subsystem. + */ +void +GDS_HELLO_init (void); + + +/** + * Shutdown HELLO subsystem. + */ +void +GDS_HELLO_done (void); + +#endif diff --git a/src/dht/gnunet-service-dht_neighbours.c b/src/dht/gnunet-service-dht_neighbours.c new file mode 100644 index 0000000..4ea5dd6 --- /dev/null +++ b/src/dht/gnunet-service-dht_neighbours.c @@ -0,0 +1,2029 @@ +/* + 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 dht/gnunet-service-dht_neighbours.c + * @brief GNUnet DHT service's bucket and neighbour management code + * @author Christian Grothoff + * @author Nathan Evans + */ + +#include "platform.h" +#include "gnunet_block_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_nse_service.h" +#include "gnunet_ats_service.h" +#include "gnunet_core_service.h" +#include "gnunet_datacache_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_dht_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet-service-dht.h" +#include "gnunet-service-dht_clients.h" +#include "gnunet-service-dht_datacache.h" +#include "gnunet-service-dht_hello.h" +#include "gnunet-service-dht_neighbours.h" +#include "gnunet-service-dht_nse.h" +#include "gnunet-service-dht_routing.h" +#include <fenv.h> +#include "dht.h" + +/** + * How many buckets will we allow total. + */ +#define MAX_BUCKETS sizeof (GNUNET_HashCode) * 8 + +/** + * What is the maximum number of peers in a given bucket. + */ +#define DEFAULT_BUCKET_SIZE 8 + +/** + * Desired replication level for FIND PEER requests + */ +#define FIND_PEER_REPLICATION_LEVEL 4 + +/** + * Maximum allowed replication level for all requests. + */ +#define MAXIMUM_REPLICATION_LEVEL 16 + +/** + * How often to update our preference levels for peers in our routing tables. + */ +#define DHT_DEFAULT_PREFERENCE_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2) + +/** + * How long at least to wait before sending another find peer request. + */ +#define DHT_MINIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * How long at most to wait before sending another find peer request. + */ +#define DHT_MAXIMUM_FIND_PEER_INTERVAL GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 10) + +/** + * How long at most to wait for transmission of a GET request to another peer? + */ +#define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2) + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * P2P PUT message + */ +struct PeerPutMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_P2P_PUT + */ + struct GNUNET_MessageHeader header; + + /** + * Processing options + */ + uint32_t options GNUNET_PACKED; + + /** + * Content type. + */ + uint32_t type GNUNET_PACKED; + + /** + * Hop count + */ + uint32_t hop_count GNUNET_PACKED; + + /** + * Replication level for this message + */ + uint32_t desired_replication_level GNUNET_PACKED; + + /** + * Length of the PUT path that follows (if tracked). + */ + uint32_t put_path_length GNUNET_PACKED; + + /** + * When does the content expire? + */ + struct GNUNET_TIME_AbsoluteNBO expiration_time; + + /** + * Bloomfilter (for peer identities) to stop circular routes + */ + char bloomfilter[DHT_BLOOM_SIZE]; + + /** + * The key we are storing under. + */ + GNUNET_HashCode key; + + /* put path (if tracked) */ + + /* Payload */ + +}; + + +/** + * P2P Result message + */ +struct PeerResultMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT + */ + struct GNUNET_MessageHeader header; + + /** + * Content type. + */ + uint32_t type GNUNET_PACKED; + + /** + * Length of the PUT path that follows (if tracked). + */ + uint32_t put_path_length GNUNET_PACKED; + + /** + * Length of the GET path that follows (if tracked). + */ + uint32_t get_path_length GNUNET_PACKED; + + /** + * When does the content expire? + */ + struct GNUNET_TIME_AbsoluteNBO expiration_time; + + /** + * The key of the corresponding GET request. + */ + GNUNET_HashCode key; + + /* put path (if tracked) */ + + /* get path (if tracked) */ + + /* Payload */ + +}; + + +/** + * P2P GET message + */ +struct PeerGetMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_DHT_P2P_GET + */ + struct GNUNET_MessageHeader header; + + /** + * Processing options + */ + uint32_t options GNUNET_PACKED; + + /** + * Desired content type. + */ + uint32_t type GNUNET_PACKED; + + /** + * Hop count + */ + uint32_t hop_count GNUNET_PACKED; + + /** + * Desired replication level for this request. + */ + uint32_t desired_replication_level GNUNET_PACKED; + + /** + * Size of the extended query. + */ + uint32_t xquery_size; + + /** + * Bloomfilter mutator. + */ + uint32_t bf_mutator; + + /** + * Bloomfilter (for peer identities) to stop circular routes + */ + char bloomfilter[DHT_BLOOM_SIZE]; + + /** + * The key we are looking for. + */ + GNUNET_HashCode key; + + /* xquery */ + + /* result bloomfilter */ + +}; +GNUNET_NETWORK_STRUCT_END + +/** + * Linked list of messages to send to a particular other peer. + */ +struct P2PPendingMessage +{ + /** + * Pointer to next item in the list + */ + struct P2PPendingMessage *next; + + /** + * Pointer to previous item in the list + */ + struct P2PPendingMessage *prev; + + /** + * Message importance level. FIXME: used? useful? + */ + unsigned int importance; + + /** + * When does this message time out? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Actual message to be sent, allocated at the end of the struct: + * // msg = (cast) &pm[1]; + * // memcpy (&pm[1], data, len); + */ + const struct GNUNET_MessageHeader *msg; + +}; + + +/** + * Entry for a peer in a bucket. + */ +struct PeerInfo +{ + /** + * Next peer entry (DLL) + */ + struct PeerInfo *next; + + /** + * Prev peer entry (DLL) + */ + struct PeerInfo *prev; + + /** + * Count of outstanding messages for peer. FIXME: NEEDED? + * FIXME: bound queue size!? + */ + unsigned int pending_count; + + /** + * Head of pending messages to be sent to this peer. + */ + struct P2PPendingMessage *head; + + /** + * Tail of pending messages to be sent to this peer. + */ + struct P2PPendingMessage *tail; + + /** + * Core handle for sending messages to this peer. + */ + struct GNUNET_CORE_TransmitHandle *th; + + /** + * Task for scheduling preference updates + */ + GNUNET_SCHEDULER_TaskIdentifier preference_task; + + /** + * What is the identity of the peer? + */ + struct GNUNET_PeerIdentity id; + +#if 0 + /** + * What is the average latency for replies received? + */ + struct GNUNET_TIME_Relative latency; + + /** + * Transport level distance to peer. + */ + unsigned int distance; +#endif + +}; + + +/** + * Peers are grouped into buckets. + */ +struct PeerBucket +{ + /** + * Head of DLL + */ + struct PeerInfo *head; + + /** + * Tail of DLL + */ + struct PeerInfo *tail; + + /** + * Number of peers in the bucket. + */ + unsigned int peers_size; +}; + + +/** + * The lowest currently used bucket, initially 0 (for 0-bits matching bucket). + */ +static unsigned int closest_bucket; + +/** + * How many peers have we added since we sent out our last + * find peer request? + */ +static unsigned int newly_found_peers; + +/** + * The buckets. Array of size MAX_BUCKET_SIZE. Offset 0 means 0 bits matching. + */ +static struct PeerBucket k_buckets[MAX_BUCKETS]; + +/** + * Hash map of all known peers, for easy removal from k_buckets on disconnect. + */ +static struct GNUNET_CONTAINER_MultiHashMap *all_known_peers; + +/** + * Maximum size for each bucket. + */ +static unsigned int bucket_size = DEFAULT_BUCKET_SIZE; + +/** + * Task that sends FIND PEER requests. + */ +static GNUNET_SCHEDULER_TaskIdentifier find_peer_task; + +/** + * Identity of this peer. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Handle to CORE. + */ +static struct GNUNET_CORE_Handle *coreAPI; + +/** + * Handle to ATS. + */ +static struct GNUNET_ATS_PerformanceHandle *atsAPI; + + + +/** + * Find the optimal bucket for this key. + * + * @param hc the hashcode to compare our identity to + * @return the proper bucket index, or GNUNET_SYSERR + * on error (same hashcode) + */ +static int +find_bucket (const GNUNET_HashCode * hc) +{ + unsigned int bits; + + bits = GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey, hc); + if (bits == MAX_BUCKETS) + { + /* How can all bits match? Got my own ID? */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + return MAX_BUCKETS - bits - 1; +} + + +/** + * Let GNUnet core know that we like the given peer. + * + * @param cls the 'struct PeerInfo' of the peer + * @param tc scheduler context. + */ +static void +update_core_preference (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerInfo *peer = cls; + uint64_t preference; + unsigned int matching; + int bucket; + + peer->preference_task = GNUNET_SCHEDULER_NO_TASK; + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; + matching = + GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey, + &peer->id.hashPubKey); + if (matching >= 64) + matching = 63; + bucket = find_bucket (&peer->id.hashPubKey); + if (bucket == GNUNET_SYSERR) + preference = 0; + else + { + GNUNET_assert (k_buckets[bucket].peers_size != 0); + preference = (1LL << matching) / k_buckets[bucket].peers_size; + } + if (preference == 0) + { + peer->preference_task = + GNUNET_SCHEDULER_add_delayed (DHT_DEFAULT_PREFERENCE_INTERVAL, + &update_core_preference, peer); + return; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# Preference updates given to core"), + 1, GNUNET_NO); + GNUNET_ATS_change_preference (atsAPI, &peer->id, + GNUNET_ATS_PREFERENCE_BANDWIDTH, + (double) preference, GNUNET_ATS_PREFERENCE_END); + peer->preference_task = + GNUNET_SCHEDULER_add_delayed (DHT_DEFAULT_PREFERENCE_INTERVAL, + &update_core_preference, peer); + + +} + + +/** + * Closure for 'add_known_to_bloom'. + */ +struct BloomConstructorContext +{ + /** + * Bloom filter under construction. + */ + struct GNUNET_CONTAINER_BloomFilter *bloom; + + /** + * Mutator to use. + */ + uint32_t bf_mutator; +}; + + +/** + * Add each of the peers we already know to the bloom filter of + * the request so that we don't get duplicate HELLOs. + * + * @param cls the 'struct BloomConstructorContext'. + * @param key peer identity to add to the bloom filter + * @param value value the peer information (unused) + * @return GNUNET_YES (we should continue to iterate) + */ +static int +add_known_to_bloom (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct BloomConstructorContext *ctx = cls; + GNUNET_HashCode mh; + + GNUNET_BLOCK_mingle_hash (key, ctx->bf_mutator, &mh); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding known peer (%s) to bloomfilter for FIND PEER with mutation %u\n", + GNUNET_h2s (key), ctx->bf_mutator); +#endif + GNUNET_CONTAINER_bloomfilter_add (ctx->bloom, &mh); + return GNUNET_YES; +} + + +/** + * Task to send a find peer message for our own peer identifier + * so that we can find the closest peers in the network to ourselves + * and attempt to connect to them. + * + * @param cls closure for this task + * @param tc the context under which the task is running + */ +static void +send_find_peer_message (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TIME_Relative next_send_time; + struct BloomConstructorContext bcc; + struct GNUNET_CONTAINER_BloomFilter *peer_bf; + + find_peer_task = GNUNET_SCHEDULER_NO_TASK; + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; + if (newly_found_peers > bucket_size) + { + /* If we are finding many peers already, no need to send out our request right now! */ + find_peer_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &send_find_peer_message, NULL); + newly_found_peers = 0; + return; + } + bcc.bf_mutator = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); + bcc.bloom = + GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + GNUNET_CONTAINER_multihashmap_iterate (all_known_peers, &add_known_to_bloom, + &bcc); + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# FIND PEER messages initiated"), 1, + GNUNET_NO); + peer_bf = + GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + // FIXME: pass priority!? + GDS_NEIGHBOURS_handle_get (GNUNET_BLOCK_TYPE_DHT_HELLO, + GNUNET_DHT_RO_FIND_PEER, + FIND_PEER_REPLICATION_LEVEL, 0, + &my_identity.hashPubKey, NULL, 0, bcc.bloom, + bcc.bf_mutator, peer_bf); + GNUNET_CONTAINER_bloomfilter_free (peer_bf); + GNUNET_CONTAINER_bloomfilter_free (bcc.bloom); + /* schedule next round */ + next_send_time.rel_value = + DHT_MINIMUM_FIND_PEER_INTERVAL.rel_value + + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + DHT_MAXIMUM_FIND_PEER_INTERVAL.rel_value / + (newly_found_peers + 1)); + newly_found_peers = 0; + find_peer_task = + GNUNET_SCHEDULER_add_delayed (next_send_time, &send_find_peer_message, + NULL); +} + + +/** + * Method called whenever a peer connects. + * + * @param cls closure + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + */ +static void +handle_core_connect (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PeerInfo *ret; + int peer_bucket; + + /* Check for connect to self message */ + if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) + return; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected %s to %s\n", + GNUNET_i2s (&my_identity), GNUNET_h2s (&peer->hashPubKey)); +#endif + if (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (all_known_peers, + &peer->hashPubKey)) + { + GNUNET_break (0); + return; + } + GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Peers connected"), 1, + GNUNET_NO); + peer_bucket = find_bucket (&peer->hashPubKey); + GNUNET_assert ((peer_bucket >= 0) && (peer_bucket < MAX_BUCKETS)); + ret = GNUNET_malloc (sizeof (struct PeerInfo)); +#if 0 + ret->latency = latency; + ret->distance = distance; +#endif + ret->id = *peer; + GNUNET_CONTAINER_DLL_insert_tail (k_buckets[peer_bucket].head, + k_buckets[peer_bucket].tail, ret); + k_buckets[peer_bucket].peers_size++; + closest_bucket = GNUNET_MAX (closest_bucket, peer_bucket); + if ((peer_bucket > 0) && (k_buckets[peer_bucket].peers_size <= bucket_size)) + { + ret->preference_task = + GNUNET_SCHEDULER_add_now (&update_core_preference, ret); + newly_found_peers++; + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (all_known_peers, + &peer->hashPubKey, ret, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + if (1 == GNUNET_CONTAINER_multihashmap_size (all_known_peers)) + { + /* got a first connection, good time to start with FIND PEER requests... */ + find_peer_task = GNUNET_SCHEDULER_add_now (&send_find_peer_message, NULL); + } +} + + +/** + * Method called whenever a peer disconnects. + * + * @param cls closure + * @param peer peer identity this notification is about + */ +static void +handle_core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct PeerInfo *to_remove; + int current_bucket; + struct P2PPendingMessage *pos; + unsigned int discarded; + + /* Check for disconnect from self message */ + if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) + return; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnected %s from %s\n", + GNUNET_i2s (&my_identity), GNUNET_h2s (&peer->hashPubKey)); +#endif + to_remove = + GNUNET_CONTAINER_multihashmap_get (all_known_peers, &peer->hashPubKey); + if (NULL == to_remove) + { + GNUNET_break (0); + return; + } + GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Peers connected"), -1, + GNUNET_NO); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (all_known_peers, + &peer->hashPubKey, + to_remove)); + if (GNUNET_SCHEDULER_NO_TASK != to_remove->preference_task) + { + GNUNET_SCHEDULER_cancel (to_remove->preference_task); + to_remove->preference_task = GNUNET_SCHEDULER_NO_TASK; + } + current_bucket = find_bucket (&to_remove->id.hashPubKey); + GNUNET_assert (current_bucket >= 0); + GNUNET_CONTAINER_DLL_remove (k_buckets[current_bucket].head, + k_buckets[current_bucket].tail, to_remove); + GNUNET_assert (k_buckets[current_bucket].peers_size > 0); + k_buckets[current_bucket].peers_size--; + while ((closest_bucket > 0) && (k_buckets[closest_bucket].peers_size == 0)) + closest_bucket--; + + if (to_remove->th != NULL) + { + GNUNET_CORE_notify_transmit_ready_cancel (to_remove->th); + to_remove->th = NULL; + } + discarded = 0; + while (NULL != (pos = to_remove->head)) + { + GNUNET_CONTAINER_DLL_remove (to_remove->head, to_remove->tail, pos); + discarded++; + GNUNET_free (pos); + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Queued messages discarded (peer disconnected)"), + discarded, GNUNET_NO); + GNUNET_free (to_remove); +} + + +/** + * Called when core is ready to send a message we asked for + * out to the destination. + * + * @param cls the 'struct PeerInfo' of the target peer + * @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 +core_transmit_notify (void *cls, size_t size, void *buf) +{ + struct PeerInfo *peer = cls; + char *cbuf = buf; + struct P2PPendingMessage *pending; + size_t off; + size_t msize; + + peer->th = NULL; + while ((NULL != (pending = peer->head)) && + (GNUNET_TIME_absolute_get_remaining (pending->timeout).rel_value == 0)) + { + peer->pending_count--; + GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, pending); + GNUNET_free (pending); + } + if (pending == NULL) + { + /* no messages pending */ + return 0; + } + if (buf == NULL) + { + peer->th = + GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_YES, + pending->importance, + GNUNET_TIME_absolute_get_remaining + (pending->timeout), &peer->id, + ntohs (pending->msg->size), + &core_transmit_notify, peer); + GNUNET_break (NULL != peer->th); + return 0; + } + off = 0; + while ((NULL != (pending = peer->head)) && + (size - off >= (msize = ntohs (pending->msg->size)))) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Bytes transmitted to other peers"), msize, + GNUNET_NO); + memcpy (&cbuf[off], pending->msg, msize); + off += msize; + peer->pending_count--; + GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, pending); + GNUNET_free (pending); + } + if (peer->head != NULL) + { + peer->th = + GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_YES, + pending->importance, + GNUNET_TIME_absolute_get_remaining + (pending->timeout), &peer->id, msize, + &core_transmit_notify, peer); + GNUNET_break (NULL != peer->th); + } + return off; +} + + +/** + * Transmit all messages in the peer's message queue. + * + * @param peer message queue to process + */ +static void +process_peer_queue (struct PeerInfo *peer) +{ + struct P2PPendingMessage *pending; + + if (NULL == (pending = peer->head)) + return; + if (NULL != peer->th) + return; + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Bytes of bandwdith requested from core"), + ntohs (pending->msg->size), GNUNET_NO); + peer->th = + GNUNET_CORE_notify_transmit_ready (coreAPI, GNUNET_YES, + pending->importance, + GNUNET_TIME_absolute_get_remaining + (pending->timeout), &peer->id, + ntohs (pending->msg->size), + &core_transmit_notify, peer); + GNUNET_break (NULL != peer->th); +} + + +/** + * To how many peers should we (on average) forward the request to + * obtain the desired target_replication count (on average). + * + * @param hop_count number of hops the message has traversed + * @param target_replication the number of total paths desired + * @return Some number of peers to forward the message to + */ +static unsigned int +get_forward_count (uint32_t hop_count, uint32_t target_replication) +{ + uint32_t random_value; + uint32_t forward_count; + float target_value; + + if (hop_count > GDS_NSE_get () * 6.0) + { + /* forcefully terminate */ + return 0; + } + if (hop_count > GDS_NSE_get () * 4.0) + { + /* Once we have reached our ideal number of hops, only forward to 1 peer */ + return 1; + } + /* bound by system-wide maximum */ + target_replication = + GNUNET_MIN (MAXIMUM_REPLICATION_LEVEL, target_replication); + target_value = + 1 + (target_replication - 1.0) / (GDS_NSE_get () + + ((float) (target_replication - 1.0) * + hop_count)); + /* Set forward count to floor of target_value */ + forward_count = (uint32_t) target_value; + /* Subtract forward_count (floor) from target_value (yields value between 0 and 1) */ + target_value = target_value - forward_count; + random_value = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); + if (random_value < (target_value * UINT32_MAX)) + forward_count++; + return forward_count; +} + + +/** + * Compute the distance between have and target as a 32-bit value. + * Differences in the lower bits must count stronger than differences + * in the higher bits. + * + * @return 0 if have==target, otherwise a number + * that is larger as the distance between + * the two hash codes increases + */ +static unsigned int +get_distance (const GNUNET_HashCode * target, const GNUNET_HashCode * have) +{ + unsigned int bucket; + unsigned int msb; + unsigned int lsb; + unsigned int i; + + /* We have to represent the distance between two 2^9 (=512)-bit + * numbers as a 2^5 (=32)-bit number with "0" being used for the + * two numbers being identical; furthermore, we need to + * guarantee that a difference in the number of matching + * bits is always represented in the result. + * + * We use 2^32/2^9 numerical values to distinguish between + * hash codes that have the same LSB bit distance and + * use the highest 2^9 bits of the result to signify the + * number of (mis)matching LSB bits; if we have 0 matching + * and hence 512 mismatching LSB bits we return -1 (since + * 512 itself cannot be represented with 9 bits) */ + + /* first, calculate the most significant 9 bits of our + * result, aka the number of LSBs */ + bucket = GNUNET_CRYPTO_hash_matching_bits (target, have); + /* bucket is now a value between 0 and 512 */ + if (bucket == 512) + return 0; /* perfect match */ + if (bucket == 0) + return (unsigned int) -1; /* LSB differs; use max (if we did the bit-shifting + * below, we'd end up with max+1 (overflow)) */ + + /* calculate the most significant bits of the final result */ + msb = (512 - bucket) << (32 - 9); + /* calculate the 32-9 least significant bits of the final result by + * looking at the differences in the 32-9 bits following the + * mismatching bit at 'bucket' */ + lsb = 0; + for (i = bucket + 1; + (i < sizeof (GNUNET_HashCode) * 8) && (i < bucket + 1 + 32 - 9); i++) + { + if (GNUNET_CRYPTO_hash_get_bit (target, i) != + GNUNET_CRYPTO_hash_get_bit (have, i)) + lsb |= (1 << (bucket + 32 - 9 - i)); /* first bit set will be 10, + * last bit set will be 31 -- if + * i does not reach 512 first... */ + } + return msb | lsb; +} + + +/** + * Check whether my identity is closer than any known peers. If a + * non-null bloomfilter is given, check if this is the closest peer + * that hasn't already been routed to. + * + * @param key hash code to check closeness to + * @param bloom bloomfilter, exclude these entries from the decision + * @return GNUNET_YES if node location is closest, + * GNUNET_NO otherwise. + */ +static int +am_closest_peer (const GNUNET_HashCode * key, + const struct GNUNET_CONTAINER_BloomFilter *bloom) +{ + int bits; + int other_bits; + int bucket_num; + int count; + struct PeerInfo *pos; + + if (0 == memcmp (&my_identity.hashPubKey, key, sizeof (GNUNET_HashCode))) + return GNUNET_YES; + bucket_num = find_bucket (key); + GNUNET_assert (bucket_num >= 0); + bits = GNUNET_CRYPTO_hash_matching_bits (&my_identity.hashPubKey, key); + pos = k_buckets[bucket_num].head; + count = 0; + while ((pos != NULL) && (count < bucket_size)) + { + if ((bloom != NULL) && + (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))) + { + pos = pos->next; + continue; /* Skip already checked entries */ + } + other_bits = GNUNET_CRYPTO_hash_matching_bits (&pos->id.hashPubKey, key); + if (other_bits > bits) + return GNUNET_NO; + if (other_bits == bits) /* We match the same number of bits */ + return GNUNET_YES; + pos = pos->next; + } + /* No peers closer, we are the closest! */ + return GNUNET_YES; +} + + +/** + * Select a peer from the routing table that would be a good routing + * destination for sending a message for "key". The resulting peer + * must not be in the set of blocked peers.<p> + * + * Note that we should not ALWAYS select the closest peer to the + * target, peers further away from the target should be chosen with + * exponentially declining probability. + * + * FIXME: double-check that this is fine + * + * + * @param key the key we are selecting a peer to route to + * @param bloom a bloomfilter containing entries this request has seen already + * @param hops how many hops has this message traversed thus far + * @return Peer to route to, or NULL on error + */ +static struct PeerInfo * +select_peer (const GNUNET_HashCode * key, + const struct GNUNET_CONTAINER_BloomFilter *bloom, uint32_t hops) +{ + unsigned int bc; + unsigned int count; + unsigned int selected; + struct PeerInfo *pos; + unsigned int dist; + unsigned int smallest_distance; + struct PeerInfo *chosen; + + if (hops >= GDS_NSE_get ()) + { + /* greedy selection (closest peer that is not in bloomfilter) */ + smallest_distance = UINT_MAX; + chosen = NULL; + for (bc = 0; bc <= closest_bucket; bc++) + { + pos = k_buckets[bc].head; + count = 0; + while ((pos != NULL) && (count < bucket_size)) + { + if ((bloom == NULL) || + (GNUNET_NO == + GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))) + { + dist = get_distance (key, &pos->id.hashPubKey); + if (dist < smallest_distance) + { + chosen = pos; + smallest_distance = dist; + } + } + else + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Excluded peer `%s' due to BF match in greedy routing for %s\n", + GNUNET_i2s (&pos->id), GNUNET_h2s (key)); +#endif + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Peers excluded from routing due to Bloomfilter"), + 1, GNUNET_NO); + } + count++; + pos = pos->next; + } + } + if (NULL == chosen) + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# Peer selection failed"), 1, + GNUNET_NO); + return chosen; + } + + /* select "random" peer */ + /* count number of peers that are available and not filtered */ + count = 0; + for (bc = 0; bc <= closest_bucket; bc++) + { + pos = k_buckets[bc].head; + while ((pos != NULL) && (count < bucket_size)) + { + if ((bloom != NULL) && + (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Peers excluded from routing due to Bloomfilter"), + 1, GNUNET_NO); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Excluded peer `%s' due to BF match in random routing for %s\n", + GNUNET_i2s (&pos->id), GNUNET_h2s (key)); +#endif + pos = pos->next; + continue; /* Ignore bloomfiltered peers */ + } + count++; + pos = pos->next; + } + } + if (count == 0) /* No peers to select from! */ + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# Peer selection failed"), 1, + GNUNET_NO); + return NULL; + } + /* Now actually choose a peer */ + selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, count); + count = 0; + for (bc = 0; bc <= closest_bucket; bc++) + { + pos = k_buckets[bc].head; + while ((pos != NULL) && (count < bucket_size)) + { + if ((bloom != NULL) && + (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (bloom, &pos->id.hashPubKey))) + { + pos = pos->next; + continue; /* Ignore bloomfiltered peers */ + } + if (0 == selected--) + return pos; + pos = pos->next; + } + } + GNUNET_break (0); + return NULL; +} + + +/** + * Compute the set of peers that the given request should be + * forwarded to. + * + * @param key routing key + * @param bloom bloom filter excluding peers as targets, all selected + * peers will be added to the bloom filter + * @param hop_count number of hops the request has traversed so far + * @param target_replication desired number of replicas + * @param targets where to store an array of target peers (to be + * free'd by the caller) + * @return number of peers returned in 'targets'. + */ +static unsigned int +get_target_peers (const GNUNET_HashCode * key, + struct GNUNET_CONTAINER_BloomFilter *bloom, + uint32_t hop_count, uint32_t target_replication, + struct PeerInfo ***targets) +{ + unsigned int ret; + unsigned int off; + struct PeerInfo **rtargets; + struct PeerInfo *nxt; + + GNUNET_assert (NULL != bloom); + ret = get_forward_count (hop_count, target_replication); + if (ret == 0) + { + *targets = NULL; + return 0; + } + rtargets = GNUNET_malloc (sizeof (struct PeerInfo *) * ret); + for (off = 0; off < ret; off++) + { + nxt = select_peer (key, bloom, hop_count); + if (nxt == NULL) + break; + rtargets[off] = nxt; + GNUNET_break (GNUNET_NO == + GNUNET_CONTAINER_bloomfilter_test (bloom, + &nxt->id.hashPubKey)); + GNUNET_CONTAINER_bloomfilter_add (bloom, &rtargets[off]->id.hashPubKey); + } +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selected %u/%u peers at hop %u for %s (target was %u)\n", off, + GNUNET_CONTAINER_multihashmap_size (all_known_peers), + (unsigned int) hop_count, GNUNET_h2s (key), ret); +#endif + if (0 == off) + { + GNUNET_free (rtargets); + *targets = NULL; + return 0; + } + *targets = rtargets; + return off; +} + + +/** + * Perform a PUT operation. Forwards the given request to other + * peers. Does not store the data locally. Does not give the + * data to local clients. May do nothing if this is the only + * peer in the network (or if we are the closest peer in the + * network). + * + * @param type type of the block + * @param options routing options + * @param desired_replication_level desired replication count + * @param expiration_time when does the content expire + * @param hop_count how many hops has this message traversed so far + * @param bf Bloom filter of peers this PUT has already traversed + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers this request has traversed so far (if tracked) + * @param data payload to store + * @param data_size number of bytes in data + */ +void +GDS_NEIGHBOURS_handle_put (enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + uint32_t desired_replication_level, + struct GNUNET_TIME_Absolute expiration_time, + uint32_t hop_count, + struct GNUNET_CONTAINER_BloomFilter *bf, + const GNUNET_HashCode * key, + unsigned int put_path_length, + struct GNUNET_PeerIdentity *put_path, + const void *data, size_t data_size) +{ + unsigned int target_count; + unsigned int i; + struct PeerInfo **targets; + struct PeerInfo *target; + struct P2PPendingMessage *pending; + size_t msize; + struct PeerPutMessage *ppm; + struct GNUNET_PeerIdentity *pp; + + GNUNET_assert (NULL != bf); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding myself (%s) to PUT bloomfilter for %s\n", + GNUNET_i2s (&my_identity), GNUNET_h2s (key)); +#endif + GNUNET_CONTAINER_bloomfilter_add (bf, &my_identity.hashPubKey); + GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# PUT requests routed"), + 1, GNUNET_NO); + target_count = + get_target_peers (key, bf, hop_count, desired_replication_level, + &targets); + if (0 == target_count) + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing PUT for %s terminates after %u hops at %s\n", + GNUNET_h2s (key), (unsigned int) hop_count, + GNUNET_i2s (&my_identity)); +#endif + return; + } + msize = + put_path_length * sizeof (struct GNUNET_PeerIdentity) + data_size + + sizeof (struct PeerPutMessage); + if (msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + put_path_length = 0; + msize = data_size + sizeof (struct PeerPutMessage); + } + if (msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + GNUNET_free (targets); + return; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# PUT messages queued for transmission"), + target_count, GNUNET_NO); + for (i = 0; i < target_count; i++) + { + target = targets[i]; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing PUT for %s after %u hops to %s\n", GNUNET_h2s (key), + (unsigned int) hop_count, GNUNET_i2s (&target->id)); +#endif + pending = GNUNET_malloc (sizeof (struct P2PPendingMessage) + msize); + pending->importance = 0; /* FIXME */ + pending->timeout = expiration_time; + ppm = (struct PeerPutMessage *) &pending[1]; + pending->msg = &ppm->header; + ppm->header.size = htons (msize); + ppm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_PUT); + ppm->options = htonl (options); + ppm->type = htonl (type); + ppm->hop_count = htonl (hop_count + 1); + ppm->desired_replication_level = htonl (desired_replication_level); + ppm->put_path_length = htonl (put_path_length); + ppm->expiration_time = GNUNET_TIME_absolute_hton (expiration_time); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (bf, + &target->id.hashPubKey)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, + ppm->bloomfilter, + DHT_BLOOM_SIZE)); + ppm->key = *key; + pp = (struct GNUNET_PeerIdentity *) &ppm[1]; + memcpy (pp, put_path, + sizeof (struct GNUNET_PeerIdentity) * put_path_length); + memcpy (&pp[put_path_length], data, data_size); + GNUNET_CONTAINER_DLL_insert_tail (target->head, target->tail, pending); + target->pending_count++; + process_peer_queue (target); + } + GNUNET_free (targets); +} + + +/** + * Perform a GET operation. Forwards the given request to other + * peers. Does not lookup the key locally. May do nothing if this is + * the only peer in the network (or if we are the closest peer in the + * network). + * + * @param type type of the block + * @param options routing options + * @param desired_replication_level desired replication count + * @param hop_count how many hops did this request traverse so far? + * @param key key for the content + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf bloomfilter to filter duplicates + * @param reply_bf_mutator mutator for reply_bf + * @param peer_bf filter for peers not to select (again) + */ +void +GDS_NEIGHBOURS_handle_get (enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + uint32_t desired_replication_level, + uint32_t hop_count, const GNUNET_HashCode * key, + const void *xquery, size_t xquery_size, + const struct GNUNET_CONTAINER_BloomFilter *reply_bf, + uint32_t reply_bf_mutator, + struct GNUNET_CONTAINER_BloomFilter *peer_bf) +{ + unsigned int target_count; + unsigned int i; + struct PeerInfo **targets; + struct PeerInfo *target; + struct P2PPendingMessage *pending; + size_t msize; + struct PeerGetMessage *pgm; + char *xq; + size_t reply_bf_size; + + GNUNET_assert (NULL != peer_bf); + GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# GET requests routed"), + 1, GNUNET_NO); + target_count = + get_target_peers (key, peer_bf, hop_count, desired_replication_level, + &targets); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding myself (%s) to GET bloomfilter for %s\n", + GNUNET_i2s (&my_identity), GNUNET_h2s (key)); +#endif + GNUNET_CONTAINER_bloomfilter_add (peer_bf, &my_identity.hashPubKey); + if (0 == target_count) + { +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing GET for %s terminates after %u hops at %s\n", + GNUNET_h2s (key), (unsigned int) hop_count, + GNUNET_i2s (&my_identity)); +#endif + return; + } + reply_bf_size = GNUNET_CONTAINER_bloomfilter_get_size (reply_bf); + msize = xquery_size + sizeof (struct PeerGetMessage) + reply_bf_size; + if (msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + GNUNET_free (targets); + return; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# GET messages queued for transmission"), + target_count, GNUNET_NO); + /* forward request */ + for (i = 0; i < target_count; i++) + { + target = targets[i]; +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing GET for %s after %u hops to %s\n", GNUNET_h2s (key), + (unsigned int) hop_count, GNUNET_i2s (&target->id)); +#endif + pending = GNUNET_malloc (sizeof (struct P2PPendingMessage) + msize); + pending->importance = 0; /* FIXME */ + pending->timeout = GNUNET_TIME_relative_to_absolute (GET_TIMEOUT); + pgm = (struct PeerGetMessage *) &pending[1]; + pending->msg = &pgm->header; + pgm->header.size = htons (msize); + pgm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_GET); + pgm->options = htonl (options); + pgm->type = htonl (type); + pgm->hop_count = htonl (hop_count + 1); + pgm->desired_replication_level = htonl (desired_replication_level); + pgm->xquery_size = htonl (xquery_size); + pgm->bf_mutator = reply_bf_mutator; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (peer_bf, + &target->id.hashPubKey)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_bloomfilter_get_raw_data (peer_bf, + pgm->bloomfilter, + DHT_BLOOM_SIZE)); + pgm->key = *key; + xq = (char *) &pgm[1]; + memcpy (xq, xquery, xquery_size); + if (NULL != reply_bf) + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_bloomfilter_get_raw_data (reply_bf, + &xq + [xquery_size], + reply_bf_size)); + GNUNET_CONTAINER_DLL_insert_tail (target->head, target->tail, pending); + target->pending_count++; + process_peer_queue (target); + } + GNUNET_free (targets); +} + + +/** + * Handle a reply (route to origin). Only forwards the reply back to + * the given peer. Does not do local caching or forwarding to local + * clients. + * + * @param target neighbour that should receive the block (if still connected) + * @param type type of the block + * @param expiration_time when does the content expire + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers the original PUT traversed (if tracked) + * @param get_path_length number of entries in put_path + * @param get_path peers this reply has traversed so far (if tracked) + * @param data payload of the reply + * @param data_size number of bytes in data + */ +void +GDS_NEIGHBOURS_handle_reply (const struct GNUNET_PeerIdentity *target, + enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute expiration_time, + const GNUNET_HashCode * key, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + const void *data, size_t data_size) +{ + struct PeerInfo *pi; + struct P2PPendingMessage *pending; + size_t msize; + struct PeerResultMessage *prm; + struct GNUNET_PeerIdentity *paths; + + msize = + data_size + sizeof (struct PeerResultMessage) + (get_path_length + + put_path_length) * + sizeof (struct GNUNET_PeerIdentity); + if ((msize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (get_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity)) || + (put_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity)) || + (data_size > GNUNET_SERVER_MAX_MESSAGE_SIZE)) + { + GNUNET_break (0); + return; + } + pi = GNUNET_CONTAINER_multihashmap_get (all_known_peers, &target->hashPubKey); + if (NULL == pi) + { + /* peer disconnected in the meantime, drop reply */ + return; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# RESULT messages queued for transmission"), 1, + GNUNET_NO); + pending = GNUNET_malloc (sizeof (struct P2PPendingMessage) + msize); + pending->importance = 0; /* FIXME */ + pending->timeout = expiration_time; + prm = (struct PeerResultMessage *) &pending[1]; + pending->msg = &prm->header; + prm->header.size = htons (msize); + prm->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT); + prm->type = htonl (type); + prm->put_path_length = htonl (put_path_length); + prm->get_path_length = htonl (get_path_length); + prm->expiration_time = GNUNET_TIME_absolute_hton (expiration_time); + prm->key = *key; + paths = (struct GNUNET_PeerIdentity *) &prm[1]; + memcpy (paths, put_path, + put_path_length * sizeof (struct GNUNET_PeerIdentity)); + memcpy (&paths[put_path_length], get_path, + get_path_length * sizeof (struct GNUNET_PeerIdentity)); + memcpy (&paths[put_path_length + get_path_length], data, data_size); + GNUNET_CONTAINER_DLL_insert (pi->head, pi->tail, pending); + pi->pending_count++; + process_peer_queue (pi); +} + + +/** + * To be called on core init/fail. + * + * @param cls service closure + * @param server handle to the server for this service + * @param identity the public identity of this peer + */ +static void +core_init (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *identity) +{ + GNUNET_assert (server != NULL); + my_identity = *identity; +} + + +/** + * Core handler for p2p put requests. + * + * @param cls closure + * @param peer sender of the request + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_dht_p2p_put (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct PeerPutMessage *put; + const struct GNUNET_PeerIdentity *put_path; + const void *payload; + uint32_t putlen; + uint16_t msize; + size_t payload_size; + enum GNUNET_DHT_RouteOption options; + struct GNUNET_CONTAINER_BloomFilter *bf; + GNUNET_HashCode test_key; + + msize = ntohs (message->size); + if (msize < sizeof (struct PeerPutMessage)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + put = (const struct PeerPutMessage *) message; + putlen = ntohl (put->put_path_length); + if ((msize < + sizeof (struct PeerPutMessage) + + putlen * sizeof (struct GNUNET_PeerIdentity)) || + (putlen > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# P2P PUT requests received"), 1, + GNUNET_NO); + put_path = (const struct GNUNET_PeerIdentity *) &put[1]; + payload = &put_path[putlen]; + options = ntohl (put->options); + payload_size = + msize - (sizeof (struct PeerPutMessage) + + putlen * sizeof (struct GNUNET_PeerIdentity)); + switch (GNUNET_BLOCK_get_key + (GDS_block_context, ntohl (put->type), payload, payload_size, + &test_key)) + { + case GNUNET_YES: + if (0 != memcmp (&test_key, &put->key, sizeof (GNUNET_HashCode))) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + break; + case GNUNET_NO: + GNUNET_break_op (0); + return GNUNET_YES; + case GNUNET_SYSERR: + /* cannot verify, good luck */ + break; + } +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PUT for %s at %s\n", + GNUNET_h2s (&put->key), GNUNET_i2s (&my_identity)); +#endif + bf = GNUNET_CONTAINER_bloomfilter_init (put->bloomfilter, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + GNUNET_break_op (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (bf, &peer->hashPubKey)); + { + struct GNUNET_PeerIdentity pp[putlen + 1]; + + /* extend 'put path' by sender */ + if (0 != (options & GNUNET_DHT_RO_RECORD_ROUTE)) + { + memcpy (pp, put_path, putlen * sizeof (struct GNUNET_PeerIdentity)); + pp[putlen] = *peer; + putlen++; + } + else + putlen = 0; + + /* give to local clients */ + GDS_CLIENTS_handle_reply (GNUNET_TIME_absolute_ntoh (put->expiration_time), + &put->key, 0, NULL, putlen, pp, ntohl (put->type), + payload_size, payload); + /* store locally */ + if ((0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) || + (am_closest_peer (&put->key, bf))) + GDS_DATACACHE_handle_put (GNUNET_TIME_absolute_ntoh + (put->expiration_time), &put->key, putlen, pp, + ntohl (put->type), payload_size, payload); + /* route to other peers */ + GDS_NEIGHBOURS_handle_put (ntohl (put->type), options, + ntohl (put->desired_replication_level), + GNUNET_TIME_absolute_ntoh (put->expiration_time), + ntohl (put->hop_count), bf, &put->key, putlen, + pp, payload, payload_size); + } + GNUNET_CONTAINER_bloomfilter_free (bf); + GDS_CLIENTS_process_monitor (GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT, + GNUNET_TIME_absolute_ntoh (put->expiration_time), &put->key, + putlen, put_path, 0, NULL, ntohl(put->desired_replication_level), + ntohl (put->type), payload, payload_size); + return GNUNET_YES; +} + + +/** + * We have received a FIND PEER request. Send matching + * HELLOs back. + * + * @param sender sender of the FIND PEER request + * @param key peers close to this key are desired + * @param bf peers matching this bf are excluded + * @param bf_mutator mutator for bf + */ +static void +handle_find_peer (const struct GNUNET_PeerIdentity *sender, + const GNUNET_HashCode * key, + struct GNUNET_CONTAINER_BloomFilter *bf, uint32_t bf_mutator) +{ + int bucket_idx; + struct PeerBucket *bucket; + struct PeerInfo *peer; + unsigned int choice; + GNUNET_HashCode mhash; + const struct GNUNET_HELLO_Message *hello; + + /* first, check about our own HELLO */ + if (NULL != GDS_my_hello) + { + GNUNET_BLOCK_mingle_hash (&my_identity.hashPubKey, bf_mutator, &mhash); + if ((NULL == bf) || + (GNUNET_YES != GNUNET_CONTAINER_bloomfilter_test (bf, &mhash))) + { + GDS_NEIGHBOURS_handle_reply (sender, GNUNET_BLOCK_TYPE_DHT_HELLO, + GNUNET_TIME_relative_to_absolute + (GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION), + key, 0, NULL, 0, NULL, GDS_my_hello, + GNUNET_HELLO_size ((const struct + GNUNET_HELLO_Message *) + GDS_my_hello)); + } + else + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# FIND PEER requests ignored due to Bloomfilter"), + 1, GNUNET_NO); + } + } + else + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# FIND PEER requests ignored due to lack of HELLO"), + 1, GNUNET_NO); + } + + /* then, also consider sending a random HELLO from the closest bucket */ + if (0 == memcmp (&my_identity.hashPubKey, key, sizeof (GNUNET_HashCode))) + bucket_idx = closest_bucket; + else + bucket_idx = GNUNET_MIN (closest_bucket, find_bucket (key)); + if (bucket_idx == GNUNET_SYSERR) + return; + bucket = &k_buckets[bucket_idx]; + if (bucket->peers_size == 0) + return; + choice = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, bucket->peers_size); + peer = bucket->head; + while (choice > 0) + { + GNUNET_assert (peer != NULL); + peer = peer->next; + choice--; + } + choice = bucket->peers_size; + do + { + peer = peer->next; + if (choice-- == 0) + return; /* no non-masked peer available */ + if (peer == NULL) + peer = bucket->head; + GNUNET_BLOCK_mingle_hash (&peer->id.hashPubKey, bf_mutator, &mhash); + hello = GDS_HELLO_get (&peer->id); + } + while ((hello == NULL) || + (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test (bf, &mhash))); + GDS_NEIGHBOURS_handle_reply (sender, GNUNET_BLOCK_TYPE_DHT_HELLO, + GNUNET_TIME_relative_to_absolute + (GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION), key, + 0, NULL, 0, NULL, hello, + GNUNET_HELLO_size (hello)); +} + + +/** + * Core handler for p2p get requests. + * + * @param cls closure + * @param peer sender of the request + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_dht_p2p_get (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct PeerGetMessage *get; + uint32_t xquery_size; + size_t reply_bf_size; + uint16_t msize; + enum GNUNET_BLOCK_Type type; + enum GNUNET_DHT_RouteOption options; + enum GNUNET_BLOCK_EvaluationResult eval; + struct GNUNET_CONTAINER_BloomFilter *reply_bf; + struct GNUNET_CONTAINER_BloomFilter *peer_bf; + const char *xquery; + + GNUNET_break (0 != + memcmp (peer, &my_identity, + sizeof (struct GNUNET_PeerIdentity))); + /* parse and validate message */ + msize = ntohs (message->size); + if (msize < sizeof (struct PeerGetMessage)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + get = (struct PeerGetMessage *) message; + xquery_size = ntohl (get->xquery_size); + if (msize < sizeof (struct PeerGetMessage) + xquery_size) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# P2P GET requests received"), 1, + GNUNET_NO); + reply_bf_size = msize - (sizeof (struct PeerGetMessage) + xquery_size); + type = ntohl (get->type); + options = ntohl (get->options); + xquery = (const char *) &get[1]; + reply_bf = NULL; + if (reply_bf_size > 0) + reply_bf = + GNUNET_CONTAINER_bloomfilter_init (&xquery[xquery_size], reply_bf_size, + GNUNET_CONSTANTS_BLOOMFILTER_K); + eval = + GNUNET_BLOCK_evaluate (GDS_block_context, type, &get->key, &reply_bf, + get->bf_mutator, xquery, xquery_size, NULL, 0); + if (eval != GNUNET_BLOCK_EVALUATION_REQUEST_VALID) + { + /* request invalid or block type not supported */ + GNUNET_break_op (eval == GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED); + if (NULL != reply_bf) + GNUNET_CONTAINER_bloomfilter_free (reply_bf); + return GNUNET_YES; + } + peer_bf = + GNUNET_CONTAINER_bloomfilter_init (get->bloomfilter, DHT_BLOOM_SIZE, + GNUNET_CONSTANTS_BLOOMFILTER_K); + GNUNET_break_op (GNUNET_YES == + GNUNET_CONTAINER_bloomfilter_test (peer_bf, + &peer->hashPubKey)); + /* remember request for routing replies */ + GDS_ROUTING_add (peer, type, options, &get->key, xquery, xquery_size, + reply_bf, get->bf_mutator); +#if DEBUG_DHT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GET for %s at %s after %u hops\n", + GNUNET_h2s (&get->key), GNUNET_i2s (&my_identity), + (unsigned int) ntohl (get->hop_count)); +#endif + /* local lookup (this may update the reply_bf) */ + if ((0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) || + (am_closest_peer (&get->key, peer_bf))) + { + if ((0 != (options & GNUNET_DHT_RO_FIND_PEER))) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# P2P FIND PEER requests processed"), 1, + GNUNET_NO); + handle_find_peer (peer, &get->key, reply_bf, get->bf_mutator); + } + else + { + eval = + GDS_DATACACHE_handle_get (&get->key, type, xquery, xquery_size, + &reply_bf, get->bf_mutator); + } + } + else + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# P2P GET requests ONLY routed"), + 1, GNUNET_NO); + } + + GDS_CLIENTS_process_monitor (GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET, + GNUNET_TIME_UNIT_FOREVER_ABS, &get->key, 0, NULL, 0, NULL, + ntohl (get->desired_replication_level), type, NULL, 0); + + /* P2P forwarding */ + if (eval != GNUNET_BLOCK_EVALUATION_OK_LAST) + GDS_NEIGHBOURS_handle_get (type, options, + ntohl (get->desired_replication_level), + ntohl (get->hop_count), &get->key, xquery, + xquery_size, reply_bf, get->bf_mutator, peer_bf); + /* clean up */ + if (NULL != reply_bf) + GNUNET_CONTAINER_bloomfilter_free (reply_bf); + GNUNET_CONTAINER_bloomfilter_free (peer_bf); + return GNUNET_YES; +} + + +/** + * Core handler for p2p result messages. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_YES (do not cut p2p connection) + */ +static int +handle_dht_p2p_result (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct PeerResultMessage *prm; + const struct GNUNET_PeerIdentity *put_path; + const struct GNUNET_PeerIdentity *get_path; + const void *data; + uint32_t get_path_length; + uint32_t put_path_length; + uint16_t msize; + size_t data_size; + enum GNUNET_BLOCK_Type type; + + /* parse and validate message */ + msize = ntohs (message->size); + if (msize < sizeof (struct PeerResultMessage)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + prm = (struct PeerResultMessage *) message; + put_path_length = ntohl (prm->put_path_length); + get_path_length = ntohl (prm->get_path_length); + if ((msize < + sizeof (struct PeerResultMessage) + (get_path_length + + put_path_length) * + sizeof (struct GNUNET_PeerIdentity)) || + (get_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity)) || + (put_path_length > + GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# P2P RESULTS received"), + 1, GNUNET_NO); + put_path = (const struct GNUNET_PeerIdentity *) &prm[1]; + get_path = &put_path[put_path_length]; + type = ntohl (prm->type); + data = (const void *) &get_path[get_path_length]; + data_size = + msize - (sizeof (struct PeerResultMessage) + + (get_path_length + + put_path_length) * sizeof (struct GNUNET_PeerIdentity)); + + /* if we got a HELLO, consider it for our own routing table */ + if (type == GNUNET_BLOCK_TYPE_DHT_HELLO) + { + const struct GNUNET_MessageHeader *h; + struct GNUNET_PeerIdentity pid; + int bucket; + + /* Should be a HELLO, validate and consider using it! */ + if (data_size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + h = data; + if (data_size != ntohs (h->size)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + if (GNUNET_OK != + GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) h, &pid)) + { + GNUNET_break_op (0); + return GNUNET_YES; + } + if (0 != memcmp (&my_identity, &pid, sizeof (struct GNUNET_PeerIdentity))) + { + bucket = find_bucket (&pid.hashPubKey); + if ((bucket >= 0) && (k_buckets[bucket].peers_size < bucket_size)) + { + if (NULL != GDS_transport_handle) + { + GNUNET_TRANSPORT_offer_hello (GDS_transport_handle, h, NULL, NULL); + GNUNET_TRANSPORT_try_connect (GDS_transport_handle, &pid); + } + } + } + } + + /* append 'peer' to 'get_path' */ + { + struct GNUNET_PeerIdentity xget_path[get_path_length + 1]; + + memcpy (xget_path, get_path, + get_path_length * sizeof (struct GNUNET_PeerIdentity)); + xget_path[get_path_length] = *peer; + get_path_length++; + + /* forward to local clients */ + GDS_CLIENTS_handle_reply (GNUNET_TIME_absolute_ntoh (prm->expiration_time), + &prm->key, get_path_length, xget_path, + put_path_length, put_path, type, data_size, data); + + /* forward to other peers */ + GDS_ROUTING_process (type, GNUNET_TIME_absolute_ntoh (prm->expiration_time), + &prm->key, put_path_length, put_path, get_path_length, + xget_path, data, data_size); + } + + GDS_CLIENTS_process_monitor (GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET_RESP, + GNUNET_TIME_absolute_ntoh (prm->expiration_time), &prm->key, + put_path_length, put_path, get_path_length, get_path, + 0, type, data, data_size); + + return GNUNET_YES; +} + + +/** + * Initialize neighbours subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GDS_NEIGHBOURS_init () +{ + static struct GNUNET_CORE_MessageHandler core_handlers[] = { + {&handle_dht_p2p_get, GNUNET_MESSAGE_TYPE_DHT_P2P_GET, 0}, + {&handle_dht_p2p_put, GNUNET_MESSAGE_TYPE_DHT_P2P_PUT, 0}, + {&handle_dht_p2p_result, GNUNET_MESSAGE_TYPE_DHT_P2P_RESULT, 0}, + {NULL, 0, 0} + }; + unsigned long long temp_config_num; + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (GDS_cfg, "DHT", "bucket_size", + &temp_config_num)) + bucket_size = (unsigned int) temp_config_num; + atsAPI = GNUNET_ATS_performance_init (GDS_cfg, NULL, NULL); + coreAPI = + GNUNET_CORE_connect (GDS_cfg, 1, NULL, &core_init, &handle_core_connect, + &handle_core_disconnect, NULL, GNUNET_NO, NULL, + GNUNET_NO, core_handlers); + if (coreAPI == NULL) + return GNUNET_SYSERR; + all_known_peers = GNUNET_CONTAINER_multihashmap_create (256); + return GNUNET_OK; +} + + +/** + * Shutdown neighbours subsystem. + */ +void +GDS_NEIGHBOURS_done () +{ + if (coreAPI == NULL) + return; + GNUNET_CORE_disconnect (coreAPI); + coreAPI = NULL; + GNUNET_ATS_performance_done (atsAPI); + atsAPI = NULL; + GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (all_known_peers)); + GNUNET_CONTAINER_multihashmap_destroy (all_known_peers); + all_known_peers = NULL; + if (GNUNET_SCHEDULER_NO_TASK != find_peer_task) + { + GNUNET_SCHEDULER_cancel (find_peer_task); + find_peer_task = GNUNET_SCHEDULER_NO_TASK; + } +} + + +/* end of gnunet-service-dht_neighbours.c */ diff --git a/src/dht/gnunet-service-dht_neighbours.h b/src/dht/gnunet-service-dht_neighbours.h new file mode 100644 index 0000000..b6e0f0e --- /dev/null +++ b/src/dht/gnunet-service-dht_neighbours.h @@ -0,0 +1,138 @@ +/* + 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 dht/gnunet-service-dht_neighbours.h + * @brief GNUnet DHT routing code + * @author Christian Grothoff + * @author Nathan Evans + */ +#ifndef GNUNET_SERVICE_DHT_NEIGHBOURS_H +#define GNUNET_SERVICE_DHT_NEIGHBOURS_H + +#include "gnunet_util_lib.h" +#include "gnunet_block_lib.h" +#include "gnunet_dht_service.h" + +/** + * Perform a PUT operation. Forwards the given request to other + * peers. Does not store the data locally. Does not give the + * data to local clients. May do nothing if this is the only + * peer in the network (or if we are the closest peer in the + * network). + * + * @param type type of the block + * @param options routing options + * @param desired_replication_level desired replication level + * @param expiration_time when does the content expire + * @param hop_count how many hops has this message traversed so far + * @param bf Bloom filter of peers this PUT has already traversed + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers this request has traversed so far (if tracked) + * @param data payload to store + * @param data_size number of bytes in data + */ +void +GDS_NEIGHBOURS_handle_put (enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + uint32_t desired_replication_level, + struct GNUNET_TIME_Absolute expiration_time, + uint32_t hop_count, + struct GNUNET_CONTAINER_BloomFilter *bf, + const GNUNET_HashCode * key, + unsigned int put_path_length, + struct GNUNET_PeerIdentity *put_path, + const void *data, size_t data_size); + + +/** + * Perform a GET operation. Forwards the given request to other + * peers. Does not lookup the key locally. May do nothing if this is + * the only peer in the network (or if we are the closest peer in the + * network). + * + * @param type type of the block + * @param options routing options + * @param desired_replication_level desired replication count + * @param hop_count how many hops did this request traverse so far? + * @param key key for the content + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf bloomfilter to filter duplicates + * @param reply_bf_mutator mutator for reply_bf + * @param peer_bf filter for peers not to select (again, updated) + */ +void +GDS_NEIGHBOURS_handle_get (enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + uint32_t desired_replication_level, + uint32_t hop_count, const GNUNET_HashCode * key, + const void *xquery, size_t xquery_size, + const struct GNUNET_CONTAINER_BloomFilter *reply_bf, + uint32_t reply_bf_mutator, + struct GNUNET_CONTAINER_BloomFilter *peer_bf); + + +/** + * Handle a reply (route to origin). Only forwards the reply back to + * other peers waiting for it. Does not do local caching or + * forwarding to local clients. + * + * @param target neighbour that should receive the block (if still connected) + * @param type type of the block + * @param expiration_time when does the content expire + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers the original PUT traversed (if tracked) + * @param get_path_length number of entries in put_path + * @param get_path peers this reply has traversed so far (if tracked) + * @param data payload of the reply + * @param data_size number of bytes in data + */ +void +GDS_NEIGHBOURS_handle_reply (const struct GNUNET_PeerIdentity *target, + enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute expiration_time, + const GNUNET_HashCode * key, + unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + const void *data, size_t data_size); + + +/** + * Initialize neighbours subsystem. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GDS_NEIGHBOURS_init (void); + + +/** + * Shutdown neighbours subsystem. + */ +void +GDS_NEIGHBOURS_done (void); + + +#endif diff --git a/src/dht/gnunet-service-dht_nse.c b/src/dht/gnunet-service-dht_nse.c new file mode 100644 index 0000000..7779989 --- /dev/null +++ b/src/dht/gnunet-service-dht_nse.c @@ -0,0 +1,101 @@ +/* + 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 dht/gnunet-service-dht_nse.c + * @brief GNUnet DHT integration with NSE + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_nse_service.h" +#include "gnunet-service-dht.h" +#include "gnunet-service-dht_nse.h" + +/** + * log of the current network size estimate, used as the point where + * we switch between random and deterministic routing. Default + * value of 4.0 is used if NSE module is not available (i.e. not + * configured). + */ +static double log_of_network_size_estimate = 4.0; + +/** + * Network size estimation handle. + */ +static struct GNUNET_NSE_Handle *nse; + + +/** + * Callback that is called when network size estimate is updated. + * + * @param cls closure + * @param timestamp time when the estimate was received from the server (or created by the server) + * @param logestimate the log(Base 2) value of the current network size estimate + * @param std_dev standard deviation for the estimate + * + */ +static void +update_network_size_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp, + double logestimate, double std_dev) +{ + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# Network size estimates received"), + 1, GNUNET_NO); + /* do not allow estimates < 0.5 */ + log_of_network_size_estimate = GNUNET_MAX (0.5, logestimate); +} + + +/** + * Return the log of the current network size estimate. + * + * @return log of NSE + */ +double +GDS_NSE_get () +{ + return log_of_network_size_estimate; +} + + +/** + * Initialize NSE subsystem. + */ +void +GDS_NSE_init () +{ + nse = GNUNET_NSE_connect (GDS_cfg, &update_network_size_estimate, NULL); +} + + +/** + * Shutdown NSE subsystem. + */ +void +GDS_NSE_done () +{ + if (NULL != nse) + { + GNUNET_NSE_disconnect (nse); + nse = NULL; + } +} + +/* end of gnunet-service-dht_nse.c */ diff --git a/src/dht/gnunet-service-dht_nse.h b/src/dht/gnunet-service-dht_nse.h new file mode 100644 index 0000000..bddd4bd --- /dev/null +++ b/src/dht/gnunet-service-dht_nse.h @@ -0,0 +1,52 @@ +/* + 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 dht/gnunet-service-dht_nse.h + * @brief GNUnet DHT integration with NSE + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_DHT_NSE_H +#define GNUNET_SERVICE_DHT_NSE_H + + +/** + * Return the log of the current network size estimate. + * + * @return log of NSE + */ +double +GDS_NSE_get (void); + + +/** + * Initialize NSE subsystem. + */ +void +GDS_NSE_init (void); + + +/** + * Shutdown NSE subsystem. + */ +void +GDS_NSE_done (void); + +#endif diff --git a/src/dht/gnunet-service-dht_routing.c b/src/dht/gnunet-service-dht_routing.c new file mode 100644 index 0000000..a880bf7 --- /dev/null +++ b/src/dht/gnunet-service-dht_routing.c @@ -0,0 +1,383 @@ +/* + 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 dht/gnunet-service-dht_routing.c + * @brief GNUnet DHT tracking of requests for routing replies + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet-service-dht_neighbours.h" +#include "gnunet-service-dht_routing.h" +#include "gnunet-service-dht.h" + + +/** + * Number of requests we track at most (for routing replies). + */ +#define DHT_MAX_RECENT (1024 * 16) + + +/** + * Information we keep about all recent GET requests + * so that we can route replies. + */ +struct RecentRequest +{ + + /** + * The peer this request was received from. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Key of this request. + */ + GNUNET_HashCode key; + + /** + * Position of this node in the min heap. + */ + struct GNUNET_CONTAINER_HeapNode *heap_node; + + /** + * Bloomfilter for replies to drop. + */ + struct GNUNET_CONTAINER_BloomFilter *reply_bf; + + /** + * Type of the requested block. + */ + enum GNUNET_BLOCK_Type type; + + /** + * extended query (see gnunet_block_lib.h). Allocated at the + * end of this struct. + */ + const void *xquery; + + /** + * Number of bytes in xquery. + */ + size_t xquery_size; + + /** + * Mutator value for the reply_bf, see gnunet_block_lib.h + */ + uint32_t reply_bf_mutator; + + /** + * Request options. + */ + enum GNUNET_DHT_RouteOption options; + +}; + + +/** + * Recent requests by time inserted. + */ +static struct GNUNET_CONTAINER_Heap *recent_heap; + +/** + * Recently seen requests by key. + */ +static struct GNUNET_CONTAINER_MultiHashMap *recent_map; + + +/** + * Closure for the 'process' function. + */ +struct ProcessContext +{ + /** + * Path of the original PUT + */ + const struct GNUNET_PeerIdentity *put_path; + + /** + * Path of the reply. + */ + const struct GNUNET_PeerIdentity *get_path; + + /** + * Payload of the reply. + */ + const void *data; + + /** + * Expiration time of the result. + */ + struct GNUNET_TIME_Absolute expiration_time; + + /** + * Number of entries in 'put_path'. + */ + unsigned int put_path_length; + + /** + * Number of entries in 'get_path'. + */ + unsigned int get_path_length; + + /** + * Number of bytes in 'data'. + */ + size_t data_size; + + /** + * Type of the reply. + */ + enum GNUNET_BLOCK_Type type; + +}; + + +/** + * Forward the result to the given peer if it matches the request. + * + * @param cls the 'struct ProcessContext' with the result + * @param key the query + * @param value the 'struct RecentRequest' with the request + * @return GNUNET_OK (continue to iterate), + * GNUNET_SYSERR if the result is malformed or type unsupported + */ +static int +process (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ProcessContext *pc = cls; + struct RecentRequest *rr = value; + enum GNUNET_BLOCK_EvaluationResult eval; + unsigned int gpl; + unsigned int ppl; + GNUNET_HashCode hc; + const GNUNET_HashCode *eval_key; + + if ((rr->type != GNUNET_BLOCK_TYPE_ANY) && (rr->type != pc->type)) + return GNUNET_OK; /* type missmatch */ + + if (0 != (rr->options & GNUNET_DHT_RO_RECORD_ROUTE)) + { + gpl = pc->get_path_length; + ppl = pc->put_path_length; + } + else + { + gpl = 0; + ppl = 0; + } + if ((0 != (rr->options & GNUNET_DHT_RO_FIND_PEER)) && + (pc->type == GNUNET_BLOCK_TYPE_DHT_HELLO)) + { + /* key may not match HELLO, which is OK since + * the search is approximate. Still, the evaluation + * would fail since the match is not exact. So + * we fake it by changing the key to the actual PID ... */ + GNUNET_BLOCK_get_key (GDS_block_context, GNUNET_BLOCK_TYPE_DHT_HELLO, + pc->data, pc->data_size, &hc); + eval_key = &hc; + } + else + { + eval_key = key; + } + eval = + GNUNET_BLOCK_evaluate (GDS_block_context, pc->type, eval_key, + &rr->reply_bf, rr->reply_bf_mutator, rr->xquery, + rr->xquery_size, pc->data, pc->data_size); + switch (eval) + { + case GNUNET_BLOCK_EVALUATION_OK_MORE: + case GNUNET_BLOCK_EVALUATION_OK_LAST: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Good REPLIES matched against routing table"), + 1, GNUNET_NO); + GDS_NEIGHBOURS_handle_reply (&rr->peer, pc->type, pc->expiration_time, key, + ppl, pc->put_path, gpl, pc->get_path, pc->data, + pc->data_size); + break; + case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Duplicate REPLIES matched against routing table"), + 1, GNUNET_NO); + return GNUNET_OK; + case GNUNET_BLOCK_EVALUATION_RESULT_INVALID: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Invalid REPLIES matched against routing table"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + case GNUNET_BLOCK_EVALUATION_REQUEST_VALID: + case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID: + GNUNET_break (0); + return GNUNET_OK; + case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED: + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Unsupported REPLIES matched against routing table"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle a reply (route to origin). Only forwards the reply back to + * other peers waiting for it. Does not do local caching or + * forwarding to local clients. Essentially calls + * GDS_NEIGHBOURS_handle_reply for all peers that sent us a matching + * request recently. + * + * @param type type of the block + * @param expiration_time when does the content expire + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers the original PUT traversed (if tracked) + * @param get_path_length number of entries in put_path + * @param get_path peers this reply has traversed so far (if tracked) + * @param data payload of the reply + * @param data_size number of bytes in data + */ +void +GDS_ROUTING_process (enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute expiration_time, + const GNUNET_HashCode * key, unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + const void *data, size_t data_size) +{ + struct ProcessContext pc; + + pc.type = type; + pc.expiration_time = expiration_time; + pc.put_path_length = put_path_length; + pc.put_path = put_path; + pc.get_path_length = get_path_length; + pc.get_path = get_path; + pc.data = data; + pc.data_size = data_size; + GNUNET_CONTAINER_multihashmap_get_multiple (recent_map, key, &process, &pc); +} + + +/** + * Add a new entry to our routing table. + * + * @param sender peer that originated the request + * @param type type of the block + * @param options options for processing + * @param key key for the content + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf bloomfilter to filter duplicates + * @param reply_bf_mutator mutator for reply_bf +*/ +void +GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender, + enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + const GNUNET_HashCode * key, const void *xquery, + size_t xquery_size, + const struct GNUNET_CONTAINER_BloomFilter *reply_bf, + uint32_t reply_bf_mutator) +{ + struct RecentRequest *recent_req; + + while (GNUNET_CONTAINER_heap_get_size (recent_heap) >= DHT_MAX_RECENT) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Entries removed from routing table"), 1, + GNUNET_NO); + recent_req = GNUNET_CONTAINER_heap_peek (recent_heap); + GNUNET_assert (recent_req != NULL); + GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node); + GNUNET_CONTAINER_bloomfilter_free (recent_req->reply_bf); + GNUNET_free (recent_req); + } + + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop ("# Entries added to routing table"), + 1, GNUNET_NO); + recent_req = GNUNET_malloc (sizeof (struct RecentRequest) + xquery_size); + recent_req->peer = *sender; + recent_req->key = *key; + recent_req->heap_node = + GNUNET_CONTAINER_heap_insert (recent_heap, recent_req, + GNUNET_TIME_absolute_get ().abs_value); + recent_req->reply_bf = GNUNET_CONTAINER_bloomfilter_copy (reply_bf); + recent_req->type = type; + recent_req->options = options; + recent_req->xquery = &recent_req[1]; + recent_req->xquery_size = xquery_size; + recent_req->reply_bf_mutator = reply_bf_mutator; + GNUNET_CONTAINER_multihashmap_put (recent_map, key, recent_req, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + + +} + + +/** + * Initialize routing subsystem. + */ +void +GDS_ROUTING_init () +{ + recent_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + recent_map = GNUNET_CONTAINER_multihashmap_create (DHT_MAX_RECENT * 4 / 3); +} + + +/** + * Shutdown routing subsystem. + */ +void +GDS_ROUTING_done () +{ + struct RecentRequest *recent_req; + + while (GNUNET_CONTAINER_heap_get_size (recent_heap) > 0) + { + GNUNET_STATISTICS_update (GDS_stats, + gettext_noop + ("# Entries removed from routing table"), 1, + GNUNET_NO); + recent_req = GNUNET_CONTAINER_heap_peek (recent_heap); + GNUNET_assert (recent_req != NULL); + GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node); + GNUNET_CONTAINER_bloomfilter_free (recent_req->reply_bf); + GNUNET_free (recent_req); + } + GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (recent_heap)); + GNUNET_CONTAINER_heap_destroy (recent_heap); + recent_heap = NULL; + GNUNET_CONTAINER_multihashmap_destroy (recent_map); + recent_map = NULL; +} + +/* end of gnunet-service-dht_routing.c */ diff --git a/src/dht/gnunet-service-dht_routing.h b/src/dht/gnunet-service-dht_routing.h new file mode 100644 index 0000000..9b12c71 --- /dev/null +++ b/src/dht/gnunet-service-dht_routing.h @@ -0,0 +1,96 @@ +/* + 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 dht/gnunet-service-dht_routing.h + * @brief GNUnet DHT tracking of requests for routing replies + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_DHT_ROUTING_H +#define GNUNET_SERVICE_DHT_ROUTING_H + +#include "gnunet_util_lib.h" +#include "gnunet_block_lib.h" +#include "gnunet_dht_service.h" + + +/** + * Handle a reply (route to origin). Only forwards the reply back to + * other peers waiting for it. Does not do local caching or + * forwarding to local clients. Essentially calls + * GDS_NEIGHBOURS_handle_reply for all peers that sent us a matching + * request recently. + * + * @param type type of the block + * @param expiration_time when does the content expire + * @param key key for the content + * @param put_path_length number of entries in put_path + * @param put_path peers the original PUT traversed (if tracked) + * @param get_path_length number of entries in put_path + * @param get_path peers this reply has traversed so far (if tracked) + * @param data payload of the reply + * @param data_size number of bytes in data + */ +void +GDS_ROUTING_process (enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute expiration_time, + const GNUNET_HashCode * key, unsigned int put_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *get_path, + const void *data, size_t data_size); + + +/** + * Add a new entry to our routing table. + * + * @param sender peer that originated the request + * @param type type of the block + * @param options options for processing + * @param key key for the content + * @param xquery extended query + * @param xquery_size number of bytes in xquery + * @param reply_bf bloomfilter to filter duplicates + * @param reply_bf_mutator mutator for reply_bf +*/ +void +GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender, + enum GNUNET_BLOCK_Type type, + enum GNUNET_DHT_RouteOption options, + const GNUNET_HashCode * key, const void *xquery, + size_t xquery_size, + const struct GNUNET_CONTAINER_BloomFilter *reply_bf, + uint32_t reply_bf_mutator); + + +/** + * Initialize routing subsystem. + */ +void +GDS_ROUTING_init (void); + + +/** + * Shutdown routing subsystem. + */ +void +GDS_ROUTING_done (void); + +#endif diff --git a/src/dht/multipeer_topo.dat b/src/dht/multipeer_topo.dat new file mode 100644 index 0000000..1233e6b --- /dev/null +++ b/src/dht/multipeer_topo.dat @@ -0,0 +1,31 @@ +10 +1:2 +2:3 +3:4 +4:5 +5:6 +6:7 +7:8 +8:9 +9:10 +10:1 +4:2 +5:3 +6:4 +7:5 +8:6 +9:7 +10:8 +1:9 +2:10 +3:1 +6:2 +7:3 +8:4 +9:5 +10:6 +1:7 +2:8 +3:9 +4:10 +5:1 diff --git a/src/dht/plugin_block_dht.c b/src/dht/plugin_block_dht.c new file mode 100644 index 0000000..19467b9 --- /dev/null +++ b/src/dht/plugin_block_dht.c @@ -0,0 +1,181 @@ +/* + This file is part of GNUnet + (C) 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 dht/plugin_block_dht.c + * @brief block plugin for DHT internals (right now, find-peer requests only); + * other plugins should be used to store "useful" data in the + * DHT (see fs block plugin) + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_hello_lib.h" +#include "gnunet_block_plugin.h" + +#define DEBUG_DHT GNUNET_EXTRA_LOGGING + + +/** + * Function called to validate a reply or a request. For + * request evaluation, simply pass "NULL" for the reply_block. + * + * @param cls closure + * @param type block type + * @param query original query (hash) + * @param bf pointer to bloom filter associated with query; possibly updated (!) + * @param bf_mutator mutation value for bf + * @param xquery extended query data (can be NULL, depending on type) + * @param xquery_size number of bytes in xquery + * @param reply_block response to validate + * @param reply_block_size number of bytes in reply block + * @return characterization of result + */ +static enum GNUNET_BLOCK_EvaluationResult +block_plugin_dht_evaluate (void *cls, enum GNUNET_BLOCK_Type type, + const GNUNET_HashCode * query, + struct GNUNET_CONTAINER_BloomFilter **bf, + int32_t bf_mutator, const void *xquery, + size_t xquery_size, const void *reply_block, + size_t reply_block_size) +{ + GNUNET_HashCode mhash; + const struct GNUNET_HELLO_Message *hello; + struct GNUNET_PeerIdentity pid; + const struct GNUNET_MessageHeader *msg; + + if (type != GNUNET_BLOCK_TYPE_DHT_HELLO) + return GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED; + if (xquery_size != 0) + return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; + if (reply_block_size == 0) + return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; + if (reply_block_size < sizeof (struct GNUNET_MessageHeader)) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + msg = reply_block; + if (reply_block_size != ntohs (msg->size)) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + hello = reply_block; + if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid)) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + if (NULL != bf) + { + GNUNET_BLOCK_mingle_hash (&pid.hashPubKey, bf_mutator, &mhash); + if (NULL != *bf) + { + if (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test (*bf, &mhash)) + return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; + } + else + { + *bf = + GNUNET_CONTAINER_bloomfilter_init (NULL, 8, + GNUNET_CONSTANTS_BLOOMFILTER_K); + } + GNUNET_CONTAINER_bloomfilter_add (*bf, &mhash); + } + return GNUNET_BLOCK_EVALUATION_OK_MORE; +} + + +/** + * Function called to obtain the key for a block. + * + * @param cls closure + * @param type block type + * @param block block to get the key for + * @param block_size number of bytes in block + * @param key set to the key (query) for the given block + * @return GNUNET_OK on success, GNUNET_SYSERR if type not supported + * (or if extracting a key from a block of this type does not work) + */ +static int +block_plugin_dht_get_key (void *cls, enum GNUNET_BLOCK_Type type, + const void *block, size_t block_size, + GNUNET_HashCode * key) +{ + const struct GNUNET_MessageHeader *msg; + const struct GNUNET_HELLO_Message *hello; + struct GNUNET_PeerIdentity *pid; + + if (type != GNUNET_BLOCK_TYPE_DHT_HELLO) + return GNUNET_SYSERR; + if (block_size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "block-dht", + _("Block not of type %u\n"), GNUNET_BLOCK_TYPE_DHT_HELLO); + return GNUNET_NO; + } + msg = block; + if (block_size != ntohs (msg->size)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "block-dht", + _("Size mismatch for block\n"), + GNUNET_BLOCK_TYPE_DHT_HELLO); + return GNUNET_NO; + } + hello = block; + pid = (struct GNUNET_PeerIdentity *) key; + if (GNUNET_OK != GNUNET_HELLO_get_id (hello, pid)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "block-dht", + _("Block of type %u is malformed\n"), + GNUNET_BLOCK_TYPE_DHT_HELLO); + return GNUNET_NO; + } + return GNUNET_OK; +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_block_dht_init (void *cls) +{ + static enum GNUNET_BLOCK_Type types[] = + { + GNUNET_BLOCK_TYPE_DHT_HELLO, + GNUNET_BLOCK_TYPE_ANY /* end of list */ + }; + struct GNUNET_BLOCK_PluginFunctions *api; + + api = GNUNET_malloc (sizeof (struct GNUNET_BLOCK_PluginFunctions)); + api->evaluate = &block_plugin_dht_evaluate; + api->get_key = &block_plugin_dht_get_key; + api->types = types; + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_block_dht_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + +/* end of plugin_block_dht.c */ diff --git a/src/dht/test_dht_2dtorus.conf b/src/dht/test_dht_2dtorus.conf new file mode 100644 index 0000000..d420b29 --- /dev/null +++ b/src/dht/test_dht_2dtorus.conf @@ -0,0 +1,87 @@ +[PATHS] +SERVICEHOME = /tmp/test_dht_topo/ +DEFAULTCONFIG = test_dht_2dtours.conf + +[arm] +PORT = 10010 +DEFAULTSERVICES = core dht +#DEBUG = YES + +[statistics] +AUTOSTART = YES +PORT = 10000 + +[dht] +DEBUG = NO +AUTOSTART = YES +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 10001 + +[dns] +AUTOSTART = NO +PORT = 10011 + +[transport] +PORT = 10002 +AUTOSTART = YES + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +AUTOSTART = YES +PORT = 10003 + +[peerinfo] +AUTOSTART = YES +PORT = 10004 + +[testing] +NUM_PEERS = 16 +WEAKRANDOM = YES +TOPOLOGY = NONE +CONNECT_TOPOLOGY = 2D_TORUS +BLACKLIST_TOPOLOGY = 2D_TORUS +#TOPOLOGY_FILE = small.dat +#CONNECT_TOPOLOGY = ERDOS_RENYI +#CONNECT_TOPOLOGY_OPTION = CONNECT_MINIMUM +#CONNECT_TOPOLOGY_OPTION_MODIFIER = 25 +#PERCENTAGE = 3 +#PROBABILITY = .1 +F2F = NO +CONNECT_TIMEOUT = 60 s +CONNECT_ATTEMPTS = 3 +DEBUG = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s +TOPOLOGY_OUTPUT_FILE = 2dtorus_topo_initial +MAX_OUTSTANDING_CONNECTIONS = 75 +#SINGLE_PEERINFO_PER_HOST = YES +#NUM_PEERINFO_PER_HOST = 10 +#SINGLE_STATISTICS_PER_HOST = YES +#NUM_STATISTICS_PER_HOST = 10 +DELETE_FILES = YES + +[test_dht_topo] +CONNECTION_LIMIT = 16 +#DATA_OUTPUT_FILE=data_output + + +[nse] +WORKDELAY = 500 ms +INTERVAL = 60 s +WORKBITS = 0 diff --git a/src/dht/test_dht_api.c b/src/dht/test_dht_api.c new file mode 100644 index 0000000..182856a --- /dev/null +++ b/src/dht/test_dht_api.c @@ -0,0 +1,339 @@ +/* + 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 dht/test_dht_api.c + * @brief base test case for dht api + * + * This test case tests DHT api to DUMMY DHT service communication. + * + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_hello_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_dht_service.h" +#include "gnunet_hello_lib.h" + +#define VERBOSE GNUNET_NO + +#define VERBOSE_ARM GNUNET_NO + +#define START_ARM GNUNET_YES + +/** + * How long until we really give up on a particular testcase portion? + */ +#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +/** + * How long until we give up on any particular operation (and retry)? + */ +#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3) + +#define MTYPE 12345 + +struct RetryContext +{ + /** + * When to really abort the operation. + */ + struct GNUNET_TIME_Absolute real_timeout; + + /** + * What timeout to set for the current attempt (increases) + */ + struct GNUNET_TIME_Relative next_timeout; + + /** + * The context of the peer we are dealing with. + */ + struct PeerContext *peer_ctx; + + /** + * The task identifier of the retry task, so it can be cancelled. + */ + GNUNET_SCHEDULER_TaskIdentifier retry_task; + +}; + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_DHT_Handle *dht_handle; + struct GNUNET_PeerIdentity id; + struct GNUNET_DHT_GetHandle *get_handle; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; + +struct RetryContext retry_context; + + +static int ok; + +static GNUNET_SCHEDULER_TaskIdentifier die_task; + +#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 +end (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_SCHEDULER_cancel (die_task); + die_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_DHT_disconnect (p1.dht_handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DHT disconnected, returning success!\n"); + ok = 0; +} + +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"); + GNUNET_OS_process_wait (p->arm_proc); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static void +end_badly () +{ + /* do work here */ +#if VERBOSE + FPRINTF (stderr, "%s", "Ending on an unhappy note.\n"); +#endif + + if ((retry_context.peer_ctx != NULL) && + (retry_context.peer_ctx->get_handle != NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping get request!\n"); + GNUNET_DHT_get_stop (retry_context.peer_ctx->get_handle); + } + if (retry_context.retry_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (retry_context.retry_task); + GNUNET_DHT_disconnect (p1.dht_handle); + ok = 1; +} + + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +test_get_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerContext *peer = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get_stop!\n"); + if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (die_task); + die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL); + return; + } + GNUNET_assert (peer->dht_handle != NULL); + GNUNET_DHT_get_stop (peer->get_handle); + peer->get_handle = NULL; + GNUNET_SCHEDULER_add_now (&end, &p1); +} + + +static void +test_get_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test_get_iterator called (we got a result), stopping get request!\n"); + + GNUNET_SCHEDULER_add_continuation (&test_get_stop, &p1, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); +} + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +test_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerContext *peer = cls; + GNUNET_HashCode hash; + + memset (&hash, 42, sizeof (GNUNET_HashCode)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_get!\n"); + + GNUNET_assert (peer->dht_handle != NULL); + retry_context.real_timeout = GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT); + retry_context.next_timeout = BASE_TIMEOUT; + + peer->get_handle = + GNUNET_DHT_get_start (peer->dht_handle, TOTAL_TIMEOUT, + GNUNET_BLOCK_TYPE_TEST, &hash, 1, + GNUNET_DHT_RO_NONE, NULL, 0, &test_get_iterator, + NULL); + + if (peer->get_handle == NULL) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (die_task); + die_task = GNUNET_SCHEDULER_add_now (&end_badly, &p1); + return; + } + + retry_context.peer_ctx = peer; +} + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +test_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerContext *peer = cls; + GNUNET_HashCode hash; + char *data; + size_t data_size = 42; + + memset (&hash, 42, sizeof (GNUNET_HashCode)); + data = GNUNET_malloc (data_size); + memset (data, 43, data_size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Called test_put!\n"); + peer->dht_handle = GNUNET_DHT_connect (peer->cfg, 100); + + GNUNET_assert (peer->dht_handle != NULL); + + GNUNET_DHT_put (peer->dht_handle, &hash, 1, GNUNET_DHT_RO_NONE, + GNUNET_BLOCK_TYPE_TEST, data_size, data, + GNUNET_TIME_relative_to_absolute (TOTAL_TIMEOUT), + TOTAL_TIMEOUT, &test_get, &p1); + GNUNET_free (data); +} + +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_ARM + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + +} + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + + die_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 1), &end_badly, + NULL); + + setup_peer (&p1, "test_dht_api_peer1.conf"); + + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 1), &test_put, &p1); +} + +static int +check () +{ + + char *const argv[] = { "test-dht-api", + "-c", + "test_dht_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-dht-api", "nohelp", options, &run, &ok); + stop_arm (&p1); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-dht-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-dht-peer-1"); + + return ret; +} + +/* end of test_dht_api.c */ diff --git a/src/dht/test_dht_api_data.conf b/src/dht/test_dht_api_data.conf new file mode 100644 index 0000000..0e34eb7 --- /dev/null +++ b/src/dht/test_dht_api_data.conf @@ -0,0 +1,85 @@ +[PATHS] +SERVICEHOME = /tmp/test-dht-api/ +DEFAULTCONFIG = test_dht_api_data.conf + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[datastore-sqlite] +FILENAME = $SERVICEHOME/datastore/sqlite.db + +[datastore] +AUTOSTART = NO + +[topology] +TARGET-CONNECTION-COUNT = 16 +AUTOCONNECT = YES +FRIENDS-ONLY = NO +MINIMUM-FRIENDS = 0 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +PORT = 2092 + +[dht] +DEBUG = NO +PORT = 12370 + +[block] +plugins = dht test + +[transport] +plugins = tcp +DEBUG = NO +NEIGHBOUR_LIMIT = 50 +PORT = 2091 + +[peerinfo] +PORT = 2090 + +[resolver] +PORT = 2089 + +[statistics] +PORT = 2088 + +[arm] +DEFAULTSERVICES = +PORT = 2087 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 2094 + +[TESTING] +WEAKRANDOM = NO +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[dns] +AUTOSTART = NO + + + +[nse] +AUTOSTART = NO + + diff --git a/src/dht/test_dht_api_peer1.conf b/src/dht/test_dht_api_peer1.conf new file mode 100644 index 0000000..cacc4da --- /dev/null +++ b/src/dht/test_dht_api_peer1.conf @@ -0,0 +1,76 @@ +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[dht] +DEBUG = NO +AUTOSTART = YES +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 2100 +BINARY = gnunet-service-dht + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +DEBUG = NO +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +PORT = 12092 + +[arm] +DEFAULTSERVICES = core +PORT = 12366 +DEBUG = NO + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 +BINDTO = 127.0.0.1 + +[TESTING] +WEAKRANDOM = YES + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +DEFAULTCONFIG = test_dht_api_peer1.conf +SERVICEHOME = /tmp/test-gnunetd-dht-peer-1/ + + +[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 + +[dns] +AUTOSTART = NO + + + +[nse] +AUTOSTART = NO + + diff --git a/src/dht/test_dht_line.conf b/src/dht/test_dht_line.conf new file mode 100644 index 0000000..8bcb12c --- /dev/null +++ b/src/dht/test_dht_line.conf @@ -0,0 +1,87 @@ +[PATHS] +SERVICEHOME = /tmp/test_dht_topo/ +DEFAULTCONFIG = test_dht_line.conf + +[arm] +PORT = 10010 +DEFAULTSERVICES = core dht +#DEBUG = YES + +[statistics] +AUTOSTART = YES +PORT = 10000 + +[dht] +DEBUG = NO +AUTOSTART = YES +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 10001 + +[dns] +AUTOSTART = NO +PORT = 10011 + +[transport] +PORT = 10002 +AUTOSTART = YES + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +AUTOSTART = YES +PORT = 10003 + +[peerinfo] +AUTOSTART = YES +PORT = 10004 + +[testing] +NUM_PEERS = 5 +WEAKRANDOM = YES +TOPOLOGY = NONE +CONNECT_TOPOLOGY = LINE +BLACKLIST_TOPOLOGY = LINE +#TOPOLOGY_FILE = small.dat +#CONNECT_TOPOLOGY = ERDOS_RENYI +#CONNECT_TOPOLOGY_OPTION = CONNECT_MINIMUM +#CONNECT_TOPOLOGY_OPTION_MODIFIER = 25 +#PERCENTAGE = 3 +#PROBABILITY = .1 +F2F = NO +CONNECT_TIMEOUT = 60 s +CONNECT_ATTEMPTS = 3 +DEBUG = YES +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat +MAX_CONCURRENT_SSH = 10 +USE_PROGRESSBARS = YES +PEERGROUP_TIMEOUT = 2400 s +TOPOLOGY_OUTPUT_FILE = line_topo_initial +MAX_OUTSTANDING_CONNECTIONS = 75 +#SINGLE_PEERINFO_PER_HOST = YES +#NUM_PEERINFO_PER_HOST = 10 +#SINGLE_STATISTICS_PER_HOST = YES +#NUM_STATISTICS_PER_HOST = 10 +DELETE_FILES = YES + +[test_dht_topo] +CONNECTION_LIMIT = 5 +#DATA_OUTPUT_FILE=data_output + + +[nse] +WORKDELAY = 500 ms +INTERVAL = 60 s +WORKBITS = 0 diff --git a/src/dht/test_dht_monitor.c b/src/dht/test_dht_monitor.c new file mode 100644 index 0000000..63af7e9 --- /dev/null +++ b/src/dht/test_dht_monitor.c @@ -0,0 +1,624 @@ +/* + 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 dht/test_dht_monitor.c + * + * @brief Test for the dht service: store, retrieve and monitor in a line. + * TODO: update this description + * Each peer stores it own ID in the DHT and then a different peer tries to + * retrieve that key from it. The GET starts after a first round of PUTS has + * been made. Periodically, each peer stores its ID into the DHT. If after + * a timeout no result has been returned, the test fails. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_dht_service.h" + +#define VERBOSE GNUNET_YES + +#define REMOVE_DIR GNUNET_YES + + +/** + * How long until we give up on connecting the peers? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500) + +#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) + +#define PUT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +static int ok; + +/** + * Be verbose + */ +static int verbose; + +/** + * Total number of peers in the test. + */ +static unsigned long long num_peers; + +/** + * Global configuration file + */ +static struct GNUNET_CONFIGURATION_Handle *testing_cfg; + +/** + * Total number of currently running peers. + */ +static unsigned long long peers_running; + +/** + * Total number of connections in the whole network. + */ +static unsigned int total_connections; + +/** + * The currently running peer group. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * File to report results to. + */ +static struct GNUNET_DISK_FileHandle *output_file; + +/** + * File to log connection info, statistics to. + */ +static struct GNUNET_DISK_FileHandle *data_file; + +/** + * Task called to disconnect peers. + */ +static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; + +/** + * Task To perform tests + */ +static GNUNET_SCHEDULER_TaskIdentifier test_task; + +/** + * Task to do DHT_puts + */ +static GNUNET_SCHEDULER_TaskIdentifier put_task; + +/** + * Task called to shutdown test. + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; + +static char *topology_file; + +struct GNUNET_TESTING_Daemon *d1; + +struct GNUNET_TESTING_Daemon *d2; + +struct GNUNET_DHT_Handle **hs; + +struct GNUNET_DHT_MonitorHandle **mhs; + +struct GNUNET_DHT_GetHandle *get_h_far; + +const char *id_origin = "FC74"; +const char *id_far = "2UVH"; + +struct GNUNET_TESTING_Daemon *d_far; +struct GNUNET_TESTING_Daemon *o; + +unsigned int monitor_counter; + +int in_test; + +/** + * Check whether peers successfully shut down. + */ +static void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Shutdown of peers failed!\n"); +#endif + ok++; + } + else + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test: All peers successfully shut down!\n"); +#endif + } + GNUNET_CONFIGURATION_destroy (testing_cfg); +} + + +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n"); +#endif + + if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (data_file != NULL) + GNUNET_DISK_file_close (data_file); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); +} + + +static void +disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n"); + disconnect_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_cancel (put_task); + if (NULL != get_h_far) + GNUNET_DHT_get_stop (get_h_far); + for (i = 0; i < num_peers; i++) + { + GNUNET_DHT_disconnect (hs[i]); + } + GNUNET_SCHEDULER_cancel (shutdown_handle); + shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); +} + +static void +dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test: ************* FOUND!!! ***********\n"); + if (sizeof (GNUNET_HashCode) == size) + { + const GNUNET_HashCode *h = data; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Contents: %s\n", + GNUNET_h2s_full (h)); + + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: PATH: (get %u, put %u)\n", + get_path_length, put_path_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: LOCAL\n"); + for (i = get_path_length - 1; i >= 0; i--) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: %s\n", + GNUNET_i2s (&get_path[i])); + } + for (i = put_path_length - 1; i >= 0; i--) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: %s\n", + GNUNET_i2s (&put_path[i])); + } + if (monitor_counter >= get_path_length + put_path_length) + { + ok = 0; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected at least %u hops, got %u\n", + get_path_length + put_path_length, monitor_counter); + } + else + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "expected at least %u hops, got %u\n", + get_path_length + put_path_length, monitor_counter); + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); +} + +/** + * Start test: start GET request from the first node in the line looking for + * the ID of the last node in the line. + * + * @param cls Closure (not used). + * @param tc Task context. + */ +static void +do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + { + return; + } + + in_test = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: test_task\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: looking for %s\n", + GNUNET_h2s_full (&d_far->id.hashPubKey)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: from %s\n", + GNUNET_h2s_full (&o->id.hashPubKey)); + get_h_far = GNUNET_DHT_get_start (hs[0], GNUNET_TIME_UNIT_FOREVER_REL, /* timeout */ + GNUNET_BLOCK_TYPE_TEST, /* type */ + &d_far->id.hashPubKey, /*key to search */ + 4U, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, /* xquery */ + 0, /* xquery bits */ + &dht_get_id_handler, NULL); + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL); +} + + +/** + * Periodic function used to put the ID of the far peer in the DHT. + * + * @param cls Closure (not used). + * @param tc Task context. + */ +static void +put_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TESTING_Daemon *d; + + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + { + put_task = GNUNET_SCHEDULER_NO_TASK; + return; + } + + d = GNUNET_TESTING_daemon_get (pg, 4); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: putting into DHT: %s\n", + GNUNET_h2s_full (&d->id.hashPubKey)); + GNUNET_DHT_put (hs[4], &d->id.hashPubKey, 10U, + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + GNUNET_BLOCK_TYPE_TEST, sizeof (struct GNUNET_PeerIdentity), + (const char *) &d->id, GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_TIME_UNIT_FOREVER_REL, NULL, NULL); + + put_task = GNUNET_SCHEDULER_add_delayed (PUT_FREQUENCY, &put_id, NULL); +} + +/** + * Callback called on each request going through the DHT. + * Prints the info about the intercepted packet and increments a counter. + * + * @param cls Closure (long) # of daemon that got the monitor event. + * @param mtype Type of the DHT message monitored. + * @param exp When will this value expire. + * @param key Key of the result/request. + * @param get_path Peers on reply path (or NULL if not recorded). + * @param get_path_length number of entries in get_path. + * @param put_path peers on the PUT path (or NULL if not recorded). + * @param put_path_length number of entries in get_path. + * @param desired_replication_level Desired replication level. + * @param type Type of the result/request. + * @param data Pointer to the result data. + * @param size Number of bytes in data. + */ +void +monitor_dht_cb (void *cls, + uint16_t mtype, + struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity * put_path, + unsigned int put_path_length, + uint32_t desired_replication_level, + enum GNUNET_DHT_RouteOption options, + enum GNUNET_BLOCK_Type type, + const void *data, + size_t size) +{ + const char *s_key; + const char *mtype_s; + unsigned int i; + + i = (unsigned int) (long) cls; + s_key = GNUNET_h2s(key); + switch (mtype) + { + case 149: + mtype_s = "GET "; + break; + case 150: + mtype_s = "RESULT"; + break; + case 151: + mtype_s = "PUT "; + break; + default: + GNUNET_break (0); + mtype_s = "UNKNOWN!!!"; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "%u got a message of type %s for key %s\n", + i, mtype_s, s_key); + + if ((mtype == GNUNET_MESSAGE_TYPE_DHT_MONITOR_GET || + mtype == GNUNET_MESSAGE_TYPE_DHT_MONITOR_PUT) && + strncmp (s_key, id_far, 4) == 0 && in_test == GNUNET_YES) + monitor_counter++; +} + + +/** + * peergroup_ready: start test when all peers are connected + * + * @param cls closure + * @param emsg error message + */ +static void +peergroup_ready (void *cls, const char *emsg) +{ + struct GNUNET_TESTING_Daemon *d; + char *buf; + int buf_len; + unsigned int i; + + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test: Peergroup callback called with error, aborting test!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Error from testing: `%s'\n", + emsg); + ok++; + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + return; + } +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "************************************************************\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test: Peer Group started successfully!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Have %u connections\n", + total_connections); +#endif + + if (data_file != NULL) + { + buf = NULL; + buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections); + if (buf_len > 0) + GNUNET_DISK_file_write (data_file, buf, buf_len); + GNUNET_free (buf); + } + peers_running = GNUNET_TESTING_daemons_running (pg); + + GNUNET_assert (peers_running == num_peers); + hs = GNUNET_malloc (num_peers * sizeof (struct GNUNET_DHT_Handle *)); + mhs = GNUNET_malloc (num_peers * sizeof (struct GNUNET_DHT_MonitorHandle *)); + d_far = o = NULL; + o = GNUNET_TESTING_daemon_get (pg, 0); + d_far = GNUNET_TESTING_daemon_get (pg, 4); + + for (i = 0; i < num_peers; i++) + { + d = GNUNET_TESTING_daemon_get (pg, i); + hs[i] = GNUNET_DHT_connect (d->cfg, 32); + mhs[i] = GNUNET_DHT_monitor_start(hs[i], GNUNET_BLOCK_TYPE_ANY, NULL, + &monitor_dht_cb, (void *)(long)i); + } + + if ((NULL == o) || (NULL == d_far)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "test: Error getting daemons from pg\n"); + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); + return; + } + monitor_counter = 0; + put_task = GNUNET_SCHEDULER_add_now (&put_id, NULL); + test_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 2), &do_test, + NULL); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL); + +} + + +/** + * Function that will be called whenever two daemons are connected by + * the testing library. + * + * @param cls closure + * @param first peer id for first daemon + * @param second peer id for the second daemon + * @param distance distance between the connected peers + * @param first_cfg config for the first daemon + * @param second_cfg config for the second daemon + * @param first_daemon handle for the first daemon + * @param second_daemon handle for the second daemon + * @param emsg error message (NULL on success) + */ +static void +connect_cb (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg) +{ + + if (emsg == NULL) + { + total_connections++; + GNUNET_PEER_intern (first); + GNUNET_PEER_intern (second); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "test: Problem with new connection (%s)\n", emsg); + } + +} + + +/** + * run: load configuration options and schedule test to run (start peergroup) + * @param cls closure + * @param args argv + * @param cfgfile configuration file name (can be NULL) + * @param cfg configuration handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *temp_str; + struct GNUNET_TESTING_Host *hosts; + char *data_filename; + + ok = 1; + testing_cfg = GNUNET_CONFIGURATION_dup (cfg); + + GNUNET_log_setup ("test_dht_monitor", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n"); + GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", + "use_progressbars", "YES"); +#endif + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing", + "num_peers", &num_peers)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Option TESTING:NUM_PEERS is required!\n"); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing", + "topology_output_file", + &topology_file)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Option test_dht_monitor:topology_output_file is required!\n"); + return; + } + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_dht_topo", + "data_output_file", + &data_filename)) + { + data_file = + GNUNET_DISK_file_open (data_filename, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (data_file == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", + data_filename); + GNUNET_free (data_filename); + } + } + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (cfg, "test_dht_topo", + "output_file", &temp_str)) + { + output_file = + GNUNET_DISK_file_open (temp_str, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (output_file == NULL) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", + temp_str); + } + GNUNET_free_non_null (temp_str); + + hosts = GNUNET_TESTING_hosts_load (testing_cfg); + + pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT, + &connect_cb, &peergroup_ready, NULL, + hosts); + GNUNET_assert (pg != NULL); + shutdown_handle = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); +} + + + +/** + * test_dht_monitor command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'V', "verbose", NULL, + gettext_noop ("be verbose (print progress information)"), + 0, &GNUNET_GETOPT_set_one, &verbose}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * Main: start test + */ +int +main (int xargc, char *xargv[]) +{ + char *const argv[] = { "test-dht-monitor", + "-c", + "test_dht_line.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + + in_test = GNUNET_NO; + GNUNET_PROGRAM_run (sizeof (argv) / sizeof (char *) - 1, argv, + "test_dht_monitor", + gettext_noop ("Test dht monitoring in a line."), + options, &run, NULL); +#if REMOVE_DIR + GNUNET_DISK_directory_remove ("/tmp/test_dht_monitor"); +#endif + if (0 != ok) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: FAILED!\n"); + } + return ok; +} + +/* end of test_dht_monitor.c */ diff --git a/src/dht/test_dht_multipeer.c b/src/dht/test_dht_multipeer.c new file mode 100644 index 0000000..94e39d2 --- /dev/null +++ b/src/dht/test_dht_multipeer.c @@ -0,0 +1,872 @@ +/* + 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 dht/test_dht_multipeer.c + * @brief testcase for testing DHT service with + * multiple peers. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_dht_service.h" + +/* DEFINES */ +#define VERBOSE GNUNET_NO + +/* Timeout for entire testcase */ +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 30) + +/* Timeout for waiting for replies to get requests */ +#define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 300) + +/* */ +#define START_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) + +/* Timeout for waiting for gets to complete */ +#define GET_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 50) + +/* Timeout for waiting for puts to complete */ +#define PUT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 50) + +/* If number of peers not in config file, use this number */ +#define DEFAULT_NUM_PEERS 10 + +#define TEST_DATA_SIZE 8 + +#define MAX_OUTSTANDING_PUTS 100 + +#define MAX_OUTSTANDING_GETS 100 + +#define PATH_TRACKING GNUNET_NO + + + +struct TestPutContext +{ + /** + * This is a linked list + */ + struct TestPutContext *next; + + /** + * This is a linked list + */ + struct TestPutContext *prev; + + /** + * Handle to the first peers DHT service (via the API) + */ + struct GNUNET_DHT_Handle *dht_handle; + + /** + * Handle to the PUT peer daemon + */ + struct GNUNET_TESTING_Daemon *daemon; + + /** + * Identifier for this PUT + */ + uint32_t uid; + + /** + * Task handle for processing of the put. + */ + GNUNET_SCHEDULER_TaskIdentifier task; +}; + + +struct TestGetContext +{ + /** + * This is a linked list + */ + struct TestGetContext *next; + + /** + * This is a linked list + */ + struct TestGetContext *prev; + + /** + * Handle to the first peers DHT service (via the API) + */ + struct GNUNET_DHT_Handle *dht_handle; + + /** + * Handle for the DHT get request + */ + struct GNUNET_DHT_GetHandle *get_handle; + + /** + * Handle to the GET peer daemon + */ + struct GNUNET_TESTING_Daemon *daemon; + + /** + * Identifier for this GET + */ + uint32_t uid; + + /** + * Task for disconnecting DHT handles (and stopping GET) + */ + GNUNET_SCHEDULER_TaskIdentifier task; + + /** + * Whether or not this request has been fulfilled already. + */ + int succeeded; +}; + + +/** + * List of GETS to perform + */ +static struct TestGetContext *all_gets_head; + +/** + * List of GETS to perform + */ +static struct TestGetContext *all_gets_tail; + +/** + * List of PUTS to perform + */ +static struct TestPutContext *all_puts_head; + +/** + * List of PUTS to perform + */ +static struct TestPutContext *all_puts_tail; + +/** + * Handle to the set of all peers run for this test. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * Total number of peers to run, set based on config file. + */ +static unsigned long long num_peers; + +/** + * How many puts do we currently have in flight? + */ +static unsigned long long outstanding_puts; + +/** + * How many puts are done? + */ +static unsigned long long puts_completed; + +/** + * How many puts do we currently have in flight? + */ +static unsigned long long outstanding_gets; + +/** + * How many gets are done? + */ +static unsigned long long gets_completed; + +/** + * How many gets failed? + */ +static unsigned long long gets_failed; + +/** + * Directory to remove on shutdown. + */ +static char *test_directory; + +/** + * Option to use when routing. + */ +static enum GNUNET_DHT_RouteOption route_option; + +/** + * Task handle to use to schedule test failure / success. + */ +static GNUNET_SCHEDULER_TaskIdentifier die_task; + +/** + * Task handle to use to schedule test shutdown + */ +GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +/** + * Global return value (0 for success, anything else for failure) + */ +static int ok; + + +/** + * Check whether peers successfully shut down. + */ +static void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + FPRINTF (stderr, "Failed to shutdown testing topology: %s\n", emsg); + if (ok == 0) + ok = 2; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown callback completed.\n"); +} + +static void +do_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) == 0) + { + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + { + GNUNET_SCHEDULER_cancel(shutdown_task); + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + } + } + else + { + shutdown_task = GNUNET_SCHEDULER_NO_TASK ; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown requested.\n"); + if (NULL != pg) + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + pg = NULL; +} + + +/** + * Master context for 'stat_run'. + */ +struct StatMaster +{ + struct GNUNET_STATISTICS_Handle *stat; + unsigned int daemon; + unsigned int value; +}; + +struct StatValues +{ + const char *subsystem; + const char *name; + unsigned long long total; +}; + +/** + * Statistics we print out. + */ +static struct StatValues stats[] = { + {"core", "# bytes decrypted", 0}, + {"core", "# bytes encrypted", 0}, + {"core", "# type maps received", 0}, + {"core", "# session keys confirmed via PONG", 0}, + {"core", "# entries in session map", 0}, + {"core", "# key exchanges initiated", 0}, + {"core", "# send requests dropped (disconnected)", 0}, + {"core", "# transmissions delayed due to corking", 0}, + {"core", "# messages discarded (expired prior to transmission)", 0}, + {"core", "# messages discarded (disconnected)", 0}, + {"core", "# discarded CORE_SEND requests", 0}, + {"core", "# discarded lower priority CORE_SEND requests", 0}, + {"transport", "# bytes received via TCP", 0}, + {"transport", "# bytes transmitted via TCP", 0}, + {"dht", "# PUT messages queued for transmission", 0}, + {"dht", "# P2P PUT requests received", 0}, + {"dht", "# GET messages queued for transmission", 0}, + {"dht", "# P2P GET requests received", 0}, + {"dht", "# RESULT messages queued for transmission", 0}, + {"dht", "# P2P RESULTS received", 0}, + {"dht", "# Queued messages discarded (peer disconnected)", 0}, + {"dht", "# Peers excluded from routing due to Bloomfilter", 0}, + {"dht", "# Peer selection failed", 0}, + {"dht", "# FIND PEER requests ignored due to Bloomfilter", 0}, + {"dht", "# FIND PEER requests ignored due to lack of HELLO", 0}, + {"dht", "# P2P FIND PEER requests processed", 0}, + {"dht", "# P2P GET requests ONLY routed", 0}, + {"dht", "# Preference updates given to core", 0}, + {"dht", "# REPLIES ignored for CLIENTS (no match)", 0}, + {"dht", "# GET requests from clients injected", 0}, + {"dht", "# GET requests received from clients", 0}, + {"dht", "# GET STOP requests received from clients", 0}, + {"dht", "# ITEMS stored in datacache", 0}, + {"dht", "# Good RESULTS found in datacache", 0}, + {"dht", "# GET requests given to datacache", 0}, + {NULL, NULL, 0} +}; + + +/** + * 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) +{ + struct StatMaster *sm = cls; + + stats[sm->value].total += value; + FPRINTF (stderr, "Peer %2u: %12s/%50s = %12llu\n", sm->daemon, subsystem, + name, (unsigned long long) value); + return GNUNET_OK; +} + + +/** + * Function that gathers stats from all daemons. + */ +static void +stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Function called when GET operation on stats is done. + */ +static void +get_done (void *cls, int success) +{ + struct StatMaster *sm = cls; + + GNUNET_break (GNUNET_OK == success); + sm->value++; + GNUNET_SCHEDULER_add_now (&stat_run, sm); +} + + +/** + * Function that gathers stats from all daemons. + */ +static void +stat_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct StatMaster *sm = cls; + unsigned int i; + + die_task = GNUNET_SCHEDULER_NO_TASK; + if (stats[sm->value].name != NULL) + { + GNUNET_STATISTICS_get (sm->stat, +#if 0 + NULL, NULL, +#else + stats[sm->value].subsystem, stats[sm->value].name, +#endif + GNUNET_TIME_UNIT_FOREVER_REL, &get_done, &print_stat, + sm); + return; + } + GNUNET_STATISTICS_destroy (sm->stat, GNUNET_NO); + sm->value = 0; + sm->daemon++; + if (sm->daemon == num_peers) + { + GNUNET_free (sm); + i = 0; + while (stats[i].name != NULL) + { + FPRINTF (stderr, "Total : %12s/%50s = %12llu\n", stats[i].subsystem, + stats[i].name, (unsigned long long) stats[i].total); + i++; + } + die_task = GNUNET_SCHEDULER_add_now (&do_stop, NULL); + return; + } + sm->stat = + GNUNET_STATISTICS_create ("<driver>", + GNUNET_TESTING_daemon_get (pg, + sm->daemon)->cfg); + die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm); +} + + +/** + * Function scheduled to be run on the successful completion of this + * testcase. + */ +static void +finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestPutContext *test_put; + struct TestGetContext *test_get; + struct StatMaster *sm; + + die_task = GNUNET_SCHEDULER_NO_TASK; + while (NULL != (test_put = all_puts_head)) + { + if (test_put->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (test_put->task); + if (test_put->dht_handle != NULL) + GNUNET_DHT_disconnect (test_put->dht_handle); + GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put); + GNUNET_free (test_put); + } + + while (NULL != (test_get = all_gets_head)) + { + if (test_get->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (test_get->task); + if (test_get->get_handle != NULL) + GNUNET_DHT_get_stop (test_get->get_handle); + if (test_get->dht_handle != NULL) + GNUNET_DHT_disconnect (test_get->dht_handle); + GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get); + GNUNET_free (test_get); + } + sm = GNUNET_malloc (sizeof (struct StatMaster)); + sm->stat = + GNUNET_STATISTICS_create ("<driver>", + GNUNET_TESTING_daemon_get (pg, + sm->daemon)->cfg); + die_task = GNUNET_SCHEDULER_add_now (&stat_run, sm); +} + + +/** + * Check if the get_handle is being used, if so stop the request. Either + * way, schedule the end_badly_cont function which actually shuts down the + * test. + */ +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + const char *emsg = cls; + struct TestPutContext *test_put; + struct TestGetContext *test_get; + + die_task = GNUNET_SCHEDULER_NO_TASK; + FPRINTF (stderr, "Failing test with error: `%s'!\n", emsg); + while (NULL != (test_put = all_puts_head)) + { + if (test_put->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (test_put->task); + if (test_put->dht_handle != NULL) + GNUNET_DHT_disconnect (test_put->dht_handle); + GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put); + GNUNET_free (test_put); + } + + while (NULL != (test_get = all_gets_head)) + { + if (test_get->task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (test_get->task); + if (test_get->get_handle != NULL) + GNUNET_DHT_get_stop (test_get->get_handle); + if (test_get->dht_handle != NULL) + GNUNET_DHT_disconnect (test_get->dht_handle); + GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get); + GNUNET_free (test_get); + } + ok = 1; + /* testing_peergroup will do that in its own end_badly() handler */ + /*GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); */ + pg = NULL; +} + + +/** + * Task to release get handle. + */ +static void +get_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestGetContext *test_get = cls; + GNUNET_HashCode search_key; /* Key stored under */ + char original_data[TEST_DATA_SIZE]; /* Made up data to store */ + + test_get->task = GNUNET_SCHEDULER_NO_TASK; + memset (original_data, test_get->uid, sizeof (original_data)); + GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key); + if (test_get->succeeded != GNUNET_YES) + { + gets_failed++; + FPRINTF (stderr, "Get from peer %s for key %s failed!\n", + GNUNET_i2s (&test_get->daemon->id), GNUNET_h2s (&search_key)); + } + GNUNET_assert (test_get->get_handle != NULL); + GNUNET_DHT_get_stop (test_get->get_handle); + test_get->get_handle = NULL; + + outstanding_gets--; /* GET is really finished */ + GNUNET_DHT_disconnect (test_get->dht_handle); + test_get->dht_handle = NULL; + + GNUNET_CONTAINER_DLL_remove (all_gets_head, all_gets_tail, test_get); + GNUNET_free (test_get); + if ((gets_failed > 10) && (outstanding_gets == 0)) + { + /* Had more than 10% failures */ + FPRINTF (stderr, "%llu gets succeeded, %llu gets failed!\n", gets_completed, + gets_failed); + GNUNET_SCHEDULER_cancel (die_task); + ok = 1; + die_task = + GNUNET_SCHEDULER_add_now (&finish_testing, "not all gets succeeded"); + return; + } + if ((gets_completed + gets_failed == num_peers * num_peers) && (outstanding_gets == 0)) /* All gets successful */ + { + FPRINTF (stderr, "%llu gets succeeded, %llu gets failed!\n", gets_completed, + gets_failed); + GNUNET_SCHEDULER_cancel (die_task); + ok = 0; + die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL); + } +} + + +/** + * Iterator called if the GET request initiated returns a response. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + struct TestGetContext *test_get = cls; + GNUNET_HashCode search_key; /* Key stored under */ + char original_data[TEST_DATA_SIZE]; /* Made up data to store */ + + memset (original_data, test_get->uid, sizeof (original_data)); + GNUNET_CRYPTO_hash (original_data, TEST_DATA_SIZE, &search_key); + if (test_get->succeeded == GNUNET_YES) + return; /* Get has already been successful, probably ending now */ + +#if PATH_TRACKING + if (put_path != NULL) + { + unsigned int i; + + FPRINTF (stderr, "PUT (%u) Path: ", test_get->uid); + for (i = 0; i < put_path_length; i++) + FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&put_path[i])); + FPRINTF (stderr, "%s", "\n"); + } + if (get_path != NULL) + { + unsigned int i; + + FPRINTF (stderr, "GET (%u) Path: ", test_get->uid); + for (i = 0; i < get_path_length; i++) + FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&get_path[i])); + FPRINTF (stderr, "%s%s\n", get_path_length > 0 ? "->" : "", + GNUNET_i2s (&test_get->daemon->id)); + } +#endif + + if ((0 != memcmp (&search_key, key, sizeof (GNUNET_HashCode))) || + (0 != memcmp (original_data, data, sizeof (original_data)))) + { + FPRINTF (stderr, "%s", "Key or data is not the same as was inserted!\n"); + return; + } + gets_completed++; + test_get->succeeded = GNUNET_YES; + GNUNET_SCHEDULER_cancel (test_get->task); + test_get->task = GNUNET_SCHEDULER_add_now (&get_stop_task, test_get); +} + + +/** + * Set up some data, and call API PUT function + */ +static void +do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestGetContext *test_get = cls; + GNUNET_HashCode key; /* Made up key to store data under */ + char data[TEST_DATA_SIZE]; /* Made up data to store */ + + if (outstanding_gets > MAX_OUTSTANDING_GETS) + { + test_get->task = + GNUNET_SCHEDULER_add_delayed (GET_DELAY, &do_get, test_get); + return; + } + memset (data, test_get->uid, sizeof (data)); + GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key); + test_get->dht_handle = GNUNET_DHT_connect (test_get->daemon->cfg, 10); + GNUNET_assert (test_get->dht_handle != NULL); + outstanding_gets++; + test_get->get_handle = + GNUNET_DHT_get_start (test_get->dht_handle, GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_BLOCK_TYPE_TEST, &key, 1, route_option, NULL, + 0, &get_result_iterator, test_get); + test_get->task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &get_stop_task, test_get); +} + + +/** + * Task to release DHT handles for PUT + */ +static void +put_disconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestPutContext *test_put = cls; + + test_put->task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_DHT_disconnect (test_put->dht_handle); + test_put->dht_handle = NULL; + GNUNET_CONTAINER_DLL_remove (all_puts_head, all_puts_tail, test_put); + GNUNET_free (test_put); +} + + +/** + * Schedule the GET requests + */ +static void +start_gets (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned long long i; + unsigned long long j; + struct TestGetContext *test_get; + +#if VERBOSE + FPRINTF (stderr, "Issuing %llu GETs\n", + (unsigned long long) (num_peers * num_peers)); +#endif + for (i = 0; i < num_peers; i++) + for (j = 0; j < num_peers; j++) + { + test_get = GNUNET_malloc (sizeof (struct TestGetContext)); + test_get->uid = i + j * num_peers; + test_get->daemon = GNUNET_TESTING_daemon_get (pg, j); + GNUNET_CONTAINER_DLL_insert (all_gets_head, all_gets_tail, test_get); + test_get->task = GNUNET_SCHEDULER_add_now (&do_get, test_get); + } +} + + +/** + * Called when the PUT request has been transmitted to the DHT service. + */ +static void +put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestPutContext *test_put = cls; + + outstanding_puts--; + puts_completed++; + if (GNUNET_SCHEDULER_NO_TASK != test_put->task) + { + GNUNET_SCHEDULER_cancel (test_put->task); + } + test_put->task = GNUNET_SCHEDULER_add_now (&put_disconnect_task, test_put); + if (puts_completed != num_peers * num_peers) + return; + + GNUNET_assert (outstanding_puts == 0); + GNUNET_SCHEDULER_add_delayed (START_DELAY, &start_gets, NULL); +} + + +/** + * Set up some data, and call API PUT function + */ +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TestPutContext *test_put = cls; + GNUNET_HashCode key; /* Made up key to store data under */ + char data[TEST_DATA_SIZE]; /* Made up data to store */ + + test_put->task = GNUNET_SCHEDULER_NO_TASK; + if (outstanding_puts > MAX_OUTSTANDING_PUTS) + { + test_put->task = + GNUNET_SCHEDULER_add_delayed (PUT_DELAY, &do_put, test_put); + return; + } + memset (data, test_put->uid, sizeof (data)); + GNUNET_CRYPTO_hash (data, TEST_DATA_SIZE, &key); + test_put->dht_handle = GNUNET_DHT_connect (test_put->daemon->cfg, 10); + GNUNET_assert (test_put->dht_handle != NULL); + outstanding_puts++; +#if VERBOSE > 2 + FPRINTF (stderr, "PUT %u at `%s'\n", test_put->uid, + GNUNET_i2s (&test_put->daemon->id)); +#endif + GNUNET_DHT_put (test_put->dht_handle, &key, 1, route_option, + GNUNET_BLOCK_TYPE_TEST, sizeof (data), data, + GNUNET_TIME_UNIT_FOREVER_ABS, GNUNET_TIME_UNIT_FOREVER_REL, + &put_finished, test_put); + test_put->task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &put_disconnect_task, test_put); +} + + +static void +run_dht_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned long long i; + struct TestPutContext *test_put; + + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + { + ok = 1; + return; + } +#if PATH_TRACKING + route_option = + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE; +#else + route_option = GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE; +#endif + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from setup puts/gets"); + FPRINTF (stderr, "Issuing %llu PUTs (one per peer)\n", + (unsigned long long) (num_peers * num_peers)); + for (i = 0; i < num_peers * num_peers; i++) + { + test_put = GNUNET_malloc (sizeof (struct TestPutContext)); + test_put->uid = i; + test_put->daemon = GNUNET_TESTING_daemon_get (pg, i % num_peers); + test_put->task = GNUNET_SCHEDULER_add_now (&do_put, test_put); + GNUNET_CONTAINER_DLL_insert (all_puts_head, all_puts_tail, test_put); + } +} + + +/** + * This function is called once testing has finished setting up the topology. + * + * @param cls unused + * @param emsg variable is NULL on success (peers connected), and non-NULL on + * failure (peers failed to connect). + */ +static void +startup_done (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + FPRINTF (stderr, "Failed to setup topology: %s\n", emsg); + die_task = GNUNET_SCHEDULER_add_now (&end_badly, "topology setup failed"); + return; + } + die_task = + GNUNET_SCHEDULER_add_delayed (START_DELAY, &run_dht_test, + "from setup puts/gets"); +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + /* Get path from configuration file */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome", + &test_directory)) + { + GNUNET_break (0); + ok = 404; + return; + } + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", + &num_peers)) + num_peers = DEFAULT_NUM_PEERS; + pg = GNUNET_TESTING_peergroup_start (cfg, num_peers, TIMEOUT, NULL, + &startup_done, NULL, NULL); + GNUNET_assert (NULL != pg); + shutdown_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL, + &do_stop, NULL); +} + + +static int +check () +{ + int ret; + + /* Arguments for GNUNET_PROGRAM_run */ + char *const argv[] = { "test-dht-multipeer", /* Name to give running binary */ + "-c", + "test_dht_multipeer_data.conf", /* Config file to use */ +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + /* Run the run function as a new program */ + ret = + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-dht-multipeer", "nohelp", options, &run, &ok); + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`test-dht-multipeer': Failed with error code %d\n", ret); + } + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret; + + + GNUNET_log_setup ("test-dht-multipeer", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to remove testing directory %s\n", test_directory); + } + return ret; +} + +/* end of test_dht_multipeer.c */ diff --git a/src/dht/test_dht_multipeer_data.conf b/src/dht/test_dht_multipeer_data.conf new file mode 100644 index 0000000..ff87698 --- /dev/null +++ b/src/dht/test_dht_multipeer_data.conf @@ -0,0 +1,128 @@ +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[dht] +DEBUG = NO +STOP_ON_CLOSEST = YES +AUTOSTART = YES +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +#BINARY = /home/mrwiggles/documents/research/gnunet/gnunet-ng/src/dht/.libs/gnunet-service-dht +#PREFIX = xterm -T dht -e gdb --args +#PREFIX = valgrind --log-file=dht_%p +CONFIG = $DEFAULTCONFIG +HOME = $SERVICEHOME +HOSTNAME = localhost +PORT = 2100 +STOP_FOUND = YES +USE_MAX_HOPS = YES +MAX_HOPS = 16 +CONVERGE_BINARY = YES +CONVERGE_MODIFIER = 4 + +[block] +plugins = test dht + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +DEBUG = NO +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +BINARY = gnunet-service-transport +CONFIG = $DEFAULTCONFIG +HOME = $SERVICEHOME +HOSTNAME = localhost +PORT = 12365 + +[DHTLOG] +PLUGIN = mysql_dump + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +BINARY = gnunet-service-core +CONFIG = $DEFAULTCONFIG +HOME = $SERVICEHOME +HOSTNAME = localhost +PORT = 12092 +DEBUG = NO + +[arm] +DEFAULTSERVICES = dht core +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +BINARY = gnunet-service-arm +CONFIG = $DEFAULTCONFIG +HOME = $SERVICEHOME +HOSTNAME = localhost +PORT = 12366 +DEBUG = NO + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 +BINDTO = 127.0.0.1 + +[DHT_TESTING] +MYSQL_LOGGING_EXTENDED = NO +MYSQL_LOGGING = NO +NUM_GETS = 5 +NUM_PUTS = 5 + +[TESTING] +TOPOLOGY = FROM_FILE +# file contains a ring +CONNECT_TOPOLOGY = NONE +# None == use all allowed connections +# BLACKLIST_TOPOLOGY = X +# No additional restrictions... + +TOPOLOGY_FILE = multipeer_topo.dat +MAX_CONCURRENT_SSH = 1 +PEERGROUP_TIMEOUT = 2400 s +USE_PROGRESSBARS = YES +#CONNECT_TOPOLOGY_OPTION = CONNECT_RANDOM_SUBSET +#CONNECT_TOPOLOGY_OPTION_MODIFIER = 2 +#LOGNMODIFIER = .65 +#PERCENTAGE = .75 +WEAKRANDOM = YES +NUM_PEERS = 10 +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +DEFAULTCONFIG = test_dht_multipeer_data.conf +SERVICEHOME = /tmp/test-dht-multipeer/ + +[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 + +[dns] +AUTOSTART = NO + + + +[nse] +AUTOSTART = NO + + diff --git a/src/dht/test_dht_tools.sh b/src/dht/test_dht_tools.sh new file mode 100755 index 0000000..f83c26a --- /dev/null +++ b/src/dht/test_dht_tools.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +out=`mktemp /tmp/test-gnunet-dht-logXXXXXXXX` +tempcfg=`mktemp /tmp/test_dht_api_peer1.XXXXXXXX` +checkout="check.out" +armexe="gnunet-arm -c $tempcfg " +putexe="gnunet-dht-put -c $tempcfg " +getexe="gnunet-dht-get -c $tempcfg " +peerinfo="gnunet-peerinfo -c $tempcfg -sq" +stop_arm() +{ + if ! $armexe $DEBUG -e -d > $out ; then + echo "FAIL: error running $armexe" + echo "Command output was:" + cat $out + rm -f $out $tempcfg + exit 1 + fi + rm -f $out $tempcfg +} + +cp test_dht_api_peer1.conf $tempcfg + +echo -n "TEST: Generating hostkey..." +if ! $peerinfo > $out ; then + echo "FAIL: error running $peerinfo" + echo "Command output was:" + cat $out + exit 1 +fi +echo "PASS" + +echo -n "TEST: Starting ARM..." +if ! $armexe $DEBUG -s > $out ; then + echo "FAIL: error running $armexe" + echo "Command output was:" + cat $out + stop_arm + exit 1 +fi +echo "PASS" +sleep 1 + +echo -n "TEST: Testing put..." +if ! $putexe -k testkey -d testdata -t 8 > $out ; then + echo "FAIL: error running $putexe" + echo "Command output was:" + cat $out + stop_arm + exit 1 +fi +echo "PASS" +sleep 1 + +echo -n "TEST: Testing get..." +echo "Result 0, type 8:" > $checkout +echo "testdata" >> $checkout + +if ! $getexe -k testkey -T 5 -t 8 > $out ; then + echo "FAIL: error running $putexe" + echo "Command output was:" + cat $out + stop_arm + exit 1 +fi + +if ! diff --strip-trailing-cr -q $out $checkout ; then + echo "FAIL: $out and $checkout differ" + stop_arm + exit 1 +fi +echo "PASS" +stop_arm diff --git a/src/dht/test_dht_topo.c b/src/dht/test_dht_topo.c new file mode 100644 index 0000000..81dc7cb --- /dev/null +++ b/src/dht/test_dht_topo.c @@ -0,0 +1,662 @@ +/* + This file is part of GNUnet. + (C) 2012 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 dht/test_dht_topo.c + * + * @brief Test for the dht service: store and retrieve in various topologies. + * Each peer stores it own ID in the DHT and then a different peer tries to + * retrieve that key from it. The GET starts after a first round of PUTS has + * been made. Periodically, each peer stores its ID into the DHT. If after + * a timeout no result has been returned, the test fails. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_dht_service.h" + +#define VERBOSE GNUNET_NO + +#define REMOVE_DIR GNUNET_YES + +/** + * DIFFERENT TESTS TO RUN + */ +#define LINE 0 +#define TORUS 1 + +/** + * How long until we give up on connecting the peers? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500) + +#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) + +#define PUT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * Result of the test. + */ +static int ok; + +/** + * Be verbose + */ +static int verbose; + +/** + * Total number of peers in the test. + */ +static unsigned long long num_peers; + +/** + * Global configuration file + */ +static struct GNUNET_CONFIGURATION_Handle *testing_cfg; + +/** + * Total number of currently running peers. + */ +static unsigned long long peers_running; + +/** + * Total number of connections in the whole network. + */ +static unsigned int total_connections; + +/** + * The currently running peer group. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * File to report results to. + */ +static struct GNUNET_DISK_FileHandle *output_file; + +/** + * File to log connection info, statistics to. + */ +static struct GNUNET_DISK_FileHandle *data_file; + +/** + * Task called to disconnect peers. + */ +static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; + +/** + * Task To perform tests + */ +static GNUNET_SCHEDULER_TaskIdentifier test_task; + +/** + * Task to do DHT_puts + */ +static GNUNET_SCHEDULER_TaskIdentifier put_task; + +/** + * Task called to shutdown test. + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; + +static char *topology_file; + +struct GNUNET_TESTING_Daemon *d1; + +struct GNUNET_TESTING_Daemon *d2; + +struct GNUNET_DHT_Handle **hs; + +struct GNUNET_DHT_GetHandle *get_h; + +struct GNUNET_DHT_GetHandle *get_h_2; + +struct GNUNET_DHT_GetHandle *get_h_far; + +int found_1; +int found_2; +int found_far; + +/** + * Which topology are we to run + */ +static int test_topology; + +/** + * Check whether peers successfully shut down. + */ +static void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n"); +#endif + ok++; + } + else + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All peers successfully shut down!\n"); +#endif + } + GNUNET_CONFIGURATION_destroy (testing_cfg); +} + + +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); +#endif + + if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (data_file != NULL) + GNUNET_DISK_file_close (data_file); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); +} + + +static void +disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnecting peers\n"); + disconnect_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_cancel (put_task); + if (NULL != get_h) + GNUNET_DHT_get_stop (get_h); + if (NULL != get_h_2) + GNUNET_DHT_get_stop (get_h_2); + if (NULL != get_h_far) + GNUNET_DHT_get_stop (get_h_far); + for (i = 0; i < num_peers; i++) + { + GNUNET_DHT_disconnect (hs[i]); + } + GNUNET_SCHEDULER_cancel (shutdown_handle); + shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); +} + +static void +dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + int i; + + if (sizeof (GNUNET_HashCode) == size) + { + const GNUNET_HashCode *h = data; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Contents: %s\n", + GNUNET_h2s_full (h)); + + } + else + { + GNUNET_break(0); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PATH: (get %u, put %u)\n", + get_path_length, put_path_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " LOCAL\n"); + for (i = get_path_length - 1; i >= 0; i--) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " %s\n", + GNUNET_i2s (&get_path[i])); + } + for (i = put_path_length - 1; i >= 0; i--) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " %s\n", + GNUNET_i2s (&put_path[i])); + } + switch ((long)cls) + { + case 1: + found_1++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "FOUND 1!\n"); + break; + case 2: + found_2++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "FOUND 2!\n"); + break; + case 3: + found_far++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "FOUND FAR!\n"); + break; + default: + GNUNET_break(0); + } + if (TORUS == test_topology && + (found_1 == 0 || found_2 == 0 || found_far == 0)) + return; + ok = 0; + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); +} + +static void +do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TESTING_Daemon *d; + struct GNUNET_TESTING_Daemon *d2; + struct GNUNET_TESTING_Daemon *d_far; + struct GNUNET_TESTING_Daemon *o; + struct GNUNET_TESTING_Daemon *aux; + const char *id_aux; + const char *id_origin = "FC74"; + const char *id_near = "9P6V"; + const char *id_near2 = "2GDS"; + const char *id_far = "KPST"; + unsigned int i; + + d = d2 = d_far = o = NULL; + found_1 = found_2 = found_far = 0; + if (LINE == test_topology) + { + o = GNUNET_TESTING_daemon_get (pg, 0); + d = GNUNET_TESTING_daemon_get (pg, 4); + } + else if (TORUS == test_topology) + { + for (i = 0; i < num_peers; i++) + { + aux = GNUNET_TESTING_daemon_get (pg, i); + id_aux = GNUNET_i2s (&aux->id); + if (strcmp (id_aux, id_origin) == 0) + o = aux; + if (strcmp (id_aux, id_far) == 0) + d_far = aux; + if (strcmp (id_aux, id_near) == 0) + d = aux; + if (strcmp (id_aux, id_near2) == 0) + d2 = aux; + } + if ((NULL == o) || (NULL == d) || (NULL == d2) || (NULL == d_far)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Peers not found (hostkey file changed?)\n"); + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); + return; + } + } + else + { + GNUNET_assert (0); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\ntest: from %s\n", + GNUNET_h2s_full (&o->id.hashPubKey)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " looking for %s\n", + GNUNET_h2s_full (&d->id.hashPubKey)); + get_h = GNUNET_DHT_get_start (hs[0], GNUNET_TIME_UNIT_FOREVER_REL, /* timeout */ + GNUNET_BLOCK_TYPE_TEST, /* type */ + &d->id.hashPubKey, /*key to search */ + 4U, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, /* xquery */ + 0, /* xquery bits */ + &dht_get_id_handler, (void *)1); + if (TORUS == test_topology) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " looking for %s\n", + GNUNET_h2s_full (&d2->id.hashPubKey)); + get_h_2 = GNUNET_DHT_get_start (hs[0], GNUNET_TIME_UNIT_FOREVER_REL, /* timeout */ + GNUNET_BLOCK_TYPE_TEST, /* type */ + &d2->id.hashPubKey, /*key to search */ + 4U, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, /* xquery */ + 0, /* xquery bits */ + &dht_get_id_handler, (void *)2); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " looking for %s\n", + GNUNET_h2s_full (&d_far->id.hashPubKey)); + get_h_far = GNUNET_DHT_get_start (hs[0], GNUNET_TIME_UNIT_FOREVER_REL, /* timeout */ + GNUNET_BLOCK_TYPE_TEST, /* type */ + &d_far->id.hashPubKey, /*key to search */ + 4U, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, /* xquery */ + 0, /* xquery bits */ + &dht_get_id_handler, (void *)3); + } + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL); +} + +/** + * Task to put the id of each peer into teh DHT. + * + * @param cls Closure (unused) + * @param tc Task context + * + */ +static void +put_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TESTING_Daemon *d; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "putting id's in DHT\n"); + for (i = 0; i < num_peers; i++) + { + d = GNUNET_TESTING_daemon_get (pg, i); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " putting into DHT: %s\n", + GNUNET_h2s_full (&d->id.hashPubKey)); + GNUNET_DHT_put (hs[i], &d->id.hashPubKey, 10U, + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + GNUNET_BLOCK_TYPE_TEST, sizeof (struct GNUNET_PeerIdentity), + (const char *) &d->id, GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_TIME_UNIT_FOREVER_REL, NULL, NULL); + + } + put_task = GNUNET_SCHEDULER_add_delayed (PUT_FREQUENCY, &put_id, NULL); + if (GNUNET_SCHEDULER_NO_TASK == test_task) + test_task = GNUNET_SCHEDULER_add_now (&do_test, NULL); +} + + +/** + * peergroup_ready: start test when all peers are connected + * + * @param cls closure + * @param emsg error message + * + */ +static void +peergroup_ready (void *cls, const char *emsg) +{ + struct GNUNET_TESTING_Daemon *d; + char *buf; + int buf_len; + unsigned int i; + + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peergroup callback called with error, aborting test!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error from testing: `%s'\n", + emsg); + ok++; + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + return; + } +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "************************************************************\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer Group started successfully!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %u connections\n", + total_connections); +#endif + + if (data_file != NULL) + { + buf = NULL; + buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections); + if (buf_len > 0) + GNUNET_DISK_file_write (data_file, buf, buf_len); + GNUNET_free (buf); + } + peers_running = GNUNET_TESTING_daemons_running (pg); + + GNUNET_assert (peers_running == num_peers); + hs = GNUNET_malloc (num_peers * sizeof (struct GNUNET_DHT_Handle *)); + for (i = 0; i < num_peers; i++) + { + d = GNUNET_TESTING_daemon_get (pg, i); + hs[i] = GNUNET_DHT_connect (d->cfg, 32); + } + + test_task = GNUNET_SCHEDULER_NO_TASK; + put_task = GNUNET_SCHEDULER_add_now (&put_id, NULL); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &disconnect_peers, NULL); + +} + + +/** + * Function that will be called whenever two daemons are connected by + * the testing library. + * + * @param cls closure + * @param first peer id for first daemon + * @param second peer id for the second daemon + * @param distance distance between the connected peers + * @param first_cfg config for the first daemon + * @param second_cfg config for the second daemon + * @param first_daemon handle for the first daemon + * @param second_daemon handle for the second daemon + * @param emsg error message (NULL on success) + */ +static void +connect_cb (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg) +{ + + if (emsg == NULL) + { + total_connections++; + GNUNET_PEER_intern (first); + GNUNET_PEER_intern (second); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Problem with new connection (%s)\n", emsg); + } + +} + + +/** + * run: load configuration options and schedule test to run (start peergroup) + * @param cls closure + * @param args argv + * @param cfgfile configuration file name (can be NULL) + * @param cfg configuration handle + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *temp_str; + struct GNUNET_TESTING_Host *hosts; + char *data_filename; + + ok = 1; + testing_cfg = GNUNET_CONFIGURATION_dup (cfg); + + GNUNET_log_setup ("test_dht_topo", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting daemons.\n"); + GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", + "use_progressbars", "YES"); +#endif + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing", + "num_peers", &num_peers)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Option TESTING:NUM_PEERS is required!\n"); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing", + "topology_output_file", + &topology_file)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Option test_dht_topo:topology_output_file is required!\n"); + return; + } + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_dht_topo", + "data_output_file", + &data_filename)) + { + data_file = + GNUNET_DISK_file_open (data_filename, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (data_file == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", + data_filename); + GNUNET_free (data_filename); + } + } + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (cfg, "test_dht_topo", + "output_file", &temp_str)) + { + output_file = + GNUNET_DISK_file_open (temp_str, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (output_file == NULL) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", + temp_str); + } + GNUNET_free_non_null (temp_str); + + hosts = GNUNET_TESTING_hosts_load (testing_cfg); + + pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT, + &connect_cb, &peergroup_ready, NULL, + hosts); + GNUNET_assert (pg != NULL); + shutdown_handle = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); +} + + + +/** + * test_dht_2d command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'V', "verbose", NULL, + gettext_noop ("be verbose (print progress information)"), + 0, &GNUNET_GETOPT_set_one, &verbose}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * Main: start test + */ +int +main (int xargc, char *xargv[]) +{ + char *const argv_torus[] = { "test-dht-2dtorus", + "-c", + "test_dht_2dtorus.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + char *const argv_line[] = { "test-dht-line", + "-c", + "test_dht_line.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + char *const *argv; + int argc; + + if (strstr (xargv[0], "test_dht_2dtorus") != NULL) + { + argv = argv_torus; + argc = sizeof (argv_torus) / sizeof (char *); + test_topology = TORUS; + } + else if (strstr (xargv[0], "test_dht_line") != NULL) + { + argv = argv_line; + argc = sizeof (argv_line) / sizeof (char *); + test_topology = LINE; + } + else + { + GNUNET_break (0); + return 1; + } + GNUNET_PROGRAM_run (argc - 1, argv, + xargv[0], + gettext_noop ("Test dht in different topologies."), + options, + &run, NULL); +#if REMOVE_DIR + GNUNET_DISK_directory_remove ("/tmp/test_dht_topo"); +#endif + if (found_1 == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ID 1 not found!\n"); + } + if (TORUS == test_topology) + { + if (found_2 == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ID 2 not found!\n"); + } + if (found_far == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ID far not found!\n"); + } + } + return ok; +} + +/* end of test_dht_topo.c */ diff --git a/src/dht/test_dht_twopeer.c b/src/dht/test_dht_twopeer.c new file mode 100644 index 0000000..a3b6e4a --- /dev/null +++ b/src/dht/test_dht_twopeer.c @@ -0,0 +1,513 @@ +/* + 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 dht/test_dht_twopeer.c + * @brief base testcase for testing DHT service with + * two running peers + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_dht_service.h" + +/* DEFINES */ +#define VERBOSE GNUNET_NO + +#define MAX_GET_ATTEMPTS 10 + +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5) + +#define DEFAULT_NUM_PEERS 2 + +/* Structs */ + +struct PeerGetContext +{ + struct GNUNET_PeerIdentity *peer; + + struct GNUNET_DHT_Handle *dht_handle; + + struct GNUNET_DHT_GetHandle *get_handle; + + unsigned int get_attempts; + + GNUNET_SCHEDULER_TaskIdentifier retry_task; +}; + +/* Globals */ +static char *test_directory; + +static struct PeerGetContext curr_get_ctx; + +static unsigned int expected_connections; + +static unsigned long long peers_left; + +static struct GNUNET_TESTING_PeerGroup *pg; + +static unsigned long long num_peers; + +static unsigned int total_gets; + +static unsigned int gets_succeeded; + +static unsigned int total_connections; + +static unsigned int failed_connections; + +static GNUNET_SCHEDULER_TaskIdentifier die_task; + +static int ok; + +static struct GNUNET_PeerIdentity peer1id; + +static struct GNUNET_PeerIdentity peer2id; + +static struct GNUNET_DHT_Handle *peer1dht; + +static struct GNUNET_DHT_Handle *peer2dht; + +/** + * Check whether peers successfully shut down. + */ +static void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + if (ok == 0) + ok = 2; + } +} + +static void +finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (pg != NULL); + GNUNET_assert (peer1dht != NULL); + GNUNET_assert (peer2dht != NULL); + GNUNET_DHT_disconnect (peer1dht); + GNUNET_DHT_disconnect (peer2dht); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + pg = NULL; + ok = 0; +} + +static void +end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (peer1dht != NULL) + GNUNET_DHT_disconnect (peer1dht); + + if (peer2dht != NULL) + GNUNET_DHT_disconnect (peer2dht); + + if (pg != NULL) + { + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + pg = NULL; + } + + if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task); + curr_get_ctx.retry_task = GNUNET_SCHEDULER_NO_TASK; + } +} + + +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + const char *emsg = cls; + + FPRINTF (stderr, "Error: %s\n", emsg); + if (curr_get_ctx.retry_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (curr_get_ctx.retry_task); + curr_get_ctx.retry_task = GNUNET_SCHEDULER_NO_TASK; + } + if (curr_get_ctx.get_handle != NULL) + { + GNUNET_DHT_get_stop (curr_get_ctx.get_handle); + } + + GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL); + ok = 1; +} + + +/* Forward declaration */ +static void +do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Iterator called on each result obtained for a DHT + * operation that expects a reply + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + struct PeerGetContext *get_context = cls; + + if (0 != + memcmp (&get_context->peer->hashPubKey, key, sizeof (GNUNET_HashCode))) + { + FPRINTF (stderr, "%s", "??\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Key returned is not the same key as was searched for!\n"); + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "key mismatch in get response!\n"); + return; + } + if (get_context->retry_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (get_context->retry_task); + get_context->retry_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (get_context->peer == &peer2id) + { + get_context->peer = &peer1id; + get_context->dht_handle = peer2dht; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received first correct GET request response!\n"); + GNUNET_DHT_get_stop (get_context->get_handle); + GNUNET_SCHEDULER_add_now (&do_get, get_context); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received second correct GET request response!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_DHT_get_stop (get_context->get_handle); + die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL); + } + +} + +static void +stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +static void +get_stop_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerGetContext *get_context = cls; + + if (get_context->get_attempts >= MAX_GET_ATTEMPTS) + { + FPRINTF (stderr, "%s", "?\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Too many attempts failed, ending test!\n", + get_context->get_attempts); + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "GET attempt failed, ending test!\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Get attempt %u failed, retrying request!\n", + get_context->get_attempts); + FPRINTF (stderr, "%s", "."); + get_context->get_attempts++; + get_context->retry_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 60), + &stop_retry_get, get_context); + get_context->get_handle = + GNUNET_DHT_get_start (get_context->dht_handle, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), + GNUNET_BLOCK_TYPE_DHT_HELLO, + &get_context->peer->hashPubKey, 1, + GNUNET_DHT_RO_NONE, NULL, 0, &get_result_iterator, + get_context); +} + + +static void +stop_retry_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerGetContext *get_context = cls; + + get_context->retry_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Get attempt %u failed, canceling request!\n", + get_context->get_attempts); + GNUNET_DHT_get_stop (get_context->get_handle); + get_context->get_handle = NULL; + GNUNET_SCHEDULER_add_now (&get_stop_finished, get_context); +} + + +static void +do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PeerGetContext *get_context = cls; + + get_context->retry_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 10), + &stop_retry_get, get_context); + get_context->get_handle = + GNUNET_DHT_get_start (get_context->dht_handle, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), + GNUNET_BLOCK_TYPE_DHT_HELLO, + &get_context->peer->hashPubKey, 1, + GNUNET_DHT_RO_FIND_PEER, NULL, 0, + &get_result_iterator, get_context); +} + + +static void +topology_callback (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, + const char *emsg) +{ + if (emsg == NULL) + { + total_connections++; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connected peer %s to peer %s, distance %u\n", + first_daemon->shortname, second_daemon->shortname, distance); + } + else + { + failed_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect peer %s to peer %s with error :\n%s\n", + first_daemon->shortname, second_daemon->shortname, emsg); +#endif + } + + if (total_connections == expected_connections) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created %d total connections, which is our target number! Starting next phase of testing.\n", + total_connections); +#endif + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "Timeout trying to GET"); + + curr_get_ctx.dht_handle = peer1dht; + curr_get_ctx.peer = &peer2id; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 2), &do_get, + &curr_get_ctx); + } + else if (total_connections + failed_connections == expected_connections) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from topology_callback (too many failed connections)"); + } +} + + +static void +connect_topology (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + expected_connections = -1; + if ((pg != NULL) && (peers_left == 0)) + expected_connections = + GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_CLIQUE, + GNUNET_TESTING_TOPOLOGY_OPTION_ALL, + 0.0, TIMEOUT, 12, NULL, NULL); + + GNUNET_SCHEDULER_cancel (die_task); + if (expected_connections == GNUNET_SYSERR) + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from connect topology (bad return)"); + else + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from connect topology (timeout)"); +} + + +static void +peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, const char *emsg) +{ + if (emsg != NULL) + { + FPRINTF (stderr, "Failed to start daemon: `%s'\n", emsg); + return; + } + GNUNET_assert (id != NULL); + if (peers_left == num_peers) + { + memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity)); + peer1dht = GNUNET_DHT_connect (cfg, 100); + if (peer1dht == NULL) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + else + { + memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity)); + peer2dht = GNUNET_DHT_connect (cfg, 100); + if (peer2dht == NULL) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + + + peers_left--; + + if (peers_left == 0) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d daemons started, now connecting peers!\n", num_peers); +#endif + GNUNET_SCHEDULER_cancel (die_task); + /* Set up task in case topology creation doesn't finish + * within a reasonable amount of time */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from peers_started_callback"); + + GNUNET_SCHEDULER_add_now (&connect_topology, NULL); + ok = 0; + } +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome", + &test_directory)) + { + ok = 404; + return; + } + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", + &num_peers)) + num_peers = DEFAULT_NUM_PEERS; + + peers_left = num_peers; + total_gets = num_peers; + gets_succeeded = 0; + /* Set up a task to end testing if peer start fails */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "didn't start all daemons in reasonable amount of time!!!"); + + pg = GNUNET_TESTING_daemons_start (cfg, num_peers, 10, num_peers, TIMEOUT, + NULL, NULL, &peers_started_callback, NULL, + &topology_callback, NULL, NULL); + +} + +static int +check () +{ + int ret; + + char *const argv[] = { "test-dht-twopeer", + "-c", + "test_dht_twopeer_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + ret = + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-dht-twopeer", "nohelp", options, &run, &ok); + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`test-dht-twopeer': Failed with error code %d\n", ret); + } + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-dht-twopeer", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to remove testing directory %s\n", test_directory); + } + return ret; +} + +/* end of test_dht_twopeer.c */ diff --git a/src/dht/test_dht_twopeer_data.conf b/src/dht/test_dht_twopeer_data.conf new file mode 100644 index 0000000..ba1e22b --- /dev/null +++ b/src/dht/test_dht_twopeer_data.conf @@ -0,0 +1,74 @@ +[PATHS] +DEFAULTCONFIG = test_dht_twopeer_data.conf +SERVICEHOME = /tmp/test-dht-twopeer/ + +[resolver] +AUTOSTART = YES + +[dht] +DEBUG = NO +AUTOSTART = YES +#PREFIX = xterm -T dht -e gdb --args +PORT = 2100 +BINARY = gnunet-service-dht + +[block] +plugins = test dht dns + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +DEBUG = NO +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[core] +HOSTNAME = localhost +PORT = 12092 + +[arm] +DEFAULTSERVICES = core +PORT = 12366 +DEBUG = NO + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 +BINDTO = 127.0.0.1 + +[TESTING] +WEAKRANDOM = YES +NUM_PEERS = 2 +HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[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 + +[dns] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[fs] +AUTOSTART = NO + diff --git a/src/dht/test_dht_twopeer_get_put.c b/src/dht/test_dht_twopeer_get_put.c new file mode 100644 index 0000000..0bb9fac --- /dev/null +++ b/src/dht/test_dht_twopeer_get_put.c @@ -0,0 +1,605 @@ +/* + 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 dht/test_dht_twopeer_get_put.c + * @brief base testcase for testing DHT service with + * two running peers. + * + * This testcase starts peers using the GNUNET_TESTING_daemons_start + * function call. On peer start, connects to the peers DHT service + * by calling GNUNET_DHT_connected. Once notified about all peers + * being started (by the peers_started_callback function), calls + * GNUNET_TESTING_connect_topology, which connects the peers in a + * "straight line" topology. On notification that all peers have + * been properly connected, calls the do_get function which initiates + * a GNUNET_DHT_get from the *second* peer. Once the GNUNET_DHT_get + * function starts, runs the do_put function to insert data at the first peer. + * If the GET is successful, schedules finish_testing + * to stop the test and shut down peers. If GET is unsuccessful + * after GET_TIMEOUT seconds, prints an error message and shuts down + * the peers. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_dht_service.h" +#include "block_dns.h" +#include "gnunet_signatures.h" + +/* DEFINES */ +#define VERBOSE GNUNET_NO + +/* Timeout for entire testcase */ +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 40) + +/* Timeout for waiting for replies to get requests */ +#define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) + +/* If number of peers not in config file, use this number */ +#define DEFAULT_NUM_PEERS 2 + +#define DNS GNUNET_NO + +/* Globals */ + +/** + * Directory to store temp data in, defined in config file + */ +static char *test_directory; + +/** + * Variable used to store the number of connections we should wait for. + */ +static unsigned int expected_connections; + +/** + * Variable used to keep track of how many peers aren't yet started. + */ +static unsigned long long peers_left; + +/** + * Handle to the set of all peers run for this test. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * Global handle we will use for GET requests. + */ +struct GNUNET_DHT_GetHandle *global_get_handle; + + +/** + * Total number of peers to run, set based on config file. + */ +static unsigned long long num_peers; + +/** + * Global used to count how many connections we have currently + * been notified about (how many times has topology_callback been called + * with success?) + */ +static unsigned int total_connections; + +/** + * Global used to count how many failed connections we have + * been notified about (how many times has topology_callback + * been called with failure?) + */ +static unsigned int failed_connections; + +/* Task handle to use to schedule test failure */ +GNUNET_SCHEDULER_TaskIdentifier die_task; + +/* Global return value (0 for success, anything else for failure) */ +static int ok; + +#if DNS +struct GNUNET_DNS_Record data; +#endif + +/** + * Peer identity of the first peer started. + */ +static struct GNUNET_PeerIdentity peer1id; + +/** + * Peer identity of the second peer started. + */ +static struct GNUNET_PeerIdentity peer2id; + +/** + * Handle to the first peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer1dht; + +/** + * Handle to the second peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer2dht; + +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Check whether peers successfully shut down. + */ +void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + if (ok == 0) + ok = 2; + } +} + +/** + * Function scheduled to be run on the successful completion of this + * testcase. Specifically, called when our get request completes. + */ +static void +finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (pg != NULL); + GNUNET_assert (peer1dht != NULL); + GNUNET_assert (peer2dht != NULL); + GNUNET_DHT_disconnect (peer1dht); + GNUNET_DHT_disconnect (peer2dht); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + ok = 0; +} + +/** + * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut + * down the peers without freeing memory associated with GET request. + */ +static void +end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (peer1dht != NULL) + GNUNET_DHT_disconnect (peer1dht); + + if (peer2dht != NULL) + GNUNET_DHT_disconnect (peer2dht); + + if (pg != NULL) + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); +} + +/** + * Check if the get_handle is being used, if so stop the request. Either + * way, schedule the end_badly_cont function which actually shuts down the + * test. + */ +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failing test with error: `%s'!\n", + (char *) cls); + if (global_get_handle != NULL) + { + GNUNET_DHT_get_stop (global_get_handle); + global_get_handle = NULL; + } + GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL); + ok = 1; +} + +/** + * Iterator called if the GET request initiated returns a response. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_size, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_size, enum GNUNET_BLOCK_Type type, + size_t size, const void *result_data) +{ + GNUNET_HashCode original_key; /* Key data was stored data under */ + char original_data[4]; /* Made up data that was stored */ + + memset (&original_key, 42, sizeof (GNUNET_HashCode)); /* Set the key to what it was set to previously */ + memset (original_data, 43, sizeof (original_data)); + +#if DNS + if ((sizeof (original_data) != size) || + (0 != memcmp (&data.service_descriptor, key, sizeof (GNUNET_HashCode))) || + (0 != memcmp ((char *) &data, result_data, sizeof (original_data)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Key or data is not the same as was inserted!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, + "key or data mismatch in get response!\n"); + return; + } +#else + if ((sizeof (original_data) != size) || + (0 != memcmp (&original_key, key, sizeof (GNUNET_HashCode))) || + (0 != memcmp (original_data, result_data, sizeof (original_data)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Key or data is not the same as was inserted!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, + "key or data mismatch in get response!\n"); + return; + } +#endif + + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_DHT_get_stop (global_get_handle); + GNUNET_SCHEDULER_add_now (&finish_testing, NULL); +} + +/** + * Start the GET request for the same key/data that was inserted. + */ +static void +do_get (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Key for data lookup */ + +#if DNS + memcpy (&key, &data.service_descriptor, sizeof (GNUNET_HashCode)); +#else + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to the same thing as when data was inserted */ +#endif + global_get_handle = + GNUNET_DHT_get_start (peer2dht, GNUNET_TIME_relative_get_forever (), +#if DNS + GNUNET_BLOCK_TYPE_DNS, +#else + GNUNET_BLOCK_TYPE_TEST, +#endif + &key, 1, GNUNET_DHT_RO_NONE, NULL, 0, + &get_result_iterator, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 10), &do_put, NULL); +} + +/** + * Called when the PUT request has been transmitted to the DHT service. + * Schedule the GET request for some time in the future. + */ +static void +put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &end_badly, + "waiting for get response (data not found)"); +} + + +#if !DNS +/** + * Set up some data, and call API PUT function + */ +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Made up key to store data under */ + char data[4]; /* Made up data to store */ + + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to something simple so we can issue GET request */ + memset (data, 43, sizeof (data)); + + /* Insert the data at the first peer */ + GNUNET_DHT_put (peer1dht, &key, 1, GNUNET_DHT_RO_NONE, GNUNET_BLOCK_TYPE_TEST, + sizeof (data), data, GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_TIME_UNIT_FOREVER_REL, &put_finished, NULL); +} +#else + +/** + * Set up some data, and call API PUT function + */ +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + char *name = "philipptoelke.gnunet."; + size_t size = sizeof (struct GNUNET_DNS_Record); + + memset (&data, 0, size); + + data.purpose.size = htonl (size - sizeof (struct GNUNET_CRYPTO_RsaSignature)); + data.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_DNS_RECORD; + + GNUNET_CRYPTO_hash (name, strlen (name) + 1, &data.service_descriptor); + + data.service_type = htonl (GNUNET_DNS_SERVICE_TYPE_UDP); + data.ports = htons (69); + + char *keyfile; + + GNUNET_asprintf (&keyfile, "/tmp/test_dns_data_key"); + struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key = + GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + GNUNET_assert (my_private_key != NULL); + + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &data.peer); + + data.expiration_time = + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS); + + /* Sign the block */ + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_sign (my_private_key, &data.purpose, &data.signature)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not sign DNS_Record\n"); + return; + } + GNUNET_CRYPTO_rsa_key_free (my_private_key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Putting with key %08x\n", + *((unsigned int *) &data.service_descriptor)); + + GNUNET_DHT_put (peer1dht, &data.service_descriptor, DEFAULT_PUT_REPLICATION, + GNUNET_DHT_RO_NONE, GNUNET_BLOCK_TYPE_DNS, size, + (char *) &data, + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS), + GNUNET_TIME_UNIT_MINUTES, &put_finished, NULL); +} +#endif + +/** + * This function is called whenever a connection attempt is finished between two of + * the started peers (started with GNUNET_TESTING_daemons_start). The total + * number of times this function is called should equal the number returned + * from the GNUNET_TESTING_connect_topology call. + * + * The emsg variable is NULL on success (peers connected), and non-NULL on + * failure (peers failed to connect). + */ +void +topology_callback (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, + const char *emsg) +{ + if (emsg == NULL) + { + total_connections++; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connected peer %s to peer %s, distance %u\n", + first_daemon->shortname, second_daemon->shortname, distance); +#endif + } +#if VERBOSE + else + { + failed_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect peer %s to peer %s with error :\n%s\n", + first_daemon->shortname, second_daemon->shortname, emsg); + } +#endif + + if (total_connections == expected_connections) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created %d total connections, which is our target number! Starting next phase of testing.\n", + total_connections); +#endif + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test gets"); + + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 2), &do_get, NULL); + } + else if (total_connections + failed_connections == expected_connections) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from topology_callback (too many failed connections)"); + } +} + + +/** + * Callback which is called whenever a peer is started (as a result of the + * GNUNET_TESTING_daemons_start call. + * + * @param cls closure argument given to GNUNET_TESTING_daemons_start + * @param id the GNUNET_PeerIdentity of the started peer + * @param cfg the configuration for this specific peer (needed to connect + * to the DHT) + * @param d the handle to the daemon started + * @param emsg NULL if peer started, non-NULL on error + */ +static void +peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, const char *emsg) +{ + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to start daemon with error: `%s'\n", emsg); + return; + } + GNUNET_assert (id != NULL); + + /* This is the first peer started */ + if (peers_left == num_peers) + { + memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity)); /* Save the peer id */ + peer1dht = GNUNET_DHT_connect (cfg, 100); /* Connect to the first peers DHT service */ + if (peer1dht == NULL) /* If DHT connect failed */ + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + else /* This is the second peer started */ + { + memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity)); /* Same as for first peer... */ + peer2dht = GNUNET_DHT_connect (cfg, 100); + if (peer2dht == NULL) + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + + /* Decrement number of peers left to start */ + peers_left--; + + if (peers_left == 0) /* Indicates all peers started */ + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d daemons started, now connecting peers!\n", num_peers); +#endif + expected_connections = -1; + if ((pg != NULL)) /* Sanity check */ + { + /* Connect peers in a "straight line" topology, return the number of expected connections */ + expected_connections = + GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_LINE, + GNUNET_TESTING_TOPOLOGY_OPTION_ALL, + 0.0, TIMEOUT, 12, NULL, NULL); + } + + /* Cancel current timeout fail task */ + GNUNET_SCHEDULER_cancel (die_task); + if (expected_connections == GNUNET_SYSERR) /* Some error happened */ + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from connect topology (bad return)"); + + /* Schedule timeout on failure task */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from connect topology (timeout)"); + ok = 0; + } +} + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + + /* Get path from configuration file */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome", + &test_directory)) + { + ok = 404; + return; + } + + /* Get number of peers to start from configuration (should be two) */ + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", + &num_peers)) + num_peers = DEFAULT_NUM_PEERS; + + /* Set peers_left so we know when all peers started */ + peers_left = num_peers; + + /* Set up a task to end testing if peer start fails */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "didn't start all daemons in reasonable amount of time!!!"); + + /* Start num_peers peers, call peers_started_callback on peer start, topology_callback on peer connect */ + /* Read the API documentation for other parameters! */ + pg = GNUNET_TESTING_daemons_start (cfg, num_peers, 2, 2, TIMEOUT, NULL, NULL, + &peers_started_callback, NULL, + &topology_callback, NULL, NULL); + +} + +static int +check () +{ + int ret; + + /* Arguments for GNUNET_PROGRAM_run */ + char *const argv[] = { "test-dht-twopeer-get-put", /* Name to give running binary */ + "-c", + "test_dht_twopeer_data.conf", /* Config file to use */ +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + /* Run the run function as a new program */ + ret = + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-dht-twopeer-get-put", "nohelp", options, &run, + &ok); + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`test-dht-twopeer': Failed with error code %d\n", ret); + } + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-dht-twopeer", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to remove testing directory %s\n", test_directory); + } + return ret; +} + +/* end of test_dht_twopeer_get_put.c */ diff --git a/src/dht/test_dht_twopeer_path_tracking.c b/src/dht/test_dht_twopeer_path_tracking.c new file mode 100644 index 0000000..6e764a3 --- /dev/null +++ b/src/dht/test_dht_twopeer_path_tracking.c @@ -0,0 +1,522 @@ +/* + 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 dht/test_dht_twopeer_path_tracking.c + * @brief testcase for testing DHT service with + * two running peers, logging the path of the dht requests. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_dht_service.h" + +/* DEFINES */ +#define VERBOSE GNUNET_NO + +/* Timeout for entire testcase */ +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5) + +/* Timeout for waiting for replies to get requests */ +#define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) + +/* If number of peers not in config file, use this number */ +#define DEFAULT_NUM_PEERS 2 + +/* Globals */ + +/** + * Directory to store temp data in, defined in config file + */ +static char *test_directory; + +/** + * Variable used to store the number of connections we should wait for. + */ +static unsigned int expected_connections; + +/** + * Variable used to keep track of how many peers aren't yet started. + */ +static unsigned long long peers_left; + +/** + * Handle to the set of all peers run for this test. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * Global handle we will use for GET requests. + */ +struct GNUNET_DHT_GetHandle *global_get_handle; + + +/** + * Total number of peers to run, set based on config file. + */ +static unsigned long long num_peers; + +/** + * Global used to count how many connections we have currently + * been notified about (how many times has topology_callback been called + * with success?) + */ +static unsigned int total_connections; + +/** + * Global used to count how many failed connections we have + * been notified about (how many times has topology_callback + * been called with failure?) + */ +static unsigned int failed_connections; + +/** + * Task handle to use to schedule test failure + */ +GNUNET_SCHEDULER_TaskIdentifier die_task; + +/** + * Global return value (0 for success, anything else for failure) + */ +static int ok; + +/** + * Peer identity of the first peer started. + */ +static struct GNUNET_PeerIdentity peer1id; + +/** + * Peer identity of the second peer started. + */ +static struct GNUNET_PeerIdentity peer2id; + +/** + * Handle to the first peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer1dht; + +/** + * Handle to the second peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer2dht; + +/** + * Check whether peers successfully shut down. + */ +void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + if (ok == 0) + ok = 2; + } +} + +/** + * Function scheduled to be run on the successful completion of this + * testcase. Specifically, called when our get request completes. + */ +static void +finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (pg != NULL); + GNUNET_assert (peer1dht != NULL); + GNUNET_assert (peer2dht != NULL); + GNUNET_DHT_disconnect (peer1dht); + GNUNET_DHT_disconnect (peer2dht); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + ok = 0; +} + +/** + * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut + * down the peers without freeing memory associated with GET request. + */ +static void +end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (peer1dht != NULL) + GNUNET_DHT_disconnect (peer1dht); + + if (peer2dht != NULL) + GNUNET_DHT_disconnect (peer2dht); + + if (pg != NULL) + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); +} + +/** + * Check if the get_handle is being used, if so stop the request. Either + * way, schedule the end_badly_cont function which actually shuts down the + * test. + */ +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failing test with error: `%s'!\n", + (char *) cls); + if (global_get_handle != NULL) + { + GNUNET_DHT_get_stop (global_get_handle); + global_get_handle = NULL; + } + GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL); + ok = 1; +} + +/** + * Iterator called if the GET request initiated returns a response. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + GNUNET_HashCode original_key; /* Key data was stored data under */ + char original_data[4]; /* Made up data that was stored */ + + memset (&original_key, 42, sizeof (GNUNET_HashCode)); /* Set the key to what it was set to previously */ + memset (original_data, 43, sizeof (original_data)); +#if VERBOSE + unsigned int i; +#endif + + if ((0 != memcmp (&original_key, key, sizeof (GNUNET_HashCode))) || + (0 != memcmp (original_data, data, sizeof (original_data)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Key or data is not the same as was inserted!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, + "key or data mismatch in get response!\n"); + return; + } + +#if VERBOSE + if (put_path != NULL) + { + FPRINTF (stderr, "%s", "PUT Path: "); + for (i = 0; i < put_path_length; i++) + FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&put_path[i])); + FPRINTF (stderr, "%s", "\n"); + } + if (get_path != NULL) + { + FPRINTF (stderr, "%s", "GET Path: "); + for (i = 0; i < get_path_length; i++) + FPRINTF (stderr, "%s%s", i == 0 ? "" : "->", GNUNET_i2s (&get_path[i])); + FPRINTF (stderr, "%s", "\n"); + } +#endif + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct GET response!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_DHT_get_stop (global_get_handle); + GNUNET_SCHEDULER_add_now (&finish_testing, NULL); +} + + +/** + * Called when the PUT request has been transmitted to the DHT service. + * Schedule the GET request for some time in the future. + */ +static void +put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Key for data lookup */ + + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &end_badly, + "waiting for get response (data not found)"); + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to the same thing as when data was inserted */ + global_get_handle = + GNUNET_DHT_get_start (peer2dht, GNUNET_TIME_relative_get_forever (), + GNUNET_BLOCK_TYPE_TEST, &key, 1, + GNUNET_DHT_RO_RECORD_ROUTE, NULL, 0, + &get_result_iterator, NULL); +} + +/** + * Set up some data, and call API PUT function + */ +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Made up key to store data under */ + char data[4]; /* Made up data to store */ + + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to something simple so we can issue GET request */ + memset (data, 43, sizeof (data)); + + /* Insert the data at the first peer */ + GNUNET_DHT_put (peer1dht, &key, 1, GNUNET_DHT_RO_RECORD_ROUTE, + GNUNET_BLOCK_TYPE_TEST, sizeof (data), data, + GNUNET_TIME_UNIT_FOREVER_ABS, GNUNET_TIME_UNIT_FOREVER_REL, + &put_finished, NULL); +} + +/** + * This function is called whenever a connection attempt is finished between two of + * the started peers (started with GNUNET_TESTING_daemons_start). The total + * number of times this function is called should equal the number returned + * from the GNUNET_TESTING_connect_topology call. + * + * The emsg variable is NULL on success (peers connected), and non-NULL on + * failure (peers failed to connect). + */ +static void +topology_callback (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, + const char *emsg) +{ + if (emsg == NULL) + { + total_connections++; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connected peer %s to peer %s, distance %u\n", + first_daemon->shortname, second_daemon->shortname, distance); +#endif + } +#if VERBOSE + else + { + failed_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect peer %s to peer %s with error :\n%s\n", + first_daemon->shortname, second_daemon->shortname, emsg); + } +#endif + + if (total_connections == expected_connections) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created %d total connections, which is our target number! Starting next phase of testing.\n", + total_connections); +#endif + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test gets"); + + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 2), &do_put, NULL); + } + else if (total_connections + failed_connections == expected_connections) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from topology_callback (too many failed connections)"); + } +} + + +/** + * Callback which is called whenever a peer is started (as a result of the + * GNUNET_TESTING_daemons_start call. + * + * @param cls closure argument given to GNUNET_TESTING_daemons_start + * @param id the GNUNET_PeerIdentity of the started peer + * @param cfg the configuration for this specific peer (needed to connect + * to the DHT) + * @param d the handle to the daemon started + * @param emsg NULL if peer started, non-NULL on error + */ +static void +peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, const char *emsg) +{ + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to start daemon with error: `%s'\n", emsg); + return; + } + GNUNET_assert (id != NULL); + + /* This is the first peer started */ + if (peers_left == num_peers) + { + memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity)); /* Save the peer id */ + peer1dht = GNUNET_DHT_connect (cfg, 100); /* Connect to the first peers DHT service */ + if (peer1dht == NULL) /* If DHT connect failed */ + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + else /* This is the second peer started */ + { + memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity)); /* Same as for first peer... */ + peer2dht = GNUNET_DHT_connect (cfg, 100); + if (peer2dht == NULL) + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + + /* Decrement number of peers left to start */ + peers_left--; + + if (peers_left == 0) /* Indicates all peers started */ + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d daemons started, now connecting peers!\n", num_peers); +#endif + expected_connections = -1; + if ((pg != NULL)) /* Sanity check */ + { + /* Connect peers in a "straight line" topology, return the number of expected connections */ + expected_connections = + GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_LINE, + GNUNET_TESTING_TOPOLOGY_OPTION_ALL, + 0.0, TIMEOUT, 2, NULL, NULL); + } + + /* Cancel current timeout fail task */ + GNUNET_SCHEDULER_cancel (die_task); + if (expected_connections == GNUNET_SYSERR) /* Some error happened */ + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from connect topology (bad return)"); + + /* Schedule timeout on failure task */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from connect topology (timeout)"); + ok = 0; + } +} + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + + /* Get path from configuration file */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome", + &test_directory)) + { + ok = 404; + return; + } + + /* Get number of peers to start from configuration (should be two) */ + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", + &num_peers)) + num_peers = DEFAULT_NUM_PEERS; + + /* Set peers_left so we know when all peers started */ + peers_left = num_peers; + + /* Set up a task to end testing if peer start fails */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "didn't start all daemons in reasonable amount of time!!!"); + + /* Start num_peers peers, call peers_started_callback on peer start, topology_callback on peer connect */ + /* Read the API documentation for other parameters! */ + pg = GNUNET_TESTING_daemons_start (cfg, peers_left, /* Total number of peers */ + peers_left, /* Number of outstanding connections */ + peers_left, /* Number of parallel ssh connections, or peers being started at once */ + TIMEOUT, NULL, NULL, + &peers_started_callback, NULL, + &topology_callback, NULL, NULL); + +} + +static int +check () +{ + int ret; + + /* Arguments for GNUNET_PROGRAM_run */ + char *const argv[] = { "test-dht-twopeer-put-get", /* Name to give running binary */ + "-c", + "test_dht_twopeer_data.conf", /* Config file to use */ +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + /* Run the run function as a new program */ + ret = + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-dht-twopeer-put-get", "nohelp", options, &run, + &ok); + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`test-dht-twopeer': Failed with error code %d\n", ret); + } + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-dht-twopeer", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to remove testing directory %s\n", test_directory); + } + return ret; +} + +/* end of test_dht_twopeer_put_get.c */ diff --git a/src/dht/test_dht_twopeer_put_get.c b/src/dht/test_dht_twopeer_put_get.c new file mode 100644 index 0000000..48bf9f8 --- /dev/null +++ b/src/dht/test_dht_twopeer_put_get.c @@ -0,0 +1,512 @@ +/* + 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 dht/test_dht_twopeer_put_get.c + * @brief base testcase for testing DHT service with + * two running peers. + * + * This testcase starts peers using the GNUNET_TESTING_daemons_start + * function call. On peer start, connects to the peers DHT service + * by calling GNUNET_DHT_connected. Once notified about all peers + * being started (by the peers_started_callback function), calls + * GNUNET_TESTING_connect_topology, which connects the peers in a + * "straight line" topology. On notification that all peers have + * been properly connected, runs the do_put function to insert data + * at the first peer. Once the GNUNET_DHT_put function completes, + * calls the do_get function which initiates a GNUNET_DHT_get from + * the *second* peer. If the GET is successful, schedules finish_testing + * to stop the test and shut down peers. If GET is unsuccessful + * after GET_TIMEOUT seconds, prints an error message and shuts down + * the peers. + */ +#include "platform.h" +#include "gnunet_testing_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_dht_service.h" +#include "block_dns.h" +#include "gnunet_signatures.h" + +/* DEFINES */ +#define VERBOSE GNUNET_NO + +/* Timeout for entire testcase */ +#define TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5) + +/* Timeout for waiting for replies to get requests */ +#define GET_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) + +/* If number of peers not in config file, use this number */ +#define DEFAULT_NUM_PEERS 2 + +/* Globals */ + +/** + * Directory to store temp data in, defined in config file + */ +static char *test_directory; + +/** + * Variable used to store the number of connections we should wait for. + */ +static unsigned int expected_connections; + +/** + * Variable used to keep track of how many peers aren't yet started. + */ +static unsigned long long peers_left; + +/** + * Handle to the set of all peers run for this test. + */ +static struct GNUNET_TESTING_PeerGroup *pg; + +/** + * Global handle we will use for GET requests. + */ +struct GNUNET_DHT_GetHandle *global_get_handle; + + +/** + * Total number of peers to run, set based on config file. + */ +static unsigned long long num_peers; + +/** + * Global used to count how many connections we have currently + * been notified about (how many times has topology_callback been called + * with success?) + */ +static unsigned int total_connections; + +/** + * Global used to count how many failed connections we have + * been notified about (how many times has topology_callback + * been called with failure?) + */ +static unsigned int failed_connections; + +/* Task handle to use to schedule test failure */ +static GNUNET_SCHEDULER_TaskIdentifier die_task; + +/* Global return value (0 for success, anything else for failure) */ +static int ok; + +/** + * Peer identity of the first peer started. + */ +static struct GNUNET_PeerIdentity peer1id; + +/** + * Peer identity of the second peer started. + */ +static struct GNUNET_PeerIdentity peer2id; + +/** + * Handle to the first peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer1dht; + +/** + * Handle to the second peers DHT service (via the API) + */ +static struct GNUNET_DHT_Handle *peer2dht; + +/** + * Check whether peers successfully shut down. + */ +void +shutdown_callback (void *cls, const char *emsg) +{ + if (emsg != NULL) + { + if (ok == 0) + ok = 2; + } +} + +/** + * Function scheduled to be run on the successful completion of this + * testcase. Specifically, called when our get request completes. + */ +static void +finish_testing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (pg != NULL); + GNUNET_assert (peer1dht != NULL); + GNUNET_assert (peer2dht != NULL); + GNUNET_DHT_disconnect (peer1dht); + GNUNET_DHT_disconnect (peer2dht); + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + ok = 0; +} + +/** + * Continuation for the GNUNET_DHT_get_stop call, so that we don't shut + * down the peers without freeing memory associated with GET request. + */ +static void +end_badly_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (peer1dht != NULL) + GNUNET_DHT_disconnect (peer1dht); + + if (peer2dht != NULL) + GNUNET_DHT_disconnect (peer2dht); + + if (pg != NULL) + GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); +} + +/** + * Check if the get_handle is being used, if so stop the request. Either + * way, schedule the end_badly_cont function which actually shuts down the + * test. + */ +static void +end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failing test with error: `%s'!\n", + (char *) cls); + if (global_get_handle != NULL) + { + GNUNET_DHT_get_stop (global_get_handle); + global_get_handle = NULL; + } + GNUNET_SCHEDULER_add_now (&end_badly_cont, NULL); + ok = 1; +} + +/** + * Iterator called if the GET request initiated returns a response. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +get_result_iterator (void *cls, struct GNUNET_TIME_Absolute exp, + const GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_size, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_size, enum GNUNET_BLOCK_Type type, + size_t size, const void *result_data) +{ + GNUNET_HashCode original_key; /* Key data was stored data under */ + char original_data[4]; /* Made up data that was stored */ + + memset (&original_key, 42, sizeof (GNUNET_HashCode)); /* Set the key to what it was set to previously */ + memset (original_data, 43, sizeof (original_data)); + + if ((sizeof (original_data) != size) || + (0 != memcmp (&original_key, key, sizeof (GNUNET_HashCode))) || + (0 != memcmp (original_data, result_data, sizeof (original_data)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Key or data is not the same as was inserted!\n"); + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, + "key or data mismatch in get response!\n"); + return; + } + + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_DHT_get_stop (global_get_handle); + global_get_handle = NULL; + GNUNET_SCHEDULER_add_now (&finish_testing, NULL); +} + + +/** + * Called when the PUT request has been transmitted to the DHT service. + * Schedule the GET request for some time in the future. + */ +static void +put_finished (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Key for data lookup */ + + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (GET_TIMEOUT, &end_badly, + "waiting for get response (data not found)"); + + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to the same thing as when data was inserted */ + global_get_handle = + GNUNET_DHT_get_start (peer2dht, GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_BLOCK_TYPE_TEST, &key, 1, GNUNET_DHT_RO_NONE, + NULL, 0, &get_result_iterator, NULL); +} + + +/** + * Set up some data, and call API PUT function + */ +static void +do_put (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_HashCode key; /* Made up key to store data under */ + char data[4]; /* Made up data to store */ + + memset (&key, 42, sizeof (GNUNET_HashCode)); /* Set the key to something simple so we can issue GET request */ + memset (data, 43, sizeof (data)); + + /* Insert the data at the first peer */ + GNUNET_DHT_put (peer1dht, &key, 1, GNUNET_DHT_RO_NONE, GNUNET_BLOCK_TYPE_TEST, + sizeof (data), data, GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_TIME_UNIT_FOREVER_REL, &put_finished, NULL); +} + + +/** + * This function is called whenever a connection attempt is finished between two of + * the started peers (started with GNUNET_TESTING_daemons_start). The total + * number of times this function is called should equal the number returned + * from the GNUNET_TESTING_connect_topology call. + * + * The emsg variable is NULL on success (peers connected), and non-NULL on + * failure (peers failed to connect). + */ +static void +topology_callback (void *cls, const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second, uint32_t distance, + const struct GNUNET_CONFIGURATION_Handle *first_cfg, + const struct GNUNET_CONFIGURATION_Handle *second_cfg, + struct GNUNET_TESTING_Daemon *first_daemon, + struct GNUNET_TESTING_Daemon *second_daemon, + const char *emsg) +{ + if (emsg == NULL) + { + total_connections++; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connected peer %s to peer %s, distance %u\n", + first_daemon->shortname, second_daemon->shortname, distance); +#endif + } +#if VERBOSE + else + { + failed_connections++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect peer %s to peer %s with error :\n%s\n", + first_daemon->shortname, second_daemon->shortname, emsg); + } +#endif + + if (total_connections == expected_connections) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created %d total connections, which is our target number! Starting next phase of testing.\n", + total_connections); +#endif + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, "from test gets"); + + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_put, NULL); + } + else if (total_connections + failed_connections == expected_connections) + { + GNUNET_SCHEDULER_cancel (die_task); + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from topology_callback (too many failed connections)"); + } +} + + +/** + * Callback which is called whenever a peer is started (as a result of the + * GNUNET_TESTING_daemons_start call. + * + * @param cls closure argument given to GNUNET_TESTING_daemons_start + * @param id the GNUNET_PeerIdentity of the started peer + * @param cfg the configuration for this specific peer (needed to connect + * to the DHT) + * @param d the handle to the daemon started + * @param emsg NULL if peer started, non-NULL on error + */ +static void +peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, const char *emsg) +{ + if (emsg != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to start daemon with error: `%s'\n", emsg); + return; + } + GNUNET_assert (id != NULL); + + /* This is the first peer started */ + if (peers_left == num_peers) + { + memcpy (&peer1id, id, sizeof (struct GNUNET_PeerIdentity)); /* Save the peer id */ + peer1dht = GNUNET_DHT_connect (cfg, 100); /* Connect to the first peers DHT service */ + if (peer1dht == NULL) /* If DHT connect failed */ + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + else /* This is the second peer started */ + { + memcpy (&peer2id, id, sizeof (struct GNUNET_PeerIdentity)); /* Same as for first peer... */ + peer2dht = GNUNET_DHT_connect (cfg, 100); + if (peer2dht == NULL) + { + GNUNET_SCHEDULER_cancel (die_task); + GNUNET_SCHEDULER_add_now (&end_badly, "Failed to get dht handle!\n"); + } + } + + /* Decrement number of peers left to start */ + peers_left--; + + if (peers_left == 0) /* Indicates all peers started */ + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %d daemons started, now connecting peers!\n", num_peers); +#endif + expected_connections = -1; + if ((pg != NULL)) /* Sanity check */ + { + /* Connect peers in a "straight line" topology, return the number of expected connections */ + expected_connections = + GNUNET_TESTING_connect_topology (pg, GNUNET_TESTING_TOPOLOGY_LINE, + GNUNET_TESTING_TOPOLOGY_OPTION_ALL, + 0.0, TIMEOUT, 12, NULL, NULL); + } + + /* Cancel current timeout fail task */ + GNUNET_SCHEDULER_cancel (die_task); + if (expected_connections == GNUNET_SYSERR) /* Some error happened */ + die_task = + GNUNET_SCHEDULER_add_now (&end_badly, + "from connect topology (bad return)"); + + /* Schedule timeout on failure task */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "from connect topology (timeout)"); + ok = 0; + } +} + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + + /* Get path from configuration file */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome", + &test_directory)) + { + ok = 404; + return; + } + + /* Get number of peers to start from configuration (should be two) */ + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers", + &num_peers)) + num_peers = DEFAULT_NUM_PEERS; + + /* Set peers_left so we know when all peers started */ + peers_left = num_peers; + + /* Set up a task to end testing if peer start fails */ + die_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, + "didn't start all daemons in reasonable amount of time!!!"); + + /* Start num_peers peers, call peers_started_callback on peer start, topology_callback on peer connect */ + /* Read the API documentation for other parameters! */ + pg = GNUNET_TESTING_daemons_start (cfg, num_peers, 2, 2, TIMEOUT, NULL, NULL, + &peers_started_callback, NULL, + &topology_callback, NULL, NULL); + +} + +static int +check () +{ + int ret; + + /* Arguments for GNUNET_PROGRAM_run */ + char *const argv[] = { "test-dht-twopeer-put-get", /* Name to give running binary */ + "-c", + "test_dht_twopeer_data.conf", /* Config file to use */ +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + /* Run the run function as a new program */ + ret = + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv, + "test-dht-twopeer-put-get", "nohelp", options, &run, + &ok); + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "`test-dht-twopeer': Failed with error code %d\n", ret); + } + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-dht-twopeer", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + /** + * Need to remove base directory, subdirectories taken care + * of by the testing framework. + */ + if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to remove testing directory %s\n", test_directory); + } + return ret; +} + +/* end of test_dht_twopeer_put_get.c */ |