aboutsummaryrefslogtreecommitdiff
path: root/src/dht
diff options
context:
space:
mode:
authorBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
committerBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
commit2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch)
tree394774c0f735199b57d51a2d3840356317853fe1 /src/dht
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/dht')
-rw-r--r--src/dht/Makefile.am204
-rw-r--r--src/dht/Makefile.in1197
-rw-r--r--src/dht/dht.conf.in39
-rw-r--r--src/dht/dht.h261
-rw-r--r--src/dht/dht_api.c997
-rw-r--r--src/dht/gnunet-dht-get.c236
-rw-r--r--src/dht/gnunet-dht-put.c207
-rw-r--r--src/dht/gnunet-service-dht.c190
-rw-r--r--src/dht/gnunet-service-dht.h60
-rw-r--r--src/dht/gnunet-service-dht_clients.c1164
-rw-r--r--src/dht/gnunet-service-dht_clients.h103
-rw-r--r--src/dht/gnunet-service-dht_datacache.c309
-rw-r--r--src/dht/gnunet-service-dht_datacache.h86
-rw-r--r--src/dht/gnunet-service-dht_hello.c135
-rw-r--r--src/dht/gnunet-service-dht_hello.h55
-rw-r--r--src/dht/gnunet-service-dht_neighbours.c2029
-rw-r--r--src/dht/gnunet-service-dht_neighbours.h138
-rw-r--r--src/dht/gnunet-service-dht_nse.c101
-rw-r--r--src/dht/gnunet-service-dht_nse.h52
-rw-r--r--src/dht/gnunet-service-dht_routing.c383
-rw-r--r--src/dht/gnunet-service-dht_routing.h96
-rw-r--r--src/dht/multipeer_topo.dat31
-rw-r--r--src/dht/plugin_block_dht.c181
-rw-r--r--src/dht/test_dht_2dtorus.conf87
-rw-r--r--src/dht/test_dht_api.c339
-rw-r--r--src/dht/test_dht_api_data.conf85
-rw-r--r--src/dht/test_dht_api_peer1.conf76
-rw-r--r--src/dht/test_dht_line.conf87
-rw-r--r--src/dht/test_dht_monitor.c624
-rw-r--r--src/dht/test_dht_multipeer.c872
-rw-r--r--src/dht/test_dht_multipeer_data.conf128
-rwxr-xr-xsrc/dht/test_dht_tools.sh73
-rw-r--r--src/dht/test_dht_topo.c662
-rw-r--r--src/dht/test_dht_twopeer.c513
-rw-r--r--src/dht/test_dht_twopeer_data.conf74
-rw-r--r--src/dht/test_dht_twopeer_get_put.c605
-rw-r--r--src/dht/test_dht_twopeer_path_tracking.c522
-rw-r--r--src/dht/test_dht_twopeer_put_get.c512
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 */