aboutsummaryrefslogtreecommitdiff
path: root/src/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib')
-rw-r--r--src/contrib/Makefile.am3
-rw-r--r--src/contrib/cli/Makefile.am2
-rw-r--r--src/contrib/cli/meson.build1
-rw-r--r--src/contrib/cli/template/.gitignore1
-rw-r--r--src/contrib/cli/template/Makefile.am21
-rw-r--r--src/contrib/cli/template/gnunet-template.c86
-rw-r--r--src/contrib/cli/template/meson.build5
-rw-r--r--src/contrib/meson.build5
-rw-r--r--src/contrib/service/Makefile.am23
-rw-r--r--src/contrib/service/abd/.gitignore2
-rw-r--r--src/contrib/service/abd/Makefile.am106
-rw-r--r--src/contrib/service/abd/abd.conf.in5
-rw-r--r--src/contrib/service/abd/abd.h251
-rw-r--r--src/contrib/service/abd/abd_api.c563
-rw-r--r--src/contrib/service/abd/abd_serialization.c516
-rw-r--r--src/contrib/service/abd/abd_serialization.h167
-rw-r--r--src/contrib/service/abd/delegate_misc.c279
-rw-r--r--src/contrib/service/abd/delegate_misc.h36
-rw-r--r--src/contrib/service/abd/gnunet-abd.c1108
-rw-r--r--src/contrib/service/abd/gnunet-service-abd.c1778
-rw-r--r--src/contrib/service/abd/plugin_gnsrecord_abd.c357
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_and.sh98
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_and2.sh94
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_and3.sh96
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_and4.sh83
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_bw.sh87
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_bw_link.sh92
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_bw_link2.sh93
-rwxr-xr-xsrc/contrib/service/abd/test_abd_bi_fw.sh92
-rw-r--r--src/contrib/service/abd/test_abd_defaults.conf24
-rwxr-xr-xsrc/contrib/service/abd/test_abd_issue.sh46
-rw-r--r--src/contrib/service/abd/test_abd_lookup.conf28
-rwxr-xr-xsrc/contrib/service/abd/test_abd_own.sh139
-rwxr-xr-xsrc/contrib/service/abd/test_abd_verify.sh87
-rwxr-xr-xsrc/contrib/service/abd/test_abd_verify_and.sh86
-rwxr-xr-xsrc/contrib/service/abd/test_abd_verify_simple.sh56
-rw-r--r--src/contrib/service/auction/.gitignore5
-rw-r--r--src/contrib/service/auction/Makefile.am75
-rw-r--r--src/contrib/service/auction/auction.conf4
-rw-r--r--src/contrib/service/auction/auction.h77
-rw-r--r--src/contrib/service/auction/gnunet-auction-create.c219
-rw-r--r--src/contrib/service/auction/gnunet-auction-info.c86
-rw-r--r--src/contrib/service/auction/gnunet-auction-join.c86
-rw-r--r--src/contrib/service/auction/gnunet-service-auction.c155
-rw-r--r--src/contrib/service/auction/test_auction_api.c44
-rwxr-xr-xsrc/contrib/service/auction/test_auction_create.sh80
-rw-r--r--src/contrib/service/consensus/.gitignore6
-rw-r--r--src/contrib/service/consensus/Makefile.am106
-rw-r--r--src/contrib/service/consensus/consensus-simulation.py.in112
-rw-r--r--src/contrib/service/consensus/consensus.conf.in10
-rw-r--r--src/contrib/service/consensus/consensus.h92
-rw-r--r--src/contrib/service/consensus/consensus_api.c346
-rw-r--r--src/contrib/service/consensus/consensus_protocol.h137
-rw-r--r--src/contrib/service/consensus/gnunet-service-consensus.c3572
-rw-r--r--src/contrib/service/consensus/meson.build66
-rw-r--r--src/contrib/service/consensus/plugin_block_consensus.c222
-rw-r--r--src/contrib/service/consensus/test_consensus.conf87
-rw-r--r--src/contrib/service/consensus/test_consensus_api.c120
-rw-r--r--src/contrib/service/conversation/.gitignore8
-rw-r--r--src/contrib/service/conversation/Makefile.am263
-rw-r--r--src/contrib/service/conversation/build_gst_test.sh9
-rw-r--r--src/contrib/service/conversation/conversation.conf.in29
-rw-r--r--src/contrib/service/conversation/conversation.h407
-rw-r--r--src/contrib/service/conversation/conversation_api.c897
-rw-r--r--src/contrib/service/conversation/conversation_api_call.c749
-rw-r--r--src/contrib/service/conversation/displaydot.sh3
-rw-r--r--src/contrib/service/conversation/gnunet-conversation-test.c265
-rw-r--r--src/contrib/service/conversation/gnunet-conversation.c1232
-rw-r--r--src/contrib/service/conversation/gnunet-helper-audio-playback-gst.c405
-rw-r--r--src/contrib/service/conversation/gnunet-helper-audio-playback.c888
-rw-r--r--src/contrib/service/conversation/gnunet-helper-audio-record-gst.c393
-rw-r--r--src/contrib/service/conversation/gnunet-helper-audio-record.c807
-rw-r--r--src/contrib/service/conversation/gnunet-service-conversation.c1461
-rw-r--r--src/contrib/service/conversation/gnunet_gst.c1154
-rw-r--r--src/contrib/service/conversation/gnunet_gst.h62
-rw-r--r--src/contrib/service/conversation/gnunet_gst_def.h219
-rw-r--r--src/contrib/service/conversation/gnunet_gst_test.c135
-rw-r--r--src/contrib/service/conversation/mediahelper.conf7
-rw-r--r--src/contrib/service/conversation/meson.build203
-rw-r--r--src/contrib/service/conversation/microphone.c200
-rw-r--r--src/contrib/service/conversation/plugin_gnsrecord_conversation.c262
-rw-r--r--src/contrib/service/conversation/speaker.c189
-rw-r--r--src/contrib/service/conversation/test.sh4
-rw-r--r--src/contrib/service/conversation/test_conversation.conf12
-rw-r--r--src/contrib/service/conversation/test_conversation_api.c510
-rw-r--r--src/contrib/service/conversation/test_conversation_api_reject.c363
-rw-r--r--src/contrib/service/conversation/test_conversation_api_twocalls.c642
-rw-r--r--src/contrib/service/meson.build11
-rw-r--r--src/contrib/service/rps/.gitignore16
-rw-r--r--src/contrib/service/rps/Makefile.am149
-rw-r--r--src/contrib/service/rps/gnunet-rps.c294
-rw-r--r--src/contrib/service/rps/gnunet-service-rps.c5028
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_custommap.c356
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_custommap.h159
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_sampler.c268
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_sampler.h157
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_sampler_elem.c170
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_sampler_elem.h153
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_view.c294
-rw-r--r--src/contrib/service/rps/gnunet-service-rps_view.h145
-rw-r--r--src/contrib/service/rps/profiler_rps.conf135
-rw-r--r--src/contrib/service/rps/rps-sampler_client.c439
-rw-r--r--src/contrib/service/rps/rps-sampler_client.h156
-rw-r--r--src/contrib/service/rps/rps-sampler_common.c742
-rw-r--r--src/contrib/service/rps/rps-sampler_common.h389
-rw-r--r--src/contrib/service/rps/rps-test_util.c550
-rw-r--r--src/contrib/service/rps/rps-test_util.h144
-rw-r--r--src/contrib/service/rps/rps.conf.in38
-rw-r--r--src/contrib/service/rps/rps.h336
-rw-r--r--src/contrib/service/rps/rps_api.c1320
-rw-r--r--src/contrib/service/rps/test_rps.c3183
-rw-r--r--src/contrib/service/rps/test_rps.conf135
-rw-r--r--src/contrib/service/rps/test_rps_api.c87
-rw-r--r--src/contrib/service/rps/test_service_rps_custommap.c128
-rw-r--r--src/contrib/service/rps/test_service_rps_sampler_elem.c214
-rw-r--r--src/contrib/service/rps/test_service_rps_view.c146
-rw-r--r--src/contrib/service/scalarproduct/.gitignore6
-rw-r--r--src/contrib/service/scalarproduct/Makefile.am117
-rw-r--r--src/contrib/service/scalarproduct/gnunet-scalarproduct.c401
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h118
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c1150
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c1060
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h142
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c1388
-rw-r--r--src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c1384
-rw-r--r--src/contrib/service/scalarproduct/meson.build101
-rwxr-xr-xsrc/contrib/service/scalarproduct/perf_scalarproduct.sh73
-rw-r--r--src/contrib/service/scalarproduct/scalarproduct.conf.in27
-rw-r--r--src/contrib/service/scalarproduct/scalarproduct.h177
-rw-r--r--src/contrib/service/scalarproduct/scalarproduct_api.c468
-rw-r--r--src/contrib/service/scalarproduct/test_ecc_scalarproduct.c212
-rw-r--r--src/contrib/service/scalarproduct/test_scalarproduct.conf23
-rwxr-xr-xsrc/contrib/service/scalarproduct/test_scalarproduct.sh48
-rwxr-xr-xsrc/contrib/service/scalarproduct/test_scalarproduct_negative.sh49
-rwxr-xr-xsrc/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh46
-rw-r--r--src/contrib/service/secretsharing/.gitignore3
-rw-r--r--src/contrib/service/secretsharing/Makefile.am61
-rw-r--r--src/contrib/service/secretsharing/gnunet-service-secretsharing.c2414
-rw-r--r--src/contrib/service/secretsharing/meson.build45
-rw-r--r--src/contrib/service/secretsharing/secretsharing.conf.in20
-rw-r--r--src/contrib/service/secretsharing/secretsharing.h227
-rw-r--r--src/contrib/service/secretsharing/secretsharing_api.c492
-rw-r--r--src/contrib/service/secretsharing/secretsharing_common.c159
-rw-r--r--src/contrib/service/secretsharing/secretsharing_protocol.h147
-rw-r--r--src/contrib/service/secretsharing/test_secretsharing.conf40
-rw-r--r--src/contrib/service/secretsharing/test_secretsharing_api.c103
-rw-r--r--src/contrib/service/set/.gitignore7
-rw-r--r--src/contrib/service/set/Makefile.am111
-rw-r--r--src/contrib/service/set/gnunet-service-set.c1983
-rw-r--r--src/contrib/service/set/gnunet-service-set.h665
-rw-r--r--src/contrib/service/set/gnunet-service-set_intersection.c1324
-rw-r--r--src/contrib/service/set/gnunet-service-set_intersection.h79
-rw-r--r--src/contrib/service/set/gnunet-service-set_protocol.h226
-rw-r--r--src/contrib/service/set/gnunet-service-set_union.c2463
-rw-r--r--src/contrib/service/set/gnunet-service-set_union.h246
-rw-r--r--src/contrib/service/set/gnunet-service-set_union_strata_estimator.c297
-rw-r--r--src/contrib/service/set/gnunet-service-set_union_strata_estimator.h169
-rw-r--r--src/contrib/service/set/gnunet-set-ibf-profiler.c308
-rw-r--r--src/contrib/service/set/ibf.c410
-rw-r--r--src/contrib/service/set/ibf.h256
-rw-r--r--src/contrib/service/set/ibf_sim.c143
-rw-r--r--src/contrib/service/set/meson.build45
-rw-r--r--src/contrib/service/set/plugin_block_set_test.c167
-rw-r--r--src/contrib/service/set/set.conf.in12
-rw-r--r--src/contrib/service/set/set.h400
-rw-r--r--src/contrib/service/set/set_api.c1199
-rw-r--r--src/contrib/service/set/test_set.conf33
-rw-r--r--src/contrib/service/set/test_set_api.c403
-rw-r--r--src/contrib/service/set/test_set_intersection_result_full.c393
-rw-r--r--src/contrib/service/set/test_set_union_copy.c311
-rw-r--r--src/contrib/service/set/test_set_union_result_symmetric.c455
-rw-r--r--src/contrib/service/template/.gitignore3
-rw-r--r--src/contrib/service/template/Makefile.am41
-rw-r--r--src/contrib/service/template/gnunet-service-template.c104
-rw-r--r--src/contrib/service/template/meson.build19
-rw-r--r--src/contrib/service/template/template.conf23
-rw-r--r--src/contrib/service/template/test_template_api.c44
177 files changed, 62383 insertions, 0 deletions
diff --git a/src/contrib/Makefile.am b/src/contrib/Makefile.am
new file mode 100644
index 000000000..abaefb409
--- /dev/null
+++ b/src/contrib/Makefile.am
@@ -0,0 +1,3 @@
1SUBDIRS = \
2 service \
3 cli
diff --git a/src/contrib/cli/Makefile.am b/src/contrib/cli/Makefile.am
new file mode 100644
index 000000000..3315e4bbb
--- /dev/null
+++ b/src/contrib/cli/Makefile.am
@@ -0,0 +1,2 @@
1SUBDIRS = \
2 template
diff --git a/src/contrib/cli/meson.build b/src/contrib/cli/meson.build
new file mode 100644
index 000000000..26b120864
--- /dev/null
+++ b/src/contrib/cli/meson.build
@@ -0,0 +1 @@
subdir('template')
diff --git a/src/contrib/cli/template/.gitignore b/src/contrib/cli/template/.gitignore
new file mode 100644
index 000000000..6ace894c0
--- /dev/null
+++ b/src/contrib/cli/template/.gitignore
@@ -0,0 +1 @@
gnunet-template
diff --git a/src/contrib/cli/template/Makefile.am b/src/contrib/cli/template/Makefile.am
new file mode 100644
index 000000000..b60095d80
--- /dev/null
+++ b/src/contrib/cli/template/Makefile.am
@@ -0,0 +1,21 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8if USE_COVERAGE
9 AM_CFLAGS = -fprofile-arcs -ftest-coverage
10endif
11
12noinst_PROGRAMS = \
13 gnunet-template
14
15
16gnunet_template_SOURCES = \
17 gnunet-template.c
18gnunet_template_LDADD = \
19 $(top_builddir)/src/lib/util/libgnunetutil.la
20gnunet_template_LDFLAGS = \
21 $(GN_LIBINTL)
diff --git a/src/contrib/cli/template/gnunet-template.c b/src/contrib/cli/template/gnunet-template.c
new file mode 100644
index 000000000..5e250bbdf
--- /dev/null
+++ b/src/contrib/cli/template/gnunet-template.c
@@ -0,0 +1,86 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file template/gnunet-template.c
23 * @brief template for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28/* #include "gnunet_template_service.h" */
29
30/**
31 * Final status code.
32 */
33static int ret;
34
35
36/**
37 * Main function that will be run by the scheduler.
38 *
39 * @param cls closure
40 * @param args remaining command-line arguments
41 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
42 * @param cfg configuration
43 */
44static void
45run (void *cls,
46 char *const *args,
47 const char *cfgfile,
48 const struct GNUNET_CONFIGURATION_Handle *cfg)
49{
50 /* main code here */
51}
52
53
54/**
55 * The main function.
56 *
57 * @param argc number of arguments from the command line
58 * @param argv command line arguments
59 * @return 0 ok, 1 on error
60 */
61int
62main (int argc, char *const *argv)
63{
64 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
65 /* FIMXE: add options here */
66 GNUNET_GETOPT_OPTION_END
67 };
68
69 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
70 return 2;
71
72 ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
73 argv,
74 "gnunet-template",
75 gettext_noop ("help text"),
76 options,
77 &run,
78 NULL))
79 ? ret
80 : 1;
81 GNUNET_free_nz ((void *) argv);
82 return ret;
83}
84
85
86/* end of gnunet-template.c */
diff --git a/src/contrib/cli/template/meson.build b/src/contrib/cli/template/meson.build
new file mode 100644
index 000000000..dba66a8f2
--- /dev/null
+++ b/src/contrib/cli/template/meson.build
@@ -0,0 +1,5 @@
1executable ('gnunet-template',
2 ['gnunet-template.c'],
3 dependencies: [libgnunetutil_dep],
4 include_directories: [incdir, configuration_inc],
5 install: false)
diff --git a/src/contrib/meson.build b/src/contrib/meson.build
new file mode 100644
index 000000000..e856ea7ef
--- /dev/null
+++ b/src/contrib/meson.build
@@ -0,0 +1,5 @@
1#subdir('lib')
2subdir('service')
3#subdir('plugin')
4#subdir('rest-plugin')
5subdir('cli')
diff --git a/src/contrib/service/Makefile.am b/src/contrib/service/Makefile.am
new file mode 100644
index 000000000..2d365106b
--- /dev/null
+++ b/src/contrib/service/Makefile.am
@@ -0,0 +1,23 @@
1if HAVE_EXPERIMENTAL
2 EXP_DIR = \
3 rps \
4 abd \
5 auction
6endif
7
8if BUILD_PULSE_HELPERS
9CONVERSATION_DIR = conversation
10else
11if BUILD_GST_HELPERS
12CONVERSATION_DIR = conversation
13endif
14endif
15
16SUBDIRS = \
17 template \
18 set \
19 consensus \
20 secretsharing \
21 scalarproduct \
22 $(CONVERSATION_DIR) \
23 $(EXP_DIR)
diff --git a/src/contrib/service/abd/.gitignore b/src/contrib/service/abd/.gitignore
new file mode 100644
index 000000000..ee1eb7837
--- /dev/null
+++ b/src/contrib/service/abd/.gitignore
@@ -0,0 +1,2 @@
1gnunet-abd
2gnunet-service-abd
diff --git a/src/contrib/service/abd/Makefile.am b/src/contrib/service/abd/Makefile.am
new file mode 100644
index 000000000..f199d72ed
--- /dev/null
+++ b/src/contrib/service/abd/Makefile.am
@@ -0,0 +1,106 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4EXTRA_DIST = \
5 test_abd_defaults.conf \
6 test_abd_lookup.conf \
7 $(check_SCRIPTS) \
8 $(pkgdata_DATA)
9
10
11if USE_COVERAGE
12 AM_CFLAGS = --coverage -O0
13endif
14
15pkgcfgdir = $(pkgdatadir)/config.d/
16
17libexecdir= $(pkglibdir)/libexec/
18
19plugindir = $(libdir)/gnunet
20
21pkgcfg_DATA = \
22 abd.conf
23
24
25# /usr/lib - compiles a layer which can be used to be communicagte with the service
26lib_LTLIBRARIES = \
27 libgnunetabd.la
28
29# /usr/lib/gnunet/libexec - Business logic . Separate process
30libexec_PROGRAMS = \
31 gnunet-service-abd
32
33bin_PROGRAMS = \
34 gnunet-abd
35
36plugin_LTLIBRARIES = \
37 libgnunet_plugin_gnsrecord_abd.la
38
39
40gnunet_abd_SOURCES = \
41 gnunet-abd.c
42gnunet_abd_LDADD = \
43 libgnunetabd.la \
44 $(top_builddir)/src/lib/util/libgnunetutil.la \
45 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
46 $(top_builddir)/src/service/identity/libgnunetidentity.la \
47 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
48 $(GN_LIBINTL)
49
50
51libgnunet_plugin_gnsrecord_abd_la_SOURCES = \
52 plugin_gnsrecord_abd.c
53libgnunet_plugin_gnsrecord_abd_la_LIBADD = \
54 libgnunetabd.la \
55 $(top_builddir)/src/lib/util/libgnunetutil.la \
56 $(top_builddir)/src/service/identity/libgnunetidentity.la \
57 $(LTLIBINTL)
58libgnunet_plugin_gnsrecord_abd_la_LDFLAGS = \
59 $(GN_PLUGIN_LDFLAGS)
60
61
62
63gnunet_service_abd_SOURCES = \
64 gnunet-service-abd.c
65gnunet_service_abd_LDADD = \
66 libgnunetabd.la \
67 $(top_builddir)/src/lib/util/libgnunetutil.la \
68 $(top_builddir)/src/service/gns/libgnunetgns.la \
69 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
70 $(top_builddir)/src/service/identity/libgnunetidentity.la \
71 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
72 $(GN_LIBINTL)
73
74
75libgnunetabd_la_SOURCES = \
76 abd_api.c abd.h\
77 abd_serialization.c \
78 abd_serialization.h \
79 delegate_misc.c \
80 delegate_misc.h
81libgnunetabd_la_LIBADD = \
82 $(top_builddir)/src/lib/util/libgnunetutil.la \
83 $(top_builddir)/src/service/identity/libgnunetidentity.la \
84 $(XLIB)
85libgnunetabd_la_LDFLAGS = \
86 $(GN_LIB_LDFLAGS)
87
88
89
90check_SCRIPTS = \
91 test_abd_issue.sh
92 # test_abd_verify_simple.sh \
93 # test_abd_verify.sh \
94 # test_abd_verify_and.sh
95
96if ENABLE_TEST_RUN
97if HAVE_SQLITE
98 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;
99 TESTS = $(check_SCRIPTS)
100endif
101endif
102
103
104
105
106
diff --git a/src/contrib/service/abd/abd.conf.in b/src/contrib/service/abd/abd.conf.in
new file mode 100644
index 000000000..7baf95fc8
--- /dev/null
+++ b/src/contrib/service/abd/abd.conf.in
@@ -0,0 +1,5 @@
1[abd]
2BINARY = gnunet-service-abd
3UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-abd.sock
4RUN_PER_USER = YES
5OPTIONS = -L DEBUG
diff --git a/src/contrib/service/abd/abd.h b/src/contrib/service/abd/abd.h
new file mode 100644
index 000000000..26b8e222e
--- /dev/null
+++ b/src/contrib/service/abd/abd.h
@@ -0,0 +1,251 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file abd/abd.h
22 * @brief IPC messages between ABD API and ABD service
23 * @author Martin Schanzenbach
24 */
25#ifndef ABD_H
26#define ABD_H
27
28#include "gnunet_abd_service.h"
29
30GNUNET_NETWORK_STRUCT_BEGIN
31
32/**
33 * Message from client to Credential service to collect credentials.
34 */
35struct CollectMessage
36{
37 /**
38 * Header of type #GNUNET_MESSAGE_TYPE_ABD_VERIFY
39 */
40 struct GNUNET_MessageHeader header;
41
42 /**
43 * Subject public key
44 */
45 struct GNUNET_CRYPTO_PrivateKey subject_key;
46
47 /**
48 * Trust anchor
49 */
50 struct GNUNET_CRYPTO_PublicKey issuer_key;
51
52 /**
53 * Length of the issuer attribute
54 */
55 uint16_t issuer_attribute_len;
56
57 /**
58 * Direction of the resolution algo
59 */
60 uint16_t resolution_algo;
61
62 /**
63 * Unique identifier for this request (for key collisions).
64 */
65 uint32_t id GNUNET_PACKED;
66
67 /* Followed by the zero-terminated attribute */
68};
69
70
71/**
72 * Message from client to Credential service to verify attributes.
73 */
74struct VerifyMessage
75{
76 /**
77 * Header of type #GNUNET_MESSAGE_TYPE_ABD_VERIFY
78 */
79 struct GNUNET_MessageHeader header;
80
81 /**
82 * Subject public key
83 */
84 struct GNUNET_CRYPTO_PublicKey subject_key;
85
86 /**
87 * Trust anchor
88 */
89 struct GNUNET_CRYPTO_PublicKey issuer_key;
90
91 /**
92 * Number of delegates
93 */
94 uint32_t d_count;
95
96 /**
97 * Length of the issuer attribute
98 */
99 uint16_t issuer_attribute_len;
100
101 /**
102 * Direction of the resolution algo
103 */
104 uint16_t resolution_algo;
105
106 /**
107 * Unique identifier for this request (for key collisions).
108 */
109 uint32_t id GNUNET_PACKED;
110
111 /* Followed by the zero-terminated attribute and credentials to look up */
112};
113
114
115/**
116 * Message from ABD service to client: new results.
117 */
118struct DelegationChainResultMessage
119{
120 /**
121 * Header of type #GNUNET_MESSAGE_TYPE_ABD_VERIFY_RESULT
122 */
123 struct GNUNET_MessageHeader header;
124
125 /**
126 * Unique identifier for this request (for key collisions).
127 */
128 uint32_t id GNUNET_PACKED;
129
130 /**
131 * Indicates if credential has been found at all
132 */
133 uint32_t del_found GNUNET_PACKED;
134
135 /**
136 * The number of delegations in the response
137 */
138 uint32_t d_count GNUNET_PACKED;
139
140 /**
141 * The number of credentials in the response
142 */
143 uint32_t c_count GNUNET_PACKED;
144
145 /* followed by ad_count GNUNET_ABD_RecordData structs*/
146};
147
148/**
149 * Message from ABD service to client: new results.
150 */
151struct DelegationChainIntermediateMessage
152{
153 /**
154 * Header of type #GNUNET_MESSAGE_TYPE_ABD_INTERMEDIATE_RESULT
155 */
156 struct GNUNET_MessageHeader header;
157
158 /**
159 * Unique identifier for this request (for key collisions).
160 */
161 uint32_t id GNUNET_PACKED;
162
163 uint16_t is_bw GNUNET_PACKED;
164
165 uint32_t size GNUNET_PACKED;
166};
167
168struct DelegationRecordData
169{
170 /**
171 * Subject key
172 */
173 struct GNUNET_CRYPTO_PublicKey subject_key;
174
175 /**
176 * Subject attributes
177 */
178 uint32_t subject_attribute_len GNUNET_PACKED;
179};
180
181
182struct ChainEntry
183{
184 /**
185 * Issuer key
186 */
187 struct GNUNET_CRYPTO_PublicKey issuer_key;
188
189 /**
190 * Subject key
191 */
192 struct GNUNET_CRYPTO_PublicKey subject_key;
193
194 /**
195 * Issuer attributes
196 */
197 uint32_t issuer_attribute_len GNUNET_PACKED;
198
199 /**
200 * Subject attributes
201 */
202 uint32_t subject_attribute_len GNUNET_PACKED;
203};
204
205
206struct DelegateEntry
207{
208 /**
209 * The signature for this credential by the issuer
210 */
211 struct GNUNET_CRYPTO_Signature signature;
212
213 /**
214 * Signature meta
215 */
216 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
217
218 /**
219 * Public key of the issuer
220 */
221 struct GNUNET_CRYPTO_PublicKey issuer_key;
222
223 /**
224 * Public key of the subject this credential was issued to
225 */
226 struct GNUNET_CRYPTO_PublicKey subject_key;
227
228 /**
229 * Expiration time of this credential
230 */
231 uint64_t expiration GNUNET_PACKED;
232
233 /**
234 * Issuer subject attribute length
235 */
236 uint32_t issuer_attribute_len;
237
238 /**
239 * Issuer attribute length
240 */
241 uint32_t subject_attribute_len;
242
243 /**
244 * Followed by the subject attribute string
245 */
246};
247
248
249GNUNET_NETWORK_STRUCT_END
250
251#endif
diff --git a/src/contrib/service/abd/abd_api.c b/src/contrib/service/abd/abd_api.c
new file mode 100644
index 000000000..3682f41ba
--- /dev/null
+++ b/src/contrib/service/abd/abd_api.c
@@ -0,0 +1,563 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20/**
21 * @file abd/abd_api.c
22 * @brief library to access the ABD service
23 * @author Martin Schanzenbach
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_constants.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_protocols.h"
30#include "gnunet_signatures.h"
31#include "abd.h"
32#include "abd_serialization.h"
33#include "gnunet_abd_service.h"
34#include "gnunet_identity_service.h"
35
36
37#define LOG(kind, ...) GNUNET_log_from (kind, "abd-api", __VA_ARGS__)
38
39/**
40 * Handle to a verify request
41 */
42struct GNUNET_ABD_Request
43{
44
45 /**
46 * DLL
47 */
48 struct GNUNET_ABD_Request *next;
49
50 /**
51 * DLL
52 */
53 struct GNUNET_ABD_Request *prev;
54
55 /**
56 * handle to abd service
57 */
58 struct GNUNET_ABD_Handle *abd_handle;
59
60 /**
61 * processor to call on verify result
62 */
63 GNUNET_ABD_CredentialResultProcessor verify_proc;
64
65 /**
66 * @e verify_proc closure
67 */
68 void *proc_cls;
69
70 /**
71 * processor to call on intermediate result
72 */
73 GNUNET_ABD_IntermediateResultProcessor int_proc;
74
75 /**
76 * @e verify_proc2 closure
77 */
78 void *proc2_cls;
79
80 /**
81 * Envelope with the message for this queue entry.
82 */
83 struct GNUNET_MQ_Envelope *env;
84
85 /**
86 * request id
87 */
88 uint32_t r_id;
89};
90
91
92/**
93 * Connection to the ABD service.
94 */
95struct GNUNET_ABD_Handle
96{
97
98 /**
99 * Configuration to use.
100 */
101 const struct GNUNET_CONFIGURATION_Handle *cfg;
102
103 /**
104 * Connection to service (if available).
105 */
106 struct GNUNET_MQ_Handle *mq;
107
108 /**
109 * Head of linked list of active verify requests.
110 */
111 struct GNUNET_ABD_Request *request_head;
112
113 /**
114 * Tail of linked list of active verify requests.
115 */
116 struct GNUNET_ABD_Request *request_tail;
117
118 /**
119 * Reconnect task
120 */
121 struct GNUNET_SCHEDULER_Task *reconnect_task;
122
123 /**
124 * How long do we wait until we try to reconnect?
125 */
126 struct GNUNET_TIME_Relative reconnect_backoff;
127
128 /**
129 * Request Id generator. Incremented by one for each request.
130 */
131 uint32_t r_id_gen;
132};
133
134
135/**
136 * Reconnect to ABD service.
137 *
138 * @param handle the handle to the ABD service
139 */
140static void
141reconnect (struct GNUNET_ABD_Handle *handle);
142
143
144/**
145 * Reconnect to ABD
146 *
147 * @param cls the handle
148 */
149static void
150reconnect_task (void *cls)
151{
152 struct GNUNET_ABD_Handle *handle = cls;
153
154 handle->reconnect_task = NULL;
155 reconnect (handle);
156}
157
158
159/**
160 * Disconnect from service and then reconnect.
161 *
162 * @param handle our handle
163 */
164static void
165force_reconnect (struct GNUNET_ABD_Handle *handle)
166{
167 GNUNET_MQ_destroy (handle->mq);
168 handle->mq = NULL;
169 handle->reconnect_backoff =
170 GNUNET_TIME_STD_BACKOFF (handle->reconnect_backoff);
171 handle->reconnect_task =
172 GNUNET_SCHEDULER_add_delayed (handle->reconnect_backoff,
173 &reconnect_task,
174 handle);
175}
176
177
178/**
179 * Generic error handler, called with the appropriate error code and
180 * the same closure specified at the creation of the message queue.
181 * Not every message queue implementation supports an error handler.
182 *
183 * @param cls closure with the `struct GNUNET_ABD_Handle *`
184 * @param error error code
185 */
186static void
187mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
188{
189 struct GNUNET_ABD_Handle *handle = cls;
190
191 force_reconnect (handle);
192}
193
194
195/**
196 * Check validity of message received from the ABD service
197 *
198 * @param cls the `struct GNUNET_ABD_Handle *`
199 * @param vr_msg the incoming message
200 */
201static int
202check_result (void *cls, const struct DelegationChainResultMessage *vr_msg)
203{
204 // TODO
205 return GNUNET_OK;
206}
207
208
209/**
210 * Handler for messages received from the ABD service
211 *
212 * @param cls the `struct GNUNET_ABD_Handle *`
213 * @param vr_msg the incoming message
214 */
215static void
216handle_result (void *cls, const struct DelegationChainResultMessage *vr_msg)
217{
218 struct GNUNET_ABD_Handle *handle = cls;
219 uint32_t r_id = ntohl (vr_msg->id);
220 struct GNUNET_ABD_Request *vr;
221 size_t mlen = ntohs (vr_msg->header.size) - sizeof (*vr_msg);
222 uint32_t d_count = ntohl (vr_msg->d_count);
223 uint32_t c_count = ntohl (vr_msg->c_count);
224 struct GNUNET_ABD_Delegation d_chain[d_count];
225 struct GNUNET_ABD_Delegate dels[c_count];
226 GNUNET_ABD_CredentialResultProcessor proc;
227 void *proc_cls;
228
229 LOG (GNUNET_ERROR_TYPE_DEBUG,
230 "Received verify reply from ABD service\n");
231 for (vr = handle->request_head; NULL != vr; vr = vr->next)
232 if (vr->r_id == r_id)
233 break;
234 if (NULL == vr)
235 return;
236 proc = vr->verify_proc;
237 proc_cls = vr->proc_cls;
238 GNUNET_CONTAINER_DLL_remove (handle->request_head, handle->request_tail, vr);
239 GNUNET_MQ_discard (vr->env);
240 GNUNET_free (vr);
241 GNUNET_assert (
242 GNUNET_OK ==
243 GNUNET_ABD_delegation_chain_deserialize (mlen,
244 (const char *) &vr_msg[1],
245 d_count,
246 d_chain,
247 c_count,
248 dels));
249 if (GNUNET_NO == ntohl (vr_msg->del_found))
250 {
251 proc (proc_cls, 0, NULL, 0,
252 NULL);
253 }
254 else
255 {
256 proc (proc_cls, d_count, d_chain, c_count, dels);
257 }
258}
259
260
261static int
262check_intermediate (void *cls, const struct
263 DelegationChainIntermediateMessage *vr_msg)
264{
265 // TODO
266 return GNUNET_OK;
267}
268
269
270static void
271handle_intermediate (void *cls, const struct
272 DelegationChainIntermediateMessage *vr_msg)
273{
274 struct GNUNET_ABD_Handle *handle = cls;
275 uint32_t r_id = ntohl (vr_msg->id);
276 uint32_t size = ntohl (vr_msg->size);
277 bool is_bw = ntohs (vr_msg->is_bw);
278 struct GNUNET_ABD_Request *vr;
279 GNUNET_ABD_IntermediateResultProcessor proc;
280 void *proc_cls;
281 struct GNUNET_ABD_Delegation *dd;
282
283
284 LOG (GNUNET_ERROR_TYPE_DEBUG,
285 "Received intermediate reply from ABD service\n");
286
287 for (vr = handle->request_head; NULL != vr; vr = vr->next)
288 if (vr->r_id == r_id)
289 break;
290 if (NULL == vr)
291 return;
292
293 proc = vr->int_proc;
294 proc_cls = vr->proc2_cls;
295
296 dd = GNUNET_new (struct GNUNET_ABD_Delegation);
297 GNUNET_assert (
298 GNUNET_OK ==
299 GNUNET_ABD_delegation_chain_deserialize (size,
300 (const char *) &vr_msg[1],
301 1,
302 dd,
303 0,
304 NULL));
305 proc (proc_cls, dd, is_bw);
306}
307
308
309/**
310 * Reconnect to ABD service.
311 *
312 * @param handle the handle to the ABD service
313 */
314static void
315reconnect (struct GNUNET_ABD_Handle *handle)
316{
317 struct GNUNET_MQ_MessageHandler handlers[] =
318 {GNUNET_MQ_hd_var_size (result,
319 GNUNET_MESSAGE_TYPE_ABD_VERIFY_RESULT,
320 struct DelegationChainResultMessage,
321 handle),
322 GNUNET_MQ_hd_var_size (result,
323 GNUNET_MESSAGE_TYPE_ABD_COLLECT_RESULT,
324 struct DelegationChainResultMessage,
325 handle),
326 GNUNET_MQ_hd_var_size (intermediate,
327 GNUNET_MESSAGE_TYPE_ABD_INTERMEDIATE_RESULT,
328 struct DelegationChainIntermediateMessage,
329 handle),
330 GNUNET_MQ_handler_end ()};
331 struct GNUNET_ABD_Request *vr;
332
333 GNUNET_assert (NULL == handle->mq);
334 LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to ABD\n");
335 handle->mq = GNUNET_CLIENT_connect (handle->cfg,
336 "abd",
337 handlers,
338 &mq_error_handler,
339 handle);
340 if (NULL == handle->mq)
341 return;
342 for (vr = handle->request_head; NULL != vr; vr = vr->next)
343 GNUNET_MQ_send_copy (handle->mq, vr->env);
344}
345
346
347/**
348 * Initialize the connection with the ABD service.
349 *
350 * @param cfg configuration to use
351 * @return handle to the ABD service, or NULL on error
352 */
353struct GNUNET_ABD_Handle *
354GNUNET_ABD_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
355{
356 struct GNUNET_ABD_Handle *handle;
357
358 handle = GNUNET_new (struct GNUNET_ABD_Handle);
359 handle->cfg = cfg;
360 reconnect (handle);
361 if (NULL == handle->mq)
362 {
363 GNUNET_free (handle);
364 return NULL;
365 }
366 return handle;
367}
368
369
370/**
371 * Shutdown connection with the ABD service.
372 *
373 * @param handle handle of the ABD connection to stop
374 */
375void
376GNUNET_ABD_disconnect (struct GNUNET_ABD_Handle *handle)
377{
378 if (NULL != handle->mq)
379 {
380 GNUNET_MQ_destroy (handle->mq);
381 handle->mq = NULL;
382 }
383 if (NULL != handle->reconnect_task)
384 {
385 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
386 handle->reconnect_task = NULL;
387 }
388 GNUNET_assert (NULL == handle->request_head);
389 GNUNET_free (handle);
390}
391
392
393/**
394 * Cancel pending verify request
395 *
396 * @param lr the verify request to cancel
397 */
398void
399GNUNET_ABD_request_cancel (struct GNUNET_ABD_Request *lr)
400{
401 struct GNUNET_ABD_Handle *handle = lr->abd_handle;
402
403 GNUNET_CONTAINER_DLL_remove (handle->request_head, handle->request_tail, lr);
404 GNUNET_MQ_discard (lr->env);
405 GNUNET_free (lr);
406}
407
408
409/**
410 * Performs attribute collection.
411 * Collects all abds of subject to fulfill the
412 * attribute, if possible
413 *
414 * @param handle handle to the Credential service
415 * @param issuer_key the issuer public key
416 * @param issuer_attribute the issuer attribute
417 * @param subject_key the subject public key
418 * @param proc function to call on result
419 * @param proc_cls closure for processor
420 * @return handle to the queued request
421 */
422struct GNUNET_ABD_Request *
423GNUNET_ABD_collect (
424 struct GNUNET_ABD_Handle *handle,
425 const struct GNUNET_CRYPTO_PublicKey *issuer_key,
426 const char *issuer_attribute,
427 const struct GNUNET_CRYPTO_PrivateKey *subject_key,
428 enum GNUNET_ABD_AlgoDirectionFlags direction,
429 GNUNET_ABD_CredentialResultProcessor proc,
430 void *proc_cls,
431 GNUNET_ABD_IntermediateResultProcessor proc2,
432 void *proc2_cls)
433{
434 /* IPC to shorten abd names, return shorten_handle */
435 struct CollectMessage *c_msg;
436 struct GNUNET_ABD_Request *vr;
437 size_t nlen;
438
439 if (NULL == issuer_attribute)
440 {
441 GNUNET_break (0);
442 return NULL;
443 }
444
445 // DEBUG LOG
446 LOG (GNUNET_ERROR_TYPE_DEBUG,
447 "Trying to collect `%s' in ABD\n",
448 issuer_attribute);
449 nlen = strlen (issuer_attribute) + 1;
450 if (nlen >= GNUNET_MAX_MESSAGE_SIZE - sizeof (*vr))
451 {
452 GNUNET_break (0);
453 return NULL;
454 }
455 vr = GNUNET_new (struct GNUNET_ABD_Request);
456 vr->abd_handle = handle;
457 vr->verify_proc = proc;
458 vr->proc_cls = proc_cls;
459 vr->int_proc = proc2;
460 vr->proc2_cls = proc2_cls;
461 vr->r_id = handle->r_id_gen++;
462 vr->env =
463 GNUNET_MQ_msg_extra (c_msg, nlen, GNUNET_MESSAGE_TYPE_ABD_COLLECT);
464 c_msg->id = htonl (vr->r_id);
465 c_msg->subject_key = *subject_key;
466 c_msg->issuer_key = *issuer_key;
467 c_msg->issuer_attribute_len = htons (strlen (issuer_attribute));
468 c_msg->resolution_algo = htons (direction);
469
470 GNUNET_memcpy (&c_msg[1], issuer_attribute, strlen (issuer_attribute));
471 GNUNET_CONTAINER_DLL_insert (handle->request_head, handle->request_tail, vr);
472 if (NULL != handle->mq)
473 GNUNET_MQ_send_copy (handle->mq, vr->env);
474 return vr;
475}
476
477
478/**
479 * Performs attribute verification.
480 * Checks if there is a delegation chain from
481 * attribute ``issuer_attribute'' issued by the issuer
482 * with public key ``issuer_key'' maps to the attribute
483 * ``subject_attribute'' claimed by the subject with key
484 * ``subject_key''
485 *
486 * @param handle handle to the Credential service
487 * @param issuer_key the issuer public key
488 * @param issuer_attribute the issuer attribute
489 * @param subject_key the subject public key
490 * @param delegate_count number of delegates provided
491 * @param delegates subject delegates
492 * @param proc function to call on result
493 * @param proc_cls closure for processor
494 * @return handle to the queued request
495 */
496struct GNUNET_ABD_Request *
497GNUNET_ABD_verify (
498 struct GNUNET_ABD_Handle *handle,
499 const struct GNUNET_CRYPTO_PublicKey *issuer_key,
500 const char *issuer_attribute,
501 const struct GNUNET_CRYPTO_PublicKey *subject_key,
502 uint32_t delegate_count,
503 const struct GNUNET_ABD_Delegate *delegates,
504 enum GNUNET_ABD_AlgoDirectionFlags direction,
505 GNUNET_ABD_CredentialResultProcessor proc,
506 void *proc_cls,
507 GNUNET_ABD_IntermediateResultProcessor proc2,
508 void *proc2_cls)
509{
510 /* IPC to shorten abd names, return shorten_handle */
511 struct VerifyMessage *v_msg;
512 struct GNUNET_ABD_Request *vr;
513 size_t nlen;
514 size_t clen;
515
516 if ((NULL == issuer_attribute) || (NULL == delegates))
517 {
518 GNUNET_break (0);
519 return NULL;
520 }
521
522 clen = GNUNET_ABD_delegates_get_size (delegate_count, delegates);
523
524 // DEBUG LOG
525 LOG (GNUNET_ERROR_TYPE_DEBUG,
526 "Trying to verify `%s' in ABD\n",
527 issuer_attribute);
528 nlen = strlen (issuer_attribute) + 1 + clen;
529 if (nlen >= GNUNET_MAX_MESSAGE_SIZE - sizeof (*vr))
530 {
531 GNUNET_break (0);
532 return NULL;
533 }
534 vr = GNUNET_new (struct GNUNET_ABD_Request);
535 vr->abd_handle = handle;
536 vr->verify_proc = proc;
537 vr->proc_cls = proc_cls;
538 vr->int_proc = proc2;
539 vr->proc2_cls = proc2_cls;
540 vr->r_id = handle->r_id_gen++;
541 vr->env =
542 GNUNET_MQ_msg_extra (v_msg, nlen, GNUNET_MESSAGE_TYPE_ABD_VERIFY);
543 v_msg->id = htonl (vr->r_id);
544 v_msg->subject_key = *subject_key;
545 v_msg->d_count = htonl (delegate_count);
546 v_msg->issuer_key = *issuer_key;
547 v_msg->issuer_attribute_len = htons (strlen (issuer_attribute));
548 v_msg->resolution_algo = htons (direction);
549
550 GNUNET_memcpy (&v_msg[1], issuer_attribute, strlen (issuer_attribute));
551 GNUNET_ABD_delegates_serialize (delegate_count,
552 delegates,
553 clen,
554 ((char *) &v_msg[1])
555 + strlen (issuer_attribute) + 1);
556 GNUNET_CONTAINER_DLL_insert (handle->request_head, handle->request_tail, vr);
557 if (NULL != handle->mq)
558 GNUNET_MQ_send_copy (handle->mq, vr->env);
559 return vr;
560}
561
562
563/* end of abd_api.c */
diff --git a/src/contrib/service/abd/abd_serialization.c b/src/contrib/service/abd/abd_serialization.c
new file mode 100644
index 000000000..14960e804
--- /dev/null
+++ b/src/contrib/service/abd/abd_serialization.c
@@ -0,0 +1,516 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21
22/**
23 * @file abd/abd_serialization.c
24 * @brief API to serialize and deserialize delegation chains
25 * and abds
26 * @author Martin Schanzenbach
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_abd_service.h"
32#include "gnunet_signatures.h"
33#include "abd.h"
34
35/**
36 * Calculate how many bytes we will need to serialize
37 * the given delegation chain
38 *
39 * @param ds_count number of delegation chain entries
40 * @param dsr array of #GNUNET_ABD_DelegationSet
41 * @return the required size to serialize
42 */
43size_t
44GNUNET_ABD_delegation_set_get_size (
45 unsigned int ds_count,
46 const struct GNUNET_ABD_DelegationSet *dsr)
47{
48 unsigned int i;
49 size_t ret;
50
51 ret = sizeof (struct DelegationRecordData) * (ds_count);
52
53 for (i = 0; i < ds_count; i++)
54 {
55 GNUNET_assert ((ret + dsr[i].subject_attribute_len) >= ret);
56 ret += dsr[i].subject_attribute_len;
57 }
58 return ret;
59}
60
61
62/**
63 * Serizalize the given delegation chain entries and abd
64 *
65 * @param d_count number of delegation chain entries
66 * @param dsr array of #GNUNET_ABD_DelegationSet
67 * @param dest_size size of the destination
68 * @param dest where to store the result
69 * @return the size of the data, -1 on failure
70 */
71ssize_t
72GNUNET_ABD_delegation_set_serialize (
73 unsigned int d_count,
74 const struct GNUNET_ABD_DelegationSet *dsr,
75 size_t dest_size,
76 char *dest)
77{
78 struct DelegationRecordData rec;
79 unsigned int i;
80 size_t off;
81
82 off = 0;
83 for (i = 0; i < d_count; i++)
84 {
85 rec.subject_attribute_len = htonl ((uint32_t) dsr[i].subject_attribute_len);
86 rec.subject_key = dsr[i].subject_key;
87 if (off + sizeof (rec) > dest_size)
88 return -1;
89 GNUNET_memcpy (&dest[off], &rec, sizeof (rec));
90 off += sizeof (rec);
91 if (0 == dsr[i].subject_attribute_len)
92 continue;
93 if (off + dsr[i].subject_attribute_len > dest_size)
94 return -1;
95 GNUNET_memcpy (&dest[off],
96 dsr[i].subject_attribute,
97 dsr[i].subject_attribute_len);
98 off += dsr[i].subject_attribute_len;
99 }
100 return off;
101}
102
103
104/**
105 * Deserialize the given destination
106 *
107 * @param len size of the serialized delegation chain and cred
108 * @param src the serialized data
109 * @param d_count the number of delegation chain entries
110 * @param dsr where to put the delegation chain entries
111 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
112 */
113int
114GNUNET_ABD_delegation_set_deserialize (
115 size_t len,
116 const char *src,
117 unsigned int d_count,
118 struct GNUNET_ABD_DelegationSet *dsr)
119{
120 struct DelegationRecordData rec;
121 unsigned int i;
122 size_t off;
123
124 off = 0;
125 for (i = 0; i < d_count; i++)
126 {
127 if (off + sizeof (rec) > len)
128 return GNUNET_SYSERR;
129 GNUNET_memcpy (&rec, &src[off], sizeof (rec));
130 dsr[i].subject_key = rec.subject_key;
131 off += sizeof (rec);
132 dsr[i].subject_attribute_len = ntohl ((uint32_t) rec.subject_attribute_len);
133 if (off + dsr[i].subject_attribute_len > len)
134 return GNUNET_SYSERR;
135 dsr[i].subject_attribute = (char *) &src[off];
136 off += dsr[i].subject_attribute_len;
137 }
138 return GNUNET_OK;
139}
140
141
142/**
143 * Calculate how many bytes we will need to serialize
144 * the abds
145 *
146 * @param c_count number of abd entries
147 * @param cd a #GNUNET_ABD_Credential
148 * @return the required size to serialize
149 */
150size_t
151GNUNET_ABD_delegates_get_size (
152 unsigned int c_count,
153 const struct GNUNET_ABD_Delegate *cd)
154{
155 unsigned int i;
156 size_t ret;
157
158 ret = sizeof (struct DelegateEntry) * (c_count);
159
160 for (i = 0; i < c_count; i++)
161 {
162 GNUNET_assert ((ret + cd[i].issuer_attribute_len
163 + cd[i].subject_attribute_len) >= ret);
164 // subject_attribute_len should be 0
165 ret += cd[i].issuer_attribute_len + cd[i].subject_attribute_len;
166 }
167 return ret;
168}
169
170
171/**
172 * Serizalize the given abds
173 *
174 * @param c_count number of abd entries
175 * @param cd a #GNUNET_ABD_Credential
176 * @param dest_size size of the destination
177 * @param dest where to store the result
178 * @return the size of the data, -1 on failure
179 */
180ssize_t
181GNUNET_ABD_delegates_serialize (
182 unsigned int c_count,
183 const struct GNUNET_ABD_Delegate *cd,
184 size_t dest_size,
185 char *dest)
186{
187 struct DelegateEntry c_rec;
188 unsigned int i;
189 size_t off;
190
191 off = 0;
192 for (i = 0; i < c_count; i++)
193 {
194 c_rec.subject_attribute_len = htonl (cd[i].subject_attribute_len);
195 c_rec.issuer_attribute_len = htonl (cd[i].issuer_attribute_len);
196 c_rec.issuer_key = cd[i].issuer_key;
197 c_rec.subject_key = cd[i].subject_key;
198 c_rec.signature = cd[i].signature;
199 c_rec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DELEGATE);
200 c_rec.purpose.size =
201 htonl ((sizeof (struct DelegateEntry) + cd[i].issuer_attribute_len)
202 - sizeof (struct GNUNET_CRYPTO_Signature));
203 c_rec.expiration = GNUNET_htonll (cd[i].expiration.abs_value_us);
204 if (off + sizeof (c_rec) > dest_size)
205 return -1;
206 GNUNET_memcpy (&dest[off], &c_rec, sizeof (c_rec));
207 off += sizeof (c_rec);
208 if (off + cd[i].issuer_attribute_len > dest_size)
209 return -1;
210 GNUNET_memcpy (&dest[off],
211 cd[i].issuer_attribute,
212 cd[i].issuer_attribute_len);
213 off += cd[i].issuer_attribute_len;
214 }
215
216 return off;
217}
218
219
220/**
221 * Deserialize the given destination
222 *
223 * @param len size of the serialized creds
224 * @param src the serialized data
225 * @param c_count the number of abd entries
226 * @param cd where to put the abd data
227 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
228 */
229int
230GNUNET_ABD_delegates_deserialize (size_t len,
231 const char *src,
232 unsigned int c_count,
233 struct GNUNET_ABD_Delegate *cd)
234{
235 struct DelegateEntry c_rec;
236 unsigned int i;
237 size_t off;
238
239 off = 0;
240 for (i = 0; i < c_count; i++)
241 {
242 if (off + sizeof (c_rec) > len)
243 return GNUNET_SYSERR;
244 GNUNET_memcpy (&c_rec, &src[off], sizeof (c_rec));
245 cd[i].issuer_attribute_len = ntohl ((uint32_t) c_rec.issuer_attribute_len);
246 cd[i].issuer_key = c_rec.issuer_key;
247 cd[i].subject_key = c_rec.subject_key;
248 cd[i].signature = c_rec.signature;
249 cd[i].expiration.abs_value_us = GNUNET_ntohll (c_rec.expiration);
250 off += sizeof (c_rec);
251 if (off + cd[i].issuer_attribute_len > len)
252 return GNUNET_SYSERR;
253 cd[i].issuer_attribute = &src[off];
254 off += cd[i].issuer_attribute_len;
255 cd[i].subject_attribute_len = 0;
256 }
257 return GNUNET_OK;
258}
259
260
261/**
262 * Calculate how many bytes we will need to serialize
263 * the given delegation chain and abd
264 *
265 * @param d_count number of delegation chain entries
266 * @param dd array of #GNUNET_ABD_Delegation
267 * @param c_count number of abd entries
268 * @param cd a #GNUNET_ABD_Credential
269 * @return the required size to serialize
270 */
271size_t
272GNUNET_ABD_delegation_chain_get_size (
273 unsigned int d_count,
274 const struct GNUNET_ABD_Delegation *dd,
275 unsigned int c_count,
276 const struct GNUNET_ABD_Delegate *cd)
277{
278 unsigned int i;
279 size_t ret;
280
281 ret = sizeof (struct ChainEntry) * (d_count);
282
283 for (i = 0; i < d_count; i++)
284 {
285 GNUNET_assert (
286 (ret + dd[i].issuer_attribute_len + dd[i].subject_attribute_len) >= ret);
287 ret += dd[i].issuer_attribute_len + dd[i].subject_attribute_len;
288 }
289 return ret + GNUNET_ABD_delegates_get_size (c_count, cd);
290}
291
292
293/**
294 * Serizalize the given delegation chain entries and abd
295 *
296 * @param d_count number of delegation chain entries
297 * @param dd array of #GNUNET_ABD_Delegation
298 * @param c_count number of abd entries
299 * @param cd a #GNUNET_ABD_Credential
300 * @param dest_size size of the destination
301 * @param dest where to store the result
302 * @return the size of the data, -1 on failure
303 */
304ssize_t
305GNUNET_ABD_delegation_chain_serialize (
306 unsigned int d_count,
307 const struct GNUNET_ABD_Delegation *dd,
308 unsigned int c_count,
309 const struct GNUNET_ABD_Delegate *cd,
310 size_t dest_size,
311 char *dest)
312{
313 struct ChainEntry rec;
314 unsigned int i;
315 size_t off;
316
317 off = 0;
318 for (i = 0; i < d_count; i++)
319 {
320 rec.issuer_attribute_len = htonl ((uint32_t) dd[i].issuer_attribute_len);
321 rec.subject_attribute_len = htonl ((uint32_t) dd[i].subject_attribute_len);
322 rec.issuer_key = dd[i].issuer_key;
323 rec.subject_key = dd[i].subject_key;
324 if (off + sizeof (rec) > dest_size)
325 return -1;
326 GNUNET_memcpy (&dest[off], &rec, sizeof (rec));
327 off += sizeof (rec);
328 if (off + dd[i].issuer_attribute_len > dest_size)
329 return -1;
330 GNUNET_memcpy (&dest[off],
331 dd[i].issuer_attribute,
332 dd[i].issuer_attribute_len);
333 off += dd[i].issuer_attribute_len;
334 if (0 == dd[i].subject_attribute_len)
335 continue;
336 if (off + dd[i].subject_attribute_len > dest_size)
337 return -1;
338 GNUNET_memcpy (&dest[off],
339 dd[i].subject_attribute,
340 dd[i].subject_attribute_len);
341 off += dd[i].subject_attribute_len;
342 }
343 return off + GNUNET_ABD_delegates_serialize (c_count,
344 cd,
345 dest_size - off,
346 &dest[off]);
347}
348
349
350/**
351 * Deserialize the given destination
352 *
353 * @param len size of the serialized delegation chain and cred
354 * @param src the serialized data
355 * @param d_count the number of delegation chain entries
356 * @param dd where to put the delegation chain entries
357 * @param c_count the number of abd entries
358 * @param cd where to put the abd data
359 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
360 */
361int
362GNUNET_ABD_delegation_chain_deserialize (
363 size_t len,
364 const char *src,
365 unsigned int d_count,
366 struct GNUNET_ABD_Delegation *dd,
367 unsigned int c_count,
368 struct GNUNET_ABD_Delegate *cd)
369{
370 struct ChainEntry rec;
371 unsigned int i;
372 size_t off;
373
374 off = 0;
375 for (i = 0; i < d_count; i++)
376 {
377 if (off + sizeof (rec) > len)
378 return GNUNET_SYSERR;
379 GNUNET_memcpy (&rec, &src[off], sizeof (rec));
380 dd[i].issuer_attribute_len = ntohl ((uint32_t) rec.issuer_attribute_len);
381 dd[i].issuer_key = rec.issuer_key;
382 dd[i].subject_key = rec.subject_key;
383 off += sizeof (rec);
384 if (off + dd[i].issuer_attribute_len > len)
385 return GNUNET_SYSERR;
386 dd[i].issuer_attribute = &src[off];
387 off += dd[i].issuer_attribute_len;
388 dd[i].subject_attribute_len = ntohl ((uint32_t) rec.subject_attribute_len);
389 if (off + dd[i].subject_attribute_len > len)
390 return GNUNET_SYSERR;
391 dd[i].subject_attribute = &src[off];
392 off += dd[i].subject_attribute_len;
393 }
394 return GNUNET_ABD_delegates_deserialize (len - off,
395 &src[off],
396 c_count,
397 cd);
398}
399
400
401int
402GNUNET_ABD_delegate_serialize (struct GNUNET_ABD_Delegate *dele,
403 char **data)
404{
405 size_t size;
406 struct DelegateEntry *cdata;
407 int attr_len;
408
409 // +1 for \0
410 if (0 == dele->subject_attribute_len)
411 {
412 attr_len = dele->issuer_attribute_len + 1;
413 }
414 else
415 {
416 attr_len = dele->issuer_attribute_len + dele->subject_attribute_len + 2;
417 }
418 size = sizeof (struct DelegateEntry) + attr_len;
419
420 char tmp_str[attr_len];
421 GNUNET_memcpy (tmp_str, dele->issuer_attribute, dele->issuer_attribute_len);
422 if (0 != dele->subject_attribute_len)
423 {
424 tmp_str[dele->issuer_attribute_len] = '\0';
425 GNUNET_memcpy (tmp_str + dele->issuer_attribute_len + 1,
426 dele->subject_attribute,
427 dele->subject_attribute_len);
428 }
429 tmp_str[attr_len - 1] = '\0';
430
431 *data = GNUNET_malloc (size);
432 cdata = (struct DelegateEntry *) *data;
433 cdata->subject_key = dele->subject_key;
434 cdata->issuer_key = dele->issuer_key;
435 cdata->expiration = GNUNET_htonll (dele->expiration.abs_value_us);
436 cdata->signature = dele->signature;
437 cdata->issuer_attribute_len = htonl (dele->issuer_attribute_len + 1);
438 if (0 == dele->subject_attribute_len)
439 {
440 cdata->subject_attribute_len = htonl (0);
441 }
442 else
443 {
444 cdata->subject_attribute_len = htonl (dele->subject_attribute_len + 1);
445 }
446 cdata->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DELEGATE);
447 cdata->purpose.size =
448 htonl (size - sizeof (struct GNUNET_CRYPTO_Signature));
449
450 GNUNET_memcpy (&cdata[1], tmp_str, attr_len);
451
452 if (GNUNET_OK !=
453 GNUNET_CRYPTO_signature_verify_ (GNUNET_SIGNATURE_PURPOSE_DELEGATE,
454 &cdata->purpose,
455 &cdata->signature,
456 &cdata->issuer_key))
457 {
458 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Serialize: Invalid delegate\n");
459 return 0;
460 }
461 return size;
462}
463
464
465struct GNUNET_ABD_Delegate *
466GNUNET_ABD_delegate_deserialize (const char *data, size_t data_size)
467{
468 struct GNUNET_ABD_Delegate *dele;
469 struct DelegateEntry *cdata;
470 char *attr_combo_str;
471
472 if (data_size < sizeof (struct DelegateEntry))
473 return NULL;
474 cdata = (struct DelegateEntry *) data;
475 if (GNUNET_OK !=
476 GNUNET_CRYPTO_signature_verify_ (GNUNET_SIGNATURE_PURPOSE_DELEGATE,
477 &cdata->purpose,
478 &cdata->signature,
479 &cdata->issuer_key))
480 {
481 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Deserialize: Invalid delegate\n");
482 return NULL;
483 }
484 attr_combo_str = (char *) &cdata[1];
485 int iss_len = ntohl (cdata->issuer_attribute_len);
486 int sub_len = ntohl (cdata->subject_attribute_len);
487 int attr_combo_len = iss_len + sub_len;
488
489 dele =
490 GNUNET_malloc (sizeof (struct GNUNET_ABD_Delegate) + attr_combo_len);
491
492 dele->issuer_key = cdata->issuer_key;
493 dele->subject_key = cdata->subject_key;
494 GNUNET_memcpy (&dele[1], attr_combo_str, attr_combo_len);
495 dele->signature = cdata->signature;
496
497 // Set the pointers for the attributes
498 dele->issuer_attribute = (char *) &dele[1];
499 dele->issuer_attribute_len = iss_len;
500 dele->subject_attribute_len = sub_len;
501 if (0 == sub_len)
502 {
503 dele->subject_attribute = NULL;
504 }
505 else
506 {
507 dele->subject_attribute = (char *) &dele[1] + iss_len;
508 }
509
510 dele->expiration.abs_value_us = GNUNET_ntohll (cdata->expiration);
511
512 return dele;
513}
514
515
516/* end of abd_serialization.c */
diff --git a/src/contrib/service/abd/abd_serialization.h b/src/contrib/service/abd/abd_serialization.h
new file mode 100644
index 000000000..21dc45a7b
--- /dev/null
+++ b/src/contrib/service/abd/abd_serialization.h
@@ -0,0 +1,167 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21
22/**
23 * @file abd/abd_serialization.h
24 * @brief API to serialize and deserialize delegation chains
25 * and abds
26 * @author Martin Schanzenbach
27 */
28#ifndef ABD_SERIALIZATION_H
29#define ABD_SERIALIZATION_H
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_constants.h"
34#include "gnunet_abd_service.h"
35
36/**
37 * Calculate how many bytes we will need to serialize
38 * the given delegation record
39 *
40 * @param ds_count number of delegation chain entries
41 * @param dsr array of #GNUNET_ABD_Delegation
42 * @return the required size to serialize
43 */
44size_t
45GNUNET_ABD_delegation_set_get_size (
46 unsigned int ds_count,
47 const struct GNUNET_ABD_DelegationSet *dsr);
48
49/**
50 * Serizalize the given delegation record entries
51 *
52 * @param d_count number of delegation chain entries
53 * @param dsr array of #GNUNET_ABD_Delegation
54 * @param dest_size size of the destination
55 * @param dest where to store the result
56 * @return the size of the data, -1 on failure
57 */
58ssize_t
59GNUNET_ABD_delegation_set_serialize (
60 unsigned int d_count,
61 const struct GNUNET_ABD_DelegationSet *dsr,
62 size_t dest_size,
63 char *dest);
64
65
66/**
67 * Deserialize the given destination
68 *
69 * @param len size of the serialized delegation recird
70 * @param src the serialized data
71 * @param d_count the number of delegation chain entries
72 * @param dsr where to put the delegation chain entries
73 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
74 */
75int
76GNUNET_ABD_delegation_set_deserialize (
77 size_t len,
78 const char *src,
79 unsigned int d_count,
80 struct GNUNET_ABD_DelegationSet *dsr);
81
82/**
83 * Calculate how many bytes we will need to serialize
84 * the given delegation chain and abd
85 *
86 * @param d_count number of delegation chain entries
87 * @param dd array of #GNUNET_ABD_Delegation
88 * @param c_count number of abd entries
89 * @param cd a #GNUNET_ABD_Delegate
90 * @return the required size to serialize
91 */
92size_t
93GNUNET_ABD_delegation_chain_get_size (
94 unsigned int d_count,
95 const struct GNUNET_ABD_Delegation *dd,
96 unsigned int c_count,
97 const struct GNUNET_ABD_Delegate *cd);
98
99/**
100 * Serizalize the given delegation chain entries and abd
101 *
102 * @param d_count number of delegation chain entries
103 * @param dd array of #GNUNET_ABD_Delegation
104 * @param c_count number of abd entries
105 * @param cd a #GNUNET_ABD_Delegate
106 * @param dest_size size of the destination
107 * @param dest where to store the result
108 * @return the size of the data, -1 on failure
109 */
110ssize_t
111GNUNET_ABD_delegation_chain_serialize (
112 unsigned int d_count,
113 const struct GNUNET_ABD_Delegation *dd,
114 unsigned int c_count,
115 const struct GNUNET_ABD_Delegate *cd,
116 size_t dest_size,
117 char *dest);
118
119
120/**
121 * Deserialize the given destination
122 *
123 * @param len size of the serialized delegation chain and cred
124 * @param src the serialized data
125 * @param d_count the number of delegation chain entries
126 * @param dd where to put the delegation chain entries
127 * @param c_count number of abd entries
128 * @param cd where to put the abd data
129 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
130 */
131int
132GNUNET_ABD_delegation_chain_deserialize (
133 size_t len,
134 const char *src,
135 unsigned int d_count,
136 struct GNUNET_ABD_Delegation *dd,
137 unsigned int c_count,
138 struct GNUNET_ABD_Delegate *cd);
139
140size_t
141GNUNET_ABD_delegates_get_size (
142 unsigned int c_count,
143 const struct GNUNET_ABD_Delegate *cd);
144
145ssize_t
146GNUNET_ABD_delegates_serialize (
147 unsigned int c_count,
148 const struct GNUNET_ABD_Delegate *cd,
149 size_t dest_size,
150 char *dest);
151
152
153int
154GNUNET_ABD_delegates_deserialize (size_t len,
155 const char *src,
156 unsigned int c_count,
157 struct GNUNET_ABD_Delegate *cd);
158
159int
160GNUNET_ABD_delegate_serialize (struct GNUNET_ABD_Delegate *cred,
161 char **data);
162
163struct GNUNET_ABD_Delegate *
164GNUNET_ABD_delegate_deserialize (const char *data, size_t data_size);
165
166#endif
167/* end of abd_serialization.h */
diff --git a/src/contrib/service/abd/delegate_misc.c b/src/contrib/service/abd/delegate_misc.c
new file mode 100644
index 000000000..446c0b8d2
--- /dev/null
+++ b/src/contrib/service/abd/delegate_misc.c
@@ -0,0 +1,279 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21
22/**
23 * @file abd/delegate_misc.c
24 * @brief Misc API for delegate
25 *
26 * @author Martin Schanzenbach
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_abd_service.h"
32#include "gnunet_signatures.h"
33#include "abd.h"
34#include <inttypes.h>
35
36char *
37GNUNET_ABD_delegate_to_string (
38 const struct GNUNET_ABD_Delegate *cred)
39{
40 char *cred_str;
41 char *subject_pkey;
42 char *issuer_pkey;
43 char *signature;
44
45 subject_pkey = GNUNET_CRYPTO_public_key_to_string (&cred->subject_key);
46 issuer_pkey = GNUNET_CRYPTO_public_key_to_string (&cred->issuer_key);
47 GNUNET_STRINGS_base64_encode ((char *) &cred->signature,
48 sizeof (struct GNUNET_CRYPTO_Signature),
49 &signature);
50 if (0 == cred->subject_attribute_len)
51 {
52 GNUNET_asprintf (&cred_str,
53 "%s.%s -> %s | %s | %" SCNu64,
54 issuer_pkey,
55 cred->issuer_attribute,
56 subject_pkey,
57 signature,
58 cred->expiration.abs_value_us);
59 }
60 else
61 {
62 GNUNET_asprintf (&cred_str,
63 "%s.%s -> %s.%s | %s | %" SCNu64,
64 issuer_pkey,
65 cred->issuer_attribute,
66 subject_pkey,
67 cred->subject_attribute,
68 signature,
69 cred->expiration.abs_value_us);
70 }
71 GNUNET_free (subject_pkey);
72 GNUNET_free (issuer_pkey);
73 GNUNET_free (signature);
74
75 return cred_str;
76}
77
78
79struct GNUNET_ABD_Delegate *
80GNUNET_ABD_delegate_from_string (const char *s)
81{
82 struct GNUNET_ABD_Delegate *dele;
83 size_t enclen = (sizeof (struct GNUNET_CRYPTO_PublicKey)) * 8;
84 if (enclen % 5 > 0)
85 enclen += 5 - enclen % 5;
86 enclen /= 5; /* 260/5 = 52 */
87 char subject_pkey[enclen + 1];
88 char issuer_pkey[enclen + 1];
89 char iss_attr[253 + 1];
90 // Needs to be initialized, in case of Type 1 credential (A.a <- B)
91 char sub_attr[253 + 1] = "";
92 char signature[256]; // TODO max payload size
93
94 struct GNUNET_CRYPTO_Signature *sig;
95 struct GNUNET_TIME_Absolute etime_abs;
96
97 // If it's A.a <- B.b...
98 if (6 != sscanf (s,
99 "%58s.%253s -> %58s.%253s | %s | %" SCNu64,
100 issuer_pkey,
101 iss_attr,
102 subject_pkey,
103 sub_attr,
104 signature,
105 &etime_abs.abs_value_us))
106 {
107 // Try if it's A.a <- B
108 if (5 != sscanf (s,
109 "%58s.%253s -> %58s | %s | %" SCNu64,
110 issuer_pkey,
111 iss_attr,
112 subject_pkey,
113 signature,
114 &etime_abs.abs_value_us))
115 {
116 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
117 "Unable to parse DEL record string `%s'\n",
118 s);
119 return NULL;
120 }
121 }
122
123 // +1 for \0
124 int attr_len;
125 if (strcmp (sub_attr, "") == 0)
126 {
127 attr_len = strlen (iss_attr) + 1;
128 }
129 else
130 {
131 attr_len = strlen (iss_attr) + strlen (sub_attr) + 2;
132 }
133 dele = GNUNET_malloc (sizeof (struct GNUNET_ABD_Delegate) + attr_len);
134
135 char tmp_str[attr_len];
136 GNUNET_memcpy (tmp_str, iss_attr, strlen (iss_attr));
137 if (strcmp (sub_attr, "") != 0)
138 {
139 tmp_str[strlen (iss_attr)] = '\0';
140 GNUNET_memcpy (tmp_str + strlen (iss_attr) + 1,
141 sub_attr,
142 strlen (sub_attr));
143 }
144 tmp_str[attr_len - 1] = '\0';
145
146 if (GNUNET_SYSERR ==
147 GNUNET_CRYPTO_public_key_from_string (subject_pkey,
148 &dele->subject_key))
149 {
150 GNUNET_free (dele);
151 return NULL;
152 }
153 if (GNUNET_SYSERR ==
154 GNUNET_CRYPTO_public_key_from_string (issuer_pkey,
155 &dele->issuer_key))
156 {
157 GNUNET_free (dele);
158 return NULL;
159 }
160 GNUNET_assert (sizeof (struct GNUNET_CRYPTO_Signature) ==
161 GNUNET_STRINGS_base64_decode (signature,
162 strlen (signature),
163 (void **) &sig));
164 dele->signature = *sig;
165 dele->expiration = etime_abs;
166 GNUNET_free (sig);
167
168 GNUNET_memcpy (&dele[1], tmp_str, attr_len);
169
170 dele->issuer_attribute = (char *) &dele[1];
171 dele->issuer_attribute_len = strlen (iss_attr);
172 if (strcmp (sub_attr, "") == 0)
173 {
174 dele->subject_attribute = NULL;
175 dele->subject_attribute_len = 0;
176 }
177 else
178 {
179 dele->subject_attribute = (char *) &dele[1] + strlen (iss_attr) + 1;
180 dele->subject_attribute_len = strlen (sub_attr);
181 }
182
183 return dele;
184}
185
186
187/**
188 * Issue an attribute to a subject
189 *
190 * @param issuer the ego that should be used to issue the attribute
191 * @param subject the subject of the attribute
192 * @param iss_attr the name of the attribute
193 * @param expiration the TTL of the credential
194 * @return handle to the queued request
195 */
196
197struct GNUNET_ABD_Delegate *
198GNUNET_ABD_delegate_issue (
199 const struct GNUNET_CRYPTO_PrivateKey *issuer,
200 struct GNUNET_CRYPTO_PublicKey *subject,
201 const char *iss_attr,
202 const char *sub_attr,
203 struct GNUNET_TIME_Absolute *expiration)
204{
205 struct DelegateEntry *del;
206 struct GNUNET_ABD_Delegate *dele;
207 size_t size;
208 int attr_len;
209
210 if (NULL == sub_attr)
211 {
212 // +1 for \0
213 attr_len = strlen (iss_attr) + 1;
214 }
215 else
216 {
217 // +2 for both strings need to be terminated with \0
218 attr_len = strlen (iss_attr) + strlen (sub_attr) + 2;
219 }
220 size = sizeof (struct DelegateEntry) + attr_len;
221
222 char tmp_str[attr_len];
223 GNUNET_memcpy (tmp_str, iss_attr, strlen (iss_attr));
224 if (NULL != sub_attr)
225 {
226 tmp_str[strlen (iss_attr)] = '\0';
227 GNUNET_memcpy (tmp_str + strlen (iss_attr) + 1,
228 sub_attr,
229 strlen (sub_attr));
230 }
231 tmp_str[attr_len - 1] = '\0';
232
233 del = GNUNET_malloc (size);
234 del->purpose.size =
235 htonl (size - sizeof (struct GNUNET_CRYPTO_Signature));
236 del->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_DELEGATE);
237 GNUNET_CRYPTO_key_get_public (issuer, &del->issuer_key);
238 del->subject_key = *subject;
239 del->expiration = GNUNET_htonll (expiration->abs_value_us);
240 del->issuer_attribute_len = htonl (strlen (iss_attr) + 1);
241 if (NULL == sub_attr)
242 {
243 del->subject_attribute_len = htonl (0);
244 }
245 else
246 {
247 del->subject_attribute_len = htonl (strlen (sub_attr) + 1);
248 }
249
250 GNUNET_memcpy (&del[1], tmp_str, attr_len);
251
252 GNUNET_CRYPTO_sign_ (issuer, &del->purpose, &del->signature);
253
254 dele = GNUNET_malloc (sizeof (struct GNUNET_ABD_Delegate) + attr_len);
255 dele->signature = del->signature;
256 dele->expiration = *expiration;
257 GNUNET_CRYPTO_key_get_public (issuer, &dele->issuer_key);
258
259 dele->subject_key = *subject;
260
261 // Copy the combined string at the part in the memory where the struct ends
262 GNUNET_memcpy (&dele[1], tmp_str, attr_len);
263
264 dele->issuer_attribute = (char *) &dele[1];
265 dele->issuer_attribute_len = strlen (iss_attr);
266 if (NULL == sub_attr)
267 {
268 dele->subject_attribute = NULL;
269 dele->subject_attribute_len = 0;
270 }
271 else
272 {
273 dele->subject_attribute = (char *) &dele[1] + strlen (iss_attr) + 1;
274 dele->subject_attribute_len = strlen (sub_attr);
275 }
276
277 GNUNET_free (del);
278 return dele;
279}
diff --git a/src/contrib/service/abd/delegate_misc.h b/src/contrib/service/abd/delegate_misc.h
new file mode 100644
index 000000000..0a4ddd9eb
--- /dev/null
+++ b/src/contrib/service/abd/delegate_misc.h
@@ -0,0 +1,36 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file abd/delegate_misc.h
22 * @brief Delegate helper functions
23 */
24#ifndef DELEGATE_MISC_H
25#define DELEGATE_MISC_H
26
27#include "gnunet_abd_service.h"
28
29char *
30GNUNET_ABD_delegate_to_string (
31 const struct GNUNET_ABD_Delegate *cred);
32
33struct GNUNET_ABD_Delegate *
34GNUNET_ABD_delegate_from_string (const char *str);
35
36#endif
diff --git a/src/contrib/service/abd/gnunet-abd.c b/src/contrib/service/abd/gnunet-abd.c
new file mode 100644
index 000000000..1ed1adbe7
--- /dev/null
+++ b/src/contrib/service/abd/gnunet-abd.c
@@ -0,0 +1,1108 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20/**
21 * @file gnunet-abd.c
22 * @brief command line tool to access command line Credential service
23 * @author Martin Schanzenbach
24 */
25#include "platform.h"
26#include <gnunet_util_lib.h>
27#include <gnunet_abd_service.h>
28#include <gnunet_gnsrecord_lib.h>
29#include <gnunet_namestore_service.h>
30#include "delegate_misc.h"
31#include "abd_serialization.h"
32
33/**
34 * Configuration we are using.
35 */
36static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38/**
39 * Handle to the namestore.
40 */
41static struct GNUNET_NAMESTORE_Handle *ns;
42
43/**
44 * Private key for the our zone.
45 */
46static struct GNUNET_CRYPTO_PrivateKey zone_pkey;
47
48/**
49 * EgoLookup
50 */
51static struct GNUNET_IDENTITY_EgoLookup *el;
52
53/**
54 * Handle to Credential service.
55 */
56static struct GNUNET_ABD_Handle *abd;
57
58/**
59 * Desired timeout for the lookup (default is no timeout).
60 */
61static struct GNUNET_TIME_Relative timeout;
62
63/**
64 * Handle to verify request
65 */
66static struct GNUNET_ABD_Request *verify_request;
67
68/**
69 * Handle to collect request
70 */
71static struct GNUNET_ABD_Request *collect_request;
72
73/**
74 * Task scheduled to handle timeout.
75 */
76static struct GNUNET_SCHEDULER_Task *tt;
77
78/**
79 * Return value of the commandline.
80 */
81static int ret = 0;
82
83/**
84 * Subject pubkey string
85 */
86static char *subject;
87
88/**
89 * Subject delegate string
90 */
91static char *subject_delegate;
92
93/**
94 * Credential TTL
95 */
96static char *expiration;
97
98/**
99 * Subject key
100 */
101struct GNUNET_CRYPTO_PublicKey subject_pkey;
102
103/**
104 * Issuer key
105 */
106struct GNUNET_CRYPTO_PublicKey issuer_pkey;
107
108
109/**
110 * Issuer pubkey string
111 */
112static char *issuer_key;
113
114/**
115 * ego
116 */
117static char *ego_name;
118
119/**
120 * Issuer attribute
121 */
122static char *issuer_attr;
123
124/**
125 * Verify mode
126 */
127static int verify;
128
129/**
130 * Collect mode
131 */
132static int collect;
133
134/**
135 * Create mode
136 */
137static int create_is;
138
139/**
140 * Create mode
141 */
142static int create_ss;
143
144/**
145 * Create mode
146 */
147static int sign_ss;
148
149/**
150 * Signed issue credentials
151 */
152static char *import;
153
154/**
155 * Is record private
156 */
157static int is_private;
158
159/**
160 * Search direction: forward
161 */
162static int forward;
163
164/**
165 * Search direction: backward
166 */
167static int backward;
168
169/**
170 * API enum, filled and passed for collect/verify
171 */
172enum GNUNET_ABD_AlgoDirectionFlags direction = 0;
173
174/**
175 * Queue entry for the 'add' operation.
176 */
177static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
178
179/**
180 * Value in binary format.
181 */
182static void *data;
183
184/**
185 * Number of bytes in #data.
186 */
187static size_t data_size;
188
189/**
190 * Type string converted to DNS type value.
191 */
192static uint32_t type;
193
194/**
195 * Type of the record to add/remove, NULL to remove all.
196 */
197static char *typestring;
198/**
199 * Expiration string converted to numeric value.
200 */
201static uint64_t etime;
202
203/**
204 * Is expiration time relative or absolute time?
205 */
206static int etime_is_rel = GNUNET_SYSERR;
207
208/**
209 * Record label for storing delegations
210 */
211static char *record_label;
212
213/**
214 * Task run on shutdown. Cleans up everything.
215 *
216 * @param cls unused
217 */
218static void
219do_shutdown (void *cls)
220{
221 if (NULL != verify_request)
222 {
223 GNUNET_ABD_request_cancel (verify_request);
224 verify_request = NULL;
225 }
226 if (NULL != abd)
227 {
228 GNUNET_ABD_disconnect (abd);
229 abd = NULL;
230 }
231 if (NULL != tt)
232 {
233 GNUNET_SCHEDULER_cancel (tt);
234 tt = NULL;
235 }
236 if (NULL != el)
237 {
238 GNUNET_IDENTITY_ego_lookup_cancel (el);
239 el = NULL;
240 }
241 if (NULL != add_qe)
242 {
243 GNUNET_NAMESTORE_cancel (add_qe);
244 add_qe = NULL;
245 }
246 if (NULL != ns)
247 {
248 GNUNET_NAMESTORE_disconnect (ns);
249 ns = NULL;
250 }
251}
252
253
254/**
255 * Task run on timeout. Triggers shutdown.
256 *
257 * @param cls unused
258 */
259static void
260do_timeout (void *cls)
261{
262 tt = NULL;
263 GNUNET_SCHEDULER_shutdown ();
264}
265
266
267static void
268handle_intermediate_result (void *cls,
269 struct GNUNET_ABD_Delegation *dd,
270 bool is_bw)
271{
272 char *prefix = "";
273 if (is_bw)
274 prefix = "Backward -";
275 else
276 prefix = "Forward -";
277
278 printf ("%s Intermediate result: %s.%s <- %s.%s\n",
279 prefix,
280 GNUNET_CRYPTO_public_key_to_string (&dd->issuer_key),
281 dd->issuer_attribute,
282 GNUNET_CRYPTO_public_key_to_string (&dd->subject_key),
283 dd->subject_attribute);
284}
285
286
287static void
288handle_collect_result (void *cls,
289 unsigned int d_count,
290 struct GNUNET_ABD_Delegation *dc,
291 unsigned int c_count,
292 struct GNUNET_ABD_Delegate *dele)
293{
294 int i;
295 char *line;
296
297 verify_request = NULL;
298 if (NULL != dele)
299 {
300 for (i = 0; i < c_count; i++)
301 {
302 line = GNUNET_ABD_delegate_to_string (&dele[i]);
303 printf ("%s\n", line);
304 GNUNET_free (line);
305 }
306 }
307 else
308 {
309 printf ("Received NULL\n");
310 }
311
312 GNUNET_SCHEDULER_shutdown ();
313}
314
315
316static void
317handle_verify_result (void *cls,
318 unsigned int d_count,
319 struct GNUNET_ABD_Delegation *dc,
320 unsigned int c_count,
321 struct GNUNET_ABD_Delegate *dele)
322{
323 int i;
324 char *iss_key;
325 char *sub_key;
326
327 verify_request = NULL;
328 if (NULL == dele)
329 ret = 1;
330 else
331 {
332 printf ("Delegation Chain:\n");
333 for (i = 0; i < d_count; i++)
334 {
335 iss_key = GNUNET_CRYPTO_public_key_to_string (&dc[i].issuer_key);
336 sub_key = GNUNET_CRYPTO_public_key_to_string (&dc[i].subject_key);
337
338 if (0 != dc[i].subject_attribute_len)
339 {
340 printf ("(%d) %s.%s <- %s.%s\n",
341 i,
342 iss_key,
343 dc[i].issuer_attribute,
344 sub_key,
345 dc[i].subject_attribute);
346 }
347 else
348 {
349 printf ("(%d) %s.%s <- %s\n",
350 i,
351 iss_key,
352 dc[i].issuer_attribute,
353 sub_key);
354 }
355 GNUNET_free (iss_key);
356 GNUNET_free (sub_key);
357 }
358 printf ("\nDelegate(s):\n");
359 for (i = 0; i < c_count; i++)
360 {
361 iss_key = GNUNET_CRYPTO_public_key_to_string (&dele[i].issuer_key);
362 sub_key = GNUNET_CRYPTO_public_key_to_string (&dele[i].subject_key);
363 printf ("%s.%s <- %s\n", iss_key, dele[i].issuer_attribute, sub_key);
364 GNUNET_free (iss_key);
365 GNUNET_free (sub_key);
366 }
367 printf ("Successful.\n");
368 }
369
370 GNUNET_SCHEDULER_shutdown ();
371}
372
373
374/**
375 * Callback invoked from identity service with ego information.
376 * An @a ego of NULL means the ego was not found.
377 *
378 * @param cls closure with the configuration
379 * @param ego an ego known to identity service, or NULL
380 */
381static void
382identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
383{
384 const struct GNUNET_CRYPTO_PrivateKey *privkey;
385
386 el = NULL;
387 if (NULL == ego)
388 {
389 if (NULL != ego_name)
390 {
391 fprintf (stderr,
392 _ ("Ego `%s' not known to identity service\n"),
393 ego_name);
394 }
395 GNUNET_SCHEDULER_shutdown ();
396 return;
397 }
398
399 if (GNUNET_YES == collect)
400 {
401
402 if (GNUNET_OK !=
403 GNUNET_CRYPTO_public_key_from_string (issuer_key,
404 &issuer_pkey))
405 {
406 fprintf (stderr,
407 _ ("Issuer public key `%s' is not well-formed\n"),
408 issuer_key);
409 GNUNET_SCHEDULER_shutdown ();
410 }
411 privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
412
413 collect_request = GNUNET_ABD_collect (abd,
414 &issuer_pkey,
415 issuer_attr,
416 privkey,
417 direction,
418 &handle_collect_result,
419 NULL,
420 &handle_intermediate_result,
421 NULL);
422 return;
423 }
424 GNUNET_SCHEDULER_shutdown ();
425}
426
427
428/**
429 * Parse expiration time.
430 *
431 * @param expirationstring text to parse
432 * @param[out] etime_is_rel set to #GNUNET_YES if time is relative
433 * @param[out] etime set to expiration time (abs or rel)
434 * @return #GNUNET_OK on success
435 */
436static int
437parse_expiration (const char *expirationstring,
438 int *etime_is_rel,
439 uint64_t *etime)
440{
441 // copied from namestore/gnunet-namestore.c
442 struct GNUNET_TIME_Relative etime_rel;
443 struct GNUNET_TIME_Absolute etime_abs;
444
445 if (0 == strcmp (expirationstring, "never"))
446 {
447 *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
448 *etime_is_rel = GNUNET_NO;
449 return GNUNET_OK;
450 }
451 if (GNUNET_OK ==
452 GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel))
453 {
454 *etime_is_rel = GNUNET_YES;
455 *etime = etime_rel.rel_value_us;
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "Storing record with relative expiration time of %s\n",
458 GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO));
459 return GNUNET_OK;
460 }
461 if (GNUNET_OK ==
462 GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs))
463 {
464 *etime_is_rel = GNUNET_NO;
465 *etime = etime_abs.abs_value_us;
466 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467 "Storing record with absolute expiration time of %s\n",
468 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
469 return GNUNET_OK;
470 }
471 return GNUNET_SYSERR;
472}
473
474
475/**
476 * Function called if lookup fails.
477 */
478static void
479error_cb (void *cls)
480{
481 fprintf (stderr, "Error occurred during lookup, shutting down.\n");
482 GNUNET_SCHEDULER_shutdown ();
483 return;
484}
485
486
487static void
488add_continuation (void *cls, int32_t success, const char *emsg)
489{
490 struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
491 *qe = NULL;
492
493 if (GNUNET_OK == success)
494 printf ("Adding successful.\n");
495 else
496 fprintf (stderr, "Error occurred during adding, shutting down.\n");
497
498 GNUNET_SCHEDULER_shutdown ();
499}
500
501
502static void
503get_existing_record (void *cls,
504 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
505 const char *rec_name,
506 unsigned int rd_count,
507 const struct GNUNET_GNSRECORD_Data *rd)
508{
509 struct GNUNET_GNSRECORD_Data *rde;
510 struct GNUNET_GNSRECORD_Data*rdn =
511 GNUNET_malloc (sizeof(*rdn) * (rd_count + 1));
512
513 memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
514 GNUNET_memcpy (&rdn[1], rd,
515 rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
516 rde = &rdn[0];
517 rde->data = data;
518 rde->data_size = data_size;
519 rde->record_type = type;
520
521 // Set flags
522 if (GNUNET_YES == is_private)
523 rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
524 rde->expiration_time = etime;
525 if (GNUNET_YES == etime_is_rel)
526 rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
527 else if (GNUNET_NO != etime_is_rel)
528 rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
529
530 GNUNET_assert (NULL != rec_name);
531 add_qe = GNUNET_NAMESTORE_records_store (ns,
532 &zone_pkey,
533 rec_name,
534 rd_count + 1,
535 rde,
536 &add_continuation,
537 &add_qe);
538 GNUNET_free (rdn);
539 return;
540}
541
542
543static void
544store_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
545{
546 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
547 struct GNUNET_ABD_Delegate *cred;
548 struct GNUNET_CRYPTO_PublicKey zone_pubkey;
549 char *subject_pubkey_str;
550 char *zone_pubkey_str;
551
552 el = NULL;
553
554 ns = GNUNET_NAMESTORE_connect (cfg);
555 if (NULL == ns)
556 {
557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
558 _ ("Failed to connect to namestore\n"));
559 GNUNET_SCHEDULER_shutdown ();
560 return;
561 }
562
563 if (NULL == ego)
564 {
565 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566 _("Ego does not exist!\n"));
567 GNUNET_SCHEDULER_shutdown ();
568 return;
569 }
570
571 // Key handling
572 zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
573
574 if (GNUNET_GNSRECORD_TYPE_DELEGATE == type)
575 {
576 // Parse import
577 cred = GNUNET_ABD_delegate_from_string (import);
578
579 if (NULL == cred)
580 {
581 fprintf (stderr,
582 "%s is not a valid credential\n", import);
583 GNUNET_SCHEDULER_shutdown();
584 return;
585 }
586
587 // Get import subject public key string
588 subject_pubkey_str =
589 GNUNET_CRYPTO_public_key_to_string (&cred->subject_key);
590
591 // Get zone public key string
592 GNUNET_IDENTITY_ego_get_public_key (ego, &zone_pubkey);
593 zone_pubkey_str =
594 GNUNET_CRYPTO_public_key_to_string (&zone_pubkey);
595
596 // Check if the subject key in the signed import matches the zone's key it is issued to
597 if (strcmp (zone_pubkey_str, subject_pubkey_str) != 0)
598 {
599 fprintf (stderr,
600 "Import signed delegate does not match this ego's public key.\n");
601 GNUNET_free (cred);
602 GNUNET_SCHEDULER_shutdown ();
603 return;
604 }
605
606 // Expiration
607 etime = cred->expiration.abs_value_us;
608 etime_is_rel = GNUNET_NO;
609
610 // Prepare the data to be store in the record
611 data_size = GNUNET_ABD_delegate_serialize (cred, (char **) &data);
612 GNUNET_free (cred);
613 }
614 else
615 {
616 // For all other types e.g. GNUNET_GNSRECORD_TYPE_ATTRIBUTE
617 if (GNUNET_OK !=
618 GNUNET_GNSRECORD_string_to_value (type, subject, &data, &data_size))
619 {
620 if (typestring == NULL)
621 {
622 fputs ("Value for unknown record type not well-formed.\n", stderr);
623 }
624 else if (subject == NULL)
625 {
626 fprintf (stderr,
627 "Value for record type `%s' not well-formed.\n",
628 typestring);
629 }
630 else
631 {
632 fprintf (stderr,
633 "Value `%s' invalid for record type `%s'\n",
634 subject,
635 typestring);
636 }
637 GNUNET_SCHEDULER_shutdown ();
638 return;
639 }
640
641 // Take care of expiration
642 if (NULL == expiration)
643 {
644 fprintf (stderr, "Missing option -e for operation 'create'\n");
645 GNUNET_SCHEDULER_shutdown ();
646 return;
647 }
648 if (GNUNET_OK != parse_expiration (expiration, &etime_is_rel, &etime))
649 {
650 fprintf (stderr, "Invalid time format `%s'\n", expiration);
651 GNUNET_SCHEDULER_shutdown ();
652 return;
653 }
654 }
655
656 // Start lookup
657 add_qe = GNUNET_NAMESTORE_records_lookup (ns,
658 &zone_pkey,
659 record_label,
660 &error_cb,
661 NULL,
662 &get_existing_record,
663 NULL);
664 return;
665}
666
667
668static void
669sign_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
670{
671 const struct GNUNET_CRYPTO_PrivateKey *privkey;
672 struct GNUNET_ABD_Delegate *dele;
673 struct GNUNET_TIME_Absolute etime_abs;
674 char *res;
675
676 el = NULL;
677
678 // work on expiration time
679 if (NULL == expiration)
680 {
681 fprintf (stderr, "Please specify a TTL\n");
682 GNUNET_SCHEDULER_shutdown ();
683 return;
684 }
685 else if (GNUNET_OK !=
686 GNUNET_STRINGS_fancy_time_to_absolute (expiration, &etime_abs))
687 {
688 fprintf (stderr,
689 "%s is not a valid ttl! Only absolute times are accepted!\n",
690 expiration);
691 GNUNET_SCHEDULER_shutdown ();
692 return;
693 }
694
695 // If contains a space - split it by the first space only - assume first entry is subject followed by attribute(s)
696 char *subject_pubkey_str;
697 char *subject_attr = NULL;
698 char *token;
699
700 // Subject Public Key
701 token = strtok (subject, " ");
702 subject_pubkey_str = token;
703 // Subject Attribute(s)
704 token = strtok (NULL, " ");
705 if (NULL != token)
706 {
707 subject_attr = token;
708 }
709
710 // work on keys
711 privkey = GNUNET_IDENTITY_ego_get_private_key (ego);
712
713 if (NULL == subject_pubkey_str)
714 {
715 fprintf (stderr,
716 "Subject pubkey not given\n");
717 GNUNET_SCHEDULER_shutdown ();
718 return;
719 }
720 if (GNUNET_OK !=
721 GNUNET_CRYPTO_public_key_from_string (subject_pubkey_str,
722 &subject_pkey))
723 {
724 fprintf (stderr,
725 "Subject public key `%s' is not well-formed\n",
726 subject_pubkey_str);
727 GNUNET_SCHEDULER_shutdown ();
728 return;
729 }
730
731 // Sign delegate
732 dele = GNUNET_ABD_delegate_issue (privkey,
733 &subject_pkey,
734 issuer_attr,
735 subject_attr,
736 &etime_abs);
737 res = GNUNET_ABD_delegate_to_string (dele);
738 GNUNET_free (dele);
739 printf ("%s\n", res);
740
741 GNUNET_free (ego_name);
742 ego_name = NULL;
743
744 GNUNET_SCHEDULER_shutdown ();
745}
746
747
748/**
749 * Main function that will be run.
750 *
751 * @param cls closure
752 * @param args remaining command-line arguments
753 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
754 * @param c configuration
755 */
756static void
757run (void *cls,
758 char *const *args,
759 const char *cfgfile,
760 const struct GNUNET_CONFIGURATION_Handle *c)
761{
762 cfg = c;
763
764 tt = GNUNET_SCHEDULER_add_delayed (timeout, &do_timeout, NULL);
765 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
766
767 // Check relevant cmdline parameters
768 if (GNUNET_YES == create_is)
769 {
770 if (NULL == ego_name)
771 {
772 fprintf (stderr, "Missing option '-ego'\n");
773 GNUNET_SCHEDULER_shutdown ();
774 return;
775 }
776 if (NULL == issuer_attr)
777 {
778 fprintf (stderr, "Missing option '-attribute' for issuer attribute\n");
779 GNUNET_SCHEDULER_shutdown ();
780 return;
781 }
782 if (NULL == subject)
783 {
784 fprintf (stderr, "Missing option -subject for operation 'create'.'\n");
785 GNUNET_SCHEDULER_shutdown ();
786 return;
787 }
788
789 // Lookup ego, on success call store_cb and store as ATTRIBUTE type
790 type = GNUNET_GNSRECORD_TYPE_ATTRIBUTE;
791 record_label = issuer_attr;
792 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &store_cb, (void *) cfg);
793 return;
794 }
795
796 if (GNUNET_YES == create_ss)
797 {
798
799 // check if signed parameter has been passed in cmd line call
800 if (NULL == import)
801 {
802 fprintf (stderr, "'import' required\n");
803 GNUNET_SCHEDULER_shutdown ();
804 return;
805 }
806
807 type = GNUNET_GNSRECORD_TYPE_DELEGATE;
808 record_label = GNUNET_GNS_EMPTY_LABEL_AT;
809 // Store subject side
810 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &store_cb, (void *) cfg);
811
812 return;
813 }
814
815 if (GNUNET_YES == sign_ss)
816 {
817 if (NULL == ego_name)
818 {
819 fprintf (stderr, "ego required\n");
820 GNUNET_SCHEDULER_shutdown ();
821 return;
822 }
823 if (NULL == subject)
824 {
825 fprintf (stderr, "Subject public key needed\n");
826 GNUNET_SCHEDULER_shutdown ();
827 return;
828 }
829
830 // lookup ego and call function sign_cb on success
831 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &sign_cb, (void *) cfg);
832 return;
833 }
834
835 if ((GNUNET_NO == forward) && (GNUNET_NO == backward))
836 {
837 // set default: bidirectional
838 forward = GNUNET_YES;
839 backward = GNUNET_YES;
840 }
841 if (GNUNET_YES == forward)
842 direction |= GNUNET_ABD_FLAG_FORWARD;
843 if (GNUNET_YES == backward)
844 direction |= GNUNET_ABD_FLAG_BACKWARD;
845
846 if (GNUNET_YES == collect)
847 {
848 if (NULL == issuer_key)
849 {
850 fprintf (stderr, _ ("Issuer public key not well-formed\n"));
851 GNUNET_SCHEDULER_shutdown ();
852 return;
853 }
854
855 abd = GNUNET_ABD_connect (cfg);
856
857 if (NULL == abd)
858 {
859 fprintf (stderr, _ ("Failed to connect to ABD\n"));
860 GNUNET_SCHEDULER_shutdown ();
861 return;
862 }
863 if (NULL == issuer_attr)
864 {
865 fprintf (stderr, _ ("You must provide issuer the attribute\n"));
866 GNUNET_SCHEDULER_shutdown ();
867 return;
868 }
869
870 if (NULL == ego_name)
871 {
872 fprintf (stderr, _ ("ego required\n"));
873 GNUNET_SCHEDULER_shutdown ();
874 return;
875 }
876 el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg);
877 return;
878 }
879
880 if (NULL == subject)
881 {
882 fprintf (stderr, _ ("Subject public key needed\n"));
883 GNUNET_SCHEDULER_shutdown ();
884 return;
885 }
886 if (GNUNET_OK != GNUNET_CRYPTO_public_key_from_string (subject,
887 &subject_pkey))
888 {
889 fprintf (stderr,
890 _ ("Subject public key `%s' is not well-formed\n"),
891 subject);
892 GNUNET_SCHEDULER_shutdown ();
893 return;
894 }
895
896 if (GNUNET_YES == verify)
897 {
898 if (NULL == issuer_key)
899 {
900 fprintf (stderr, _ ("Issuer public key not well-formed\n"));
901 GNUNET_SCHEDULER_shutdown ();
902 return;
903 }
904 if (GNUNET_OK !=
905 GNUNET_CRYPTO_public_key_from_string (issuer_key,
906 &issuer_pkey))
907 {
908 fprintf (stderr,
909 _ ("Issuer public key `%s' is not well-formed\n"),
910 issuer_key);
911 GNUNET_SCHEDULER_shutdown ();
912 return;
913 }
914 abd = GNUNET_ABD_connect (cfg);
915
916 if (NULL == abd)
917 {
918 fprintf (stderr, _ ("Failed to connect to ABD\n"));
919 GNUNET_SCHEDULER_shutdown ();
920 return;
921 }
922 if ((NULL == issuer_attr) || (NULL == subject_delegate))
923 {
924 fprintf (stderr, _ ("You must provide issuer and subject attributes\n"));
925 GNUNET_SCHEDULER_shutdown ();
926 return;
927 }
928
929 // Subject credentials are comma separated
930 char *tmp = GNUNET_strdup (subject_delegate);
931 char *tok = strtok (tmp, ",");
932 if (NULL == tok)
933 {
934 fprintf (stderr, "Invalid subject credentials\n");
935 GNUNET_free (tmp);
936 GNUNET_SCHEDULER_shutdown ();
937 return;
938 }
939 int count = 1;
940 int i;
941 while (NULL != (tok = strtok (NULL, ",")))
942 count++;
943 struct GNUNET_ABD_Delegate*delegates =
944 GNUNET_malloc (sizeof(*delegates) * count);
945 struct GNUNET_ABD_Delegate *dele;
946 GNUNET_free (tmp);
947 tmp = GNUNET_strdup (subject_delegate);
948 tok = strtok (tmp, ",");
949 for (i = 0; i < count; i++)
950 {
951 dele = GNUNET_ABD_delegate_from_string (tok);
952 GNUNET_memcpy (&delegates[i],
953 dele,
954 sizeof (struct GNUNET_ABD_Delegate));
955 delegates[i].issuer_attribute = GNUNET_strdup (dele->issuer_attribute);
956 tok = strtok (NULL, ",");
957 GNUNET_free (dele);
958 }
959
960 verify_request = GNUNET_ABD_verify (abd,
961 &issuer_pkey,
962 issuer_attr,
963 &subject_pkey,
964 count,
965 delegates,
966 direction,
967 &handle_verify_result,
968 NULL,
969 &handle_intermediate_result,
970 NULL);
971 for (i = 0; i < count; i++)
972 {
973 GNUNET_free_nz ((char *) delegates[i].issuer_attribute);
974 delegates[i].issuer_attribute = NULL;
975 }
976 GNUNET_free (tmp);
977 GNUNET_free (delegates);
978 }
979 else
980 {
981 fprintf (stderr,
982 _ (
983 "Please specify name to lookup, subject key and issuer key!\n"));
984 GNUNET_SCHEDULER_shutdown ();
985 }
986 return;
987}
988
989
990/**
991 * The main function for gnunet-gns.
992 *
993 * @param argc number of arguments from the command line
994 * @param argv command line arguments
995 * @return 0 ok, 1 on error
996 */
997int
998main (int argc, char *const *argv)
999{
1000 struct GNUNET_GETOPT_CommandLineOption options[] =
1001 {GNUNET_GETOPT_option_flag ('V',
1002 "verify",
1003 gettext_noop (
1004 "verify credential against attribute"),
1005 &verify),
1006 GNUNET_GETOPT_option_string (
1007 's',
1008 "subject",
1009 "PKEY",
1010 gettext_noop (
1011 "The public key of the subject to lookup the"
1012 "credential for, or for issuer side storage: subject and its attributes"),
1013 &subject),
1014 GNUNET_GETOPT_option_string (
1015 'd',
1016 "delegate",
1017 "DELE",
1018 gettext_noop ("The private, signed delegate presented by the subject"),
1019 &subject_delegate),
1020 GNUNET_GETOPT_option_string (
1021 'i',
1022 "issuer",
1023 "PKEY",
1024 gettext_noop (
1025 "The public key of the authority to verify the credential against"),
1026 &issuer_key),
1027 GNUNET_GETOPT_option_string ('e',
1028 "ego",
1029 "EGO",
1030 gettext_noop ("The ego/zone name to use"),
1031 &ego_name),
1032 GNUNET_GETOPT_option_string (
1033 'a',
1034 "attribute",
1035 "ATTR",
1036 gettext_noop ("The issuer attribute to verify against or to issue"),
1037 &issuer_attr),
1038 GNUNET_GETOPT_option_string ('T',
1039 "ttl",
1040 "EXP",
1041 gettext_noop (
1042 "The time to live for the credential."
1043 "e.g. 5m, 6h, \"1990-12-30 12:00:00\""),
1044 &expiration),
1045 GNUNET_GETOPT_option_flag ('g',
1046 "collect",
1047 gettext_noop ("collect credentials"),
1048 &collect),
1049 GNUNET_GETOPT_option_flag ('U',
1050 "createIssuerSide",
1051 gettext_noop (
1052 "Create and issue a credential issuer side."),
1053 &create_is),
1054 GNUNET_GETOPT_option_flag ('C',
1055 "createSubjectSide",
1056 gettext_noop (
1057 "Issue a credential subject side."),
1058 &create_ss),
1059 GNUNET_GETOPT_option_flag (
1060 'S',
1061 "signSubjectSide",
1062 gettext_noop ("Create, sign and return a credential subject side."),
1063 &sign_ss),
1064 GNUNET_GETOPT_option_string (
1065 'x',
1066 "import",
1067 "IMP",
1068 gettext_noop (
1069 "Import signed credentials that should be issued to a zone/ego"),
1070 &import),
1071 GNUNET_GETOPT_option_flag ('P',
1072 "private",
1073 gettext_noop ("Create private record entry."),
1074 &is_private),
1075 GNUNET_GETOPT_option_flag (
1076 'F',
1077 "forward",
1078 gettext_noop (
1079 "Indicates that the collect/verify process is done via forward search."),
1080 &forward),
1081 GNUNET_GETOPT_option_flag (
1082 'B',
1083 "backward",
1084 gettext_noop (
1085 "Indicates that the collect/verify process is done via forward search."),
1086 &backward),
1087 GNUNET_GETOPT_OPTION_END};
1088
1089
1090 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1091 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1092 return 2;
1093
1094 GNUNET_log_setup ("gnunet-abd", "WARNING", NULL);
1095 if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
1096 argv,
1097 "gnunet-abd",
1098 _ ("GNUnet abd resolver tool"),
1099 options,
1100 &run,
1101 NULL))
1102 ret = 1;
1103 GNUNET_free_nz ((void *) argv);
1104 return ret;
1105}
1106
1107
1108/* end of gnunet-abd.c */
diff --git a/src/contrib/service/abd/gnunet-service-abd.c b/src/contrib/service/abd/gnunet-service-abd.c
new file mode 100644
index 000000000..5cbc105f6
--- /dev/null
+++ b/src/contrib/service/abd/gnunet-service-abd.c
@@ -0,0 +1,1778 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20/**
21 * @file abd/gnunet-service-abd.c
22 * @brief GNUnet Credential Service (main service)
23 * @author Martin Schanzenbach
24 */
25#include "platform.h"
26
27#include "gnunet_util_lib.h"
28
29#include "abd.h"
30#include "abd_serialization.h"
31#include "gnunet_abd_service.h"
32#include "gnunet_protocols.h"
33#include "gnunet_signatures.h"
34#include "gnunet_statistics_service.h"
35#include <gnunet_gns_service.h>
36#include <gnunet_gnsrecord_lib.h>
37#include <gnunet_identity_service.h>
38#include <gnunet_namestore_service.h>
39
40
41#define GNUNET_ABD_MAX_LENGTH 255
42
43struct VerifyRequestHandle;
44
45struct DelegationSetQueueEntry;
46
47
48struct DelegationChainEntry
49{
50 /**
51 * DLL
52 */
53 struct DelegationChainEntry *next;
54
55 /**
56 * DLL
57 */
58 struct DelegationChainEntry *prev;
59
60 /**
61 * The issuer
62 */
63 struct GNUNET_CRYPTO_PublicKey issuer_key;
64
65 /**
66 * The subject
67 */
68 struct GNUNET_CRYPTO_PublicKey subject_key;
69
70 /**
71 * The issued attribute
72 */
73 char *issuer_attribute;
74
75 /**
76 * The delegated attribute
77 */
78 char *subject_attribute;
79};
80
81/**
82 * DLL for record
83 */
84struct DelegateRecordEntry
85{
86 /**
87 * DLL
88 */
89 struct DelegateRecordEntry *next;
90
91 /**
92 * DLL
93 */
94 struct DelegateRecordEntry *prev;
95
96 /**
97 * Number of references in delegation chains
98 */
99 uint32_t refcount;
100
101 /**
102 * Payload
103 */
104 struct GNUNET_ABD_Delegate *delegate;
105};
106
107/**
108 * DLL used for delegations
109 * Used for OR delegations
110 */
111struct DelegationQueueEntry
112{
113 /**
114 * DLL
115 */
116 struct DelegationQueueEntry *next;
117
118 /**
119 * DLL
120 */
121 struct DelegationQueueEntry *prev;
122
123 /**
124 * Parent set
125 */
126 struct DelegationSetQueueEntry *parent_set;
127
128 /**
129 * Required solutions
130 */
131 uint32_t required_solutions;
132};
133
134/**
135 * DLL for delegation sets
136 * Used for AND delegation set
137 */
138struct DelegationSetQueueEntry
139{
140 /**
141 * DLL
142 */
143 struct DelegationSetQueueEntry *next;
144
145 /**
146 * DLL
147 */
148 struct DelegationSetQueueEntry *prev;
149
150 /**
151 * GNS handle
152 */
153 struct GNUNET_GNS_LookupRequest *lookup_request;
154
155 /**
156 * Verify handle
157 */
158 struct VerifyRequestHandle *handle;
159
160 /**
161 * Parent attribute delegation
162 */
163 struct DelegationQueueEntry *parent;
164
165 /**
166 * Issuer key
167 */
168 struct GNUNET_CRYPTO_PublicKey *issuer_key;
169
170 /**
171 * Queue entries of this set
172 */
173 struct DelegationQueueEntry *queue_entries_head;
174
175 /**
176 * Queue entries of this set
177 */
178 struct DelegationQueueEntry *queue_entries_tail;
179
180 /**
181 * Parent QueueEntry
182 */
183 struct DelegationQueueEntry *parent_queue_entry;
184
185 /**
186 * Issuer attribute delegated to
187 */
188 char *issuer_attribute;
189
190 /**
191 * The current attribute to look up
192 */
193 char *lookup_attribute;
194
195 /**
196 * Trailing attribute context
197 */
198 char *attr_trailer;
199
200 /**
201 * Still to resolve delegation as string
202 */
203 char *unresolved_attribute_delegation;
204
205 /**
206 * The delegation chain entry
207 */
208 struct DelegationChainEntry *delegation_chain_entry;
209
210 /**
211 * True if added by backward resolution
212 */
213 bool from_bw;
214};
215
216
217/**
218 * Handle to a lookup operation from api
219 */
220struct VerifyRequestHandle
221{
222 /**
223 * True if created by a collect request.
224 */
225 bool is_collect;
226 /**
227 * We keep these in a DLL.
228 */
229 struct VerifyRequestHandle *next;
230
231 /**
232 * We keep these in a DLL.
233 */
234 struct VerifyRequestHandle *prev;
235
236 /**
237 * Handle to the requesting client
238 */
239 struct GNUNET_SERVICE_Client *client;
240
241 /**
242 * Size of delegation tree
243 */
244 uint32_t delegation_chain_size;
245
246 /**
247 * Children of this attribute
248 */
249 struct DelegationChainEntry *delegation_chain_head;
250
251 /**
252 * Children of this attribute
253 */
254 struct DelegationChainEntry *delegation_chain_tail;
255
256 /**
257 * List for bidirectional matching
258 */
259 struct DelegationSetQueueEntry *dsq_head;
260
261 /**
262 * List for bidirectional matching
263 */
264 struct DelegationSetQueueEntry *dsq_tail;
265
266 /**
267 * Issuer public key
268 */
269 struct GNUNET_CRYPTO_PublicKey issuer_key;
270
271 /**
272 * Issuer attribute
273 */
274 char *issuer_attribute;
275
276 /**
277 * Subject public key
278 */
279 struct GNUNET_CRYPTO_PublicKey subject_key;
280
281 /**
282 * Delegate DLL
283 */
284 struct DelegateRecordEntry *del_chain_head;
285
286 /**
287 * Delegate DLL
288 */
289 struct DelegateRecordEntry *del_chain_tail;
290
291 /**
292 * Delegate DLL size
293 */
294 uint32_t del_chain_size;
295
296 /**
297 * Current Delegation Pointer
298 */
299 struct DelegationQueueEntry *current_delegation;
300
301 /**
302 * request id
303 */
304 uint32_t request_id;
305
306 /**
307 * Pending lookups
308 */
309 uint64_t pending_lookups;
310
311 /**
312 * Direction of the resolution algo
313 */
314 enum GNUNET_ABD_AlgoDirectionFlags resolution_algo;
315
316 /**
317 * Delegate iterator for lookup
318 */
319 struct GNUNET_NAMESTORE_QueueEntry *dele_qe;
320};
321
322
323/**
324 * Head of the DLL.
325 */
326static struct VerifyRequestHandle *vrh_head = NULL;
327
328/**
329 * Tail of the DLL.
330 */
331static struct VerifyRequestHandle *vrh_tail = NULL;
332
333/**
334 * Handle to the statistics service
335 */
336static struct GNUNET_STATISTICS_Handle *statistics;
337
338/**
339 * Handle to GNS service.
340 */
341static struct GNUNET_GNS_Handle *gns;
342
343/**
344 * Handle to namestore service
345 */
346static struct GNUNET_NAMESTORE_Handle *namestore;
347
348static void
349print_deleset (struct DelegationSetQueueEntry *dsentry, char *text)
350{
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352 "%s %s.%s <- %s.%s\n",
353 text,
354 GNUNET_CRYPTO_public_key_to_string (
355 &dsentry->delegation_chain_entry->issuer_key),
356 dsentry->delegation_chain_entry->issuer_attribute,
357 GNUNET_CRYPTO_public_key_to_string (
358 &dsentry->delegation_chain_entry->subject_key),
359 dsentry->delegation_chain_entry->subject_attribute);
360}
361
362
363static void
364cleanup_dsq_entry (struct DelegationSetQueueEntry *ds_entry)
365{
366 GNUNET_free (ds_entry->issuer_key);
367 GNUNET_free (ds_entry->issuer_attribute);
368 GNUNET_free (ds_entry->attr_trailer);
369 // those fields are only set/used in bw search
370 if (ds_entry->from_bw)
371 {
372 GNUNET_free (ds_entry->lookup_attribute);
373 GNUNET_free (ds_entry->unresolved_attribute_delegation);
374 }
375 if (NULL != ds_entry->lookup_request)
376 {
377 GNUNET_GNS_lookup_cancel (ds_entry->lookup_request);
378 ds_entry->lookup_request = NULL;
379 }
380 if (NULL != ds_entry->delegation_chain_entry)
381 {
382 GNUNET_free (
383 ds_entry->delegation_chain_entry->subject_attribute);
384 GNUNET_free (ds_entry->delegation_chain_entry->issuer_attribute);
385 GNUNET_free (ds_entry->delegation_chain_entry);
386 }
387 // Free DQ entries
388 for (struct DelegationQueueEntry *dq_entry = ds_entry->queue_entries_head;
389 NULL != ds_entry->queue_entries_head;
390 dq_entry = ds_entry->queue_entries_head)
391 {
392 GNUNET_CONTAINER_DLL_remove (ds_entry->queue_entries_head,
393 ds_entry->queue_entries_tail,
394 dq_entry);
395 GNUNET_free (dq_entry);
396 }
397 GNUNET_free (ds_entry);
398}
399
400
401static void
402cleanup_handle (struct VerifyRequestHandle *vrh)
403{
404 struct DelegateRecordEntry *del_entry;
405 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up...\n");
406
407 if (NULL != vrh->dsq_head)
408 {
409 for (struct DelegationSetQueueEntry *ds_entry = vrh->dsq_head; NULL !=
410 vrh->dsq_head;
411 ds_entry = vrh->dsq_head)
412 {
413 GNUNET_CONTAINER_DLL_remove (vrh->dsq_head, vrh->dsq_tail, ds_entry);
414 cleanup_dsq_entry (ds_entry);
415 }
416 }
417 if (NULL != vrh->del_chain_head)
418 {
419 for (del_entry = vrh->del_chain_head; NULL != vrh->del_chain_head;
420 del_entry = vrh->del_chain_head)
421 {
422 GNUNET_CONTAINER_DLL_remove (vrh->del_chain_head,
423 vrh->del_chain_tail,
424 del_entry);
425 GNUNET_free (del_entry->delegate);
426 GNUNET_free (del_entry);
427 }
428 }
429 GNUNET_free (vrh->issuer_attribute);
430 GNUNET_free (vrh);
431}
432
433
434static void
435shutdown_task (void *cls)
436{
437 struct VerifyRequestHandle *vrh;
438
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down!\n");
440
441 while (NULL != (vrh = vrh_head))
442 {
443 // ABD_resolver_lookup_cancel (clh->lookup);
444 GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
445 cleanup_handle (vrh);
446 }
447
448 if (NULL != gns)
449 {
450 GNUNET_GNS_disconnect (gns);
451 gns = NULL;
452 }
453 if (NULL != namestore)
454 {
455 GNUNET_NAMESTORE_disconnect (namestore);
456 namestore = NULL;
457 }
458 if (NULL != statistics)
459 {
460 GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
461 statistics = NULL;
462 }
463}
464
465
466static void
467send_intermediate_response (struct VerifyRequestHandle *vrh, struct
468 DelegationChainEntry *ch_entry, bool is_bw)
469{
470 struct DelegationChainIntermediateMessage *rmsg;
471 struct GNUNET_MQ_Envelope *env;
472 struct GNUNET_ABD_Delegation *dd;
473 size_t size;
474
475 // Don't report immediate results during collect
476 if (vrh->is_collect)
477 return;
478
479 dd = GNUNET_new (struct GNUNET_ABD_Delegation);
480 dd->issuer_key = ch_entry->issuer_key;
481 dd->subject_key = ch_entry->subject_key;
482 dd->issuer_attribute = ch_entry->issuer_attribute;
483 dd->issuer_attribute_len = strlen (ch_entry->issuer_attribute) + 1;
484 dd->subject_attribute_len = 0;
485 dd->subject_attribute = NULL;
486 if (NULL != ch_entry->subject_attribute)
487 {
488 dd->subject_attribute = ch_entry->subject_attribute;
489 dd->subject_attribute_len = strlen (ch_entry->subject_attribute) + 1;
490 }
491
492
493 size = GNUNET_ABD_delegation_chain_get_size (1,
494 dd,
495 0,
496 NULL);
497
498 env = GNUNET_MQ_msg_extra (rmsg,
499 size,
500 GNUNET_MESSAGE_TYPE_ABD_INTERMEDIATE_RESULT);
501 // Assign id so that client can find associated request
502 rmsg->id = vrh->request_id;
503 rmsg->is_bw = htons (is_bw);
504 rmsg->size = htonl (size);
505
506 GNUNET_assert (
507 -1 != GNUNET_ABD_delegation_chain_serialize (1,
508 dd,
509 0,
510 NULL,
511 size,
512 (char *) &rmsg[1]));
513 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (vrh->client), env);
514
515 GNUNET_free (dd);
516}
517
518
519static void
520send_lookup_response (struct VerifyRequestHandle *vrh)
521{
522 struct GNUNET_MQ_Envelope *env;
523 struct DelegationChainResultMessage *rmsg;
524 struct DelegationChainEntry *dce;
525 struct GNUNET_ABD_Delegation dd[vrh->delegation_chain_size];
526 struct GNUNET_ABD_Delegate dele[vrh->del_chain_size];
527 struct DelegateRecordEntry *del;
528 struct DelegateRecordEntry *tmp;
529 size_t size;
530
531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending response\n");
532 dce = vrh->delegation_chain_head;
533 for (uint32_t i = 0; i < vrh->delegation_chain_size; i++)
534 {
535 dd[i].issuer_key = dce->issuer_key;
536 dd[i].subject_key = dce->subject_key;
537 dd[i].issuer_attribute = dce->issuer_attribute;
538 dd[i].issuer_attribute_len = strlen (dce->issuer_attribute) + 1;
539 dd[i].subject_attribute_len = 0;
540 dd[i].subject_attribute = NULL;
541 if (NULL != dce->subject_attribute)
542 {
543 dd[i].subject_attribute = dce->subject_attribute;
544 dd[i].subject_attribute_len = strlen (dce->subject_attribute) + 1;
545 }
546 dce = dce->next;
547 }
548
549 // Remove all not needed credentials
550 for (del = vrh->del_chain_head; NULL != del;)
551 {
552 if (del->refcount > 0)
553 {
554 del = del->next;
555 continue;
556 }
557 tmp = del;
558 del = del->next;
559 GNUNET_CONTAINER_DLL_remove (vrh->del_chain_head, vrh->del_chain_tail, tmp);
560 GNUNET_free (tmp->delegate);
561 GNUNET_free (tmp);
562 vrh->del_chain_size--;
563 }
564
565 // Get serialized record data
566 // Append at the end of rmsg
567 del = vrh->del_chain_head;
568 for (uint32_t i = 0; i < vrh->del_chain_size; i++)
569 {
570 dele[i].issuer_key = del->delegate->issuer_key;
571 dele[i].subject_key = del->delegate->subject_key;
572 dele[i].issuer_attribute_len = strlen (del->delegate->issuer_attribute) + 1;
573 dele[i].issuer_attribute = del->delegate->issuer_attribute;
574 dele[i].subject_attribute_len = del->delegate->subject_attribute_len;
575 dele[i].subject_attribute = del->delegate->subject_attribute;
576 dele[i].expiration = del->delegate->expiration;
577 dele[i].signature = del->delegate->signature;
578 del = del->next;
579 }
580 size =
581 GNUNET_ABD_delegation_chain_get_size (vrh->delegation_chain_size,
582 dd,
583 vrh->del_chain_size,
584 dele);
585 env = GNUNET_MQ_msg_extra (rmsg,
586 size,
587 GNUNET_MESSAGE_TYPE_ABD_VERIFY_RESULT);
588 // Assign id so that client can find associated request
589 rmsg->id = vrh->request_id;
590 rmsg->d_count = htonl (vrh->delegation_chain_size);
591 rmsg->c_count = htonl (vrh->del_chain_size);
592
593 if (0 < vrh->del_chain_size)
594 rmsg->del_found = htonl (GNUNET_YES);
595 else
596 rmsg->del_found = htonl (GNUNET_NO);
597
598 GNUNET_assert (
599 -1 !=
600 GNUNET_ABD_delegation_chain_serialize (vrh->delegation_chain_size,
601 dd,
602 vrh->del_chain_size,
603 dele,
604 size,
605 (char *) &rmsg[1]));
606
607 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (vrh->client), env);
608 GNUNET_CONTAINER_DLL_remove (vrh_head, vrh_tail, vrh);
609 cleanup_handle (vrh);
610 GNUNET_STATISTICS_update (statistics,
611 "Completed verifications",
612 1,
613 GNUNET_NO);
614}
615
616
617static char *
618partial_match (char *tmp_trail,
619 char *tmp_subattr,
620 char *parent_trail,
621 char *issuer_attribute)
622{
623 char *saveptr1, *saveptr2;
624 char *trail_token;
625 char *sub_token;
626 char *attr_trailer;
627
628 // tok both, parent->attr_trailer and del->sub_attr to see how far they match,
629 // take rest of parent trailer (only when del->sub_attr token is null), and
630 // create new/actual trailer with del->iss_attr
631 trail_token = strtok_r (tmp_trail, ".", &saveptr1);
632 sub_token = strtok_r (tmp_subattr, ".", &saveptr2);
633 while (NULL != trail_token && NULL != sub_token)
634 {
635 if (0 == strcmp (trail_token, sub_token))
636 {
637 // good, matches, remove
638 }
639 else
640 {
641 // not relevant for solving the chain, end for iteration here
642 return NULL;
643 }
644
645 trail_token = strtok_r (NULL, ".", &saveptr1);
646 sub_token = strtok_r (NULL, ".", &saveptr2);
647 }
648 // skip this entry and go to next for if:
649 // 1. at some point the attr of the trailer and the subject dont match
650 // 2. the trailer is NULL, but the subject has more attributes
651 // Reason: This will lead to "startzone.attribute" but we're looking for a solution
652 // for "<- startzone"
653 if (NULL == trail_token)
654 {
655 return NULL;
656 }
657
658 // do not have to check sub_token == NULL, if both would be NULL
659 // at the same time, the complete match part above should have triggered already
660
661 // otherwise, above while only ends when sub_token == NULL
662 GNUNET_asprintf (&attr_trailer, "%s", trail_token);
663 trail_token = strtok_r (NULL, ".", &saveptr1);
664 while (NULL != trail_token)
665 {
666 GNUNET_asprintf (&attr_trailer, "%s.%s", parent_trail, trail_token);
667 trail_token = strtok_r (NULL, ".", &saveptr1);
668 }
669 GNUNET_asprintf (&attr_trailer, "%s.%s", issuer_attribute, attr_trailer);
670 return attr_trailer;
671}
672
673
674static int
675handle_bidirectional_match (struct DelegationSetQueueEntry *actual_entry,
676 struct DelegationSetQueueEntry *match_entry,
677 struct VerifyRequestHandle *vrh)
678{
679 struct DelegationSetQueueEntry *old_fw_parent;
680 struct DelegationSetQueueEntry *fw_entry = actual_entry;
681 struct DelegationSetQueueEntry *last_entry = match_entry;
682 // parent fixing, combine backward and forward chain parts
683 while (NULL != fw_entry->parent_queue_entry)
684 {
685 old_fw_parent = fw_entry->parent_queue_entry->parent_set;
686 // set parent
687 fw_entry->parent_queue_entry->parent_set = last_entry;
688
689 last_entry = fw_entry;
690 fw_entry = old_fw_parent;
691 }
692 // set last entry of chain as actual_entry
693 // actual_entry = last_entry;
694 // set refcount, loop all delegations
695 for (struct DelegateRecordEntry *del_entry = vrh->del_chain_head;
696 del_entry != NULL;
697 del_entry = del_entry->next)
698 {
699 if (0 != memcmp (&last_entry->delegation_chain_entry->subject_key,
700 &del_entry->delegate->issuer_key,
701 sizeof (struct GNUNET_CRYPTO_PublicKey)))
702 continue;
703 if (0 != strcmp (last_entry->delegation_chain_entry->subject_attribute,
704 del_entry->delegate->issuer_attribute))
705 continue;
706
707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found delegate.\n");
708 // increase refcount of the start delegation
709 del_entry->refcount++;
710 }
711 // backtrack
712 for (struct DelegationSetQueueEntry *tmp_set = last_entry;
713 NULL != tmp_set->parent_queue_entry;
714 tmp_set = tmp_set->parent_queue_entry->parent_set)
715 {
716 tmp_set->parent_queue_entry->required_solutions--;
717
718 // add new found entry to vrh
719 vrh->delegation_chain_size++;
720 GNUNET_CONTAINER_DLL_insert (vrh->delegation_chain_head,
721 vrh->delegation_chain_tail,
722 tmp_set->delegation_chain_entry);
723
724 // if one node on the path still needs solutions, this current
725 // patch cannot fulfill the conditions and therefore stops here
726 // however, it is in the vrh and can be used by the other paths
727 // related to this path/collection/verification
728 if (0 < tmp_set->parent_queue_entry->required_solutions)
729 {
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
731 "Chain requires more solutions, waiting...\n");
732 return GNUNET_NO;
733 }
734 }
735 return GNUNET_YES;
736}
737
738
739static void
740forward_resolution (void *cls,
741 uint32_t rd_count,
742 const struct GNUNET_GNSRECORD_Data *rd)
743{
744 struct VerifyRequestHandle *vrh;
745 struct DelegationSetQueueEntry *current_set;
746 struct DelegationSetQueueEntry *ds_entry;
747 struct DelegationQueueEntry *dq_entry;
748 struct GNUNET_ABD_Delegate *del;
749
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %d entries.\n", rd_count);
751
752 current_set = cls;
753 // set handle to NULL (as el = NULL)
754 current_set->lookup_request = NULL;
755 vrh = current_set->handle;
756 vrh->pending_lookups--;
757
758 // Loop record entries
759 for (uint32_t i = 0; i < rd_count; i++)
760 {
761 if (GNUNET_GNSRECORD_TYPE_DELEGATE != rd[i].record_type)
762 continue;
763
764 // Start deserialize into Delegate
765 del = GNUNET_ABD_delegate_deserialize (rd[i].data, rd[i].data_size);
766
767 if (NULL == del)
768 continue;
769
770 // Start: Create DQ Entry
771 dq_entry = GNUNET_new (struct DelegationQueueEntry);
772 // AND delegations are not possible, only 1 solution
773 dq_entry->required_solutions = 1;
774 dq_entry->parent_set = current_set;
775
776 // Insert it into the current set
777 GNUNET_CONTAINER_DLL_insert (current_set->queue_entries_head,
778 current_set->queue_entries_tail,
779 dq_entry);
780
781 // Start: Create DS Entry
782 ds_entry = GNUNET_new (struct DelegationSetQueueEntry);
783 GNUNET_CONTAINER_DLL_insert (vrh->dsq_head, vrh->dsq_tail, ds_entry);
784 ds_entry->from_bw = false;
785
786 // (1) A.a <- A.b.c
787 // (2) A.b <- D.d
788 // (3) D.d <- E
789 // (4) E.c <- F.c
790 // (5) F.c <- G
791 // Possibilities:
792 // 1. complete match: trailer = 0, validate
793 // 2. partial match: replace
794 // 3. new solution: replace, add trailer
795
796 // At resolution chain start trailer of parent is NULL
797 if (NULL == current_set->attr_trailer)
798 {
799 // for (5) F.c <- G, remember .c when going upwards
800 ds_entry->attr_trailer = GNUNET_strdup (del->issuer_attribute);
801 }
802 else
803 {
804 if (0 == del->subject_attribute_len)
805 {
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found: New solution\n");
807 // new solution
808 // create new trailer del->issuer_attribute, ds_entry->attr_trailer
809 GNUNET_asprintf (&ds_entry->attr_trailer,
810 "%s.%s",
811 del->issuer_attribute,
812 current_set->attr_trailer);
813 }
814 else if (0 == strcmp (del->subject_attribute, current_set->attr_trailer))
815 {
816 // complete match
817 // new trailer == issuer attribute (e.g. (5) to (4))
818 ds_entry->attr_trailer = GNUNET_strdup (del->issuer_attribute);
819 }
820 else
821 {
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found: Partial match\n");
823 // partial match
824
825 char *trail = partial_match (GNUNET_strdup (current_set->attr_trailer),
826 GNUNET_strdup (del->subject_attribute),
827 current_set->attr_trailer,
828 GNUNET_strdup (del->issuer_attribute));
829
830 // if null: skip this record entry (reasons: mismatch or overmatch, both not relevant)
831 if (NULL == trail)
832 {
833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
834 "Entry not relevant, discarding: %s.%s <- %s.%s\n",
835 GNUNET_CRYPTO_public_key_to_string (
836 &del->issuer_key),
837 del->issuer_attribute,
838 GNUNET_CRYPTO_public_key_to_string (
839 &del->subject_key),
840 del->subject_attribute);
841 GNUNET_free (del);
842 continue;
843 }
844 else
845 ds_entry->attr_trailer = trail;
846 }
847 }
848
849
850 // Start: Credential Chain Entry
851 // issuer key is subject key, who needs to be contacted to resolve this (forward, therefore subject)
852 ds_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_PublicKey);
853 GNUNET_memcpy (ds_entry->issuer_key,
854 &del->subject_key,
855 sizeof (struct GNUNET_CRYPTO_PublicKey));
856
857 ds_entry->delegation_chain_entry = GNUNET_new (struct DelegationChainEntry);
858 ds_entry->delegation_chain_entry->subject_key = del->subject_key;
859 if (0 < del->subject_attribute_len)
860 ds_entry->delegation_chain_entry->subject_attribute =
861 GNUNET_strdup (del->subject_attribute);
862 ds_entry->delegation_chain_entry->issuer_key = del->issuer_key;
863 ds_entry->delegation_chain_entry->issuer_attribute =
864 GNUNET_strdup (del->issuer_attribute);
865
866 // Found new entry, repoting intermediate result
867 send_intermediate_response (vrh, ds_entry->delegation_chain_entry, false);
868
869 // current delegation as parent
870 ds_entry->parent_queue_entry = dq_entry;
871
872 // Check for solution
873 // if: issuer key we looking for
874 if (0 == memcmp (&del->issuer_key,
875 &vrh->issuer_key,
876 sizeof (struct GNUNET_CRYPTO_PublicKey)))
877 {
878 // if: issuer attr we looking for
879 if (0 == strcmp (del->issuer_attribute, vrh->issuer_attribute))
880 {
881 // if: complete match, meaning new trailer == issuer attr
882 if (0 == strcmp (vrh->issuer_attribute, ds_entry->attr_trailer))
883 {
884 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found: Solution\n");
885
886 // Add found solution into delegation_chain
887 struct DelegationSetQueueEntry *tmp_set;
888 for (tmp_set = ds_entry; NULL != tmp_set->parent_queue_entry;
889 tmp_set = tmp_set->parent_queue_entry->parent_set)
890 {
891 if (NULL != tmp_set->delegation_chain_entry)
892 {
893 vrh->delegation_chain_size++;
894 GNUNET_CONTAINER_DLL_insert (vrh->delegation_chain_head,
895 vrh->delegation_chain_tail,
896 tmp_set->delegation_chain_entry);
897 }
898 }
899
900 // Increase refcount for this delegate
901 for (struct DelegateRecordEntry *del_entry = vrh->del_chain_head;
902 del_entry != NULL;
903 del_entry = del_entry->next)
904 {
905 if (0 == memcmp (&del_entry->delegate->issuer_key,
906 &vrh->delegation_chain_head->subject_key,
907 sizeof (struct GNUNET_CRYPTO_PublicKey)))
908 {
909 if (0 == strcmp (del_entry->delegate->issuer_attribute,
910 vrh->delegation_chain_head->subject_attribute))
911 {
912 del_entry->refcount++;
913 }
914 }
915 }
916
917 send_lookup_response (vrh);
918 GNUNET_free (del);
919 return;
920 }
921 }
922 }
923
924 // Check for bidirectional crossmatch
925 for (struct DelegationSetQueueEntry *del_entry = vrh->dsq_head;
926 del_entry != NULL;
927 del_entry = del_entry->next)
928 {
929 // only check entries not by backward algorithm
930 if (del_entry->from_bw)
931 {
932 // key of list entry matches actual key
933 if (0 == memcmp (&del_entry->delegation_chain_entry->subject_key,
934 &ds_entry->delegation_chain_entry->issuer_key,
935 sizeof (struct GNUNET_CRYPTO_PublicKey)))
936 {
937 // compare entry subject attributes to this trailer (iss attr + old trailer)
938 if (0 == strcmp (del_entry->unresolved_attribute_delegation,
939 ds_entry->attr_trailer))
940 {
941 print_deleset (del_entry, "Forward:");
942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943 "Forward: Found match with above!\n");
944
945 GNUNET_free (del);
946 // one node on the path still needs solutions: return
947 if (GNUNET_NO ==
948 handle_bidirectional_match (ds_entry, del_entry, vrh))
949 return;
950
951 send_lookup_response (vrh);
952 return;
953 }
954 }
955 }
956 }
957
958 // Starting a new GNS lookup
959 vrh->pending_lookups++;
960 ds_entry->handle = vrh;
961
962 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
963 "Starting to look up trailer %s in zone %s\n",
964 ds_entry->attr_trailer,
965 GNUNET_CRYPTO_public_key_to_string (&del->issuer_key));
966
967 ds_entry->lookup_request =
968 GNUNET_GNS_lookup (gns,
969 GNUNET_GNS_EMPTY_LABEL_AT,
970 &del->issuer_key,
971 GNUNET_GNSRECORD_TYPE_DELEGATE,
972 GNUNET_GNS_LO_DEFAULT,
973 &forward_resolution,
974 ds_entry);
975 GNUNET_free (del);
976 }
977
978 if (0 == vrh->pending_lookups)
979 {
980 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "We are all out of attributes...\n");
981 send_lookup_response (vrh);
982 return;
983 }
984}
985
986
987static void
988backward_resolution (void *cls,
989 uint32_t rd_count,
990 const struct GNUNET_GNSRECORD_Data *rd)
991{
992 struct VerifyRequestHandle *vrh;
993 const struct GNUNET_ABD_DelegationRecord *sets;
994 struct DelegateRecordEntry *del_pointer;
995 struct DelegationSetQueueEntry *current_set;
996 struct DelegationSetQueueEntry *ds_entry;
997 struct DelegationSetQueueEntry *tmp_set;
998 struct DelegationQueueEntry *dq_entry;
999 char *expanded_attr;
1000 char *lookup_attribute;
1001
1002 current_set = cls;
1003 current_set->lookup_request = NULL;
1004 vrh = current_set->handle;
1005 vrh->pending_lookups--;
1006
1007 // Each OR
1008 for (uint32_t i = 0; i < rd_count; i++)
1009 {
1010 if (GNUNET_GNSRECORD_TYPE_ATTRIBUTE != rd[i].record_type)
1011 continue;
1012
1013 sets = rd[i].data;
1014 struct GNUNET_ABD_DelegationSet set[ntohl (sets->set_count)];
1015 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1016 "Found new attribute delegation with %d sets. Creating new Job...\n",
1017 ntohl (sets->set_count));
1018
1019 if (GNUNET_OK !=
1020 GNUNET_ABD_delegation_set_deserialize (GNUNET_ntohll (
1021 sets->data_size),
1022 (const char *) &sets[1],
1023 ntohl (sets->set_count),
1024 set))
1025 {
1026 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to deserialize!\n");
1027 continue;
1028 }
1029 dq_entry = GNUNET_new (struct DelegationQueueEntry);
1030 dq_entry->required_solutions = ntohl (sets->set_count);
1031 dq_entry->parent_set = current_set;
1032
1033 GNUNET_CONTAINER_DLL_insert (current_set->queue_entries_head,
1034 current_set->queue_entries_tail,
1035 dq_entry);
1036 // Each AND
1037 for (uint32_t j = 0; j < ntohl (sets->set_count); j++)
1038 {
1039 ds_entry = GNUNET_new (struct DelegationSetQueueEntry);
1040 GNUNET_CONTAINER_DLL_insert (vrh->dsq_head, vrh->dsq_tail, ds_entry);
1041 ds_entry->from_bw = true;
1042
1043 if (NULL != current_set->attr_trailer)
1044 {
1045 if (0 == set[j].subject_attribute_len)
1046 {
1047 GNUNET_asprintf (&expanded_attr, "%s", current_set->attr_trailer);
1048 }
1049 else
1050 {
1051 GNUNET_asprintf (&expanded_attr,
1052 "%s.%s",
1053 set[j].subject_attribute,
1054 current_set->attr_trailer);
1055 }
1056 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Expanded to %s\n", expanded_attr);
1057 ds_entry->unresolved_attribute_delegation = expanded_attr;
1058 }
1059 else
1060 {
1061 if (0 != set[j].subject_attribute_len)
1062 {
1063 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064 "Not Expanding %s\n",
1065 set[j].subject_attribute);
1066 ds_entry->unresolved_attribute_delegation =
1067 GNUNET_strdup (set[j].subject_attribute);
1068 }
1069 }
1070
1071 // Add a credential chain entry
1072 ds_entry->delegation_chain_entry =
1073 GNUNET_new (struct DelegationChainEntry);
1074 ds_entry->delegation_chain_entry->subject_key = set[j].subject_key;
1075 ds_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_PublicKey);
1076 GNUNET_memcpy (ds_entry->issuer_key,
1077 &set[j].subject_key,
1078 sizeof (struct GNUNET_CRYPTO_PublicKey));
1079 if (0 < set[j].subject_attribute_len)
1080 ds_entry->delegation_chain_entry->subject_attribute =
1081 GNUNET_strdup (set[j].subject_attribute);
1082 ds_entry->delegation_chain_entry->issuer_key = *current_set->issuer_key;
1083 ds_entry->delegation_chain_entry->issuer_attribute =
1084 GNUNET_strdup (current_set->lookup_attribute);
1085
1086 // Found new entry, repoting intermediate result
1087 send_intermediate_response (vrh, ds_entry->delegation_chain_entry, true);
1088
1089 ds_entry->parent_queue_entry = dq_entry; // current_delegation;
1090
1091 /**
1092 * Check if this delegation already matches one of our credentials
1093 */
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking for cred match\n");
1095
1096 for (del_pointer = vrh->del_chain_head; del_pointer != NULL;
1097 del_pointer = del_pointer->next)
1098 {
1099 // If key and attribute match credential: continue and backtrack
1100 if (0 != memcmp (&set[j].subject_key,
1101 &del_pointer->delegate->issuer_key,
1102 sizeof (struct GNUNET_CRYPTO_PublicKey)))
1103 continue;
1104 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1105 "Checking if %s matches %s\n",
1106 ds_entry->unresolved_attribute_delegation,
1107 del_pointer->delegate->issuer_attribute);
1108
1109 if (0 != strcmp (ds_entry->unresolved_attribute_delegation,
1110 del_pointer->delegate->issuer_attribute))
1111 continue;
1112
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found issuer\n");
1114 // increase refcount of the start delegation
1115 del_pointer->refcount++;
1116
1117 // Backtrack
1118 for (tmp_set = ds_entry; NULL != tmp_set->parent_queue_entry;
1119 tmp_set = tmp_set->parent_queue_entry->parent_set)
1120 {
1121 tmp_set->parent_queue_entry->required_solutions--;
1122 if (NULL != tmp_set->delegation_chain_entry)
1123 {
1124 vrh->delegation_chain_size++;
1125 GNUNET_CONTAINER_DLL_insert (vrh->delegation_chain_head,
1126 vrh->delegation_chain_tail,
1127 tmp_set->delegation_chain_entry);
1128 }
1129 if (0 < tmp_set->parent_queue_entry->required_solutions)
1130 break;
1131 }
1132
1133 // if the break above is not called the condition of the for is met
1134 if (NULL == tmp_set->parent_queue_entry)
1135 {
1136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All solutions found\n");
1137 // Found match
1138 send_lookup_response (vrh);
1139 return;
1140 }
1141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not all solutions found yet.\n");
1142 continue;
1143 }
1144
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146 "Building new lookup request from %s\n",
1147 ds_entry->unresolved_attribute_delegation);
1148 // Continue with next/new backward resolution
1149 char issuer_attribute_name[strlen (
1150 ds_entry->unresolved_attribute_delegation)
1151 + 1];
1152 strcpy (issuer_attribute_name, ds_entry->unresolved_attribute_delegation);
1153 char *next_attr = strtok (issuer_attribute_name, ".");
1154 if (NULL == next_attr)
1155 {
1156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1157 "Failed to parse next attribute\n");
1158 continue;
1159 }
1160 GNUNET_asprintf (&lookup_attribute, "%s", next_attr);
1161 GNUNET_asprintf (&ds_entry->lookup_attribute, "%s", next_attr);
1162 if (strlen (next_attr) ==
1163 strlen (ds_entry->unresolved_attribute_delegation))
1164 {
1165 ds_entry->attr_trailer = NULL;
1166 }
1167 else
1168 {
1169 next_attr += strlen (next_attr) + 1;
1170 ds_entry->attr_trailer = GNUNET_strdup (next_attr);
1171 }
1172
1173 // Check for bidirectional crossmatch
1174 for (struct DelegationSetQueueEntry *del_entry = vrh->dsq_head;
1175 del_entry != NULL;
1176 del_entry = del_entry->next)
1177 {
1178 // only check entries added by forward algorithm
1179 if (! del_entry->from_bw)
1180 {
1181 // key of list entry matches actual key
1182 if (0 == memcmp (&del_entry->delegation_chain_entry->issuer_key,
1183 &ds_entry->delegation_chain_entry->subject_key,
1184 sizeof (struct GNUNET_CRYPTO_PublicKey)))
1185 {
1186 // compare entry subject attributes to this trailer (iss attr + old trailer)
1187 if (0 == strcmp (del_entry->attr_trailer,
1188 ds_entry->unresolved_attribute_delegation))
1189 {
1190 print_deleset (del_entry, "Backward:");
1191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192 "Backward: Found match with above!\n");
1193
1194 // if one node on the path still needs solutions: return
1195 if (GNUNET_NO ==
1196 handle_bidirectional_match (del_entry, ds_entry, vrh))
1197 break;
1198
1199 // Send lookup response
1200 send_lookup_response (vrh);
1201 return;
1202 }
1203 }
1204 }
1205 }
1206
1207 // Starting a new GNS lookup
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "Looking up %s\n",
1210 ds_entry->lookup_attribute);
1211 if (NULL != ds_entry->attr_trailer)
1212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1213 "%s still to go...\n",
1214 ds_entry->attr_trailer);
1215
1216 vrh->pending_lookups++;
1217 ds_entry->handle = vrh;
1218 ds_entry->lookup_request =
1219 GNUNET_GNS_lookup (gns,
1220 lookup_attribute,
1221 ds_entry->issuer_key, // issuer_key,
1222 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
1223 GNUNET_GNS_LO_DEFAULT,
1224 &backward_resolution,
1225 ds_entry);
1226
1227 GNUNET_free (lookup_attribute);
1228 }
1229 }
1230
1231 if (0 == vrh->pending_lookups)
1232 {
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "We are all out of attributes...\n");
1234 send_lookup_response (vrh);
1235 return;
1236 }
1237}
1238
1239
1240/**
1241 * Result from GNS lookup.
1242 *
1243 * @param cls the closure (our client lookup handle)
1244 */
1245static int
1246delegation_chain_bw_resolution_start (void *cls)
1247{
1248 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Start Backward Resolution...\n");
1249
1250 struct VerifyRequestHandle *vrh = cls;
1251 struct DelegationSetQueueEntry *ds_entry;
1252 struct DelegateRecordEntry *del_entry;
1253
1254 if (0 == vrh->del_chain_size)
1255 {
1256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No delegates found\n");
1257 send_lookup_response (vrh);
1258 return 1;
1259 }
1260
1261 // Pre-check with vrh->dele_chain_.. if match issuer_key
1262 // Backward: check every cred entry if match issuer key
1263 // otherwise: start at issuer and go down till match
1264 // A.a <- ...
1265 // X.x <- C
1266 // Y.y <- C
1267 // if not X.x or Y.y == A.a start at A
1268 for (del_entry = vrh->del_chain_head; del_entry != NULL;
1269 del_entry = del_entry->next)
1270 {
1271 if (0 != memcmp (&del_entry->delegate->issuer_key,
1272 &vrh->issuer_key,
1273 sizeof (struct GNUNET_CRYPTO_PublicKey)))
1274 continue;
1275 if (0 !=
1276 strcmp (del_entry->delegate->issuer_attribute, vrh->issuer_attribute))
1277 continue;
1278 del_entry->refcount++;
1279 // Found match prematurely
1280 send_lookup_response (vrh);
1281 return 1;
1282 }
1283
1284
1285 // Check for attributes from the issuer and follow the chain
1286 // till you get the required subject's attributes
1287 char issuer_attribute_name[strlen (vrh->issuer_attribute) + 1];
1288 strcpy (issuer_attribute_name, vrh->issuer_attribute);
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "Looking up %s\n",
1291 issuer_attribute_name);
1292 ds_entry = GNUNET_new (struct DelegationSetQueueEntry);
1293 GNUNET_CONTAINER_DLL_insert (vrh->dsq_head, vrh->dsq_tail, ds_entry);
1294 ds_entry->from_bw = true;
1295 ds_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_PublicKey);
1296 GNUNET_memcpy (ds_entry->issuer_key,
1297 &vrh->issuer_key,
1298 sizeof (struct GNUNET_CRYPTO_PublicKey));
1299 ds_entry->issuer_attribute = GNUNET_strdup (vrh->issuer_attribute);
1300
1301 ds_entry->delegation_chain_entry = GNUNET_new (struct DelegationChainEntry);
1302 ds_entry->delegation_chain_entry->issuer_key = vrh->issuer_key;
1303 ds_entry->delegation_chain_entry->issuer_attribute =
1304 GNUNET_strdup (vrh->issuer_attribute);
1305
1306 ds_entry->handle = vrh;
1307 ds_entry->lookup_attribute = GNUNET_strdup (vrh->issuer_attribute);
1308 ds_entry->unresolved_attribute_delegation = NULL;
1309 vrh->pending_lookups = 1;
1310
1311 // Start with backward resolution
1312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start Backward Resolution\n");
1313
1314 ds_entry->lookup_request = GNUNET_GNS_lookup (gns,
1315 issuer_attribute_name,
1316 &vrh->issuer_key, // issuer_key,
1317 GNUNET_GNSRECORD_TYPE_ATTRIBUTE,
1318 GNUNET_GNS_LO_DEFAULT,
1319 &backward_resolution,
1320 ds_entry);
1321 return 0;
1322}
1323
1324
1325static int
1326delegation_chain_fw_resolution_start (void *cls)
1327{
1328 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Start Forward Resolution...\n");
1329
1330 struct VerifyRequestHandle *vrh = cls;
1331 struct DelegationSetQueueEntry *ds_entry;
1332 struct DelegateRecordEntry *del_entry;
1333
1334 // set to 0 and increase on each lookup: for fw multiple lookups (may be) started
1335 vrh->pending_lookups = 0;
1336
1337 if (0 == vrh->del_chain_size)
1338 {
1339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No delegations found\n");
1340 send_lookup_response (vrh);
1341 return 1;
1342 }
1343
1344 // Pre-check with vrh->dele_chain_.. if match issuer_key
1345 // otherwise FW: start multiple lookups for each vrh->dele_chain
1346 // A.a <- ...
1347 // X.x <- C
1348 // Y.y <- C
1349 // if not X.x or Y.y == A.a start at X and at Y
1350 for (del_entry = vrh->del_chain_head; del_entry != NULL;
1351 del_entry = del_entry->next)
1352 {
1353 if (0 != memcmp (&del_entry->delegate->issuer_key,
1354 &vrh->issuer_key,
1355 sizeof (struct GNUNET_CRYPTO_PublicKey)))
1356 continue;
1357 if (0 !=
1358 strcmp (del_entry->delegate->issuer_attribute, vrh->issuer_attribute))
1359 continue;
1360 del_entry->refcount++;
1361 // Found match prematurely
1362 send_lookup_response (vrh);
1363 return 1;
1364 }
1365
1366 // None match, therefore start for every delegation found a lookup chain
1367 // Return and end collect process on first chain iss <-> sub found
1368
1369 // ds_entry created belongs to the first lookup, vrh still has the
1370 // issuer+attr we look for
1371 for (del_entry = vrh->del_chain_head; del_entry != NULL;
1372 del_entry = del_entry->next)
1373 {
1374
1375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1376 "Looking for %s.%s\n",
1377 GNUNET_CRYPTO_public_key_to_string (
1378 &del_entry->delegate->issuer_key),
1379 del_entry->delegate->issuer_attribute);
1380
1381 ds_entry = GNUNET_new (struct DelegationSetQueueEntry);
1382 GNUNET_CONTAINER_DLL_insert (vrh->dsq_head, vrh->dsq_tail, ds_entry);
1383 ds_entry->from_bw = false;
1384 ds_entry->issuer_key = GNUNET_new (struct GNUNET_CRYPTO_PublicKey);
1385 GNUNET_memcpy (ds_entry->issuer_key,
1386 &del_entry->delegate->subject_key,
1387 sizeof (struct GNUNET_CRYPTO_PublicKey));
1388
1389 ds_entry->delegation_chain_entry = GNUNET_new (struct DelegationChainEntry);
1390 ds_entry->delegation_chain_entry->subject_key =
1391 del_entry->delegate->subject_key;
1392 ds_entry->delegation_chain_entry->subject_attribute = NULL;
1393 ds_entry->delegation_chain_entry->issuer_key =
1394 del_entry->delegate->issuer_key;
1395 ds_entry->delegation_chain_entry->issuer_attribute =
1396 GNUNET_strdup (del_entry->delegate->issuer_attribute);
1397
1398 ds_entry->attr_trailer =
1399 GNUNET_strdup (del_entry->delegate->issuer_attribute);
1400 ds_entry->handle = vrh;
1401
1402 vrh->pending_lookups++;
1403 // Start with forward resolution
1404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start Forward Resolution\n");
1405
1406 ds_entry->lookup_request =
1407 GNUNET_GNS_lookup (gns,
1408 GNUNET_GNS_EMPTY_LABEL_AT,
1409 &del_entry->delegate->issuer_key, // issuer_key,
1410 GNUNET_GNSRECORD_TYPE_DELEGATE,
1411 GNUNET_GNS_LO_DEFAULT,
1412 &forward_resolution,
1413 ds_entry);
1414 }
1415 return 0;
1416}
1417
1418
1419static int
1420check_verify (void *cls, const struct VerifyMessage *v_msg)
1421{
1422 size_t msg_size;
1423 const char *attr;
1424
1425 msg_size = ntohs (v_msg->header.size);
1426 if (msg_size < sizeof (struct VerifyMessage))
1427 {
1428 GNUNET_break (0);
1429 return GNUNET_SYSERR;
1430 }
1431 if (ntohs (v_msg->issuer_attribute_len) > GNUNET_ABD_MAX_LENGTH)
1432 {
1433 GNUNET_break (0);
1434 return GNUNET_SYSERR;
1435 }
1436 attr = (const char *) &v_msg[1];
1437
1438 if (strlen (attr) > GNUNET_ABD_MAX_LENGTH)
1439 {
1440 GNUNET_break (0);
1441 return GNUNET_SYSERR;
1442 }
1443 return GNUNET_OK;
1444}
1445
1446
1447static void
1448handle_verify (void *cls, const struct VerifyMessage *v_msg)
1449{
1450 struct VerifyRequestHandle *vrh;
1451 struct GNUNET_SERVICE_Client *client = cls;
1452 struct DelegateRecordEntry *del_entry;
1453 uint32_t delegate_count;
1454 uint32_t delegate_data_size;
1455 char attr[GNUNET_ABD_MAX_LENGTH + 1];
1456 char issuer_attribute[GNUNET_ABD_MAX_LENGTH + 1];
1457 char *attrptr = attr;
1458 char *delegate_data;
1459 const char *utf_in;
1460
1461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received VERIFY message\n");
1462 utf_in = (const char *) &v_msg[1];
1463 GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
1464 GNUNET_memcpy (issuer_attribute, attr, ntohs (v_msg->issuer_attribute_len));
1465 issuer_attribute[ntohs (v_msg->issuer_attribute_len)] = '\0';
1466 vrh = GNUNET_new (struct VerifyRequestHandle);
1467 vrh->is_collect = false;
1468 GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
1469 vrh->client = client;
1470 vrh->request_id = v_msg->id;
1471 vrh->issuer_key = v_msg->issuer_key;
1472 vrh->subject_key = v_msg->subject_key;
1473 vrh->issuer_attribute = GNUNET_strdup (issuer_attribute);
1474 vrh->resolution_algo = ntohs (v_msg->resolution_algo);
1475
1476 vrh->del_chain_head = NULL;
1477 vrh->del_chain_tail = NULL;
1478 vrh->dsq_head = NULL;
1479 vrh->dsq_tail = NULL;
1480 vrh->del_chain_head = NULL;
1481 vrh->del_chain_tail = NULL;
1482
1483 GNUNET_SERVICE_client_continue (vrh->client);
1484 if (0 == strlen (issuer_attribute))
1485 {
1486 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No issuer attribute provided!\n");
1487 send_lookup_response (vrh);
1488 return;
1489 }
1490
1491 // Parse delegates from verifaction message
1492 delegate_count = ntohl (v_msg->d_count);
1493 delegate_data_size = ntohs (v_msg->header.size)
1494 - sizeof (struct VerifyMessage)
1495 - ntohs (v_msg->issuer_attribute_len) - 1;
1496 struct GNUNET_ABD_Delegate delegates[delegate_count];
1497 memset (delegates,
1498 0,
1499 sizeof (struct GNUNET_ABD_Delegate) * delegate_count);
1500 delegate_data = (char *) &v_msg[1] + ntohs (v_msg->issuer_attribute_len) + 1;
1501 if (GNUNET_OK != GNUNET_ABD_delegates_deserialize (delegate_data_size,
1502 delegate_data,
1503 delegate_count,
1504 delegates))
1505 {
1506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot deserialize delegates!\n");
1507 send_lookup_response (vrh);
1508 return;
1509 }
1510
1511 // Prepare vrh delegation chain for later validation
1512 for (uint32_t i = 0; i < delegate_count; i++)
1513 {
1514 del_entry = GNUNET_new (struct DelegateRecordEntry);
1515 del_entry->delegate =
1516 GNUNET_malloc (sizeof (struct GNUNET_ABD_Delegate)
1517 + delegates[i].issuer_attribute_len + 1);
1518 GNUNET_memcpy (del_entry->delegate,
1519 &delegates[i],
1520 sizeof (struct GNUNET_ABD_Delegate));
1521 GNUNET_memcpy (&del_entry->delegate[1],
1522 delegates[i].issuer_attribute,
1523 delegates[i].issuer_attribute_len);
1524 del_entry->delegate->issuer_attribute_len =
1525 delegates[i].issuer_attribute_len;
1526 del_entry->delegate->issuer_attribute = (char *) &del_entry->delegate[1];
1527 GNUNET_CONTAINER_DLL_insert_tail (vrh->del_chain_head,
1528 vrh->del_chain_tail,
1529 del_entry);
1530 vrh->del_chain_size++;
1531 }
1532
1533 // Switch resolution algo
1534 if (GNUNET_ABD_FLAG_BACKWARD & vrh->resolution_algo &&
1535 GNUNET_ABD_FLAG_FORWARD & vrh->resolution_algo)
1536 {
1537 if (1 == delegation_chain_fw_resolution_start (vrh))
1538 return;
1539 delegation_chain_bw_resolution_start (vrh);
1540 }
1541 else if (GNUNET_ABD_FLAG_BACKWARD & vrh->resolution_algo)
1542 {
1543 delegation_chain_bw_resolution_start (vrh);
1544 }
1545 else if (GNUNET_ABD_FLAG_FORWARD & vrh->resolution_algo)
1546 {
1547 delegation_chain_fw_resolution_start (vrh);
1548 }
1549}
1550
1551
1552static void
1553handle_delegate_collection_error_cb (void *cls)
1554{
1555 struct VerifyRequestHandle *vrh = cls;
1556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1557 "Got disconnected from namestore database.\n");
1558 vrh->dele_qe = NULL;
1559 send_lookup_response (vrh);
1560}
1561
1562
1563static void
1564delegate_collection_finished (void *cls)
1565{
1566 struct VerifyRequestHandle *vrh = cls;
1567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done collecting delegates.\n");
1568
1569 // if both are set: bidirectional search, meaning start both chain resolutions
1570 if (GNUNET_ABD_FLAG_BACKWARD & vrh->resolution_algo &&
1571 GNUNET_ABD_FLAG_FORWARD & vrh->resolution_algo)
1572 {
1573 // if premature match found don't start bw resolution
1574 if (1 == delegation_chain_fw_resolution_start (vrh))
1575 return;
1576 delegation_chain_bw_resolution_start (vrh);
1577 }
1578 else if (GNUNET_ABD_FLAG_BACKWARD & vrh->resolution_algo)
1579 {
1580 delegation_chain_bw_resolution_start (vrh);
1581 }
1582 else if (GNUNET_ABD_FLAG_FORWARD & vrh->resolution_algo)
1583 {
1584 delegation_chain_fw_resolution_start (vrh);
1585 }
1586}
1587
1588
1589static void
1590handle_delegate_collection_cb (void *cls,
1591 const struct GNUNET_CRYPTO_PrivateKey *key,
1592 const char *label,
1593 unsigned int rd_count,
1594 const struct GNUNET_GNSRECORD_Data *rd)
1595{
1596 struct VerifyRequestHandle *vrh = cls;
1597 struct DelegateRecordEntry *del_entry;
1598 vrh->dele_qe = NULL;
1599
1600 for (uint32_t i = 0; i < rd_count; i++)
1601 {
1602 if (GNUNET_GNSRECORD_TYPE_DELEGATE != rd[i].record_type)
1603 continue;
1604 // only add the entries that are explicitly marked as private
1605 // and therefore symbolize the end of a chain
1606 if (0 == (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
1607 continue;
1608 del_entry = GNUNET_new (struct DelegateRecordEntry);
1609 del_entry->delegate = GNUNET_ABD_delegate_deserialize (rd[i].data, rd[i].data_size);
1610 if (NULL == del_entry->delegate)
1611 {
1612 GNUNET_free (del_entry);
1613 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid delegate found\n");
1614 continue;
1615 }
1616 GNUNET_CONTAINER_DLL_insert_tail (vrh->del_chain_head,
1617 vrh->del_chain_tail,
1618 del_entry);
1619 vrh->del_chain_size++;
1620 }
1621
1622 delegate_collection_finished (vrh);
1623}
1624
1625
1626static void
1627handle_collect (void *cls, const struct CollectMessage *c_msg)
1628{
1629 char attr[GNUNET_ABD_MAX_LENGTH + 1];
1630 char issuer_attribute[GNUNET_ABD_MAX_LENGTH + 1];
1631 struct VerifyRequestHandle *vrh;
1632 struct GNUNET_SERVICE_Client *client = cls;
1633 char *attrptr = attr;
1634 const char *utf_in;
1635
1636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received COLLECT message\n");
1637
1638 utf_in = (const char *) &c_msg[1];
1639 GNUNET_STRINGS_utf8_tolower (utf_in, attrptr);
1640
1641 GNUNET_memcpy (issuer_attribute, attr, ntohs (c_msg->issuer_attribute_len));
1642 issuer_attribute[ntohs (c_msg->issuer_attribute_len)] = '\0';
1643 vrh = GNUNET_new (struct VerifyRequestHandle);
1644 vrh->is_collect = true;
1645 GNUNET_CONTAINER_DLL_insert (vrh_head, vrh_tail, vrh);
1646 vrh->client = client;
1647 vrh->request_id = c_msg->id;
1648 vrh->issuer_key = c_msg->issuer_key;
1649 GNUNET_CRYPTO_key_get_public (&c_msg->subject_key, &vrh->subject_key);
1650 vrh->issuer_attribute = GNUNET_strdup (issuer_attribute);
1651 vrh->resolution_algo = ntohs (c_msg->resolution_algo);
1652
1653 vrh->del_chain_head = NULL;
1654 vrh->del_chain_tail = NULL;
1655 vrh->dsq_head = NULL;
1656 vrh->dsq_tail = NULL;
1657 vrh->del_chain_head = NULL;
1658 vrh->del_chain_tail = NULL;
1659
1660 if (0 == strlen (issuer_attribute))
1661 {
1662 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No issuer attribute provided!\n");
1663 send_lookup_response (vrh);
1664 return;
1665 }
1666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting delegates for subject\n");
1667
1668 // Get all delegates from subject
1669 vrh->dele_qe =
1670 GNUNET_NAMESTORE_records_lookup (namestore,
1671 &c_msg->subject_key,
1672 GNUNET_GNS_EMPTY_LABEL_AT,
1673 &handle_delegate_collection_error_cb,
1674 vrh,
1675 &handle_delegate_collection_cb,
1676 vrh);
1677 GNUNET_SERVICE_client_continue (vrh->client);
1678}
1679
1680
1681static int
1682check_collect (void *cls, const struct CollectMessage *c_msg)
1683{
1684 size_t msg_size;
1685 const char *attr;
1686
1687 msg_size = ntohs (c_msg->header.size);
1688 if (msg_size < sizeof (struct CollectMessage))
1689 {
1690 GNUNET_break (0);
1691 return GNUNET_SYSERR;
1692 }
1693 if (ntohs (c_msg->issuer_attribute_len) > GNUNET_ABD_MAX_LENGTH)
1694 {
1695 GNUNET_break (0);
1696 return GNUNET_SYSERR;
1697 }
1698 attr = (const char *) &c_msg[1];
1699
1700 if (('\0' != attr[msg_size - sizeof (struct CollectMessage) - 1]) ||
1701 (strlen (attr) > GNUNET_ABD_MAX_LENGTH))
1702 {
1703 GNUNET_break (0);
1704 return GNUNET_SYSERR;
1705 }
1706 return GNUNET_OK;
1707}
1708
1709
1710static void
1711client_disconnect_cb (void *cls,
1712 struct GNUNET_SERVICE_Client *client,
1713 void *app_ctx)
1714{
1715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected\n", client);
1716}
1717
1718
1719static void *
1720client_connect_cb (void *cls,
1721 struct GNUNET_SERVICE_Client *client,
1722 struct GNUNET_MQ_Handle *mq)
1723{
1724 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected\n", client);
1725 return client;
1726}
1727
1728
1729/**
1730 * Process Credential requests.
1731 *
1732 * @param cls closure
1733 * @param c configuration to use
1734 * @param handle service handle
1735 */
1736static void
1737run (void *cls,
1738 const struct GNUNET_CONFIGURATION_Handle *c,
1739 struct GNUNET_SERVICE_Handle *handle)
1740{
1741
1742 gns = GNUNET_GNS_connect (c);
1743 if (NULL == gns)
1744 {
1745 fprintf (stderr, _ ("Failed to connect to GNS\n"));
1746 }
1747 namestore = GNUNET_NAMESTORE_connect (c);
1748 if (NULL == namestore)
1749 {
1750 fprintf (stderr, _ ("Failed to connect to namestore\n"));
1751 }
1752
1753 statistics = GNUNET_STATISTICS_create ("abd", c);
1754 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1755}
1756
1757
1758/**
1759 * Define "main" method using service macro
1760 */
1761GNUNET_SERVICE_MAIN (
1762 "abd",
1763 GNUNET_SERVICE_OPTION_NONE,
1764 &run,
1765 &client_connect_cb,
1766 &client_disconnect_cb,
1767 NULL,
1768 GNUNET_MQ_hd_var_size (verify,
1769 GNUNET_MESSAGE_TYPE_ABD_VERIFY,
1770 struct VerifyMessage,
1771 NULL),
1772 GNUNET_MQ_hd_var_size (collect,
1773 GNUNET_MESSAGE_TYPE_ABD_COLLECT,
1774 struct CollectMessage,
1775 NULL),
1776 GNUNET_MQ_handler_end ());
1777
1778/* end of gnunet-service-abd.c */
diff --git a/src/contrib/service/abd/plugin_gnsrecord_abd.c b/src/contrib/service/abd/plugin_gnsrecord_abd.c
new file mode 100644
index 000000000..43abe578f
--- /dev/null
+++ b/src/contrib/service/abd/plugin_gnsrecord_abd.c
@@ -0,0 +1,357 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19*/
20
21/**
22 * @file abd/plugin_gnsrecord_abd.c
23 * @brief gnsrecord plugin to provide the API for ABD records
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27
28#include "gnunet_util_lib.h"
29
30#include "delegate_misc.h"
31#include "abd_serialization.h"
32#include "gnunet_abd_service.h"
33#include "gnunet_gnsrecord_lib.h"
34#include "gnunet_gnsrecord_plugin.h"
35#include "gnunet_signatures.h"
36/**
37 * Convert the 'value' of a record to a string.
38 *
39 * @param cls closure, unused
40 * @param type type of the record
41 * @param data value in binary encoding
42 * @param data_size number of bytes in @a data
43 * @return NULL on error, otherwise human-readable representation of the value
44 */
45static char *
46abd_value_to_string (void *cls,
47 uint32_t type,
48 const void *data,
49 size_t data_size)
50{
51 const char *cdata;
52
53 switch (type)
54 {
55 case GNUNET_GNSRECORD_TYPE_ATTRIBUTE:
56 {
57 struct GNUNET_ABD_DelegationRecord sets;
58 char *attr_str;
59 char *subject_pkey;
60 char *tmp_str;
61 int i;
62 if (data_size < sizeof (struct GNUNET_ABD_DelegationRecord))
63 return NULL; /* malformed */
64
65 GNUNET_memcpy (&sets, data, sizeof (sets));
66 cdata = data;
67
68 struct GNUNET_ABD_DelegationSet set[ntohl (sets.set_count)];
69 if (GNUNET_OK !=
70 GNUNET_ABD_delegation_set_deserialize (GNUNET_ntohll (
71 sets.data_size),
72 &cdata[sizeof (sets)],
73 ntohl (sets.set_count),
74 set))
75 return NULL;
76
77 for (i = 0; i < ntohl (sets.set_count); i++)
78 {
79 subject_pkey =
80 GNUNET_CRYPTO_public_key_to_string (&set[i].subject_key);
81
82 if (0 == set[i].subject_attribute_len)
83 {
84 if (0 == i)
85 {
86 GNUNET_asprintf (&attr_str, "%s", subject_pkey);
87 }
88 else
89 {
90 GNUNET_asprintf (&tmp_str, "%s,%s", attr_str, subject_pkey);
91 GNUNET_free (attr_str);
92 attr_str = tmp_str;
93 }
94 }
95 else
96 {
97 if (0 == i)
98 {
99 GNUNET_asprintf (&attr_str,
100 "%s %s",
101 subject_pkey,
102 set[i].subject_attribute);
103 }
104 else
105 {
106 GNUNET_asprintf (&tmp_str,
107 "%s,%s %s",
108 attr_str,
109 subject_pkey,
110 set[i].subject_attribute);
111 GNUNET_free (attr_str);
112 attr_str = tmp_str;
113 }
114 }
115 GNUNET_free (subject_pkey);
116 }
117 return attr_str;
118 }
119 case GNUNET_GNSRECORD_TYPE_DELEGATE:
120 {
121 struct GNUNET_ABD_Delegate *cred;
122 char *cred_str;
123
124 cred = GNUNET_ABD_delegate_deserialize (data, data_size);
125 cred_str = GNUNET_ABD_delegate_to_string (cred);
126 GNUNET_free (cred);
127 return cred_str;
128 }
129 default:
130 return NULL;
131 }
132}
133
134
135/**
136 * Convert human-readable version of a 'value' of a record to the binary
137 * representation.
138 *
139 * @param cls closure, unused
140 * @param type type of the record
141 * @param s human-readable string
142 * @param data set to value in binary encoding (will be allocated)
143 * @param data_size set to number of bytes in @a data
144 * @return #GNUNET_OK on success
145 */
146static int
147abd_string_to_value (void *cls,
148 uint32_t type,
149 const char *s,
150 void **data,
151 size_t *data_size)
152{
153 if (NULL == s)
154 return GNUNET_SYSERR;
155 switch (type)
156 {
157 case GNUNET_GNSRECORD_TYPE_ATTRIBUTE:
158 {
159 struct GNUNET_ABD_DelegationRecord *sets;
160 char attr_str[253 + 1];
161 char subject_pkey[58 + 1];
162 char *token;
163 char *tmp_str;
164 int matches = 0;
165 int entries;
166 size_t tmp_data_size;
167 int i;
168
169 tmp_str = GNUNET_strdup (s);
170 token = strtok (tmp_str, ",");
171 entries = 0;
172 tmp_data_size = 0;
173 *data_size = sizeof (struct GNUNET_ABD_DelegationRecord);
174 while (NULL != token)
175 {
176 // also fills the variables subject_pley and attr_str if "regex"-like match
177 matches = sscanf (token, "%s %s", subject_pkey, attr_str);
178
179 if (0 == matches)
180 {
181 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
182 _ ("Unable to parse ATTR record string `%s'\n"),
183 s);
184 GNUNET_free (tmp_str);
185 return GNUNET_SYSERR;
186 }
187
188 entries++;
189 token = strtok (NULL, ",");
190 }
191 GNUNET_free (tmp_str);
192
193 tmp_str = GNUNET_strdup (s);
194 token = strtok (tmp_str, ",");
195 if (NULL == token)
196 {
197 GNUNET_free (tmp_str);
198 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed string %s\n", s);
199 return GNUNET_SYSERR;
200 }
201
202 struct GNUNET_ABD_DelegationSet set[entries];
203 // sets memory to be 0, starting at *set for the size of struct * entries
204 memset (set, 0, sizeof (struct GNUNET_ABD_DelegationSet) * entries);
205 for (i = 0; i < entries; i++)
206 {
207 matches = sscanf (token, "%s %s", subject_pkey, attr_str);
208
209 // sets the public key for the set entry
210 if (GNUNET_SYSERR ==
211 GNUNET_CRYPTO_public_key_from_string (subject_pkey,
212 &set[i].subject_key))
213 {
214 GNUNET_free (tmp_str);
215 return GNUNET_SYSERR;
216 }
217
218 // If not just key, also set subject attribute (Not A.a <- B but A.a <- B.b)
219 if (2 == matches)
220 {
221 set[i].subject_attribute_len = strlen (attr_str) + 1;
222 set[i].subject_attribute = GNUNET_strdup (attr_str);
223 }
224 // If more entries, then token string can take the next entry (separated by ',') by calling strtok again
225 token = strtok (NULL, ",");
226 }
227 tmp_data_size = GNUNET_ABD_delegation_set_get_size (entries, set);
228
229 if (-1 == tmp_data_size)
230 {
231 GNUNET_free (tmp_str);
232 return GNUNET_SYSERR;
233 }
234 *data_size += tmp_data_size;
235 *data = sets = GNUNET_malloc (*data_size);
236 GNUNET_ABD_delegation_set_serialize (entries,
237 set,
238 tmp_data_size,
239 (char *) &sets[1]);
240 for (i = 0; i < entries; i++)
241 {
242 if (0 != set[i].subject_attribute_len)
243 {
244 GNUNET_free_nz ((char *) set[i].subject_attribute);
245 set[i].subject_attribute = NULL;
246 }
247 }
248 sets->set_count = htonl (entries);
249 sets->data_size = GNUNET_htonll (tmp_data_size);
250
251 GNUNET_free (tmp_str);
252 return GNUNET_OK;
253 }
254 case GNUNET_GNSRECORD_TYPE_DELEGATE:
255 {
256 struct GNUNET_ABD_Delegate *cred;
257 cred = GNUNET_ABD_delegate_from_string (s);
258
259 *data_size = GNUNET_ABD_delegate_serialize (cred, (char **) data);
260 GNUNET_free (cred);
261 return GNUNET_OK;
262 }
263 default:
264 return GNUNET_SYSERR;
265 }
266}
267
268
269/**
270 * Mapping of record type numbers to human-readable
271 * record type names.
272 */
273static struct
274{
275 const char *name;
276 uint32_t number;
277} name_map[] = {{"ATTR", GNUNET_GNSRECORD_TYPE_ATTRIBUTE},
278 {"DEL", GNUNET_GNSRECORD_TYPE_DELEGATE},
279 {NULL, UINT32_MAX}};
280
281
282/**
283 * Convert a type name (e.g. "AAAA") to the corresponding number.
284 *
285 * @param cls closure, unused
286 * @param gns_typename name to convert
287 * @return corresponding number, UINT32_MAX on error
288 */
289static uint32_t
290abd_typename_to_number (void *cls, const char *gns_typename)
291{
292 unsigned int i;
293
294 i = 0;
295 while ((name_map[i].name != NULL) &&
296 (0 != strcasecmp (gns_typename, name_map[i].name)))
297 i++;
298 return name_map[i].number;
299}
300
301
302/**
303 * Convert a type number to the corresponding type string (e.g. 1 to "A")
304 *
305 * @param cls closure, unused
306 * @param type number of a type to convert
307 * @return corresponding typestring, NULL on error
308 */
309static const char *
310abd_number_to_typename (void *cls, uint32_t type)
311{
312 unsigned int i;
313
314 i = 0;
315 while ((name_map[i].name != NULL) && (type != name_map[i].number))
316 i++;
317 return name_map[i].name;
318}
319
320
321/**
322 * Entry point for the plugin.
323 *
324 * @param cls NULL
325 * @return the exported block API
326 */
327void *
328libgnunet_plugin_gnsrecord_abd_init (void *cls)
329{
330 struct GNUNET_GNSRECORD_PluginFunctions *api;
331
332 api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
333 api->value_to_string = &abd_value_to_string;
334 api->string_to_value = &abd_string_to_value;
335 api->typename_to_number = &abd_typename_to_number;
336 api->number_to_typename = &abd_number_to_typename;
337 return api;
338}
339
340
341/**
342 * Exit point from the plugin.
343 *
344 * @param cls the return value from #libgnunet_plugin_block_test_init
345 * @return NULL
346 */
347void *
348libgnunet_plugin_gnsrecord_abd_done (void *cls)
349{
350 struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
351
352 GNUNET_free (api);
353 return NULL;
354}
355
356
357/* end of plugin_gnsrecord_abd.c */
diff --git a/src/contrib/service/abd/test_abd_bi_and.sh b/src/contrib/service/abd/test_abd_bi_and.sh
new file mode 100755
index 000000000..99c89a344
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_and.sh
@@ -0,0 +1,98 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31gnunet-identity -C h -c test_abd_lookup.conf
32AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
33BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
34CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
35DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
36EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
37FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
38GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
39HKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep h | awk '{print $3}')
40gnunet-identity -d
41
42# (1) (A.a) <- B.b
43# (2) (B.b) <- C.c AND G.g
44# (3) C.c <- (D.D)
45# (4) D.d <- (E.e)
46# (5) E.e <- (F) priv
47# (6) (G.g) <- H.h
48# (7) H.h <- (F) priv
49
50# BIDIRECTIONAL
51gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
52gnunet-namestore -D -z a
53gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c, $GKEY g" --ttl=5m -c test_abd_lookup.conf
54gnunet-namestore -D -z b
55gnunet-abd --createIssuerSide --ego=g --attribute="g" --subject="$HKEY h" --ttl=5m -c test_abd_lookup.conf
56gnunet-namestore -D -z b
57
58SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2049-12-12 10:00:00"`
59gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
60gnunet-namestore -D -z d
61SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2049-12-12 10:00:00"`
62gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
63gnunet-namestore -D -z e
64SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2049-12-12 10:00:00"`
65gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
66gnunet-namestore -D -z f
67SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=h --attribute="h" --subject="$FKEY" --ttl="2049-12-12 10:00:00"`
68gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
69gnunet-namestore -D -z h
70
71# Starting to resolve
72echo "+++ Starting to Resolve +++"
73
74DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f --forward --backward -c test_abd_lookup.conf | paste -d, -s - -`
75echo $DELS
76echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' --forward --backward -c test_abd_lookup.conf
77gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" --forward --backward -c test_abd_lookup.conf
78
79RES=$?
80
81# Cleanup properly
82gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
83gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
84gnunet-namestore -z g -d -n "g" -t ATTR -c test_abd_lookup.conf
85gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
86gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
87gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
88
89gnunet-arm -e -c test_abd_lookup.conf
90
91if [ "$RES" == 0 ]
92then
93 exit 0
94else
95 echo "FAIL: Failed to verify credential."
96 exit 1
97fi
98
diff --git a/src/contrib/service/abd/test_abd_bi_and2.sh b/src/contrib/service/abd/test_abd_bi_and2.sh
new file mode 100755
index 000000000..8d1601ef4
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_and2.sh
@@ -0,0 +1,94 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31gnunet-identity -C h -c test_abd_lookup.conf
32AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
33BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
34CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
35DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
36EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
37FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
38GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
39HKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep h | awk '{print $3}')
40gnunet-identity -d
41
42# (1) (A.a) <- B.b
43# (2) (B.b) <- C.c AND G.g
44# (3) C.c <- (D.D)
45# (4) D.d <- (E.e)
46# (5) E.e <- (F) priv
47# (6) G.g <- (F) priv
48
49# BIDIRECTIONAL
50gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z a
52gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c, $GKEY g" --ttl=5m -c test_abd_lookup.conf
53gnunet-namestore -D -z b
54
55SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
56gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
57gnunet-namestore -D -z d
58SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
59gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
60gnunet-namestore -D -z e
61SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
62gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
63gnunet-namestore -D -z f
64SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=g --attribute="g" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
65gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
66gnunet-namestore -D -z h
67
68# Starting to resolve
69echo "+++ Starting to Resolve +++"
70
71DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f -c test_abd_lookup.conf | paste -d, -s - -`
72echo $DELS
73echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' -c test_abd_lookup.conf
74gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" -c test_abd_lookup.conf
75
76RES=$?
77
78# Cleanup properly
79gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
80gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
81gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
82gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
83gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
84
85gnunet-arm -e -c test_abd_lookup.conf
86
87if [ "$RES" == 0 ]
88then
89 exit 0
90else
91 echo "FAIL: Failed to verify credential."
92 exit 1
93fi
94
diff --git a/src/contrib/service/abd/test_abd_bi_and3.sh b/src/contrib/service/abd/test_abd_bi_and3.sh
new file mode 100755
index 000000000..cde2a020b
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_and3.sh
@@ -0,0 +1,96 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
21gnunet-arm -s -c test_abd_lookup.conf
22
23gnunet-identity -C a -c test_abd_lookup.conf
24gnunet-identity -C b -c test_abd_lookup.conf
25gnunet-identity -C c -c test_abd_lookup.conf
26gnunet-identity -C d -c test_abd_lookup.conf
27gnunet-identity -C e -c test_abd_lookup.conf
28gnunet-identity -C f -c test_abd_lookup.conf
29gnunet-identity -C g -c test_abd_lookup.conf
30gnunet-identity -C h -c test_abd_lookup.conf
31AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
32BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
33CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
34DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
35EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
36FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
37GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
38HKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep h | awk '{print $3}')
39gnunet-identity -d
40
41# (1) (A.a) <- B.b
42# (2) (B.b) <- C.c AND G.g
43# (3) C.c <- (D.d)
44# (4) D.d <- (E.e)
45# (5) E.e <- (F) priv
46# (6) G.g <- (H.h)
47# (7) H.h <- (F) priv
48
49# BIDIRECTIONAL
50gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z a
52gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c, $GKEY g" --ttl=5m -c test_abd_lookup.conf
53gnunet-namestore -D -z b
54
55SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
56gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
57gnunet-namestore -D -z d
58SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
59gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
60gnunet-namestore -D -z e
61SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=g --attribute="g" --subject="$HKEY h" --ttl="2019-12-12 10:00:00"`
62gnunet-abd --createSubjectSide --ego=h --import="$SIGNED"
63gnunet-namestore -D -z h
64SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
65gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
66SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=h --attribute="h" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
67gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
68gnunet-namestore -D -z f
69
70# Starting to resolve
71echo "+++ Starting to Resolve +++"
72
73DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f -c test_abd_lookup.conf | paste -d, -s - -`
74echo $DELS
75echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' -c test_abd_lookup.conf
76gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" -c test_abd_lookup.conf
77
78RES=$?
79
80# Cleanup properly
81gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
82gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
83gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
84gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
85gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
86gnunet-namestore -z h -d -n "@" -t DEL -c test_abd_lookup.conf
87
88gnunet-arm -e -c test_abd_lookup.conf
89
90if [ "$RES" == 0 ]
91then
92 exit 0
93else
94 echo "FAIL: Failed to verify credential."
95 exit 1
96fi
diff --git a/src/contrib/service/abd/test_abd_bi_and4.sh b/src/contrib/service/abd/test_abd_bi_and4.sh
new file mode 100755
index 000000000..e8cd6efd6
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_and4.sh
@@ -0,0 +1,83 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31gnunet-identity -C h -c test_abd_lookup.conf
32AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
33BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
34CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
35DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
36EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
37FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
38GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
39HKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep h | awk '{print $3}')
40gnunet-identity -d
41
42# (1) (A.a) <- B.b
43# (2) (B.b) <- C.c AND G.g
44# (3) C.c <- (F) priv
45# (4) G.g <- (F) priv
46
47# BIDIRECTIONAL
48gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
49gnunet-namestore -D -z a
50gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c, $GKEY g" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z b
52
53SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=g --attribute="g" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
54gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
55SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
56gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
57gnunet-namestore -D -z f
58
59# Starting to resolve
60echo "+++ Starting to Resolve +++"
61
62DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f --backward -c test_abd_lookup.conf | paste -d, -s - -`
63echo $DELS
64echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' --backward -c test_abd_lookup.conf
65gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" --backward -c test_abd_lookup.conf
66
67RES=$?
68
69# Cleanup properly
70gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
71gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
72gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
73
74gnunet-arm -e -c test_abd_lookup.conf
75
76if [ "$RES" == 0 ]
77then
78 exit 0
79else
80 echo "FAIL: Failed to verify credential."
81 exit 1
82fi
83
diff --git a/src/contrib/service/abd/test_abd_bi_bw.sh b/src/contrib/service/abd/test_abd_bi_bw.sh
new file mode 100755
index 000000000..6b0e51722
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_bw.sh
@@ -0,0 +1,87 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
32BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
33CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
34DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
35EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
36FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
37GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
38gnunet-identity -d
39
40# (1) (A.a) <- B.b
41# (2) (B.b) <- C.c
42# (3) C.c <- (D.D)
43# (4) D.d <- (E.e)
44# (5) E.e <- (F)
45
46# BIDIRECTIONAL
47gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
48gnunet-namestore -D -z a
49gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c" --ttl=5m -c test_abd_lookup.conf
50gnunet-namestore -D -z b
51
52SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
53gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
54gnunet-namestore -D -z d
55SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
56gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
57gnunet-namestore -D -z e
58SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
59gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
60gnunet-namestore -D -z f
61
62# Starting to resolve
63echo "+++ Starting to Resolve +++"
64
65DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f --forward --backward -c test_abd_lookup.conf | paste -d, -s - -`
66echo $DELS
67echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' --forward --backward -c test_abd_lookup.conf
68gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" --forward --backward -c test_abd_lookup.conf
69
70RES=$?
71
72# Cleanup properly
73gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
74gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
75gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
76gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
77gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
78
79gnunet-arm -e -c test_abd_lookup.conf
80
81if [ "$RES" == 0 ]
82then
83 exit 0
84else
85 echo "FAIL: Failed to verify credential."
86 exit 1
87fi
diff --git a/src/contrib/service/abd/test_abd_bi_bw_link.sh b/src/contrib/service/abd/test_abd_bi_bw_link.sh
new file mode 100755
index 000000000..f60f78827
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_bw_link.sh
@@ -0,0 +1,92 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
32BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
33CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
34DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
35EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
36FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
37GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
38gnunet-identity -d
39
40# (1) (A.a) <- B.b
41# (2) (B.b) <- G.g.c
42# (3) (G.g) <- C
43# (3) C.c <- (D.D)
44# (4) D.d <- (E.e)
45# (5) E.e <- (F)
46
47# BIDIRECTIONAL
48gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
49gnunet-namestore -D -z a
50gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$GKEY g.c" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z b
52gnunet-abd --createIssuerSide --ego=g --attribute="g" --subject="$CKEY" --ttl=5m -c test_abd_lookup.conf
53gnunet-namestore -D -z b
54
55SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
56gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
57gnunet-namestore -D -z d
58SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
59gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
60gnunet-namestore -D -z e
61SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
62gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
63gnunet-namestore -D -z f
64
65# Starting to resolve
66echo "+++ Starting to Resolve +++"
67
68DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f --forward --backward -c test_abd_lookup.conf | paste -d, -s - -`
69echo $DELS
70echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' --forward --backward -c test_abd_lookup.conf
71gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" --forward --backward -c test_abd_lookup.conf
72
73RES=$?
74
75# Cleanup properly
76gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
77gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
78gnunet-namestore -z g -d -n "g" -t ATTR -c test_abd_lookup.conf
79gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
80gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
81gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
82
83gnunet-arm -e -c test_abd_lookup.conf
84
85if [ "$RES" == 0 ]
86then
87 exit 0
88else
89 echo "FAIL: Failed to verify credential."
90 exit 1
91fi
92
diff --git a/src/contrib/service/abd/test_abd_bi_bw_link2.sh b/src/contrib/service/abd/test_abd_bi_bw_link2.sh
new file mode 100755
index 000000000..b0fb49b8e
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_bw_link2.sh
@@ -0,0 +1,93 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
32BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
33CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
34DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
35EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
36FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
37GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
38gnunet-identity -d
39
40# (1) (A.a) <- B.b
41# (2) (B.b) <- G.g.c
42# (3) G.g <- (C)
43# (3) C.c <- (D.d)
44# (4) D.d <- (E.e)
45# (5) E.e <- (F)
46
47# BIDIRECTIONAL
48gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
49gnunet-namestore -D -z a
50gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$GKEY g.c" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z b
52
53SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=g --attribute="g" --subject="$CKEY" --ttl="2019-12-12 10:00:00"`
54gnunet-abd --createSubjectSide --ego=c --import="$SIGNED"
55gnunet-namestore -D -z c
56SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
57gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
58gnunet-namestore -D -z d
59SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
60gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
61gnunet-namestore -D -z e
62SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY" --ttl="2019-12-12 10:00:00"`
63gnunet-abd --createSubjectSide --ego=f --import="$SIGNED" --private
64gnunet-namestore -D -z f
65
66# Starting to resolve
67echo "+++ Starting to Resolve +++"
68
69DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=f --forward --backward -c test_abd_lookup.conf | paste -d, -s - -`
70echo $DELS
71echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate=\'$DELS\' --forward --backward -c test_abd_lookup.conf
72gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$FKEY --delegate="$DELS" --forward --backward -c test_abd_lookup.conf
73
74RES=$?
75
76# Cleanup properly
77gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
78gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
79gnunet-namestore -z c -d -n "@" -t DEL -c test_abd_lookup.conf
80gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
81gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
82gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
83
84gnunet-arm -e -c test_abd_lookup.conf
85
86if [ "$RES" == 0 ]
87then
88 exit 0
89else
90 echo "FAIL: Failed to verify credential."
91 exit 1
92fi
93
diff --git a/src/contrib/service/abd/test_abd_bi_fw.sh b/src/contrib/service/abd/test_abd_bi_fw.sh
new file mode 100755
index 000000000..75a940fbe
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_bi_fw.sh
@@ -0,0 +1,92 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
22gnunet-arm -s -c test_abd_lookup.conf
23
24gnunet-identity -C a -c test_abd_lookup.conf
25gnunet-identity -C b -c test_abd_lookup.conf
26gnunet-identity -C c -c test_abd_lookup.conf
27gnunet-identity -C d -c test_abd_lookup.conf
28gnunet-identity -C e -c test_abd_lookup.conf
29gnunet-identity -C f -c test_abd_lookup.conf
30gnunet-identity -C g -c test_abd_lookup.conf
31AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
32BKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep b | awk '{print $3}')
33CKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep c | awk '{print $3}')
34DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
35EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
36FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
37GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
38gnunet-identity -d
39
40# (1) (A.a) <- B.b
41# (2) (B.b) <- C.c
42# (3) C.c <- (D.D)
43# (4) D.d <- (E.e)
44# (5) E.e <- (F.f)
45# (6) F.f <- (G)
46
47# BIDIRECTIONAL
48gnunet-abd --createIssuerSide --ego=a --attribute="a" --subject="$BKEY b" --ttl=5m -c test_abd_lookup.conf
49gnunet-namestore -D -z a
50gnunet-abd --createIssuerSide --ego=b --attribute="b" --subject="$CKEY c" --ttl=5m -c test_abd_lookup.conf
51gnunet-namestore -D -z b
52
53SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=c --attribute="c" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
54gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
55gnunet-namestore -D -z d
56SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY e" --ttl="2019-12-12 10:00:00"`
57gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
58gnunet-namestore -D -z e
59SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="e" --subject="$FKEY f" --ttl="2019-12-12 10:00:00"`
60gnunet-abd --createSubjectSide --ego=f --import="$SIGNED"
61gnunet-namestore -D -z f
62SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=f --attribute="f" --subject="$GKEY" --ttl="2019-12-12 10:00:00"`
63gnunet-abd --createSubjectSide --ego=g --import="$SIGNED" --private
64gnunet-namestore -D -z g
65
66# Starting to resolve
67echo "+++ Starting to Resolve +++"
68
69DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=g --forward --backward -c test_abd_lookup.conf | paste -d, -s - -`
70echo $DELS
71echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$GKEY --delegate=\'$DELS\' --forward --backward -c test_abd_lookup.conf
72gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$GKEY --delegate="$DELS" --forward --backward -c test_abd_lookup.conf
73
74RES=$?
75
76# Cleanup properly
77gnunet-namestore -z a -d -n "a" -t ATTR -c test_abd_lookup.conf
78gnunet-namestore -z b -d -n "b" -t ATTR -c test_abd_lookup.conf
79gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
80gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
81gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
82gnunet-namestore -z g -d -n "@" -t DEL -c test_abd_lookup.conf
83
84gnunet-arm -e -c test_abd_lookup.conf
85
86if [ "$RES" == 0 ]
87then
88 exit 0
89else
90 echo "FAIL: Failed to verify credential."
91 exit 1
92fi
diff --git a/src/contrib/service/abd/test_abd_defaults.conf b/src/contrib/service/abd/test_abd_defaults.conf
new file mode 100644
index 000000000..50a0a907c
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_defaults.conf
@@ -0,0 +1,24 @@
1@INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-abd-testing/
5
6[namestore-sqlite]
7FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db
8
9[namecache-sqlite]
10FILENAME=$GNUNET_TEST_HOME/namecache/namecache.db
11
12[identity]
13# Directory where we store information about our egos
14EGODIR = $GNUNET_TEST_HOME/identity/egos/
15
16[dhtcache]
17DATABASE = heap
18
19[transport]
20PLUGINS = tcp
21
22[transport-tcp]
23BINDTO = 127.0.0.1
24
diff --git a/src/contrib/service/abd/test_abd_issue.sh b/src/contrib/service/abd/test_abd_issue.sh
new file mode 100755
index 000000000..8076aa921
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_issue.sh
@@ -0,0 +1,46 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18# (1) PKEY1.user -> PKEY2.resu.user
19# (2) PKEY2.resu -> PKEY3
20# (3) PKEY3.user -> PKEY4
21
22
23which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
24
25TEST_ATTR="test"
26gnunet-arm -s -c test_abd_lookup.conf
27gnunet-identity -C testissuer -c test_abd_lookup.conf
28gnunet-identity -C testsubject -c test_abd_lookup.conf
29SUBJECT_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep testsubject | awk '{print $3}')
30ISSUER_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep testissuer | awk '{print $3}')
31
32# Get abd and store it with subject (3)
33SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=testissuer --attribute=$TEST_ATTR --subject=$SUBJECT_KEY --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf`
34
35STATUS=$?
36
37if test $? != 0
38then
39 echo "Error issuing..."
40 exit 1
41fi
42#Try import
43$DO_TIMEOUT gnunet-abd --createSubjectSide --ego=testsubject --import="$SIGNED" --private -c test_abd_lookup.conf
44RES=$?
45gnunet-arm -e -c test_abd_lookup.conf
46exit $RES
diff --git a/src/contrib/service/abd/test_abd_lookup.conf b/src/contrib/service/abd/test_abd_lookup.conf
new file mode 100644
index 000000000..4072ba23b
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_lookup.conf
@@ -0,0 +1,28 @@
1@INLINE@ test_abd_defaults.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-abd-peer-1/
5
6[dht]
7START_ON_DEMAND = YES
8
9[transport]
10PLUGINS =
11
12[abd]
13START_ON_DEMAND = YES
14#PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/credlog
15
16[rest]
17PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=$GNUNET_TMP/restlog
18
19[gns]
20#PREFIX = valgrind --leak-check=full --track-origins=yes
21START_ON_DEMAND = YES
22AUTO_IMPORT_PKEY = YES
23MAX_PARALLEL_BACKGROUND_QUERIES = 10
24DEFAULT_LOOKUP_TIMEOUT = 15 s
25RECORD_PUT_INTERVAL = 1 h
26ZONE_PUBLISH_TIME_WINDOW = 1 h
27DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0
28
diff --git a/src/contrib/service/abd/test_abd_own.sh b/src/contrib/service/abd/test_abd_own.sh
new file mode 100755
index 000000000..3fc210284
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_own.sh
@@ -0,0 +1,139 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18
19
20which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 10"
21gnunet-arm -s -c test_abd_lookup.conf
22
23gnunet-identity -C a -c test_abd_lookup.conf
24gnunet-identity -C d -c test_abd_lookup.conf
25gnunet-identity -C e -c test_abd_lookup.conf
26gnunet-identity -C f -c test_abd_lookup.conf
27gnunet-identity -C g -c test_abd_lookup.conf
28AKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep a | awk '{print $3}')
29DKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep d | awk '{print $3}')
30EKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep e | awk '{print $3}')
31FKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep f | awk '{print $3}')
32GKEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep g | awk '{print $3}')
33
34############################################################################################
35# (1) EPub.discount <- EOrg.preferred
36# (2) EOrg.preferred <- StateU.student
37# (3) StateU.student <- RegistrarB.student
38# (4) RegistrarB.student <- Alice
39
40gnunet-identity -C epub -c test_abd_lookup.conf
41gnunet-identity -C eorg -c test_abd_lookup.conf
42gnunet-identity -C stateu -c test_abd_lookup.conf
43gnunet-identity -C registrarb -c test_abd_lookup.conf
44gnunet-identity -C alice -c test_abd_lookup.conf
45
46EPUB_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep epub | awk '{print $3}')
47EORG_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep eorg | awk '{print $3}')
48STATEU_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep stateu | awk '{print $3}')
49REGISTRARB_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep registrarb | awk '{print $3}')
50ALICE_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep alice | awk '{print $3}')
51
52
53DISC_ATTR="discount"
54PREF_ATTR="preferred"
55STATE_STUD_ATTR="student"
56REG_STUD_ATTR="student"
57END_ATTR="end"
58
59gnunet-identity -d
60
61# FORWARD, subject side stored (different constellations)
62SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=a --attribute="a" --subject="$AKEY b.c" --ttl="2019-12-12 10:00:00"`
63gnunet-abd --createSubjectSide --ego=a --import="$SIGNED"
64gnunet-namestore -D -z a
65
66SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=a --attribute="b" --subject="$DKEY d" --ttl="2019-12-12 10:00:00"`
67gnunet-abd --createSubjectSide --ego=d --import="$SIGNED"
68gnunet-namestore -D -z d
69
70SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="d" --subject="$EKEY" --ttl="2019-12-12 10:00:00"`
71gnunet-abd --createSubjectSide --ego=e --import="$SIGNED"
72gnunet-namestore -D -z e
73
74SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="c" --subject="$FKEY c" --ttl="2019-12-12 10:00:00"`
75gnunet-abd --createSubjectSide --ego=f --import="$SIGNED"
76SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=e --attribute="k" --subject="$FKEY c.k" --ttl="2019-12-12 10:00:00"`
77gnunet-abd --createSubjectSide --ego=f --import="$SIGNED"
78gnunet-namestore -D -z f
79
80SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=f --attribute="c" --subject="$GKEY" --ttl="2019-12-12 10:00:00"`
81gnunet-abd --createSubjectSide --ego=g --import="$SIGNED" --private
82SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=a --attribute="c" --subject="$GKEY" --ttl="2019-12-12 10:00:00"`
83gnunet-abd --createSubjectSide --ego=g --import="$SIGNED" --private
84SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=d --attribute="h.o" --subject="$GKEY" --ttl="2019-12-12 10:00:00"`
85gnunet-abd --createSubjectSide --ego=g --import="$SIGNED"
86gnunet-namestore -D -z g
87
88
89# BACKWARD, issuer side stored
90# (1) EPub assigns the attribute "discount" to all entities that have been assigned "preferred" by EOrg
91gnunet-abd --createIssuerSide --ego=epub --attribute=$DISC_ATTR --subject="$EORG_KEY $PREF_ATTR" --ttl=5m -c test_abd_lookup.conf
92
93# (2) EOrg assigns the attribute "preferred" to all entities that have been assigned "student" by StateU
94gnunet-abd --createIssuerSide --ego=eorg --attribute=$PREF_ATTR --subject="$STATEU_KEY $STATE_STUD_ATTR" --ttl=5m -c test_abd_lookup.conf
95
96# (3) StateU assigns the attribute "student" to all entities that have been asssigned "student" by RegistrarB
97gnunet-abd --createIssuerSide --ego=stateu --attribute=$STATE_STUD_ATTR --subject="$REGISTRARB_KEY $REG_STUD_ATTR" --ttl=5m -c test_abd_lookup.conf
98
99# (4) RegistrarB issues Alice the credential "student"
100SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=registrarb --attribute="$REG_STUD_ATTR" --subject="$ALICE_KEY" --ttl="2019-12-12 10:00:00"`
101gnunet-abd --createSubjectSide --ego=alice --import="$SIGNED" --private
102
103# Starting to resolve
104echo "+++ Starting to Resolve +++"
105
106# FORWARD
107#DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$AKEY --attribute="a" --ego=g --forward -c test_abd_lookup.conf | paste -d, -s - -`
108#echo $DELS
109#echo gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$GKEY --delegate=\'$DELS\' --forward -c test_abd_lookup.conf
110#RES_DELS=`gnunet-abd --verify --issuer=$AKEY --attribute="a" --subject=$GKEY --delegate="$DELS" --forward -c test_abd_lookup.conf`
111
112# BACKWARD
113DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$EPUB_KEY --attribute=$DISC_ATTR --ego=alice --backward -c test_abd_lookup.conf | paste -d, -s - -`
114echo $DELS
115echo gnunet-abd --verify --issuer=$EPUB_KEY --attribute=$DISC_ATTR --subject=$ALICE_KEY --delegate=\'$DELS\' --backward -c test_abd_lookup.conf
116gnunet-abd --verify --issuer=$EPUB_KEY --attribute=$DISC_ATTR --subject=$ALICE_KEY --delegate="$DELS" --backward -c test_abd_lookup.conf
117
118RES=$?
119
120# Cleanup properly
121gnunet-namestore -z epub -d -n $DISC_ATTR -t ATTR -c test_abd_lookup.conf
122gnunet-namestore -z eorg -d -n $PREF_ATTR -t ATTR -c test_abd_lookup.conf
123gnunet-namestore -z stateu -d -n $STATE_STUD_ATTR -t ATTR -c test_abd_lookup.conf
124#gnunet-namestore -z a -d -n "@" -t DEL -c test_abd_lookup.conf
125#gnunet-namestore -z d -d -n "@" -t DEL -c test_abd_lookup.conf
126#gnunet-namestore -z e -d -n "@" -t DEL -c test_abd_lookup.conf
127#gnunet-namestore -z f -d -n "@" -t DEL -c test_abd_lookup.conf
128#gnunet-namestore -z g -d -n "@" -t DEL -c test_abd_lookup.conf
129
130
131gnunet-arm -e -c test_abd_lookup.conf
132
133if [ "$RES" == 0 ]
134then
135 exit 0
136else
137 echo "FAIL: Failed to verify credential."
138 exit 1
139fi
diff --git a/src/contrib/service/abd/test_abd_verify.sh b/src/contrib/service/abd/test_abd_verify.sh
new file mode 100755
index 000000000..9ece91c62
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_verify.sh
@@ -0,0 +1,87 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18# (1) Service.user -> GNU.project.member
19# (2) GNU.project -> GNUnet
20# (3) GNUnet.member -> GNUnet.developer
21# (4) GNUnet.member -> GNUnet.user
22# (5) GNUnet.developer -> Alice
23
24
25which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
26gnunet-arm -s -c test_abd_lookup.conf
27gnunet-identity -C service -c test_abd_lookup.conf
28gnunet-identity -C alice -c test_abd_lookup.conf
29gnunet-identity -C gnu -c test_abd_lookup.conf
30gnunet-identity -C gnunet -c test_abd_lookup.conf
31
32GNU_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep gnu | grep -v gnunet | awk '{print $3}')
33ALICE_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep alice | awk '{print $3}')
34GNUNET_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep gnunet | awk '{print $3}')
35SERVICE_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep service | awk '{print $3}')
36
37USER_ATTR="user"
38GNU_PROJECT_ATTR="project"
39MEMBER_ATTR="member"
40DEVELOPER_ATTR="developer"
41DEV_ATTR="developer"
42TEST_CREDENTIAL="mygnunetcreds"
43
44gnunet-identity -d
45
46# (1) A service assigns the attribute "user" to all entities that have been assigned "member" by entities that werde assigned "project" from GNU
47gnunet-abd --createIssuerSide --ego=service --attribute="$USER_ATTR" --subject="$GNU_KEY $GNU_PROJECT_ATTR.$MEMBER_ATTR" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
48gnunet-namestore -D -z service
49
50# (2) GNU recognized GNUnet as a GNU project and delegates the "project" attribute
51gnunet-abd --createIssuerSide --ego=gnu --attribute="$GNU_PROJECT_ATTR" --subject="$GNUNET_KEY" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
52gnunet-namestore -D -z gnu
53
54# (3+4) GNUnet assigns the attribute "member" to all entities gnunet has also assigned "developer" or "user"
55gnunet-abd --createIssuerSide --ego=gnunet --attribute="$MEMBER_ATTR" --subject="$GNUNET_KEY $DEVELOPER_ATTR" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
56gnunet-abd --createIssuerSide --ego=gnunet --attribute="$MEMBER_ATTR" --subject="$GNUNET_KEY $USER_ATTR" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
57gnunet-namestore -D -z gnunet
58
59# (5) GNUnet signes the delegate and Alice stores it
60SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=gnunet --attribute=$DEV_ATTR --subject=$ALICE_KEY --ttl="2019-12-12 10:00:00"`
61gnunet-abd --createSubjectSide --ego=alice --import="$SIGNED" --private
62gnunet-namestore -D -z alice
63
64# Starting to resolve
65echo "+++ Starting to Resolve +++"
66
67DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$SERVICE_KEY --attribute=$USER_ATTR --ego=alice --backward -c test_abd_lookup.conf | paste -d, -s - -`
68echo $DELS
69echo gnunet-abd --verify --issuer=$SERVICE_KEY --attribute=$USER_ATTR --subject=$ALICE_KEY --delegate=\'$DELS\' --backward -c test_abd_lookup.conf
70gnunet-abd --verify --issuer=$SERVICE_KEY --attribute=$USER_ATTR --subject=$ALICE_KEY --delegate="$DELS" --backward -c test_abd_lookup.conf
71
72RES=$?
73
74# Cleanup properly
75gnunet-namestore -z alice -d -n "@" -t DEL -c test_abd_lookup.conf
76gnunet-namestore -z gnu -d -n $GNU_PROJECT_ATTR -t ATTR -c test_abd_lookup.conf
77gnunet-namestore -z gnunet -d -n $MEMBER_ATTR -t ATTR -c test_abd_lookup.conf
78gnunet-namestore -z service -d -n $USER_ATTR -t ATTR -c test_abd_lookup.conf
79gnunet-arm -e -c test_abd_lookup.conf
80
81if [ "$RES" == 0 ]
82then
83 exit 0
84else
85 echo "FAIL: Failed to verify credential."
86 exit 1
87fi
diff --git a/src/contrib/service/abd/test_abd_verify_and.sh b/src/contrib/service/abd/test_abd_verify_and.sh
new file mode 100755
index 000000000..c6287055e
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_verify_and.sh
@@ -0,0 +1,86 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18# (1) Service.user -> GNU.project.member
19# (2) GNU.project -> GNUnet
20# (3) GNUnet.member -> GNUnet.developer AND GNUnet.user
21# (4) GNUnet.developer -> Alice
22
23
24which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
25gnunet-arm -s -c test_abd_lookup.conf
26gnunet-identity -C service -c test_abd_lookup.conf
27gnunet-identity -C alice -c test_abd_lookup.conf
28gnunet-identity -C gnu -c test_abd_lookup.conf
29gnunet-identity -C gnunet -c test_abd_lookup.conf
30
31GNU_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep gnu | grep -v gnunet | awk '{print $3}')
32ALICE_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep alice | awk '{print $3}')
33GNUNET_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep gnunet | awk '{print $3}')
34SERVICE_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep service | awk '{print $3}')
35
36USER_ATTR="user"
37GNU_PROJECT_ATTR="project"
38MEMBER_ATTR="member"
39DEVELOPER_ATTR="developer"
40DEV_ATTR="developer"
41
42gnunet-identity -d
43
44# (1) A service assigns the attribute "user" to all entities that have been assigned "member" by entities that werde assigned "project" from GNU
45gnunet-abd --createIssuerSide --ego=service --attribute="$USER_ATTR" --subject="$GNU_KEY $GNU_PROJECT_ATTR.$MEMBER_ATTR" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
46gnunet-namestore -D -z service
47
48# (2) GNU recognized GNUnet as a GNU project and delegates the "project" attribute
49gnunet-abd --createIssuerSide --ego=gnu --attribute="$GNU_PROJECT_ATTR" --subject="$GNUNET_KEY" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
50gnunet-namestore -D -z gnu
51
52# (3+4) GNUnet assigns the attribute "member" to all entities gnunet has also assigned "developer" or "user"
53gnunet-abd --createIssuerSide --ego=gnunet --attribute="$MEMBER_ATTR" --subject="$GNUNET_KEY $DEVELOPER_ATTR, $GNUNET_KEY $USER_ATTR" --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf
54gnunet-namestore -D -z gnunet
55
56# (5) GNUnet signes the delegates and Alice stores it
57SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=gnunet --attribute=$DEV_ATTR --subject=$ALICE_KEY --ttl="2019-12-12 10:00:00"`
58gnunet-abd --createSubjectSide --ego=alice --import="$SIGNED" --private
59SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=gnunet --attribute=$USER_ATTR --subject=$ALICE_KEY --ttl="2019-12-12 10:00:00"`
60gnunet-abd --createSubjectSide --ego=alice --import="$SIGNED" --private
61gnunet-namestore -D -z alice
62
63# Starting to resolve
64echo "+++ Starting to Resolve +++"
65
66DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$SERVICE_KEY --attribute=$USER_ATTR --ego=alice --backward -c test_abd_lookup.conf | paste -d, -s - -`
67echo $DELS
68echo gnunet-abd --verify --issuer=$SERVICE_KEY --attribute=$USER_ATTR --subject=$ALICE_KEY --delegate=\'$DELS\' --backward -c test_abd_lookup.conf
69gnunet-abd --verify --issuer=$SERVICE_KEY --attribute=$USER_ATTR --subject=$ALICE_KEY --delegate="$DELS" --backward -c test_abd_lookup.conf
70
71RES=$?
72
73# Cleanup properly
74gnunet-namestore -z alice -d -n "@" -t DEL -c test_abd_lookup.conf
75gnunet-namestore -z gnu -d -n $GNU_PROJECT_ATTR -t ATTR -c test_abd_lookup.conf
76gnunet-namestore -z gnunet -d -n $MEMBER_ATTR -t ATTR -c test_abd_lookup.conf
77gnunet-namestore -z service -d -n $USER_ATTR -t ATTR -c test_abd_lookup.conf
78gnunet-arm -e -c test_abd_lookup.conf
79
80if [ "$RES" == 0 ]
81then
82 exit 0
83else
84 echo "FAIL: Failed to verify credentials."
85 exit 1
86fi
diff --git a/src/contrib/service/abd/test_abd_verify_simple.sh b/src/contrib/service/abd/test_abd_verify_simple.sh
new file mode 100755
index 000000000..e917f8793
--- /dev/null
+++ b/src/contrib/service/abd/test_abd_verify_simple.sh
@@ -0,0 +1,56 @@
1#!/usr/bin/env bash
2trap "gnunet-arm -e -c test_abd_lookup.conf" SIGINT
3
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_abd_lookup.conf -s PATHS -o GNUNET_HOME -f`
17
18# (1) Issuer.user -> Subject
19
20
21which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 30"
22gnunet-arm -s -c test_abd_lookup.conf
23gnunet-identity -C testissuer -c test_abd_lookup.conf
24gnunet-identity -C testsubject -c test_abd_lookup.conf
25
26TEST_ATTR="user"
27SUBJECT_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep testsubject | awk '{print $3}')
28ISSUER_KEY=$(gnunet-identity -d -c test_abd_lookup.conf | grep testissuer | awk '{print $3}')
29
30gnunet-identity -d
31
32# Create delegate (1)
33SIGNED=`$DO_TIMEOUT gnunet-abd --signSubjectSide --ego=testissuer --attribute=$TEST_ATTR --subject=$SUBJECT_KEY --ttl="2019-12-12 10:00:00" -c test_abd_lookup.conf`
34gnunet-abd --createSubjectSide --ego=testsubject --import="$SIGNED" --private
35gnunet-namestore -D -z testsubject
36
37# Starting to resolve
38echo "+++ Starting to Resolve +++"
39
40DELS=`$DO_TIMEOUT gnunet-abd --collect --issuer=$ISSUER_KEY --attribute=$TEST_ATTR --ego=testsubject -c test_abd_lookup.conf | paste -d, -s - -`
41echo $DELS
42gnunet-abd --verify --issuer=$ISSUER_KEY --attribute=$TEST_ATTR --subject=$SUBJECT_KEY --delegate="$DELS" -c test_abd_lookup.conf
43
44RES=$?
45
46# Cleanup properly
47gnunet-namestore -z testsubject -d -n "@" -t DEL -c test_abd_lookup.conf
48gnunet-arm -e -c test_abd_lookup.conf
49
50if [ "$RES" == 0 ]
51then
52 exit 0
53else
54 echo "FAIL: Failed to verify credential."
55 exit 1
56fi \ No newline at end of file
diff --git a/src/contrib/service/auction/.gitignore b/src/contrib/service/auction/.gitignore
new file mode 100644
index 000000000..5c3ec20cb
--- /dev/null
+++ b/src/contrib/service/auction/.gitignore
@@ -0,0 +1,5 @@
1gnunet-auction-create
2gnunet-auction-info
3gnunet-auction-join
4gnunet-service-auction
5test_auction_api
diff --git a/src/contrib/service/auction/Makefile.am b/src/contrib/service/auction/Makefile.am
new file mode 100644
index 000000000..ec0ffaf33
--- /dev/null
+++ b/src/contrib/service/auction/Makefile.am
@@ -0,0 +1,75 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir = $(pkgdatadir)/config.d/
5
6libexecdir = $(pkglibdir)/libexec/
7
8
9pkgcfg_DATA = \
10 auction.conf
11
12if USE_COVERAGE
13 AM_CFLAGS = -fprofile-arcs -ftest-coverage
14endif
15
16
17libexec_PROGRAMS = \
18 gnunet-service-auction
19
20gnunet_service_auction_SOURCES = \
21 gnunet-service-auction.c
22gnunet_service_auction_LDADD = \
23 $(top_builddir)/src/lib/util/libgnunetutil.la \
24 -ljansson \
25 $(GN_LIBINTL)
26
27
28bin_PROGRAMS = \
29 gnunet-auction-create \
30 gnunet-auction-info \
31 gnunet-auction-join
32
33gnunet_auction_create_SOURCES = \
34 gnunet-auction-create.c
35gnunet_auction_create_LDADD = \
36 $(top_builddir)/src/lib/util/libgnunetutil.la \
37 -ljansson \
38 $(GN_LIBINTL)
39
40gnunet_auction_info_SOURCES = \
41 gnunet-auction-info.c
42gnunet_auction_info_LDADD = \
43 $(top_builddir)/src/lib/util/libgnunetutil.la \
44 -ljansson \
45 $(GN_LIBINTL)
46
47gnunet_auction_join_SOURCES = \
48 gnunet-auction-join.c
49gnunet_auction_join_LDADD = \
50 $(top_builddir)/src/lib/util/libgnunetutil.la \
51 -ljansson \
52 $(GN_LIBINTL)
53
54
55check_PROGRAMS = \
56 test_auction_api
57
58test_auction_api_SOURCES = \
59 test_auction_api.c
60test_auction_api_LDADD = \
61 $(top_builddir)/src/lib/util/libgnunetutil.la
62
63
64check_SCRIPTS = \
65 test_auction_create.sh
66
67EXTRA_DIST = \
68 auction.h \
69 auction.conf \
70 $(check_SCRIPTS)
71
72if ENABLE_TEST_RUN
73 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
74 TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
75endif
diff --git a/src/contrib/service/auction/auction.conf b/src/contrib/service/auction/auction.conf
new file mode 100644
index 000000000..deb9ac1fc
--- /dev/null
+++ b/src/contrib/service/auction/auction.conf
@@ -0,0 +1,4 @@
1[auction]
2START_ON_DEMAND = NO
3BINARY = gnunet-service-auction
4UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-auction.sock
diff --git a/src/contrib/service/auction/auction.h b/src/contrib/service/auction/auction.h
new file mode 100644
index 000000000..dc8b933f6
--- /dev/null
+++ b/src/contrib/service/auction/auction.h
@@ -0,0 +1,77 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Markus Teich
23 * @file auction/auction.h
24 *
25 * @brief Common type definitions for the auction service and API.
26 */
27#ifndef AUCTION_H
28#define AUCTION_H
29
30#include "gnunet_common.h"
31
32GNUNET_NETWORK_STRUCT_BEGIN
33
34/**
35 * Auction creation request sent from the client to the service
36 */
37struct GNUNET_AUCTION_ClientCreateMessage
38{
39 /**
40 * Type: GNUNET_MESSAGE_TYPE_AUCTION_CLIENT_CREATE
41 */
42 struct GNUNET_MessageHeader header;
43
44 /**
45 * When should the auction start
46 */
47 struct GNUNET_TIME_AbsoluteNBO time_start;
48
49 /**
50 * How long is each round allowed to be maximally
51 */
52 struct GNUNET_TIME_RelativeNBO time_round;
53
54 /**
55 * Auction parameter m.
56 * 0 for first price auctions.
57 * >0 for M+1st price auctions.
58 */
59 uint16_t m GNUNET_PACKED;
60
61 /**
62 * Should the auction outcome be public?
63 * 0 for private outcome auctions.
64 * 1 for public outcome auctions.
65 */
66 uint16_t outcome_public GNUNET_PACKED;
67
68 /**
69 * TODO: Price mapping.
70 */
71
72 /* DESCRIPTION text copied to end of this message */
73};
74
75GNUNET_NETWORK_STRUCT_END
76
77#endif
diff --git a/src/contrib/service/auction/gnunet-auction-create.c b/src/contrib/service/auction/gnunet-auction-create.c
new file mode 100644
index 000000000..93947b89c
--- /dev/null
+++ b/src/contrib/service/auction/gnunet-auction-create.c
@@ -0,0 +1,219 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file auction/gnunet-auction-create.c
23 * @brief tool to create a new auction
24 * @author Markus Teich
25 */
26#include "platform.h"
27
28#include <float.h>
29
30#include "gnunet_util_lib.h"
31#include <jansson.h>
32/* #include "gnunet_auction_service.h" */
33
34#define FIRST_PRICE 0
35#define OUTCOME_PRIVATE 0
36#define OUTCOME_PUBLIC 1
37
38static int ret; /** Final status code. */
39static char *fndesc; /** filename of the item description */
40static char *fnprices; /** filename of the price map */
41static struct GNUNET_TIME_Relative dround; /** max round duration */
42static struct GNUNET_TIME_Relative dstart; /** time until auction starts */
43static unsigned int m = FIRST_PRICE; /** auction parameter m */
44static int outcome = OUTCOME_PRIVATE; /** outcome */
45static int interactive; /** keep running in foreground */
46
47
48/**
49 * Main function that will be run by the scheduler.
50 *
51 * @param cls closure
52 * @param args remaining command-line arguments
53 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
54 * @param cfg configuration
55 */
56static void
57run (void *cls,
58 char *const *args,
59 const char *cfgfile,
60 const struct GNUNET_CONFIGURATION_Handle *cfg)
61{
62 unsigned int i;
63 double cur, prev = DBL_MAX;
64 json_t *pmap;
65 json_t *parray;
66 json_t *pnode;
67 json_error_t jerr;
68
69 /* cmdline parsing */
70 if (GNUNET_TIME_UNIT_ZERO.rel_value_us == dstart.rel_value_us)
71 {
72 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
73 "required argument --regtime missing or invalid (zero)\n");
74 goto fail;
75 }
76 if (GNUNET_TIME_UNIT_ZERO.rel_value_us == dround.rel_value_us)
77 {
78 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
79 "required argument --roundtime missing or invalid (zero)\n");
80 goto fail;
81 }
82 if (! fndesc)
83 {
84 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
85 "required argument --description missing\n");
86 goto fail;
87 }
88 if (! fnprices)
89 {
90 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
91 "required argument --pricemap missing\n");
92 goto fail;
93 }
94
95 /* parse and check pricemap validity */
96 if (! (pmap = json_load_file (fnprices, JSON_DECODE_INT_AS_REAL, &jerr)))
97 {
98 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
99 "parsing pricemap json at %d:%d: %s\n",
100 jerr.line, jerr.column, jerr.text);
101 goto fail;
102 }
103 if (-1 == json_unpack_ex (pmap, &jerr, JSON_VALIDATE_ONLY,
104 "{s:s, s:[]}", "currency", "prices"))
105 {
106 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
107 "validating pricemap: %s\n", jerr.text);
108 goto fail;
109 }
110 if (! (parray = json_object_get (pmap, "prices")) || ! json_is_array (parray))
111 {
112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
113 "could not get `prices` array node from pricemap\n");
114 goto fail;
115 }
116 if (0 == json_array_size (parray))
117 {
118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "empty pricemap array\n");
119 goto fail;
120 }
121 json_array_foreach (parray, i, pnode)
122 {
123 if (-1 == json_unpack_ex (pnode, &jerr, 0, "F", &cur))
124 {
125 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
126 "validating pricearray index %d: %s\n", i, jerr.text);
127 goto fail;
128 }
129 if (prev <= cur)
130 {
131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
132 "validating pricearray index %d: "
133 "prices must be strictly monotonically decreasing\n",
134 i);
135 goto fail;
136 }
137 prev = cur;
138 }
139
140 return;
141
142fail:
143 ret = 1;
144 return;
145}
146
147
148/**
149 * The main function.
150 *
151 * @param argc number of arguments from the command line
152 * @param argv command line arguments
153 * @return 0 ok, 1 on error
154 */
155int
156main (int argc, char *const *argv)
157{
158 struct GNUNET_GETOPT_CommandLineOption options[] = {
159 GNUNET_GETOPT_option_filename ('d',
160 "description",
161 "FILE",
162 gettext_noop (
163 "description of the item to be sold"),
164 &fndesc),
165
166 GNUNET_GETOPT_option_filename ('p',
167 "pricemap",
168 "FILE",
169 gettext_noop ("mapping of possible prices"),
170 &fnprices),
171
172 GNUNET_GETOPT_option_relative_time ('r',
173 "roundtime",
174 "DURATION",
175 gettext_noop ("max duration per round"),
176 &dround),
177
178 GNUNET_GETOPT_option_relative_time ('s',
179 "regtime",
180 "DURATION",
181 gettext_noop (
182 "duration until auction starts"),
183 &dstart),
184 GNUNET_GETOPT_option_uint ('m',
185 "m",
186 "NUMBER",
187 gettext_noop ("number of items to sell\n"
188 "0 for first price auction\n"
189 ">0 for vickrey/M+1st price auction"),
190 &m),
191
192 GNUNET_GETOPT_option_flag ('u',
193 "public",
194 gettext_noop ("public auction outcome"),
195 &outcome),
196
197 GNUNET_GETOPT_option_flag ('i',
198 "interactive",
199 gettext_noop (
200 "keep running in foreground until auction completes"),
201 &interactive),
202
203 GNUNET_GETOPT_OPTION_END
204 };
205
206 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
207 return 2;
208
209 ret = (GNUNET_OK ==
210 GNUNET_PROGRAM_run (argc, argv,
211 "gnunet-auction-create",
212 gettext_noop ("create a new auction and "
213 "start listening for bidders"),
214 options,
215 &run,
216 NULL)) ? ret : 1;
217 GNUNET_free_nz ((void *) argv);
218 return ret;
219}
diff --git a/src/contrib/service/auction/gnunet-auction-info.c b/src/contrib/service/auction/gnunet-auction-info.c
new file mode 100644
index 000000000..dded45ce6
--- /dev/null
+++ b/src/contrib/service/auction/gnunet-auction-info.c
@@ -0,0 +1,86 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file auction/gnunet-auction.c
23 * @brief auction for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28/* #include "gnunet_auction_service.h" */
29
30/**
31 * Final status code.
32 */
33static int ret;
34
35
36/**
37 * Main function that will be run by the scheduler.
38 *
39 * @param cls closure
40 * @param args remaining command-line arguments
41 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
42 * @param cfg configuration
43 */
44static void
45run (void *cls,
46 char *const *args,
47 const char *cfgfile,
48 const struct GNUNET_CONFIGURATION_Handle *cfg)
49{
50 /* main code here */
51}
52
53
54/**
55 * The main function.
56 *
57 * @param argc number of arguments from the command line
58 * @param argv command line arguments
59 * @return 0 ok, 1 on error
60 */
61int
62main (int argc, char *const *argv)
63{
64 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
65 /* FIMXE: add options here */
66 GNUNET_GETOPT_OPTION_END
67 };
68
69 if (GNUNET_OK !=
70 GNUNET_STRINGS_get_utf8_args (argc, argv,
71 &argc, &argv))
72 return 2;
73
74 ret = (GNUNET_OK ==
75 GNUNET_PROGRAM_run (argc, argv,
76 "gnunet-auction",
77 gettext_noop ("help text"),
78 options,
79 &run,
80 NULL)) ? ret : 1;
81 GNUNET_free_nz ((void *) argv);
82 return ret;
83}
84
85
86/* end of gnunet-auction.c */
diff --git a/src/contrib/service/auction/gnunet-auction-join.c b/src/contrib/service/auction/gnunet-auction-join.c
new file mode 100644
index 000000000..dded45ce6
--- /dev/null
+++ b/src/contrib/service/auction/gnunet-auction-join.c
@@ -0,0 +1,86 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file auction/gnunet-auction.c
23 * @brief auction for writing a tool
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28/* #include "gnunet_auction_service.h" */
29
30/**
31 * Final status code.
32 */
33static int ret;
34
35
36/**
37 * Main function that will be run by the scheduler.
38 *
39 * @param cls closure
40 * @param args remaining command-line arguments
41 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
42 * @param cfg configuration
43 */
44static void
45run (void *cls,
46 char *const *args,
47 const char *cfgfile,
48 const struct GNUNET_CONFIGURATION_Handle *cfg)
49{
50 /* main code here */
51}
52
53
54/**
55 * The main function.
56 *
57 * @param argc number of arguments from the command line
58 * @param argv command line arguments
59 * @return 0 ok, 1 on error
60 */
61int
62main (int argc, char *const *argv)
63{
64 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
65 /* FIMXE: add options here */
66 GNUNET_GETOPT_OPTION_END
67 };
68
69 if (GNUNET_OK !=
70 GNUNET_STRINGS_get_utf8_args (argc, argv,
71 &argc, &argv))
72 return 2;
73
74 ret = (GNUNET_OK ==
75 GNUNET_PROGRAM_run (argc, argv,
76 "gnunet-auction",
77 gettext_noop ("help text"),
78 options,
79 &run,
80 NULL)) ? ret : 1;
81 GNUNET_free_nz ((void *) argv);
82 return ret;
83}
84
85
86/* end of gnunet-auction.c */
diff --git a/src/contrib/service/auction/gnunet-service-auction.c b/src/contrib/service/auction/gnunet-service-auction.c
new file mode 100644
index 000000000..c20f0bdbe
--- /dev/null
+++ b/src/contrib/service/auction/gnunet-service-auction.c
@@ -0,0 +1,155 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file auction/gnunet-service-auction.c
23 * @brief service for executing auctions
24 * @author Markus Teich
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29#include "auction.h"
30
31/**
32 * Check AUCTION CREATE messages from the client.
33 *
34 * @param cls the client we received this message from
35 * @param msg the actual message received
36 * @return #GNUNET_OK (always)
37 */
38static int
39check_create (void *cls, const struct GNUNET_AUCTION_ClientCreateMessage *msg)
40{
41 /* always well-formed due to arbitrary length description */
42 return GNUNET_OK;
43}
44
45
46/**
47 * Handler for CREATE messages.
48 *
49 * @param cls the client we received this message from
50 * @param msg the actual message received
51 */
52static void
53handle_create (void *cls, const struct GNUNET_AUCTION_ClientCreateMessage *msg)
54{
55 struct GNUNET_SERVICE_Client *client = cls;
56// struct GNUNET_MQ_Handle *mq;
57// struct GNUNET_MQ_Envelope *env;
58// struct GNUNET_AUCTION_blabla em;
59 //uint16_t size;
60
61 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
62 "Received CREATE message from client\n");
63
64 //size = ntohs (msg->header.size);
65
66 /**TODO: create auction and return auction object */
67// mq = GNUNET_SERVICE_client_get_mq (client);
68// setup_info_message (&em);
69// env = GNUNET_MQ_msg_copy (&em.header);
70// GNUNET_MQ_send (mq, env);
71
72 GNUNET_SERVICE_client_continue (client);
73}
74
75
76/**
77 * Task run during shutdown.
78 *
79 * @param cls unused
80 */
81static void
82cleanup_task (void *cls)
83{
84 /* FIXME: do clean up here */
85}
86
87
88/**
89 * Callback called when a client connects to the service.
90 *
91 * @param cls closure for the service
92 * @param c the new client that connected to the service
93 * @param mq the message queue used to send messages to the client
94 * @return @a c
95 */
96static void *
97client_connect_cb (void *cls,
98 struct GNUNET_SERVICE_Client *c,
99 struct GNUNET_MQ_Handle *mq)
100{
101 return c;
102}
103
104
105/**
106 * Callback called when a client disconnected from the service
107 *
108 * @param cls closure for the service
109 * @param c the client that disconnected
110 * @param internal_cls should be equal to @a c
111 */
112static void
113client_disconnect_cb (void *cls,
114 struct GNUNET_SERVICE_Client *c,
115 void *internal_cls)
116{
117 GNUNET_assert (c == internal_cls);
118}
119
120
121/**
122 * Process auction requests.
123 *
124 * @param cls closure
125 * @param cfg configuration to use
126 * @param service the initialized service
127 */
128static void
129run (void *cls,
130 const struct GNUNET_CONFIGURATION_Handle *cfg,
131 struct GNUNET_SERVICE_Handle *service)
132{
133 /* FIXME: do setup here */
134 GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL);
135}
136
137
138/**
139 * Define "main" method using service macro.
140 */
141GNUNET_SERVICE_MAIN
142 ("auction",
143 GNUNET_SERVICE_OPTION_NONE,
144 &run,
145 &client_connect_cb,
146 &client_disconnect_cb,
147 NULL,
148 GNUNET_MQ_hd_var_size (create,
149 GNUNET_MESSAGE_TYPE_AUCTION_CLIENT_CREATE,
150 struct GNUNET_AUCTION_ClientCreateMessage,
151 NULL),
152 GNUNET_MQ_handler_end ())
153
154
155/* end of gnunet-service-auction.c */
diff --git a/src/contrib/service/auction/test_auction_api.c b/src/contrib/service/auction/test_auction_api.c
new file mode 100644
index 000000000..1eac9b5c4
--- /dev/null
+++ b/src/contrib/service/auction/test_auction_api.c
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file auction/test_auction_api.c
22 * @brief testcase for auction.c
23 */
24#include "platform.h"
25
26static int
27check ()
28{
29 return 0;
30}
31
32
33int
34main (int argc, char *argv[])
35{
36 int ret;
37
38 ret = check ();
39
40 return ret;
41}
42
43
44/* end of test_auction_api.c */
diff --git a/src/contrib/service/auction/test_auction_create.sh b/src/contrib/service/auction/test_auction_create.sh
new file mode 100755
index 000000000..b0f4de738
--- /dev/null
+++ b/src/contrib/service/auction/test_auction_create.sh
@@ -0,0 +1,80 @@
1#! /bin/sh
2
3trap 'rm -f test.json' EXIT
4
5
6# missing required cmdline args
7gnunet-auction-create -r 1 -d foo -p test.json && exit 1
8gnunet-auction-create -s 1 -d foo -p test.json && exit 1
9gnunet-auction-create -s 1 -r 1 -p test.json && exit 1
10gnunet-auction-create -s 1 -r 1 -d foo && exit 1
11
12
13# no pricemap
14rm -f test.json
15gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
16
17
18# json errors
19cat <<DOG >test.json
20[,]
21DOG
22gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
23
24cat <<DOG >test.json
25bla
26DOG
27gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
28
29
30# unexpected structures
31cat <<DOG >test.json
32{"foo": "bar"}
33DOG
34gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
35
36cat <<DOG >test.json
37{"currency": "foo"}
38DOG
39gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
40
41cat <<DOG >test.json
42{"prices": []}
43DOG
44gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
45
46cat <<DOG >test.json
47{"currency": "foo", "prices": "bar"}
48DOG
49gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
50
51
52# wrong array content
53cat <<DOG >test.json
54{"currency": "foo", "prices": []}
55DOG
56gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
57
58cat <<DOG >test.json
59{"currency": "foo", "prices": ["bar"]}
60DOG
61gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
62
63cat <<DOG >test.json
64{"currency": "foo", "prices": [null]}
65DOG
66gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
67
68cat <<DOG >test.json
69{"currency": "foo", "prices": [1, 2]}
70DOG
71gnunet-auction-create -s 1 -r 1 -d foo -p test.json && exit 1
72
73
74# correct example
75cat <<DOG >test.json
76{"currency": "foo", "prices": [2, 1]}
77DOG
78gnunet-auction-create -s 1 -r 1 -d foo -p test.json || exit 1
79
80rm -f test.json
diff --git a/src/contrib/service/consensus/.gitignore b/src/contrib/service/consensus/.gitignore
new file mode 100644
index 000000000..8050d760e
--- /dev/null
+++ b/src/contrib/service/consensus/.gitignore
@@ -0,0 +1,6 @@
1gnunet-service-evil-consensus
2gnunet-consensus-profiler
3gnunet-service-consensus
4test_consensus_api
5resource.log.master
6consensus-simulation.py
diff --git a/src/contrib/service/consensus/Makefile.am b/src/contrib/service/consensus/Makefile.am
new file mode 100644
index 000000000..6b4db05fc
--- /dev/null
+++ b/src/contrib/service/consensus/Makefile.am
@@ -0,0 +1,106 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10pkgcfg_DATA = \
11 consensus.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = -fprofile-arcs -ftest-coverage
15endif
16
17
18libexec_PROGRAMS = \
19 gnunet-service-consensus
20
21if ENABLE_MALICIOUS
22libexec_PROGRAMS += \
23 gnunet-service-evil-consensus
24endif
25
26SUFFIXES = .py.in .py
27
28.py.in.py:
29 $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/scripts/dosubst.awk < $< > $@
30 chmod +x $@
31
32check-python-style:
33 flake8 consensus-simulation.py.in
34
35lib_LTLIBRARIES = \
36 libgnunetconsensus.la
37
38gnunet_service_consensus_SOURCES = \
39 gnunet-service-consensus.c
40gnunet_service_consensus_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(top_builddir)/src/service/core/libgnunetcore.la \
43 $(top_builddir)/src/contrib/service/set/libgnunetset.la \
44 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
45 $(GN_LIBINTL)
46
47gnunet_service_evil_consensus_SOURCES = \
48 gnunet-service-consensus.c \
49 consensus_protocol.h
50gnunet_service_evil_consensus_LDADD = \
51 $(top_builddir)/src/lib/util/libgnunetutil.la \
52 $(top_builddir)/src/service/core/libgnunetcore.la \
53 $(top_builddir)/src/contrib/service/set/libgnunetset.la \
54 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
55 $(GN_LIBINTL)
56gnunet_service_evil_consensus_CFLAGS = -DEVIL
57
58libgnunetconsensus_la_SOURCES = \
59 consensus_api.c \
60 consensus.h
61libgnunetconsensus_la_LIBADD = \
62 $(top_builddir)/src/lib/util/libgnunetutil.la \
63 $(LTLIBINTL)
64libgnunetconsensus_la_LDFLAGS = \
65 $(GN_LIB_LDFLAGS)
66
67
68plugin_LTLIBRARIES = \
69 libgnunet_plugin_block_consensus.la
70
71libgnunet_plugin_block_consensus_la_SOURCES = \
72 plugin_block_consensus.c
73libgnunet_plugin_block_consensus_la_LIBADD = \
74 $(top_builddir)/src/lib/block/libgnunetblock.la \
75 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la \
77 $(LTLIBINTL)
78libgnunet_plugin_block_consensus_la_LDFLAGS = \
79 $(GN_PLUGIN_LDFLAGS)
80
81
82
83check_PROGRAMS = \
84 test_consensus_api
85
86if ENABLE_TEST_RUN
87AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
88TESTS = $(check_PROGRAMS)
89endif
90
91test_consensus_api_SOURCES = \
92 test_consensus_api.c
93test_consensus_api_LDADD = \
94 $(top_builddir)/src/lib/util/libgnunetutil.la \
95 $(top_builddir)/src/service/testing/libgnunettesting.la \
96 libgnunetconsensus.la
97
98noinst_SCRIPTS = \
99 consensus-simulation.py
100
101CLEANFILES = \
102 $(noinst_SCRIPTS)
103
104EXTRA_DIST = \
105 test_consensus.conf \
106 consensus-simulation.py.in
diff --git a/src/contrib/service/consensus/consensus-simulation.py.in b/src/contrib/service/consensus/consensus-simulation.py.in
new file mode 100644
index 000000000..272a52da2
--- /dev/null
+++ b/src/contrib/service/consensus/consensus-simulation.py.in
@@ -0,0 +1,112 @@
1#!@PYTHONEXE@
2# This file is part of GNUnet
3# (C) 2013, 2018 Christian Grothoff (and other contributing authors)
4#
5# GNUnet is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# GNUnet is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19
20import argparse
21import random
22from math import ceil, log, floor
23
24
25def bsc(n):
26 """ count the bits set in n"""
27 l = n.bit_length()
28 c = 0
29 x = 1
30 for _ in range(0, l):
31 if n & x:
32 c = c + 1
33 x = x << 1
34 return c
35
36
37def simulate(k, n, verbose):
38 assert k < n
39 largest_arc = int(2**ceil(log(n, 2))) // 2
40 num_ghosts = (2 * largest_arc) - n
41 if verbose:
42 print("we have", num_ghosts, "ghost peers")
43 # n.b. all peers with idx<k are evil
44 peers = list(range(n))
45 info = [1 << x for x in range(n)]
46
47 def done_p():
48 for x in range(k, n):
49 if bsc(info[x]) < n - k:
50 return False
51 return True
52
53 rounds = 0
54 while not done_p():
55 if verbose:
56 print("-- round --")
57 arc = 1
58 while arc <= largest_arc:
59 if verbose:
60 print("-- subround --")
61 new_info = [x for x in info]
62 for peer_physical in range(n):
63 peer_logical = peers[peer_physical]
64 peer_type = None
65 partner_logical = (peer_logical + arc) % n
66 partner_physical = peers.index(partner_logical)
67 if peer_physical < k or partner_physical < k:
68 if verbose:
69 print(
70 "bad peer in connection", peer_physical, "--",
71 partner_physical
72 )
73 continue
74 if peer_logical & arc == 0:
75 # we are outgoing
76 if verbose:
77 print(peer_physical, "connects to", partner_physical)
78 peer_type = "outgoing"
79 if peer_logical < num_ghosts:
80 # we have a ghost, check if the peer who connects
81 # to our ghost is actually outgoing
82 ghost_partner_logical = (peer_logical - arc) % n
83 if ghost_partner_logical & arc == 0:
84 peer_type = peer_type + ", ghost incoming"
85 new_info[peer_physical] = new_info[peer_physical] | info[
86 peer_physical] | info[partner_physical]
87 new_info[partner_physical
88 ] = new_info[partner_physical] | info[
89 peer_physical] | info[partner_physical]
90 else:
91 peer_type = "incoming"
92 if verbose > 1:
93 print("type of", str(peer_physical) + ":", peer_type)
94 info = new_info
95 arc = arc << 1
96 rounds = rounds + 1
97 random.shuffle(peers)
98 return rounds
99
100
101if __name__ == "__main__":
102 parser = argparse.ArgumentParser()
103 parser.add_argument("k", metavar="k", type=int, help="#(bad peers)")
104 parser.add_argument("n", metavar="n", type=int, help="#(all peers)")
105 parser.add_argument("r", metavar="r", type=int, help="#(rounds)")
106 parser.add_argument('--verbose', '-v', action='count')
107
108 args = parser.parse_args()
109 sum = 0.0
110 for n in range(0, args.r):
111 sum += simulate(args.k, args.n, args.verbose)
112 print(sum // args.r)
diff --git a/src/contrib/service/consensus/consensus.conf.in b/src/contrib/service/consensus/consensus.conf.in
new file mode 100644
index 000000000..b0fbcaf5a
--- /dev/null
+++ b/src/contrib/service/consensus/consensus.conf.in
@@ -0,0 +1,10 @@
1[consensus]
2START_ON_DEMAND = @START_ON_DEMAND@
3@JAVAPORT@PORT = 2103
4HOSTNAME = localhost
5BINARY = gnunet-service-consensus
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-consensus.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
diff --git a/src/contrib/service/consensus/consensus.h b/src/contrib/service/consensus/consensus.h
new file mode 100644
index 000000000..888213d55
--- /dev/null
+++ b/src/contrib/service/consensus/consensus.h
@@ -0,0 +1,92 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Florian Dold
23 * @file consensus/consensus.h
24 * @brief
25 */
26#ifndef CONSENSUS_H
27#define CONSENSUS_H
28
29#include "gnunet_common.h"
30
31GNUNET_NETWORK_STRUCT_BEGIN
32
33/**
34 * Sent by the client to the service,
35 * when the client wants the service to join a consensus session.
36 */
37struct GNUNET_CONSENSUS_JoinMessage
38{
39 /**
40 * Type: GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN
41 */
42 struct GNUNET_MessageHeader header;
43
44 /**
45 * Number of peers (at the end of this message) that want to
46 * participate in the consensus.
47 */
48 uint32_t num_peers GNUNET_PACKED;
49
50 /**
51 * Session id of the consensus.
52 */
53 struct GNUNET_HashCode session_id;
54
55 /**
56 * Start time for the consensus.
57 */
58 struct GNUNET_TIME_AbsoluteNBO start;
59
60 /**
61 * Deadline for conclude.
62 */
63 struct GNUNET_TIME_AbsoluteNBO deadline;
64
65 /* GNUNET_PeerIdentity[num_peers] */
66};
67
68
69/**
70 * Message with an element
71 */
72struct GNUNET_CONSENSUS_ElementMessage
73{
74 /**
75 * Type:
76 * Either GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT
77 * or GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT_ELEMENT
78 */
79 struct GNUNET_MessageHeader header;
80
81 /**
82 * Type: GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_NEW_ELEMENT
83 */
84 uint16_t element_type GNUNET_PACKED; /* FIXME: alignment? => uint32_t */
85
86 /* rest: element data */
87};
88
89
90GNUNET_NETWORK_STRUCT_END
91
92#endif
diff --git a/src/contrib/service/consensus/consensus_api.c b/src/contrib/service/consensus/consensus_api.c
new file mode 100644
index 000000000..01d0ad3de
--- /dev/null
+++ b/src/contrib/service/consensus/consensus_api.c
@@ -0,0 +1,346 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/consensus_api.c
23 * @brief
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_consensus_service.h"
30#include "consensus.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "consensus-api", __VA_ARGS__)
34
35
36/**
37 * Handle for the service.
38 */
39struct GNUNET_CONSENSUS_Handle
40{
41 /**
42 * Configuration to use.
43 */
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46 /**
47 * Callback for new elements. Not called for elements added locally.
48 */
49 GNUNET_CONSENSUS_ElementCallback new_element_cb;
50
51 /**
52 * Closure for @e new_element_cb
53 */
54 void *new_element_cls;
55
56 /**
57 * The (local) session identifier for the consensus session.
58 */
59 struct GNUNET_HashCode session_id;
60
61 /**
62 * #GNUNET_YES iff the join message has been sent to the service.
63 */
64 int joined;
65
66 /**
67 * Called when the conclude operation finishes or fails.
68 */
69 GNUNET_CONSENSUS_ConcludeCallback conclude_cb;
70
71 /**
72 * Closure for the @e conclude_cb callback.
73 */
74 void *conclude_cls;
75
76 /**
77 * Deadline for the conclude operation.
78 */
79 struct GNUNET_TIME_Absolute conclude_deadline;
80
81 /**
82 * Message queue for the client.
83 */
84 struct GNUNET_MQ_Handle *mq;
85};
86
87
88/**
89 * FIXME: this should not bee necessary when the API
90 * issue has been fixed
91 */
92struct InsertDoneInfo
93{
94 GNUNET_CONSENSUS_InsertDoneCallback idc;
95 void *cls;
96};
97
98
99/**
100 * Called when the server has sent is a new element
101 *
102 * @param cls consensus handle
103 * @param msg element message
104 */
105static int
106check_new_element (void *cls,
107 const struct GNUNET_CONSENSUS_ElementMessage *msg)
108{
109 /* any size is fine, elements are variable-size */
110 return GNUNET_OK;
111}
112
113
114/**
115 * Called when the server has sent is a new element
116 *
117 * @param cls consensus handle
118 * @param msg element message
119 */
120static void
121handle_new_element (void *cls,
122 const struct GNUNET_CONSENSUS_ElementMessage *msg)
123{
124 struct GNUNET_CONSENSUS_Handle *consensus = cls;
125 struct GNUNET_SET_Element element;
126
127 LOG (GNUNET_ERROR_TYPE_DEBUG,
128 "received new element\n");
129 element.element_type = msg->element_type;
130 element.size = ntohs (msg->header.size) - sizeof(struct
131 GNUNET_CONSENSUS_ElementMessage);
132 element.data = &msg[1];
133 consensus->new_element_cb (consensus->new_element_cls,
134 &element);
135}
136
137
138/**
139 * Called when the server has announced
140 * that the conclusion is over.
141 *
142 * @param cls consensus handle
143 * @param msg conclude done message
144 */
145static void
146handle_conclude_done (void *cls,
147 const struct GNUNET_MessageHeader *msg)
148{
149 struct GNUNET_CONSENSUS_Handle *consensus = cls;
150 GNUNET_CONSENSUS_ConcludeCallback cc;
151
152 GNUNET_MQ_destroy (consensus->mq);
153 consensus->mq = NULL;
154 GNUNET_assert (NULL != (cc = consensus->conclude_cb));
155 consensus->conclude_cb = NULL;
156 cc (consensus->conclude_cls);
157}
158
159
160/**
161 * Generic error handler, called with the appropriate
162 * error code and the same closure specified at the creation of
163 * the message queue.
164 * Not every message queue implementation supports an error handler.
165 *
166 * @param cls closure, same closure as for the message handlers
167 * @param error error code
168 */
169static void
170mq_error_handler (void *cls,
171 enum GNUNET_MQ_Error error)
172{
173 LOG (GNUNET_ERROR_TYPE_WARNING,
174 "consensus service disconnected us\n");
175}
176
177
178/**
179 * Create a consensus session.
180 *
181 * @param cfg configuration to use for connecting to the consensus service
182 * @param num_peers number of peers in the peers array
183 * @param peers array of peers participating in this consensus session
184 * Inclusion of the local peer is optional.
185 * @param session_id session identifier
186 * Allows a group of peers to have more than consensus session.
187 * @param start start time of the consensus, conclude should be called before
188 * the start time.
189 * @param deadline time when the consensus should have concluded
190 * @param new_element_cb callback, called when a new element is added to the set by
191 * another peer
192 * @param new_element_cls closure for new_element
193 * @return handle to use, NULL on error
194 */
195struct GNUNET_CONSENSUS_Handle *
196GNUNET_CONSENSUS_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
197 unsigned int num_peers,
198 const struct GNUNET_PeerIdentity *peers,
199 const struct GNUNET_HashCode *session_id,
200 struct GNUNET_TIME_Absolute start,
201 struct GNUNET_TIME_Absolute deadline,
202 GNUNET_CONSENSUS_ElementCallback new_element_cb,
203 void *new_element_cls)
204{
205 struct GNUNET_CONSENSUS_Handle *consensus
206 = GNUNET_new (struct GNUNET_CONSENSUS_Handle);
207 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
208 GNUNET_MQ_hd_var_size (new_element,
209 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT,
210 struct GNUNET_CONSENSUS_ElementMessage,
211 consensus),
212 GNUNET_MQ_hd_fixed_size (conclude_done,
213 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE_DONE,
214 struct GNUNET_MessageHeader,
215 consensus),
216 GNUNET_MQ_handler_end ()
217 };
218 struct GNUNET_CONSENSUS_JoinMessage *join_msg;
219 struct GNUNET_MQ_Envelope *ev;
220
221 consensus->cfg = cfg;
222 consensus->new_element_cb = new_element_cb;
223 consensus->new_element_cls = new_element_cls;
224 consensus->session_id = *session_id;
225 consensus->mq = GNUNET_CLIENT_connect (cfg,
226 "consensus",
227 mq_handlers,
228 &mq_error_handler,
229 consensus);
230 if (NULL == consensus->mq)
231 {
232 GNUNET_free (consensus);
233 return NULL;
234 }
235 ev = GNUNET_MQ_msg_extra (join_msg,
236 (num_peers * sizeof(struct GNUNET_PeerIdentity)),
237 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN);
238
239 join_msg->session_id = consensus->session_id;
240 join_msg->start = GNUNET_TIME_absolute_hton (start);
241 join_msg->deadline = GNUNET_TIME_absolute_hton (deadline);
242 join_msg->num_peers = htonl (num_peers);
243 GNUNET_memcpy (&join_msg[1],
244 peers,
245 num_peers * sizeof(struct GNUNET_PeerIdentity));
246
247 GNUNET_MQ_send (consensus->mq, ev);
248 return consensus;
249}
250
251
252static void
253idc_adapter (void *cls)
254{
255 struct InsertDoneInfo *i = cls;
256
257 i->idc (i->cls, GNUNET_OK);
258 GNUNET_free (i);
259}
260
261
262/**
263 * Insert an element in the set being reconsiled. Must not be called after
264 * "GNUNET_CONSENSUS_conclude".
265 *
266 * @param consensus handle for the consensus session
267 * @param element the element to be inserted
268 * @param idc function called when we are done with this element and it
269 * is thus allowed to call GNUNET_CONSENSUS_insert again
270 * @param idc_cls closure for 'idc'
271 */
272void
273GNUNET_CONSENSUS_insert (struct GNUNET_CONSENSUS_Handle *consensus,
274 const struct GNUNET_SET_Element *element,
275 GNUNET_CONSENSUS_InsertDoneCallback idc,
276 void *idc_cls)
277{
278 struct GNUNET_CONSENSUS_ElementMessage *element_msg;
279 struct GNUNET_MQ_Envelope *ev;
280 struct InsertDoneInfo *i;
281
282 LOG (GNUNET_ERROR_TYPE_DEBUG, "inserting, size=%u\n", element->size);
283
284 ev = GNUNET_MQ_msg_extra (element_msg, element->size,
285 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT);
286
287 GNUNET_memcpy (&element_msg[1], element->data, element->size);
288
289 if (NULL != idc)
290 {
291 i = GNUNET_new (struct InsertDoneInfo);
292 i->idc = idc;
293 i->cls = idc_cls;
294 GNUNET_MQ_notify_sent (ev, idc_adapter, i);
295 }
296 GNUNET_MQ_send (consensus->mq, ev);
297}
298
299
300/**
301 * We are done with inserting new elements into the consensus;
302 * try to conclude the consensus within a given time window.
303 * After conclude has been called, no further elements may be
304 * inserted by the client.
305 *
306 * @param consensus consensus session
307 * @param conclude called when the conclusion was successful
308 * @param conclude_cls closure for the conclude callback
309 */
310void
311GNUNET_CONSENSUS_conclude (struct GNUNET_CONSENSUS_Handle *consensus,
312 GNUNET_CONSENSUS_ConcludeCallback conclude,
313 void *conclude_cls)
314{
315 struct GNUNET_MQ_Envelope *ev;
316
317 GNUNET_assert (NULL != conclude);
318 GNUNET_assert (NULL == consensus->conclude_cb);
319
320 consensus->conclude_cls = conclude_cls;
321 consensus->conclude_cb = conclude;
322
323 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE);
324 GNUNET_MQ_send (consensus->mq, ev);
325}
326
327
328/**
329 * Destroy a consensus handle (free all state associated with
330 * it, no longer call any of the callbacks).
331 *
332 * @param consensus handle to destroy
333 */
334void
335GNUNET_CONSENSUS_destroy (struct GNUNET_CONSENSUS_Handle *consensus)
336{
337 if (NULL != consensus->mq)
338 {
339 GNUNET_MQ_destroy (consensus->mq);
340 consensus->mq = NULL;
341 }
342 GNUNET_free (consensus);
343}
344
345
346/* end of consensus_api.c */
diff --git a/src/contrib/service/consensus/consensus_protocol.h b/src/contrib/service/consensus/consensus_protocol.h
new file mode 100644
index 000000000..0afd56b27
--- /dev/null
+++ b/src/contrib/service/consensus/consensus_protocol.h
@@ -0,0 +1,137 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21
22/**
23 * @file consensus/consensus_protocol.h
24 * @brief p2p message definitions for consensus
25 * @author Florian Dold
26 */
27
28#ifndef GNUNET_CONSENSUS_PROTOCOL_H
29#define GNUNET_CONSENSUS_PROTOCOL_H
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_common.h"
34#include "gnunet_protocols.h"
35
36
37GNUNET_NETWORK_STRUCT_BEGIN
38
39/**
40 * Sent as context message for set reconciliation.
41 *
42 * Essentially contains all the fields
43 * from 'struct TaskKey', but in NBO.
44 */
45struct GNUNET_CONSENSUS_RoundContextMessage
46{
47 /**
48 * Type: #GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT
49 */
50 struct GNUNET_MessageHeader header;
51
52 /**
53 * A value from 'enum PhaseKind'.
54 */
55 uint16_t kind GNUNET_PACKED;
56
57 /**
58 * Number of the first peer
59 * in canonical order.
60 */
61 int16_t peer1 GNUNET_PACKED;
62
63 /**
64 * Number of the second peer in canonical order.
65 */
66 int16_t peer2 GNUNET_PACKED;
67
68 /**
69 * Repetition of the gradecast phase.
70 */
71 int16_t repetition GNUNET_PACKED;
72
73 /**
74 * Leader in the gradecast phase.
75 *
76 * Can be different from both peer1 and peer2.
77 */
78 int16_t leader GNUNET_PACKED;
79
80 /**
81 * Non-zero if this set reconciliation
82 * had elements removed because they were contested.
83 *
84 * Will be considered when grading broadcasts.
85 *
86 * Ignored for set operations that are not within gradecasts.
87 */
88 uint16_t is_contested GNUNET_PACKED;
89};
90
91
92enum
93{
94 CONSENSUS_MARKER_CONTESTED = 1,
95 CONSENSUS_MARKER_SIZE = 2,
96};
97
98
99/**
100 * Consensus element, either marker or payload.
101 */
102struct ConsensusElement
103{
104 /**
105 * Payload element_type, only valid
106 * if this is not a marker element.
107 */
108 uint16_t payload_type GNUNET_PACKED;
109
110 /**
111 * Is this a marker element?
112 */
113 uint8_t marker;
114
115 /* rest: element data */
116};
117
118
119struct ConsensusSizeElement
120{
121 struct ConsensusElement ce;
122
123 uint64_t size GNUNET_PACKED;
124 uint8_t sender_index;
125};
126
127
128struct ConsensusStuffedElement
129{
130 struct ConsensusElement ce;
131 struct GNUNET_HashCode rand GNUNET_PACKED;
132};
133
134
135GNUNET_NETWORK_STRUCT_END
136
137#endif
diff --git a/src/contrib/service/consensus/gnunet-service-consensus.c b/src/contrib/service/consensus/gnunet-service-consensus.c
new file mode 100644
index 000000000..5b6b9bbd7
--- /dev/null
+++ b/src/contrib/service/consensus/gnunet-service-consensus.c
@@ -0,0 +1,3572 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2013, 2017, 2020 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file consensus/gnunet-service-consensus.c
22 * @brief multi-peer set reconciliation
23 * @author Florian Dold <flo@dold.me>
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_block_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_applications.h"
30#include "gnunet_set_service.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_consensus_service.h"
33#include "consensus_protocol.h"
34#include "consensus.h"
35
36
37enum ReferendumVote
38{
39 /**
40 * Vote that nothing should change.
41 * This option is never voted explicitly.
42 */
43 VOTE_STAY = 0,
44 /**
45 * Vote that an element should be added.
46 */
47 VOTE_ADD = 1,
48 /**
49 * Vote that an element should be removed.
50 */
51 VOTE_REMOVE = 2,
52};
53
54
55enum EarlyStoppingPhase
56{
57 EARLY_STOPPING_NONE = 0,
58 EARLY_STOPPING_ONE_MORE = 1,
59 EARLY_STOPPING_DONE = 2,
60};
61
62
63enum PhaseKind
64{
65 PHASE_KIND_ALL_TO_ALL,
66 PHASE_KIND_ALL_TO_ALL_2,
67 PHASE_KIND_GRADECAST_LEADER,
68 PHASE_KIND_GRADECAST_ECHO,
69 PHASE_KIND_GRADECAST_ECHO_GRADE,
70 PHASE_KIND_GRADECAST_CONFIRM,
71 PHASE_KIND_GRADECAST_CONFIRM_GRADE,
72 /**
73 * Apply a repetition of the all-to-all
74 * gradecast to the current set.
75 */
76 PHASE_KIND_APPLY_REP,
77 PHASE_KIND_FINISH,
78};
79
80
81enum SetKind
82{
83 SET_KIND_NONE = 0,
84 SET_KIND_CURRENT,
85 /**
86 * Last result set from a gradecast
87 */
88 SET_KIND_LAST_GRADECAST,
89 SET_KIND_LEADER_PROPOSAL,
90 SET_KIND_ECHO_RESULT,
91};
92
93enum DiffKind
94{
95 DIFF_KIND_NONE = 0,
96 DIFF_KIND_LEADER_PROPOSAL,
97 DIFF_KIND_LEADER_CONSENSUS,
98 DIFF_KIND_GRADECAST_RESULT,
99};
100
101enum RfnKind
102{
103 RFN_KIND_NONE = 0,
104 RFN_KIND_ECHO,
105 RFN_KIND_CONFIRM,
106 RFN_KIND_GRADECAST_RESULT
107};
108
109
110GNUNET_NETWORK_STRUCT_BEGIN
111
112/**
113 * Tuple of integers that together identify a task uniquely.
114 */
115struct TaskKey
116{
117 /**
118 * A value from 'enum PhaseKind'.
119 */
120 uint16_t kind GNUNET_PACKED;
121
122 /**
123 * Number of the first peer
124 * in canonical order.
125 */
126 int16_t peer1 GNUNET_PACKED;
127
128 /**
129 * Number of the second peer in canonical order.
130 */
131 int16_t peer2 GNUNET_PACKED;
132
133 /**
134 * Repetition of the gradecast phase.
135 */
136 int16_t repetition GNUNET_PACKED;
137
138 /**
139 * Leader in the gradecast phase.
140 *
141 * Can be different from both peer1 and peer2.
142 */
143 int16_t leader GNUNET_PACKED;
144};
145
146
147struct SetKey
148{
149 enum SetKind set_kind GNUNET_PACKED;
150 /**
151 * Repetition counter.
152 */
153 int k1 GNUNET_PACKED;
154 /**
155 * Leader (or 0).
156 */
157 int k2 GNUNET_PACKED;
158};
159
160
161struct SetEntry
162{
163 struct SetKey key;
164 struct GNUNET_SET_Handle *h;
165
166 /**
167 * #GNUNET_YES if the set resulted from applying a referendum with contested
168 * elements.
169 */
170 int is_contested;
171};
172
173
174struct DiffKey
175{
176 enum DiffKind diff_kind GNUNET_PACKED;
177
178 int k1 GNUNET_PACKED;
179
180 int k2 GNUNET_PACKED;
181};
182
183struct RfnKey
184{
185 enum RfnKind rfn_kind GNUNET_PACKED;
186 int k1 GNUNET_PACKED;
187 int k2 GNUNET_PACKED;
188};
189
190
191GNUNET_NETWORK_STRUCT_END
192
193
194struct SetOpCls
195{
196 struct SetKey input_set;
197
198 struct SetKey output_set;
199 struct RfnKey output_rfn;
200 struct DiffKey output_diff;
201
202 int do_not_remove;
203
204 int transceive_contested;
205
206 struct GNUNET_SET_OperationHandle *op;
207};
208
209
210struct FinishCls
211{
212 struct SetKey input_set;
213};
214
215/**
216 * Closure for both @a start_task
217 * and @a cancel_task.
218 */
219union TaskFuncCls
220{
221 struct SetOpCls setop;
222 struct FinishCls finish;
223};
224
225
226struct TaskEntry;
227
228
229typedef void
230(*TaskFunc) (struct TaskEntry *task);
231
232
233/*
234 * Node in the consensus task graph.
235 */
236struct TaskEntry
237{
238 struct TaskKey key;
239
240 struct Step *step;
241
242 int is_started;
243
244 int is_finished;
245
246 TaskFunc start;
247 TaskFunc cancel;
248
249 union TaskFuncCls cls;
250};
251
252
253struct Step
254{
255 /**
256 * All steps of one session are in a
257 * linked list for easier deallocation.
258 */
259 struct Step *prev;
260
261 /**
262 * All steps of one session are in a
263 * linked list for easier deallocation.
264 */
265 struct Step *next;
266
267 struct ConsensusSession *session;
268
269 /**
270 * Tasks that this step is composed of.
271 */
272 struct TaskEntry **tasks;
273 unsigned int tasks_len;
274 unsigned int tasks_cap;
275
276 unsigned int finished_tasks;
277
278 /*
279 * Tasks that have this task as dependency.
280 *
281 * We store pointers to subordinates rather
282 * than to prerequisites since it makes
283 * tracking the readiness of a task easier.
284 */
285 struct Step **subordinates;
286 unsigned int subordinates_len;
287 unsigned int subordinates_cap;
288
289 /**
290 * Counter for the prerequisites of this step.
291 */
292 size_t pending_prereq;
293
294 /**
295 * Task that will run this step despite any pending prerequisites.
296 */
297 struct GNUNET_SCHEDULER_Task *timeout_task;
298
299 unsigned int is_running;
300
301 unsigned int is_finished;
302
303 /**
304 * Synchrony round of the task. Determines the deadline for the task.
305 */
306 unsigned int round;
307
308 /**
309 * Human-readable name for the task, used for debugging.
310 */
311 char *debug_name;
312
313 /**
314 * When we're doing an early finish, how should this step be treated? If
315 * #GNUNET_YES, the step will be marked as finished without actually running
316 * its tasks. Otherwise, the step will still be run even after an early
317 * finish.
318 *
319 * Note that a task may never be finished early if it is already running.
320 */
321 int early_finishable;
322};
323
324
325struct RfnElementInfo
326{
327 const struct GNUNET_SET_Element *element;
328
329 /**
330 * #GNUNET_YES if the peer votes for the proposal.
331 */
332 int *votes;
333
334 /**
335 * Proposal for this element, can only be #VOTE_ADD or #VOTE_REMOVE.
336 */
337 enum ReferendumVote proposal;
338};
339
340
341struct ReferendumEntry
342{
343 struct RfnKey key;
344
345 /*
346 * Elements where there is at least one proposed change.
347 *
348 * Maps the hash of the GNUNET_SET_Element
349 * to 'struct RfnElementInfo'.
350 */
351 struct GNUNET_CONTAINER_MultiHashMap *rfn_elements;
352
353 unsigned int num_peers;
354
355 /**
356 * Stores, for every peer in the session,
357 * whether the peer finished the whole referendum.
358 *
359 * Votes from peers are only counted if they're
360 * marked as committed (#GNUNET_YES) in the referendum.
361 *
362 * Otherwise (#GNUNET_NO), the requested changes are
363 * not counted for majority votes or thresholds.
364 */
365 int *peer_commited;
366
367
368 /**
369 * Contestation state of the peer. If a peer is contested, the values it
370 * contributed are still counted for applying changes, but the grading is
371 * affected.
372 */
373 int *peer_contested;
374};
375
376
377struct DiffElementInfo
378{
379 const struct GNUNET_SET_Element *element;
380
381 /**
382 * Positive weight for 'add', negative
383 * weights for 'remove'.
384 */
385 int weight;
386};
387
388
389/**
390 * Weighted diff.
391 */
392struct DiffEntry
393{
394 struct DiffKey key;
395 struct GNUNET_CONTAINER_MultiHashMap *changes;
396};
397
398struct SetHandle
399{
400 struct SetHandle *prev;
401 struct SetHandle *next;
402
403 struct GNUNET_SET_Handle *h;
404};
405
406
407/**
408 * A consensus session consists of one local client and the remote authorities.
409 */
410struct ConsensusSession
411{
412 /**
413 * Consensus sessions are kept in a DLL.
414 */
415 struct ConsensusSession *next;
416
417 /**
418 * Consensus sessions are kept in a DLL.
419 */
420 struct ConsensusSession *prev;
421
422 unsigned int num_client_insert_pending;
423
424 struct GNUNET_CONTAINER_MultiHashMap *setmap;
425 struct GNUNET_CONTAINER_MultiHashMap *rfnmap;
426 struct GNUNET_CONTAINER_MultiHashMap *diffmap;
427
428 /**
429 * Array of peers with length 'num_peers'.
430 */
431 int *peers_blacklisted;
432
433 /*
434 * Mapping from (hashed) TaskKey to TaskEntry.
435 *
436 * We map the application_id for a round to the task that should be
437 * executed, so we don't have to go through all task whenever we get
438 * an incoming set op request.
439 */
440 struct GNUNET_CONTAINER_MultiHashMap *taskmap;
441
442 struct Step *steps_head;
443 struct Step *steps_tail;
444
445 int conclude_started;
446
447 int conclude_done;
448
449 /**
450 * Global consensus identification, computed
451 * from the session id and participating authorities.
452 */
453 struct GNUNET_HashCode global_id;
454
455 /**
456 * Client that inhabits the session
457 */
458 struct GNUNET_SERVICE_Client *client;
459
460 /**
461 * Queued messages to the client.
462 */
463 struct GNUNET_MQ_Handle *client_mq;
464
465 /**
466 * Time when the conclusion of the consensus should begin.
467 */
468 struct GNUNET_TIME_Absolute conclude_start;
469
470 /**
471 * Timeout for all rounds together, single rounds will schedule a timeout task
472 * with a fraction of the conclude timeout.
473 * Only valid once the current round is not CONSENSUS_ROUND_BEGIN.
474 */
475 struct GNUNET_TIME_Absolute conclude_deadline;
476
477 struct GNUNET_PeerIdentity *peers;
478
479 /**
480 * Number of other peers in the consensus.
481 */
482 unsigned int num_peers;
483
484 /**
485 * Index of the local peer in the peers array
486 */
487 unsigned int local_peer_idx;
488
489 /**
490 * Listener for requests from other peers.
491 * Uses the session's global id as app id.
492 */
493 struct GNUNET_SET_ListenHandle *set_listener;
494
495 /**
496 * State of our early stopping scheme.
497 */
498 int early_stopping;
499
500 /**
501 * Our set size from the first round.
502 */
503 uint64_t first_size;
504
505 uint64_t *first_sizes_received;
506
507 /**
508 * Bounded Eppstein lower bound.
509 */
510 uint64_t lower_bound;
511
512 struct SetHandle *set_handles_head;
513 struct SetHandle *set_handles_tail;
514};
515
516/**
517 * Linked list of sessions this peer participates in.
518 */
519static struct ConsensusSession *sessions_head;
520
521/**
522 * Linked list of sessions this peer participates in.
523 */
524static struct ConsensusSession *sessions_tail;
525
526/**
527 * Configuration of the consensus service.
528 */
529static const struct GNUNET_CONFIGURATION_Handle *cfg;
530
531/**
532 * Peer that runs this service.
533 */
534static struct GNUNET_PeerIdentity my_peer;
535
536/**
537 * Statistics handle.
538 */
539struct GNUNET_STATISTICS_Handle *statistics;
540
541
542static void
543finish_task (struct TaskEntry *task);
544
545
546static void
547run_ready_steps (struct ConsensusSession *session);
548
549
550static const char *
551phasename (uint16_t phase)
552{
553 switch (phase)
554 {
555 case PHASE_KIND_ALL_TO_ALL: return "ALL_TO_ALL";
556
557 case PHASE_KIND_ALL_TO_ALL_2: return "ALL_TO_ALL_2";
558
559 case PHASE_KIND_FINISH: return "FINISH";
560
561 case PHASE_KIND_GRADECAST_LEADER: return "GRADECAST_LEADER";
562
563 case PHASE_KIND_GRADECAST_ECHO: return "GRADECAST_ECHO";
564
565 case PHASE_KIND_GRADECAST_ECHO_GRADE: return "GRADECAST_ECHO_GRADE";
566
567 case PHASE_KIND_GRADECAST_CONFIRM: return "GRADECAST_CONFIRM";
568
569 case PHASE_KIND_GRADECAST_CONFIRM_GRADE: return "GRADECAST_CONFIRM_GRADE";
570
571 case PHASE_KIND_APPLY_REP: return "APPLY_REP";
572
573 default: return "(unknown)";
574 }
575}
576
577
578static const char *
579setname (uint16_t kind)
580{
581 switch (kind)
582 {
583 case SET_KIND_CURRENT: return "CURRENT";
584
585 case SET_KIND_LEADER_PROPOSAL: return "LEADER_PROPOSAL";
586
587 case SET_KIND_NONE: return "NONE";
588
589 default: return "(unknown)";
590 }
591}
592
593
594static const char *
595rfnname (uint16_t kind)
596{
597 switch (kind)
598 {
599 case RFN_KIND_NONE: return "NONE";
600
601 case RFN_KIND_ECHO: return "ECHO";
602
603 case RFN_KIND_CONFIRM: return "CONFIRM";
604
605 default: return "(unknown)";
606 }
607}
608
609
610static const char *
611diffname (uint16_t kind)
612{
613 switch (kind)
614 {
615 case DIFF_KIND_NONE: return "NONE";
616
617 case DIFF_KIND_LEADER_CONSENSUS: return "LEADER_CONSENSUS";
618
619 case DIFF_KIND_GRADECAST_RESULT: return "GRADECAST_RESULT";
620
621 case DIFF_KIND_LEADER_PROPOSAL: return "LEADER_PROPOSAL";
622
623 default: return "(unknown)";
624 }
625}
626
627
628#ifdef GNUNET_EXTRA_LOGGING
629
630
631static const char *
632debug_str_element (const struct GNUNET_SET_Element *el)
633{
634 struct GNUNET_HashCode hash;
635
636 GNUNET_SET_element_hash (el, &hash);
637
638 return GNUNET_h2s (&hash);
639}
640
641
642static const char *
643debug_str_task_key (const struct TaskKey *tk)
644{
645 static char buf[256];
646
647 snprintf (buf, sizeof(buf),
648 "TaskKey kind=%s, p1=%d, p2=%d, l=%d, rep=%d",
649 phasename (tk->kind), tk->peer1, tk->peer2,
650 tk->leader, tk->repetition);
651
652 return buf;
653}
654
655
656static const char *
657debug_str_diff_key (const struct DiffKey *dk)
658{
659 static char buf[256];
660
661 snprintf (buf, sizeof(buf),
662 "DiffKey kind=%s, k1=%d, k2=%d",
663 diffname (dk->diff_kind), dk->k1, dk->k2);
664
665 return buf;
666}
667
668
669static const char *
670debug_str_set_key (const struct SetKey *sk)
671{
672 static char buf[256];
673
674 snprintf (buf, sizeof(buf),
675 "SetKey kind=%s, k1=%d, k2=%d",
676 setname (sk->set_kind),
677 sk->k1,
678 sk->k2);
679 return buf;
680}
681
682
683static const char *
684debug_str_rfn_key (const struct RfnKey *rk)
685{
686 static char buf[256];
687
688 snprintf (buf, sizeof(buf),
689 "RfnKey kind=%s, k1=%d, k2=%d",
690 rfnname (rk->rfn_kind),
691 rk->k1,
692 rk->k2);
693 return buf;
694}
695
696
697#endif /* GNUNET_EXTRA_LOGGING */
698
699
700/**
701 * Send the final result set of the consensus to the client, element by
702 * element.
703 *
704 * @param cls closure
705 * @param element the current element, NULL if all elements have been
706 * iterated over
707 * @return #GNUNET_YES to continue iterating, #GNUNET_NO to stop.
708 */
709static int
710send_to_client_iter (void *cls,
711 const struct GNUNET_SET_Element *element)
712{
713 struct TaskEntry *task = (struct TaskEntry *) cls;
714 struct ConsensusSession *session = task->step->session;
715 struct GNUNET_MQ_Envelope *ev;
716
717 if (NULL != element)
718 {
719 struct GNUNET_CONSENSUS_ElementMessage *m;
720 const struct ConsensusElement *ce;
721
722 GNUNET_assert (GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT ==
723 element->element_type);
724 ce = element->data;
725
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "marker is %u\n",
727 (unsigned) ce->marker);
728
729 if (0 != ce->marker)
730 return GNUNET_YES;
731
732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733 "P%d: sending element %s to client\n",
734 session->local_peer_idx,
735 debug_str_element (element));
736
737 ev = GNUNET_MQ_msg_extra (m,
738 element->size - sizeof(struct ConsensusElement),
739 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_RECEIVED_ELEMENT);
740 m->element_type = ce->payload_type;
741 GNUNET_memcpy (&m[1],
742 &ce[1],
743 element->size - sizeof(struct ConsensusElement));
744 GNUNET_MQ_send (session->client_mq,
745 ev);
746 }
747 else
748 {
749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750 "P%d: finished iterating elements for client\n",
751 session->local_peer_idx);
752 ev = GNUNET_MQ_msg_header (
753 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE_DONE);
754 GNUNET_MQ_send (session->client_mq,
755 ev);
756 }
757 return GNUNET_YES;
758}
759
760
761static struct SetEntry *
762lookup_set (struct ConsensusSession *session,
763 const struct SetKey *key)
764{
765 struct GNUNET_HashCode hash;
766
767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768 "P%u: looking up set {%s}\n",
769 session->local_peer_idx,
770 debug_str_set_key (key));
771
772 GNUNET_assert (SET_KIND_NONE != key->set_kind);
773 GNUNET_CRYPTO_hash (key,
774 sizeof(struct SetKey),
775 &hash);
776 return GNUNET_CONTAINER_multihashmap_get (session->setmap,
777 &hash);
778}
779
780
781static struct DiffEntry *
782lookup_diff (struct ConsensusSession *session,
783 const struct DiffKey *key)
784{
785 struct GNUNET_HashCode hash;
786
787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788 "P%u: looking up diff {%s}\n",
789 session->local_peer_idx,
790 debug_str_diff_key (key));
791 GNUNET_assert (DIFF_KIND_NONE != key->diff_kind);
792 GNUNET_CRYPTO_hash (key,
793 sizeof(struct DiffKey),
794 &hash);
795 return GNUNET_CONTAINER_multihashmap_get (session->diffmap,
796 &hash);
797}
798
799
800static struct ReferendumEntry *
801lookup_rfn (struct ConsensusSession *session,
802 const struct RfnKey *key)
803{
804 struct GNUNET_HashCode hash;
805
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "P%u: looking up rfn {%s}\n",
808 session->local_peer_idx,
809 debug_str_rfn_key (key));
810 GNUNET_assert (RFN_KIND_NONE != key->rfn_kind);
811 GNUNET_CRYPTO_hash (key,
812 sizeof(struct RfnKey),
813 &hash);
814 return GNUNET_CONTAINER_multihashmap_get (session->rfnmap,
815 &hash);
816}
817
818
819static void
820diff_insert (struct DiffEntry *diff,
821 int weight,
822 const struct GNUNET_SET_Element *element)
823{
824 struct DiffElementInfo *di;
825 struct GNUNET_HashCode hash;
826
827 GNUNET_assert ((1 == weight) || (-1 == weight));
828
829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
830 "diff_insert with element size %u\n",
831 element->size);
832
833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
834 "hashing element\n");
835
836 GNUNET_SET_element_hash (element, &hash);
837
838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
839 "hashed element\n");
840
841 di = GNUNET_CONTAINER_multihashmap_get (diff->changes, &hash);
842
843 if (NULL == di)
844 {
845 di = GNUNET_new (struct DiffElementInfo);
846 di->element = GNUNET_SET_element_dup (element);
847 GNUNET_assert (GNUNET_OK ==
848 GNUNET_CONTAINER_multihashmap_put (diff->changes,
849 &hash,
850 di,
851 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
852 }
853
854 di->weight = weight;
855}
856
857
858static void
859rfn_commit (struct ReferendumEntry *rfn,
860 uint16_t commit_peer)
861{
862 GNUNET_assert (commit_peer < rfn->num_peers);
863
864 rfn->peer_commited[commit_peer] = GNUNET_YES;
865}
866
867
868static void
869rfn_contest (struct ReferendumEntry *rfn,
870 uint16_t contested_peer)
871{
872 GNUNET_assert (contested_peer < rfn->num_peers);
873
874 rfn->peer_contested[contested_peer] = GNUNET_YES;
875}
876
877
878static uint16_t
879rfn_noncontested (struct ReferendumEntry *rfn)
880{
881 uint16_t ret;
882
883 ret = 0;
884 for (uint16_t i = 0; i < rfn->num_peers; i++)
885 if ((GNUNET_YES == rfn->peer_commited[i]) && (GNUNET_NO ==
886 rfn->peer_contested[i]))
887 ret++;
888
889 return ret;
890}
891
892
893static void
894rfn_vote (struct ReferendumEntry *rfn,
895 uint16_t voting_peer,
896 enum ReferendumVote vote,
897 const struct GNUNET_SET_Element *element)
898{
899 struct RfnElementInfo *ri;
900 struct GNUNET_HashCode hash;
901
902 GNUNET_assert (voting_peer < rfn->num_peers);
903
904 /* Explicit voting only makes sense with VOTE_ADD or VOTE_REMOTE,
905 since VOTE_KEEP is implicit in not voting. */
906 GNUNET_assert ((VOTE_ADD == vote) || (VOTE_REMOVE == vote));
907
908 GNUNET_SET_element_hash (element, &hash);
909 ri = GNUNET_CONTAINER_multihashmap_get (rfn->rfn_elements, &hash);
910
911 if (NULL == ri)
912 {
913 ri = GNUNET_new (struct RfnElementInfo);
914 ri->element = GNUNET_SET_element_dup (element);
915 ri->votes = GNUNET_new_array (rfn->num_peers, int);
916 GNUNET_assert (GNUNET_OK ==
917 GNUNET_CONTAINER_multihashmap_put (rfn->rfn_elements,
918 &hash, ri,
919 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
920 }
921
922 ri->votes[voting_peer] = GNUNET_YES;
923 ri->proposal = vote;
924}
925
926
927static uint16_t
928task_other_peer (struct TaskEntry *task)
929{
930 uint16_t me = task->step->session->local_peer_idx;
931
932 if (task->key.peer1 == me)
933 return task->key.peer2;
934 return task->key.peer1;
935}
936
937
938static int
939cmp_uint64_t (const void *pa, const void *pb)
940{
941 uint64_t a = *(uint64_t *) pa;
942 uint64_t b = *(uint64_t *) pb;
943
944 if (a == b)
945 return 0;
946 if (a < b)
947 return -1;
948 return 1;
949}
950
951
952/**
953 * Callback for set operation results. Called for each element
954 * in the result set.
955 *
956 * @param cls closure
957 * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
958 * @param current_size current set size
959 * @param status see enum GNUNET_SET_Status
960 */
961static void
962set_result_cb (void *cls,
963 const struct GNUNET_SET_Element *element,
964 uint64_t current_size,
965 enum GNUNET_SET_Status status)
966{
967 struct TaskEntry *task = cls;
968 struct ConsensusSession *session = task->step->session;
969 struct SetEntry *output_set = NULL;
970 struct DiffEntry *output_diff = NULL;
971 struct ReferendumEntry *output_rfn = NULL;
972 unsigned int other_idx;
973 struct SetOpCls *setop;
974 const struct ConsensusElement *consensus_element = NULL;
975
976 if (NULL != element)
977 {
978 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
979 "P%u: got element of type %u, status %u\n",
980 session->local_peer_idx,
981 (unsigned) element->element_type,
982 (unsigned) status);
983 GNUNET_assert (GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT ==
984 element->element_type);
985 consensus_element = element->data;
986 }
987
988 setop = &task->cls.setop;
989
990
991 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
992 "P%u: got set result for {%s}, status %u\n",
993 session->local_peer_idx,
994 debug_str_task_key (&task->key),
995 status);
996
997 if (GNUNET_NO == task->is_started)
998 {
999 GNUNET_break_op (0);
1000 return;
1001 }
1002
1003 if (GNUNET_YES == task->is_finished)
1004 {
1005 GNUNET_break_op (0);
1006 return;
1007 }
1008
1009 other_idx = task_other_peer (task);
1010
1011 if (SET_KIND_NONE != setop->output_set.set_kind)
1012 {
1013 output_set = lookup_set (session,
1014 &setop->output_set);
1015 GNUNET_assert (NULL != output_set);
1016 }
1017
1018 if (DIFF_KIND_NONE != setop->output_diff.diff_kind)
1019 {
1020 output_diff = lookup_diff (session, &setop->output_diff);
1021 GNUNET_assert (NULL != output_diff);
1022 }
1023
1024 if (RFN_KIND_NONE != setop->output_rfn.rfn_kind)
1025 {
1026 output_rfn = lookup_rfn (session, &setop->output_rfn);
1027 GNUNET_assert (NULL != output_rfn);
1028 }
1029
1030 if (GNUNET_YES == session->peers_blacklisted[other_idx])
1031 {
1032 /* Peer might have been blacklisted
1033 by a gradecast running in parallel, ignore elements from now */
1034 if (GNUNET_SET_STATUS_ADD_LOCAL == status)
1035 return;
1036 if (GNUNET_SET_STATUS_ADD_REMOTE == status)
1037 return;
1038 }
1039
1040 if ((NULL != consensus_element) && (0 != consensus_element->marker))
1041 {
1042 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043 "P%u: got some marker\n",
1044 session->local_peer_idx);
1045 if ((GNUNET_YES == setop->transceive_contested) &&
1046 (CONSENSUS_MARKER_CONTESTED == consensus_element->marker))
1047 {
1048 GNUNET_assert (NULL != output_rfn);
1049 rfn_contest (output_rfn, task_other_peer (task));
1050 return;
1051 }
1052
1053 if (CONSENSUS_MARKER_SIZE == consensus_element->marker)
1054 {
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056 "P%u: got size marker\n",
1057 session->local_peer_idx);
1058
1059
1060 struct ConsensusSizeElement *cse = (void *) consensus_element;
1061
1062 if (cse->sender_index == other_idx)
1063 {
1064 if (NULL == session->first_sizes_received)
1065 session->first_sizes_received = GNUNET_new_array (session->num_peers,
1066 uint64_t);
1067 session->first_sizes_received[other_idx] = GNUNET_ntohll (cse->size);
1068
1069 uint64_t *copy = GNUNET_memdup (session->first_sizes_received,
1070 sizeof(uint64_t) * session->num_peers);
1071 qsort (copy, session->num_peers, sizeof(uint64_t), cmp_uint64_t);
1072 session->lower_bound = copy[session->num_peers / 3 + 1];
1073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1074 "P%u: lower bound %llu\n",
1075 session->local_peer_idx,
1076 (long long) session->lower_bound);
1077 GNUNET_free (copy);
1078 }
1079 return;
1080 }
1081
1082 return;
1083 }
1084
1085 switch (status)
1086 {
1087 case GNUNET_SET_STATUS_ADD_LOCAL:
1088 GNUNET_assert (NULL != consensus_element);
1089 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090 "Adding element in Task {%s}\n",
1091 debug_str_task_key (&task->key));
1092 if (NULL != output_set)
1093 {
1094 // FIXME: record pending adds, use callback
1095 GNUNET_SET_add_element (output_set->h,
1096 element,
1097 NULL,
1098 NULL);
1099#ifdef GNUNET_EXTRA_LOGGING
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101 "P%u: adding element %s into set {%s} of task {%s}\n",
1102 session->local_peer_idx,
1103 debug_str_element (element),
1104 debug_str_set_key (&setop->output_set),
1105 debug_str_task_key (&task->key));
1106#endif
1107 }
1108 if (NULL != output_diff)
1109 {
1110 diff_insert (output_diff, 1, element);
1111#ifdef GNUNET_EXTRA_LOGGING
1112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113 "P%u: adding element %s into diff {%s} of task {%s}\n",
1114 session->local_peer_idx,
1115 debug_str_element (element),
1116 debug_str_diff_key (&setop->output_diff),
1117 debug_str_task_key (&task->key));
1118#endif
1119 }
1120 if (NULL != output_rfn)
1121 {
1122 rfn_vote (output_rfn, task_other_peer (task), VOTE_ADD, element);
1123#ifdef GNUNET_EXTRA_LOGGING
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125 "P%u: adding element %s into rfn {%s} of task {%s}\n",
1126 session->local_peer_idx,
1127 debug_str_element (element),
1128 debug_str_rfn_key (&setop->output_rfn),
1129 debug_str_task_key (&task->key));
1130#endif
1131 }
1132 // XXX: add result to structures in task
1133 break;
1134
1135 case GNUNET_SET_STATUS_ADD_REMOTE:
1136 GNUNET_assert (NULL != consensus_element);
1137 if (GNUNET_YES == setop->do_not_remove)
1138 break;
1139 if (CONSENSUS_MARKER_CONTESTED == consensus_element->marker)
1140 break;
1141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142 "Removing element in Task {%s}\n",
1143 debug_str_task_key (&task->key));
1144 if (NULL != output_set)
1145 {
1146 // FIXME: record pending adds, use callback
1147 GNUNET_SET_remove_element (output_set->h,
1148 element,
1149 NULL,
1150 NULL);
1151#ifdef GNUNET_EXTRA_LOGGING
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "P%u: removing element %s from set {%s} of task {%s}\n",
1154 session->local_peer_idx,
1155 debug_str_element (element),
1156 debug_str_set_key (&setop->output_set),
1157 debug_str_task_key (&task->key));
1158#endif
1159 }
1160 if (NULL != output_diff)
1161 {
1162 diff_insert (output_diff, -1, element);
1163#ifdef GNUNET_EXTRA_LOGGING
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1165 "P%u: removing element %s from diff {%s} of task {%s}\n",
1166 session->local_peer_idx,
1167 debug_str_element (element),
1168 debug_str_diff_key (&setop->output_diff),
1169 debug_str_task_key (&task->key));
1170#endif
1171 }
1172 if (NULL != output_rfn)
1173 {
1174 rfn_vote (output_rfn, task_other_peer (task), VOTE_REMOVE, element);
1175#ifdef GNUNET_EXTRA_LOGGING
1176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1177 "P%u: removing element %s from rfn {%s} of task {%s}\n",
1178 session->local_peer_idx,
1179 debug_str_element (element),
1180 debug_str_rfn_key (&setop->output_rfn),
1181 debug_str_task_key (&task->key));
1182#endif
1183 }
1184 break;
1185
1186 case GNUNET_SET_STATUS_DONE:
1187 // XXX: check first if any changes to the underlying
1188 // set are still pending
1189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190 "P%u: Finishing setop in Task {%s} (%u/%u)\n",
1191 session->local_peer_idx,
1192 debug_str_task_key (&task->key),
1193 (unsigned int) task->step->finished_tasks,
1194 (unsigned int) task->step->tasks_len);
1195 if (NULL != output_rfn)
1196 {
1197 rfn_commit (output_rfn, task_other_peer (task));
1198 }
1199 if (PHASE_KIND_ALL_TO_ALL == task->key.kind)
1200 {
1201 session->first_size = current_size;
1202 }
1203 finish_task (task);
1204 break;
1205
1206 case GNUNET_SET_STATUS_FAILURE:
1207 // XXX: cleanup
1208 GNUNET_break_op (0);
1209 finish_task (task);
1210 return;
1211
1212 default:
1213 /* not reached */
1214 GNUNET_assert (0);
1215 }
1216}
1217
1218
1219#ifdef EVIL
1220
1221enum EvilnessType
1222{
1223 EVILNESS_NONE,
1224 EVILNESS_CRAM_ALL,
1225 EVILNESS_CRAM_LEAD,
1226 EVILNESS_CRAM_ECHO,
1227 EVILNESS_SLACK,
1228 EVILNESS_SLACK_A2A,
1229};
1230
1231enum EvilnessSubType
1232{
1233 EVILNESS_SUB_NONE,
1234 EVILNESS_SUB_REPLACEMENT,
1235 EVILNESS_SUB_NO_REPLACEMENT,
1236};
1237
1238struct Evilness
1239{
1240 enum EvilnessType type;
1241 enum EvilnessSubType subtype;
1242 unsigned int num;
1243};
1244
1245
1246static int
1247parse_evilness_cram_subtype (const char *evil_subtype_str,
1248 struct Evilness *evil)
1249{
1250 if (0 == strcmp ("replace", evil_subtype_str))
1251 {
1252 evil->subtype = EVILNESS_SUB_REPLACEMENT;
1253 }
1254 else if (0 == strcmp ("noreplace", evil_subtype_str))
1255 {
1256 evil->subtype = EVILNESS_SUB_NO_REPLACEMENT;
1257 }
1258 else
1259 {
1260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1261 "Malformed field '%s' in EVIL_SPEC (unknown subtype), behaving like a good peer.\n",
1262 evil_subtype_str);
1263 return GNUNET_SYSERR;
1264 }
1265 return GNUNET_OK;
1266}
1267
1268
1269static void
1270get_evilness (struct ConsensusSession *session, struct Evilness *evil)
1271{
1272 char *evil_spec;
1273 char *field;
1274 char *evil_type_str = NULL;
1275 char *evil_subtype_str = NULL;
1276
1277 GNUNET_assert (NULL != evil);
1278
1279 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "consensus",
1280 "EVIL_SPEC",
1281 &evil_spec))
1282 {
1283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1284 "P%u: no evilness\n",
1285 session->local_peer_idx);
1286 evil->type = EVILNESS_NONE;
1287 return;
1288 }
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "P%u: got evilness spec\n",
1291 session->local_peer_idx);
1292
1293 for (field = strtok (evil_spec, "/");
1294 NULL != field;
1295 field = strtok (NULL, "/"))
1296 {
1297 unsigned int peer_num;
1298 unsigned int evil_num;
1299 int ret;
1300
1301 evil_type_str = NULL;
1302 evil_subtype_str = NULL;
1303
1304 ret = sscanf (field, "%u;%m[a-z-];%m[a-z-];%u", &peer_num, &evil_type_str,
1305 &evil_subtype_str, &evil_num);
1306
1307 if (ret != 4)
1308 {
1309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1310 "Malformed field '%s' in EVIL_SPEC (expected 4 components got %d), behaving like a good peer.\n",
1311 field,
1312 ret);
1313 goto not_evil;
1314 }
1315
1316 GNUNET_assert (NULL != evil_type_str);
1317 GNUNET_assert (NULL != evil_subtype_str);
1318
1319 if (peer_num == session->local_peer_idx)
1320 {
1321 if (0 == strcmp ("slack", evil_type_str))
1322 {
1323 evil->type = EVILNESS_SLACK;
1324 }
1325 if (0 == strcmp ("slack-a2a", evil_type_str))
1326 {
1327 evil->type = EVILNESS_SLACK_A2A;
1328 }
1329 else if (0 == strcmp ("cram-all", evil_type_str))
1330 {
1331 evil->type = EVILNESS_CRAM_ALL;
1332 evil->num = evil_num;
1333 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1334 goto not_evil;
1335 }
1336 else if (0 == strcmp ("cram-lead", evil_type_str))
1337 {
1338 evil->type = EVILNESS_CRAM_LEAD;
1339 evil->num = evil_num;
1340 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1341 goto not_evil;
1342 }
1343 else if (0 == strcmp ("cram-echo", evil_type_str))
1344 {
1345 evil->type = EVILNESS_CRAM_ECHO;
1346 evil->num = evil_num;
1347 if (GNUNET_OK != parse_evilness_cram_subtype (evil_subtype_str, evil))
1348 goto not_evil;
1349 }
1350 else
1351 {
1352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1353 "Malformed field '%s' in EVIL_SPEC (unknown type), behaving like a good peer.\n",
1354 evil_type_str);
1355 goto not_evil;
1356 }
1357 goto cleanup;
1358 }
1359 /* No GNUNET_free since memory was allocated by libc */
1360 free (evil_type_str);
1361 evil_type_str = NULL;
1362 evil_subtype_str = NULL;
1363 }
1364not_evil:
1365 evil->type = EVILNESS_NONE;
1366cleanup:
1367 GNUNET_free (evil_spec);
1368 /* no GNUNET_free since it wasn't
1369 * allocated with GNUNET_malloc */
1370 if (NULL != evil_type_str)
1371 free (evil_type_str);
1372 if (NULL != evil_subtype_str)
1373 free (evil_subtype_str);
1374}
1375
1376
1377#endif
1378
1379
1380/**
1381 * Commit the appropriate set for a task.
1382 */
1383static void
1384commit_set (struct ConsensusSession *session,
1385 struct TaskEntry *task)
1386{
1387 struct SetEntry *set;
1388 struct SetOpCls *setop = &task->cls.setop;
1389
1390 GNUNET_assert (NULL != setop->op);
1391 set = lookup_set (session, &setop->input_set);
1392 GNUNET_assert (NULL != set);
1393
1394 if ((GNUNET_YES == setop->transceive_contested) && (GNUNET_YES ==
1395 set->is_contested))
1396 {
1397 struct GNUNET_SET_Element element;
1398 struct ConsensusElement ce = { 0 };
1399
1400 ce.marker = CONSENSUS_MARKER_CONTESTED;
1401 element.data = &ce;
1402 element.size = sizeof(struct ConsensusElement);
1403 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1404 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1405 }
1406
1407 if (PHASE_KIND_ALL_TO_ALL_2 == task->key.kind)
1408 {
1409 struct GNUNET_SET_Element element;
1410 struct ConsensusSizeElement cse = {
1411 .size = 0,
1412 .sender_index = 0
1413 };
1414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "inserting size marker\n");
1415 cse.ce.marker = CONSENSUS_MARKER_SIZE;
1416 cse.size = GNUNET_htonll (session->first_size);
1417 cse.sender_index = session->local_peer_idx;
1418 element.data = &cse;
1419 element.size = sizeof(struct ConsensusSizeElement);
1420 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1421 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1422 }
1423
1424#ifdef EVIL
1425 {
1426 struct Evilness evil;
1427
1428 get_evilness (session, &evil);
1429 if (EVILNESS_NONE != evil.type)
1430 {
1431 /* Useful for evaluation */
1432 GNUNET_STATISTICS_set (statistics,
1433 "is evil",
1434 1,
1435 GNUNET_NO);
1436 }
1437 switch (evil.type)
1438 {
1439 case EVILNESS_CRAM_ALL:
1440 case EVILNESS_CRAM_LEAD:
1441 case EVILNESS_CRAM_ECHO:
1442 /* We're not cramming elements in the
1443 all-to-all round, since that would just
1444 add more elements to the result set, but
1445 wouldn't test robustness. */
1446 if (PHASE_KIND_ALL_TO_ALL == task->key.kind)
1447 {
1448 GNUNET_SET_commit (setop->op, set->h);
1449 break;
1450 }
1451 if ((EVILNESS_CRAM_LEAD == evil.type) &&
1452 ((PHASE_KIND_GRADECAST_LEADER != task->key.kind) ||
1453 (SET_KIND_CURRENT != set->key.set_kind) ))
1454 {
1455 GNUNET_SET_commit (setop->op, set->h);
1456 break;
1457 }
1458 if ((EVILNESS_CRAM_ECHO == evil.type) && (PHASE_KIND_GRADECAST_ECHO !=
1459 task->key.kind))
1460 {
1461 GNUNET_SET_commit (setop->op, set->h);
1462 break;
1463 }
1464 for (unsigned int i = 0; i < evil.num; i++)
1465 {
1466 struct GNUNET_SET_Element element;
1467 struct ConsensusStuffedElement se = {
1468 .ce.payload_type = 0,
1469 .ce.marker = 0,
1470 };
1471 element.data = &se;
1472 element.size = sizeof(struct ConsensusStuffedElement);
1473 element.element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT;
1474
1475 if (EVILNESS_SUB_REPLACEMENT == evil.subtype)
1476 {
1477 /* Always generate a new element. */
1478 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
1479 &se.rand);
1480 }
1481 else if (EVILNESS_SUB_NO_REPLACEMENT == evil.subtype)
1482 {
1483 /* Always cram the same elements, derived from counter. */
1484 GNUNET_CRYPTO_hash (&i, sizeof(i), &se.rand);
1485 }
1486 else
1487 {
1488 GNUNET_assert (0);
1489 }
1490 GNUNET_SET_add_element (set->h, &element, NULL, NULL);
1491#ifdef GNUNET_EXTRA_LOGGING
1492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1493 "P%u: evil peer: cramming element %s into set {%s} of task {%s}\n",
1494 session->local_peer_idx,
1495 debug_str_element (&element),
1496 debug_str_set_key (&setop->input_set),
1497 debug_str_task_key (&task->key));
1498#endif
1499 }
1500 GNUNET_STATISTICS_update (statistics,
1501 "# stuffed elements",
1502 evil.num,
1503 GNUNET_NO);
1504 GNUNET_SET_commit (setop->op, set->h);
1505 break;
1506
1507 case EVILNESS_SLACK:
1508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1509 "P%u: evil peer: slacking\n",
1510 (unsigned int) session->local_peer_idx);
1511
1512 /* Do nothing. */
1513 case EVILNESS_SLACK_A2A:
1514 if ((PHASE_KIND_ALL_TO_ALL_2 == task->key.kind) ||
1515 (PHASE_KIND_ALL_TO_ALL == task->key.kind))
1516 {
1517 struct GNUNET_SET_Handle *empty_set;
1518 empty_set = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
1519 GNUNET_SET_commit (setop->op, empty_set);
1520 GNUNET_SET_destroy (empty_set);
1521 }
1522 else
1523 {
1524 GNUNET_SET_commit (setop->op,
1525 set->h);
1526 }
1527 break;
1528
1529 case EVILNESS_NONE:
1530 GNUNET_SET_commit (setop->op,
1531 set->h);
1532 break;
1533 }
1534 }
1535#else
1536 if (GNUNET_NO == session->peers_blacklisted[task_other_peer (task)])
1537 {
1538 GNUNET_SET_commit (setop->op, set->h);
1539 }
1540 else
1541 {
1542 /* For our testcases, we don't want the blacklisted
1543 peers to wait. */
1544 GNUNET_SET_operation_cancel (setop->op);
1545 setop->op = NULL;
1546 finish_task (task);
1547 }
1548#endif
1549}
1550
1551
1552static void
1553put_diff (struct ConsensusSession *session,
1554 struct DiffEntry *diff)
1555{
1556 struct GNUNET_HashCode hash;
1557
1558 GNUNET_CRYPTO_hash (&diff->key,
1559 sizeof(struct DiffKey),
1560 &hash);
1561 GNUNET_assert (GNUNET_OK ==
1562 GNUNET_CONTAINER_multihashmap_put (session->diffmap,
1563 &hash,
1564 diff,
1565 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1566}
1567
1568
1569static void
1570put_set (struct ConsensusSession *session,
1571 struct SetEntry *set)
1572{
1573 struct GNUNET_HashCode hash;
1574
1575 GNUNET_assert (NULL != set->h);
1576 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1577 "Putting set %s\n",
1578 debug_str_set_key (&set->key));
1579 GNUNET_CRYPTO_hash (&set->key,
1580 sizeof(struct SetKey),
1581 &hash);
1582 GNUNET_assert (GNUNET_SYSERR !=
1583 GNUNET_CONTAINER_multihashmap_put (session->setmap,
1584 &hash,
1585 set,
1586 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
1587}
1588
1589
1590static void
1591put_rfn (struct ConsensusSession *session,
1592 struct ReferendumEntry *rfn)
1593{
1594 struct GNUNET_HashCode hash;
1595
1596 GNUNET_CRYPTO_hash (&rfn->key, sizeof(struct RfnKey), &hash);
1597 GNUNET_assert (GNUNET_OK ==
1598 GNUNET_CONTAINER_multihashmap_put (session->rfnmap,
1599 &hash,
1600 rfn,
1601 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1602}
1603
1604
1605static void
1606task_cancel_reconcile (struct TaskEntry *task)
1607{
1608 /* not implemented yet */
1609 GNUNET_assert (0);
1610}
1611
1612
1613static void
1614apply_diff_to_rfn (struct DiffEntry *diff,
1615 struct ReferendumEntry *rfn,
1616 uint16_t voting_peer,
1617 uint16_t num_peers)
1618{
1619 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1620 struct DiffElementInfo *di;
1621
1622 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff->changes);
1623
1624 while (GNUNET_YES ==
1625 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1626 NULL,
1627 (const void **) &di))
1628 {
1629 if (di->weight > 0)
1630 {
1631 rfn_vote (rfn, voting_peer, VOTE_ADD, di->element);
1632 }
1633 if (di->weight < 0)
1634 {
1635 rfn_vote (rfn, voting_peer, VOTE_REMOVE, di->element);
1636 }
1637 }
1638
1639 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1640}
1641
1642
1643static struct DiffEntry *
1644diff_create (void)
1645{
1646 struct DiffEntry *d = GNUNET_new (struct DiffEntry);
1647
1648 d->changes = GNUNET_CONTAINER_multihashmap_create (8,
1649 GNUNET_NO);
1650
1651 return d;
1652}
1653
1654
1655#if 0
1656static struct DiffEntry *
1657diff_compose (struct DiffEntry *diff_1,
1658 struct DiffEntry *diff_2)
1659{
1660 struct DiffEntry *diff_new;
1661 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1662 struct DiffElementInfo *di;
1663
1664 diff_new = diff_create ();
1665
1666 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff_1->changes);
1667 while (GNUNET_YES ==
1668 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1669 NULL,
1670 (const void **) &di))
1671 {
1672 diff_insert (diff_new,
1673 di->weight,
1674 di->element);
1675 }
1676 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1677
1678 iter = GNUNET_CONTAINER_multihashmap_iterator_create (diff_2->changes);
1679 while (GNUNET_YES ==
1680 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1681 NULL,
1682 (const void **) &di))
1683 {
1684 diff_insert (diff_new,
1685 di->weight,
1686 di->element);
1687 }
1688 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
1689
1690 return diff_new;
1691}
1692
1693
1694#endif
1695
1696
1697struct ReferendumEntry *
1698rfn_create (uint16_t size)
1699{
1700 struct ReferendumEntry *rfn;
1701
1702 rfn = GNUNET_new (struct ReferendumEntry);
1703 rfn->rfn_elements = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
1704 rfn->peer_commited = GNUNET_new_array (size, int);
1705 rfn->peer_contested = GNUNET_new_array (size, int);
1706 rfn->num_peers = size;
1707
1708 return rfn;
1709}
1710
1711
1712#if UNUSED
1713static void
1714diff_destroy (struct DiffEntry *diff)
1715{
1716 GNUNET_CONTAINER_multihashmap_destroy (diff->changes);
1717 GNUNET_free (diff);
1718}
1719
1720
1721#endif
1722
1723
1724/**
1725 * For a given majority, count what the outcome
1726 * is (add/remove/keep), and give the number
1727 * of peers that voted for this outcome.
1728 */
1729static void
1730rfn_majority (const struct ReferendumEntry *rfn,
1731 const struct RfnElementInfo *ri,
1732 uint16_t *ret_majority,
1733 enum ReferendumVote *ret_vote)
1734{
1735 uint16_t votes_yes = 0;
1736 uint16_t num_commited = 0;
1737
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "Computing rfn majority for element %s of rfn {%s}\n",
1740 debug_str_element (ri->element),
1741 debug_str_rfn_key (&rfn->key));
1742
1743 for (uint16_t i = 0; i < rfn->num_peers; i++)
1744 {
1745 if (GNUNET_NO == rfn->peer_commited[i])
1746 continue;
1747 num_commited++;
1748
1749 if (GNUNET_YES == ri->votes[i])
1750 votes_yes++;
1751 }
1752
1753 if (votes_yes > (num_commited) / 2)
1754 {
1755 *ret_vote = ri->proposal;
1756 *ret_majority = votes_yes;
1757 }
1758 else
1759 {
1760 *ret_vote = VOTE_STAY;
1761 *ret_majority = num_commited - votes_yes;
1762 }
1763}
1764
1765
1766struct SetCopyCls
1767{
1768 struct TaskEntry *task;
1769 struct SetKey dst_set_key;
1770};
1771
1772
1773static void
1774set_copy_cb (void *cls,
1775 struct GNUNET_SET_Handle *copy)
1776{
1777 struct SetCopyCls *scc = cls;
1778 struct TaskEntry *task = scc->task;
1779 struct SetKey dst_set_key = scc->dst_set_key;
1780 struct SetEntry *set;
1781 struct SetHandle *sh = GNUNET_new (struct SetHandle);
1782
1783 sh->h = copy;
1784 GNUNET_CONTAINER_DLL_insert (task->step->session->set_handles_head,
1785 task->step->session->set_handles_tail,
1786 sh);
1787
1788 GNUNET_free (scc);
1789 set = GNUNET_new (struct SetEntry);
1790 set->h = copy;
1791 set->key = dst_set_key;
1792 put_set (task->step->session, set);
1793
1794 task->start (task);
1795}
1796
1797
1798/**
1799 * Call the start function of the given
1800 * task again after we created a copy of the given set.
1801 */
1802static void
1803create_set_copy_for_task (struct TaskEntry *task,
1804 struct SetKey *src_set_key,
1805 struct SetKey *dst_set_key)
1806{
1807 struct SetEntry *src_set;
1808 struct SetCopyCls *scc = GNUNET_new (struct SetCopyCls);
1809
1810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1811 "Copying set {%s} to {%s} for task {%s}\n",
1812 debug_str_set_key (src_set_key),
1813 debug_str_set_key (dst_set_key),
1814 debug_str_task_key (&task->key));
1815
1816 scc->task = task;
1817 scc->dst_set_key = *dst_set_key;
1818 src_set = lookup_set (task->step->session, src_set_key);
1819 GNUNET_assert (NULL != src_set);
1820 GNUNET_SET_copy_lazy (src_set->h,
1821 set_copy_cb,
1822 scc);
1823}
1824
1825
1826struct SetMutationProgressCls
1827{
1828 int num_pending;
1829 /**
1830 * Task to finish once all changes are through.
1831 */
1832 struct TaskEntry *task;
1833};
1834
1835
1836static void
1837set_mutation_done (void *cls)
1838{
1839 struct SetMutationProgressCls *pc = cls;
1840
1841 GNUNET_assert (pc->num_pending > 0);
1842
1843 pc->num_pending--;
1844
1845 if (0 == pc->num_pending)
1846 {
1847 struct TaskEntry *task = pc->task;
1848 GNUNET_free (pc);
1849 finish_task (task);
1850 }
1851}
1852
1853
1854static void
1855try_finish_step_early (struct Step *step)
1856{
1857 if (GNUNET_YES == step->is_running)
1858 return;
1859 if (GNUNET_YES == step->is_finished)
1860 return;
1861 if (GNUNET_NO == step->early_finishable)
1862 return;
1863
1864 step->is_finished = GNUNET_YES;
1865
1866#ifdef GNUNET_EXTRA_LOGGING
1867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1868 "Finishing step `%s' early.\n",
1869 step->debug_name);
1870#endif
1871
1872 for (unsigned int i = 0; i < step->subordinates_len; i++)
1873 {
1874 GNUNET_assert (step->subordinates[i]->pending_prereq > 0);
1875 step->subordinates[i]->pending_prereq--;
1876#ifdef GNUNET_EXTRA_LOGGING
1877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1878 "Decreased pending_prereq to %u for step `%s'.\n",
1879 (unsigned int) step->subordinates[i]->pending_prereq,
1880 step->subordinates[i]->debug_name);
1881#endif
1882 try_finish_step_early (step->subordinates[i]);
1883 }
1884
1885 // XXX: maybe schedule as task to avoid recursion?
1886 run_ready_steps (step->session);
1887}
1888
1889
1890static void
1891finish_step (struct Step *step)
1892{
1893 GNUNET_assert (step->finished_tasks == step->tasks_len);
1894 GNUNET_assert (GNUNET_YES == step->is_running);
1895 GNUNET_assert (GNUNET_NO == step->is_finished);
1896
1897#ifdef GNUNET_EXTRA_LOGGING
1898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1899 "All tasks of step `%s' with %u subordinates finished.\n",
1900 step->debug_name,
1901 step->subordinates_len);
1902#endif
1903
1904 for (unsigned int i = 0; i < step->subordinates_len; i++)
1905 {
1906 GNUNET_assert (step->subordinates[i]->pending_prereq > 0);
1907 step->subordinates[i]->pending_prereq--;
1908#ifdef GNUNET_EXTRA_LOGGING
1909 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1910 "Decreased pending_prereq to %u for step `%s'.\n",
1911 (unsigned int) step->subordinates[i]->pending_prereq,
1912 step->subordinates[i]->debug_name);
1913#endif
1914 }
1915
1916 step->is_finished = GNUNET_YES;
1917
1918 // XXX: maybe schedule as task to avoid recursion?
1919 run_ready_steps (step->session);
1920}
1921
1922
1923/**
1924 * Apply the result from one round of gradecasts (i.e. every peer
1925 * should have gradecasted) to the peer's current set.
1926 *
1927 * @param task the task with context information
1928 */
1929static void
1930task_start_apply_round (struct TaskEntry *task)
1931{
1932 struct ConsensusSession *session = task->step->session;
1933 struct SetKey sk_in;
1934 struct SetKey sk_out;
1935 struct RfnKey rk_in;
1936 struct SetEntry *set_out;
1937 struct ReferendumEntry *rfn_in;
1938 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1939 struct RfnElementInfo *ri;
1940 struct SetMutationProgressCls *progress_cls;
1941 uint16_t worst_majority = UINT16_MAX;
1942
1943 sk_in = (struct SetKey) { SET_KIND_CURRENT, task->key.repetition };
1944 rk_in = (struct RfnKey) { RFN_KIND_GRADECAST_RESULT, task->key.repetition };
1945 sk_out = (struct SetKey) { SET_KIND_CURRENT, task->key.repetition + 1 };
1946
1947 set_out = lookup_set (session, &sk_out);
1948 if (NULL == set_out)
1949 {
1950 create_set_copy_for_task (task,
1951 &sk_in,
1952 &sk_out);
1953 return;
1954 }
1955
1956 rfn_in = lookup_rfn (session, &rk_in);
1957 GNUNET_assert (NULL != rfn_in);
1958
1959 progress_cls = GNUNET_new (struct SetMutationProgressCls);
1960 progress_cls->task = task;
1961
1962 iter = GNUNET_CONTAINER_multihashmap_iterator_create (rfn_in->rfn_elements);
1963
1964 while (GNUNET_YES ==
1965 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
1966 NULL,
1967 (const void **) &ri))
1968 {
1969 uint16_t majority_num;
1970 enum ReferendumVote majority_vote;
1971
1972 rfn_majority (rfn_in, ri, &majority_num, &majority_vote);
1973
1974 if (worst_majority > majority_num)
1975 worst_majority = majority_num;
1976
1977 switch (majority_vote)
1978 {
1979 case VOTE_ADD:
1980 progress_cls->num_pending++;
1981 GNUNET_assert (GNUNET_OK ==
1982 GNUNET_SET_add_element (set_out->h,
1983 ri->element,
1984 &set_mutation_done,
1985 progress_cls));
1986 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1987 "P%u: apply round: adding element %s with %u-majority.\n",
1988 session->local_peer_idx,
1989 debug_str_element (ri->element), majority_num);
1990 break;
1991
1992 case VOTE_REMOVE:
1993 progress_cls->num_pending++;
1994 GNUNET_assert (GNUNET_OK ==
1995 GNUNET_SET_remove_element (set_out->h,
1996 ri->element,
1997 &set_mutation_done,
1998 progress_cls));
1999 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2000 "P%u: apply round: deleting element %s with %u-majority.\n",
2001 session->local_peer_idx,
2002 debug_str_element (ri->element), majority_num);
2003 break;
2004
2005 case VOTE_STAY:
2006 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2007 "P%u: apply round: keeping element %s with %u-majority.\n",
2008 session->local_peer_idx,
2009 debug_str_element (ri->element), majority_num);
2010 // do nothing
2011 break;
2012
2013 default:
2014 GNUNET_assert (0);
2015 break;
2016 }
2017 }
2018
2019 if (0 == progress_cls->num_pending)
2020 {
2021 // call closure right now, no pending ops
2022 GNUNET_free (progress_cls);
2023 finish_task (task);
2024 }
2025
2026 {
2027 uint16_t thresh = (session->num_peers / 3) * 2;
2028
2029 if (worst_majority >= thresh)
2030 {
2031 switch (session->early_stopping)
2032 {
2033 case EARLY_STOPPING_NONE:
2034 session->early_stopping = EARLY_STOPPING_ONE_MORE;
2035 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2036 "P%u: Stopping early (after one more superround)\n",
2037 session->local_peer_idx);
2038 break;
2039
2040 case EARLY_STOPPING_ONE_MORE:
2041 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2042 "P%u: finishing steps due to early finish\n",
2043 session->local_peer_idx);
2044 session->early_stopping = EARLY_STOPPING_DONE;
2045 {
2046 struct Step *step;
2047 for (step = session->steps_head; NULL != step; step = step->next)
2048 try_finish_step_early (step);
2049 }
2050 break;
2051
2052 case EARLY_STOPPING_DONE:
2053 /* We shouldn't be here anymore after early stopping */
2054 GNUNET_break (0);
2055 break;
2056
2057 default:
2058 GNUNET_assert (0);
2059 break;
2060 }
2061 }
2062 else if (EARLY_STOPPING_NONE != session->early_stopping)
2063 {
2064 // Our assumption about the number of bad peers
2065 // has been broken.
2066 GNUNET_break_op (0);
2067 }
2068 else
2069 {
2070 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2071 "P%u: NOT finishing early (majority not good enough)\n",
2072 session->local_peer_idx);
2073 }
2074 }
2075 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2076}
2077
2078
2079static void
2080task_start_grade (struct TaskEntry *task)
2081{
2082 struct ConsensusSession *session = task->step->session;
2083 struct ReferendumEntry *output_rfn;
2084 struct ReferendumEntry *input_rfn;
2085 struct DiffEntry *input_diff;
2086 struct RfnKey rfn_key;
2087 struct DiffKey diff_key;
2088 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
2089 struct RfnElementInfo *ri;
2090 unsigned int gradecast_confidence = 2;
2091
2092 rfn_key = (struct RfnKey) { RFN_KIND_GRADECAST_RESULT, task->key.repetition };
2093 output_rfn = lookup_rfn (session, &rfn_key);
2094 if (NULL == output_rfn)
2095 {
2096 output_rfn = rfn_create (session->num_peers);
2097 output_rfn->key = rfn_key;
2098 put_rfn (session, output_rfn);
2099 }
2100
2101 diff_key = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL, task->key.repetition,
2102 task->key.leader };
2103 input_diff = lookup_diff (session, &diff_key);
2104 GNUNET_assert (NULL != input_diff);
2105
2106 rfn_key = (struct RfnKey) { RFN_KIND_ECHO, task->key.repetition,
2107 task->key.leader };
2108 input_rfn = lookup_rfn (session, &rfn_key);
2109 GNUNET_assert (NULL != input_rfn);
2110
2111 iter = GNUNET_CONTAINER_multihashmap_iterator_create (
2112 input_rfn->rfn_elements);
2113
2114 apply_diff_to_rfn (input_diff, output_rfn, task->key.leader,
2115 session->num_peers);
2116
2117 while (GNUNET_YES ==
2118 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
2119 NULL,
2120 (const void **) &ri))
2121 {
2122 uint16_t majority_num;
2123 enum ReferendumVote majority_vote;
2124
2125 // XXX: we need contested votes and non-contested votes here
2126 rfn_majority (input_rfn, ri, &majority_num, &majority_vote);
2127
2128 if (majority_num <= session->num_peers / 3)
2129 majority_vote = VOTE_REMOVE;
2130
2131 switch (majority_vote)
2132 {
2133 case VOTE_STAY:
2134 break;
2135
2136 case VOTE_ADD:
2137 rfn_vote (output_rfn, task->key.leader, VOTE_ADD, ri->element);
2138 break;
2139
2140 case VOTE_REMOVE:
2141 rfn_vote (output_rfn, task->key.leader, VOTE_REMOVE, ri->element);
2142 break;
2143
2144 default:
2145 GNUNET_assert (0);
2146 break;
2147 }
2148 }
2149 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2150
2151 {
2152 uint16_t noncontested;
2153 noncontested = rfn_noncontested (input_rfn);
2154 if (noncontested < (session->num_peers / 3) * 2)
2155 {
2156 gradecast_confidence = GNUNET_MIN (1, gradecast_confidence);
2157 }
2158 if (noncontested < (session->num_peers / 3) + 1)
2159 {
2160 gradecast_confidence = 0;
2161 }
2162 }
2163
2164 if (gradecast_confidence >= 1)
2165 rfn_commit (output_rfn, task->key.leader);
2166
2167 if (gradecast_confidence <= 1)
2168 session->peers_blacklisted[task->key.leader] = GNUNET_YES;
2169
2170 finish_task (task);
2171}
2172
2173
2174static void
2175task_start_reconcile (struct TaskEntry *task)
2176{
2177 struct SetEntry *input;
2178 struct SetOpCls *setop = &task->cls.setop;
2179 struct ConsensusSession *session = task->step->session;
2180
2181 input = lookup_set (session, &setop->input_set);
2182 GNUNET_assert (NULL != input);
2183 GNUNET_assert (NULL != input->h);
2184
2185 /* We create the outputs for the operation here
2186 (rather than in the set operation callback)
2187 because we want something valid in there, even
2188 if the other peer doesn't talk to us */
2189
2190 if (SET_KIND_NONE != setop->output_set.set_kind)
2191 {
2192 /* If we don't have an existing output set,
2193 we clone the input set. */
2194 if (NULL == lookup_set (session, &setop->output_set))
2195 {
2196 create_set_copy_for_task (task,
2197 &setop->input_set,
2198 &setop->output_set);
2199 return;
2200 }
2201 }
2202
2203 if (RFN_KIND_NONE != setop->output_rfn.rfn_kind)
2204 {
2205 if (NULL == lookup_rfn (session, &setop->output_rfn))
2206 {
2207 struct ReferendumEntry *rfn;
2208
2209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2210 "P%u: output rfn <%s> missing, creating.\n",
2211 session->local_peer_idx,
2212 debug_str_rfn_key (&setop->output_rfn));
2213
2214 rfn = rfn_create (session->num_peers);
2215 rfn->key = setop->output_rfn;
2216 put_rfn (session, rfn);
2217 }
2218 }
2219
2220 if (DIFF_KIND_NONE != setop->output_diff.diff_kind)
2221 {
2222 if (NULL == lookup_diff (session, &setop->output_diff))
2223 {
2224 struct DiffEntry *diff;
2225
2226 diff = diff_create ();
2227 diff->key = setop->output_diff;
2228 put_diff (session, diff);
2229 }
2230 }
2231
2232 if ((task->key.peer1 == session->local_peer_idx) && (task->key.peer2 ==
2233 session->local_peer_idx))
2234 {
2235 /* XXX: mark the corresponding rfn as committed if necessary */
2236 finish_task (task);
2237 return;
2238 }
2239
2240 if (task->key.peer1 == session->local_peer_idx)
2241 {
2242 struct GNUNET_CONSENSUS_RoundContextMessage rcm;
2243
2244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2245 "P%u: Looking up set {%s} to run remote union\n",
2246 session->local_peer_idx,
2247 debug_str_set_key (&setop->input_set));
2248 rcm.header.type = htons (GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT);
2249 rcm.header.size = htons (sizeof(rcm));
2250 rcm.kind = htons (task->key.kind);
2251 rcm.peer1 = htons (task->key.peer1);
2252 rcm.peer2 = htons (task->key.peer2);
2253 rcm.leader = htons (task->key.leader);
2254 rcm.repetition = htons (task->key.repetition);
2255 rcm.is_contested = htons (0);
2256
2257 GNUNET_assert (NULL == setop->op);
2258 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2259 "P%u: initiating set op with P%u, our set is %s\n",
2260 session->local_peer_idx,
2261 task->key.peer2,
2262 debug_str_set_key (&setop->input_set));
2263
2264 struct GNUNET_SET_Option opts[] = {
2265 { GNUNET_SET_OPTION_BYZANTINE, { .num = session->lower_bound } },
2266 { GNUNET_SET_OPTION_END },
2267 };
2268
2269 // XXX: maybe this should be done while
2270 // setting up tasks alreays?
2271 setop->op = GNUNET_SET_prepare (&session->peers[task->key.peer2],
2272 &session->global_id,
2273 &rcm.header,
2274 GNUNET_SET_RESULT_SYMMETRIC,
2275 opts,
2276 set_result_cb,
2277 task);
2278
2279 commit_set (session, task);
2280 }
2281 else if (task->key.peer2 == session->local_peer_idx)
2282 {
2283 /* Wait for the other peer to contact us */
2284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: waiting set op with P%u\n",
2285 session->local_peer_idx, task->key.peer1);
2286
2287 if (NULL != setop->op)
2288 {
2289 commit_set (session, task);
2290 }
2291 }
2292 else
2293 {
2294 /* We made an error while constructing the task graph. */
2295 GNUNET_assert (0);
2296 }
2297}
2298
2299
2300static void
2301task_start_eval_echo (struct TaskEntry *task)
2302{
2303 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
2304 struct ReferendumEntry *input_rfn;
2305 struct RfnElementInfo *ri;
2306 struct SetEntry *output_set;
2307 struct SetMutationProgressCls *progress_cls;
2308 struct ConsensusSession *session = task->step->session;
2309 struct SetKey sk_in;
2310 struct SetKey sk_out;
2311 struct RfnKey rk_in;
2312
2313 sk_in = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, task->key.repetition,
2314 task->key.leader };
2315 sk_out = (struct SetKey) { SET_KIND_ECHO_RESULT, task->key.repetition,
2316 task->key.leader };
2317 output_set = lookup_set (session, &sk_out);
2318 if (NULL == output_set)
2319 {
2320 create_set_copy_for_task (task,
2321 &sk_in,
2322 &sk_out);
2323 return;
2324 }
2325
2326 {
2327 // FIXME: should be marked as a shallow copy, so
2328 // we can destroy everything correctly
2329 struct SetEntry *last_set = GNUNET_new (struct SetEntry);
2330
2331 last_set->h = output_set->h;
2332 last_set->key = (struct SetKey) { SET_KIND_LAST_GRADECAST };
2333 put_set (session, last_set);
2334 }
2335
2336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2337 "Evaluating referendum in Task {%s}\n",
2338 debug_str_task_key (&task->key));
2339
2340 progress_cls = GNUNET_new (struct SetMutationProgressCls);
2341 progress_cls->task = task;
2342 rk_in = (struct RfnKey) { RFN_KIND_ECHO, task->key.repetition,
2343 task->key.leader };
2344 input_rfn = lookup_rfn (session, &rk_in);
2345 GNUNET_assert (NULL != input_rfn);
2346 iter = GNUNET_CONTAINER_multihashmap_iterator_create (
2347 input_rfn->rfn_elements);
2348 GNUNET_assert (NULL != iter);
2349
2350 while (GNUNET_YES ==
2351 GNUNET_CONTAINER_multihashmap_iterator_next (iter,
2352 NULL,
2353 (const void **) &ri))
2354 {
2355 enum ReferendumVote majority_vote;
2356 uint16_t majority_num;
2357
2358 rfn_majority (input_rfn, ri, &majority_num, &majority_vote);
2359
2360 if (majority_num < session->num_peers / 3)
2361 {
2362 /* It is not the case that all nonfaulty peers
2363 echoed the same value. Since we're doing a set reconciliation, we
2364 can't simply send "nothing" for the value. Thus we mark our 'confirm'
2365 reconciliation as contested. Other peers might not know that the
2366 leader is faulty, thus we still re-distribute in the confirmation
2367 round. *///
2368 output_set->is_contested = GNUNET_YES;
2369 }
2370
2371 switch (majority_vote)
2372 {
2373 case VOTE_ADD:
2374 progress_cls->num_pending++;
2375 GNUNET_assert (GNUNET_OK ==
2376 GNUNET_SET_add_element (output_set->h,
2377 ri->element,
2378 set_mutation_done,
2379 progress_cls));
2380 break;
2381
2382 case VOTE_REMOVE:
2383 progress_cls->num_pending++;
2384 GNUNET_assert (GNUNET_OK ==
2385 GNUNET_SET_remove_element (output_set->h,
2386 ri->element,
2387 set_mutation_done,
2388 progress_cls));
2389 break;
2390
2391 case VOTE_STAY:
2392 /* Nothing to do. */
2393 break;
2394
2395 default:
2396 /* not reached */
2397 GNUNET_assert (0);
2398 }
2399 }
2400
2401 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
2402
2403 if (0 == progress_cls->num_pending)
2404 {
2405 // call closure right now, no pending ops
2406 GNUNET_free (progress_cls);
2407 finish_task (task);
2408 }
2409}
2410
2411
2412static void
2413task_start_finish (struct TaskEntry *task)
2414{
2415 struct SetEntry *final_set;
2416 struct ConsensusSession *session = task->step->session;
2417
2418 final_set = lookup_set (session,
2419 &task->cls.finish.input_set);
2420 GNUNET_assert (NULL != final_set);
2421 GNUNET_SET_iterate (final_set->h,
2422 &send_to_client_iter,
2423 task);
2424}
2425
2426
2427static void
2428start_task (struct ConsensusSession *session,
2429 struct TaskEntry *task)
2430{
2431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2432 "P%u: starting task {%s}\n",
2433 session->local_peer_idx,
2434 debug_str_task_key (&task->key));
2435 GNUNET_assert (GNUNET_NO == task->is_started);
2436 GNUNET_assert (GNUNET_NO == task->is_finished);
2437 GNUNET_assert (NULL != task->start);
2438 task->start (task);
2439 task->is_started = GNUNET_YES;
2440}
2441
2442
2443/*
2444 * Run all steps of the session that don't any
2445 * more dependencies.
2446 */
2447static void
2448run_ready_steps (struct ConsensusSession *session)
2449{
2450 struct Step *step;
2451
2452 step = session->steps_head;
2453
2454 while (NULL != step)
2455 {
2456 if ((GNUNET_NO == step->is_running) && (0 == step->pending_prereq) &&
2457 (GNUNET_NO == step->is_finished))
2458 {
2459 GNUNET_assert (0 == step->finished_tasks);
2460
2461#ifdef GNUNET_EXTRA_LOGGING
2462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2463 "P%u: Running step `%s' of round %d with %d tasks and %d subordinates\n",
2464 session->local_peer_idx,
2465 step->debug_name,
2466 step->round, step->tasks_len, step->subordinates_len);
2467#endif
2468
2469 step->is_running = GNUNET_YES;
2470 for (size_t i = 0; i < step->tasks_len; i++)
2471 start_task (session, step->tasks[i]);
2472
2473 /* Sometimes there is no task to trigger finishing the step, so we have to do it here. */
2474 if ((step->finished_tasks == step->tasks_len) && (GNUNET_NO ==
2475 step->is_finished))
2476 finish_step (step);
2477
2478 /* Running the next ready steps will be triggered by task completion */
2479 return;
2480 }
2481 step = step->next;
2482 }
2483
2484 return;
2485}
2486
2487
2488static void
2489finish_task (struct TaskEntry *task)
2490{
2491 GNUNET_assert (GNUNET_NO == task->is_finished);
2492 task->is_finished = GNUNET_YES;
2493 task->step->finished_tasks++;
2494 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2495 "P%u: Finishing Task {%s} (now %u/%u tasks finished in step)\n",
2496 task->step->session->local_peer_idx,
2497 debug_str_task_key (&task->key),
2498 (unsigned int) task->step->finished_tasks,
2499 (unsigned int) task->step->tasks_len);
2500
2501 if (task->step->finished_tasks == task->step->tasks_len)
2502 finish_step (task->step);
2503}
2504
2505
2506/**
2507 * Search peer in the list of peers in session.
2508 *
2509 * @param peer peer to find
2510 * @param session session with peer
2511 * @return index of peer, -1 if peer is not in session
2512 */
2513static int
2514get_peer_idx (const struct GNUNET_PeerIdentity *peer,
2515 const struct ConsensusSession *session)
2516{
2517 for (int i = 0; i < session->num_peers; i++)
2518 if (0 == GNUNET_memcmp (peer, &session->peers[i]))
2519 return i;
2520 return -1;
2521}
2522
2523
2524/**
2525 * Compute a global, (hopefully) unique consensus session id,
2526 * from the local id of the consensus session, and the identities of all participants.
2527 * Thus, if the local id of two consensus sessions coincide, but are not comprised of
2528 * exactly the same peers, the global id will be different.
2529 *
2530 * @param session session to generate the global id for
2531 * @param local_session_id local id of the consensus session
2532 */
2533static void
2534compute_global_id (struct ConsensusSession *session,
2535 const struct GNUNET_HashCode *local_session_id)
2536{
2537 const char *salt = "gnunet-service-consensus/session_id";
2538
2539 GNUNET_assert (GNUNET_YES ==
2540 GNUNET_CRYPTO_kdf (&session->global_id,
2541 sizeof(struct GNUNET_HashCode),
2542 salt,
2543 strlen (salt),
2544 session->peers,
2545 session->num_peers * sizeof(struct
2546 GNUNET_PeerIdentity),
2547 local_session_id,
2548 sizeof(struct GNUNET_HashCode),
2549 NULL));
2550}
2551
2552
2553/**
2554 * Compare two peer identities (for qsort()).
2555 *
2556 * @param h1 some peer identity
2557 * @param h2 some peer identity
2558 * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2.
2559 */
2560static int
2561peer_id_cmp (const void *h1,
2562 const void *h2)
2563{
2564 return memcmp (h1, h2, sizeof(struct GNUNET_PeerIdentity));
2565}
2566
2567
2568/**
2569 * Create the sorted list of peers for the session,
2570 * add the local peer if not in the join message.
2571 *
2572 * @param session session to initialize
2573 * @param join_msg join message with the list of peers participating at the end
2574 */
2575static void
2576initialize_session_peer_list (
2577 struct ConsensusSession *session,
2578 const struct GNUNET_CONSENSUS_JoinMessage *join_msg)
2579{
2580 const struct GNUNET_PeerIdentity *msg_peers
2581 = (const struct GNUNET_PeerIdentity *) &join_msg[1];
2582 int local_peer_in_list;
2583
2584 session->num_peers = ntohl (join_msg->num_peers);
2585
2586 /* Peers in the join message, may or may not include the local peer,
2587 Add it if it is missing. */
2588 local_peer_in_list = GNUNET_NO;
2589 for (unsigned int i = 0; i < session->num_peers; i++)
2590 {
2591 if (0 == GNUNET_memcmp (&msg_peers[i],
2592 &my_peer))
2593 {
2594 local_peer_in_list = GNUNET_YES;
2595 break;
2596 }
2597 }
2598 if (GNUNET_NO == local_peer_in_list)
2599 session->num_peers++;
2600
2601 session->peers = GNUNET_new_array (session->num_peers,
2602 struct GNUNET_PeerIdentity);
2603 if (GNUNET_NO == local_peer_in_list)
2604 session->peers[session->num_peers - 1] = my_peer;
2605 GNUNET_memcpy (session->peers,
2606 msg_peers,
2607 ntohl (join_msg->num_peers)
2608 * sizeof(struct GNUNET_PeerIdentity));
2609 qsort (session->peers,
2610 session->num_peers,
2611 sizeof (struct GNUNET_PeerIdentity),
2612 &peer_id_cmp);
2613}
2614
2615
2616static struct TaskEntry *
2617lookup_task (const struct ConsensusSession *session,
2618 const struct TaskKey *key)
2619{
2620 struct GNUNET_HashCode hash;
2621
2622 GNUNET_CRYPTO_hash (key,
2623 sizeof(struct TaskKey),
2624 &hash);
2625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2626 "Looking up task hash %s\n",
2627 GNUNET_h2s (&hash));
2628 return GNUNET_CONTAINER_multihashmap_get (session->taskmap,
2629 &hash);
2630}
2631
2632
2633/**
2634 * Called when another peer wants to do a set operation with the
2635 * local peer.
2636 *
2637 * @param cls closure
2638 * @param other_peer the other peer
2639 * @param context_msg message with application specific information from
2640 * the other peer
2641 * @param request request from the other peer, use GNUNET_SET_accept
2642 * to accept it, otherwise the request will be refused
2643 * Note that we don't use a return value here, as it is also
2644 * necessary to specify the set we want to do the operation with,
2645 * which sometimes can be derived from the context message.
2646 * Also necessary to specify the timeout.
2647 */
2648static void
2649set_listen_cb (void *cls,
2650 const struct GNUNET_PeerIdentity *other_peer,
2651 const struct GNUNET_MessageHeader *context_msg,
2652 struct GNUNET_SET_Request *request)
2653{
2654 struct ConsensusSession *session = cls;
2655 struct TaskKey tk;
2656 struct TaskEntry *task;
2657 struct GNUNET_CONSENSUS_RoundContextMessage *cm;
2658
2659 if (NULL == context_msg)
2660 {
2661 GNUNET_break_op (0);
2662 return;
2663 }
2664
2665 if (GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT != ntohs (
2666 context_msg->type))
2667 {
2668 GNUNET_break_op (0);
2669 return;
2670 }
2671
2672 if (sizeof(struct GNUNET_CONSENSUS_RoundContextMessage) != ntohs (
2673 context_msg->size))
2674 {
2675 GNUNET_break_op (0);
2676 return;
2677 }
2678
2679 cm = (struct GNUNET_CONSENSUS_RoundContextMessage *) context_msg;
2680
2681 tk = ((struct TaskKey) {
2682 .kind = ntohs (cm->kind),
2683 .peer1 = ntohs (cm->peer1),
2684 .peer2 = ntohs (cm->peer2),
2685 .repetition = ntohs (cm->repetition),
2686 .leader = ntohs (cm->leader),
2687 });
2688
2689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: got req for task %s\n",
2690 session->local_peer_idx, debug_str_task_key (&tk));
2691
2692 task = lookup_task (session, &tk);
2693
2694 if (NULL == task)
2695 {
2696 GNUNET_break_op (0);
2697 return;
2698 }
2699
2700 if (GNUNET_YES == task->is_finished)
2701 {
2702 GNUNET_break_op (0);
2703 return;
2704 }
2705
2706 if (task->key.peer2 != session->local_peer_idx)
2707 {
2708 /* We're being asked, so we must be the 2nd peer. */
2709 GNUNET_break_op (0);
2710 return;
2711 }
2712
2713 GNUNET_assert (! ((task->key.peer1 == session->local_peer_idx) &&
2714 (task->key.peer2 == session->local_peer_idx)));
2715
2716 struct GNUNET_SET_Option opts[] = {
2717 { GNUNET_SET_OPTION_BYZANTINE, { .num = session->lower_bound } },
2718 { GNUNET_SET_OPTION_END },
2719 };
2720
2721 task->cls.setop.op = GNUNET_SET_accept (request,
2722 GNUNET_SET_RESULT_SYMMETRIC,
2723 opts,
2724 &set_result_cb,
2725 task);
2726
2727 /* If the task hasn't been started yet,
2728 we wait for that until we commit. */
2729
2730 if (GNUNET_YES == task->is_started)
2731 {
2732 commit_set (session, task);
2733 }
2734}
2735
2736
2737static void
2738put_task (struct GNUNET_CONTAINER_MultiHashMap *taskmap,
2739 struct TaskEntry *t)
2740{
2741 struct GNUNET_HashCode round_hash;
2742 struct Step *s;
2743
2744 GNUNET_assert (NULL != t->step);
2745 t = GNUNET_memdup (t, sizeof(struct TaskEntry));
2746 s = t->step;
2747 if (s->tasks_len == s->tasks_cap)
2748 {
2749 unsigned int target_size = 3 * (s->tasks_cap + 1) / 2;
2750 GNUNET_array_grow (s->tasks,
2751 s->tasks_cap,
2752 target_size);
2753 }
2754
2755#ifdef GNUNET_EXTRA_LOGGING
2756 GNUNET_assert (NULL != s->debug_name);
2757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Putting task <%s> into step `%s'\n",
2758 debug_str_task_key (&t->key),
2759 s->debug_name);
2760#endif
2761
2762 s->tasks[s->tasks_len] = t;
2763 s->tasks_len++;
2764
2765 GNUNET_CRYPTO_hash (&t->key,
2766 sizeof(struct TaskKey),
2767 &round_hash);
2768 GNUNET_assert (GNUNET_OK ==
2769 GNUNET_CONTAINER_multihashmap_put (taskmap,
2770 &round_hash,
2771 t,
2772 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2773}
2774
2775
2776static void
2777install_step_timeouts (struct ConsensusSession *session)
2778{
2779 /* Given the fully constructed task graph
2780 with rounds for tasks, we can give the tasks timeouts. */
2781
2782 // unsigned int max_round;
2783
2784 /* XXX: implement! */
2785}
2786
2787
2788/*
2789 * Arrange two peers in some canonical order.
2790 */
2791static void
2792arrange_peers (uint16_t *p1,
2793 uint16_t *p2,
2794 uint16_t n)
2795{
2796 uint16_t a;
2797 uint16_t b;
2798
2799 GNUNET_assert (*p1 < n);
2800 GNUNET_assert (*p2 < n);
2801
2802 if (*p1 < *p2)
2803 {
2804 a = *p1;
2805 b = *p2;
2806 }
2807 else
2808 {
2809 a = *p2;
2810 b = *p1;
2811 }
2812
2813 /* For uniformly random *p1, *p2,
2814 this condition is true with 50% chance */
2815 if (((b - a) + n) % n <= n / 2)
2816 {
2817 *p1 = a;
2818 *p2 = b;
2819 }
2820 else
2821 {
2822 *p1 = b;
2823 *p2 = a;
2824 }
2825}
2826
2827
2828/**
2829 * Record @a dep as a dependency of @a step.
2830 */
2831static void
2832step_depend_on (struct Step *step,
2833 struct Step *dep)
2834{
2835 /* We're not checking for cyclic dependencies,
2836 but this is a cheap sanity check. */
2837 GNUNET_assert (step != dep);
2838 GNUNET_assert (NULL != step);
2839 GNUNET_assert (NULL != dep);
2840 GNUNET_assert (dep->round <= step->round);
2841
2842#ifdef GNUNET_EXTRA_LOGGING
2843 /* Make sure we have complete debugging information.
2844 Also checks that we don't screw up too badly
2845 constructing the task graph. */
2846 GNUNET_assert (NULL != step->debug_name);
2847 GNUNET_assert (NULL != dep->debug_name);
2848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2849 "Making step `%s' depend on `%s'\n",
2850 step->debug_name,
2851 dep->debug_name);
2852#endif
2853
2854 if (dep->subordinates_cap == dep->subordinates_len)
2855 {
2856 unsigned int target_size = 3 * (dep->subordinates_cap + 1) / 2;
2857 GNUNET_array_grow (dep->subordinates,
2858 dep->subordinates_cap,
2859 target_size);
2860 }
2861
2862 GNUNET_assert (dep->subordinates_len <= dep->subordinates_cap);
2863
2864 dep->subordinates[dep->subordinates_len] = step;
2865 dep->subordinates_len++;
2866
2867 step->pending_prereq++;
2868}
2869
2870
2871static struct Step *
2872create_step (struct ConsensusSession *session,
2873 int round,
2874 int early_finishable)
2875{
2876 struct Step *step;
2877
2878 step = GNUNET_new (struct Step);
2879 step->session = session;
2880 step->round = round;
2881 step->early_finishable = early_finishable;
2882 GNUNET_CONTAINER_DLL_insert_tail (session->steps_head,
2883 session->steps_tail,
2884 step);
2885 return step;
2886}
2887
2888
2889/**
2890 * Construct the task graph for a single gradecast.
2891 */
2892static void
2893construct_task_graph_gradecast (struct ConsensusSession *session,
2894 uint16_t rep,
2895 uint16_t lead,
2896 struct Step *step_before,
2897 struct Step *step_after)
2898{
2899 uint16_t n = session->num_peers;
2900 uint16_t me = session->local_peer_idx;
2901 uint16_t p1;
2902 uint16_t p2;
2903 /* The task we're currently setting up. */
2904 struct TaskEntry task;
2905 struct Step *step;
2906 struct Step *prev_step;
2907 uint16_t round;
2908
2909 round = step_before->round + 1;
2910
2911 /* gcast step 1: leader disseminates */
2912 step = create_step (session,
2913 round,
2914 GNUNET_YES);
2915#ifdef GNUNET_EXTRA_LOGGING
2916 GNUNET_asprintf (&step->debug_name,
2917 "disseminate leader %u rep %u",
2918 lead,
2919 rep);
2920#endif
2921 step_depend_on (step,
2922 step_before);
2923
2924 if (lead == me)
2925 {
2926 for (unsigned int k = 0; k < n; k++)
2927 {
2928 if (k == me)
2929 continue;
2930 p1 = me;
2931 p2 = k;
2932 arrange_peers (&p1, &p2, n);
2933 task = ((struct TaskEntry) {
2934 .step = step,
2935 .start = task_start_reconcile,
2936 .cancel = task_cancel_reconcile,
2937 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, p1, p2, rep,
2938 me },
2939 });
2940 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2941 put_task (session->taskmap, &task);
2942 }
2943 /* We run this task to make sure that the leader
2944 has the stored the SET_KIND_LEADER set of himself,
2945 so it can participate in the rest of the gradecast
2946 without the code having to handle any special cases. */
2947 task = ((struct TaskEntry) {
2948 .step = step,
2949 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, me, me, rep, me },
2950 .start = task_start_reconcile,
2951 .cancel = task_cancel_reconcile,
2952 });
2953 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2954 task.cls.setop.output_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
2955 me };
2956 task.cls.setop.output_diff = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL,
2957 rep, me };
2958 put_task (session->taskmap, &task);
2959 }
2960 else
2961 {
2962 p1 = me;
2963 p2 = lead;
2964 arrange_peers (&p1, &p2, n);
2965 task = ((struct TaskEntry) {
2966 .step = step,
2967 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_LEADER, p1, p2, rep,
2968 lead },
2969 .start = task_start_reconcile,
2970 .cancel = task_cancel_reconcile,
2971 });
2972 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, rep };
2973 task.cls.setop.output_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
2974 lead };
2975 task.cls.setop.output_diff = (struct DiffKey) { DIFF_KIND_LEADER_PROPOSAL,
2976 rep, lead };
2977 put_task (session->taskmap, &task);
2978 }
2979
2980 /* gcast phase 2: echo */
2981 prev_step = step;
2982 round += 1;
2983 step = create_step (session,
2984 round,
2985 GNUNET_YES);
2986#ifdef GNUNET_EXTRA_LOGGING
2987 GNUNET_asprintf (&step->debug_name,
2988 "echo leader %u rep %u",
2989 lead,
2990 rep);
2991#endif
2992 step_depend_on (step,
2993 prev_step);
2994
2995 for (unsigned int k = 0; k < n; k++)
2996 {
2997 p1 = k;
2998 p2 = me;
2999 arrange_peers (&p1, &p2, n);
3000 task = ((struct TaskEntry) {
3001 .step = step,
3002 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_ECHO, p1, p2, rep, lead },
3003 .start = task_start_reconcile,
3004 .cancel = task_cancel_reconcile,
3005 });
3006 task.cls.setop.input_set = (struct SetKey) { SET_KIND_LEADER_PROPOSAL, rep,
3007 lead };
3008 task.cls.setop.output_rfn = (struct RfnKey) { RFN_KIND_ECHO, rep, lead };
3009 put_task (session->taskmap, &task);
3010 }
3011
3012 prev_step = step;
3013 /* Same round, since step only has local tasks */
3014 step = create_step (session, round, GNUNET_YES);
3015#ifdef GNUNET_EXTRA_LOGGING
3016 GNUNET_asprintf (&step->debug_name, "echo grade leader %u rep %u", lead, rep);
3017#endif
3018 step_depend_on (step, prev_step);
3019
3020 arrange_peers (&p1, &p2, n);
3021 task = ((struct TaskEntry) {
3022 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_ECHO_GRADE, -1, -1, rep,
3023 lead },
3024 .step = step,
3025 .start = task_start_eval_echo
3026 });
3027 put_task (session->taskmap, &task);
3028
3029 prev_step = step;
3030 round += 1;
3031 step = create_step (session, round, GNUNET_YES);
3032#ifdef GNUNET_EXTRA_LOGGING
3033 GNUNET_asprintf (&step->debug_name, "confirm leader %u rep %u", lead, rep);
3034#endif
3035 step_depend_on (step, prev_step);
3036
3037 /* gcast phase 3: confirmation and grading */
3038 for (unsigned int k = 0; k < n; k++)
3039 {
3040 p1 = k;
3041 p2 = me;
3042 arrange_peers (&p1, &p2, n);
3043 task = ((struct TaskEntry) {
3044 .step = step,
3045 .start = task_start_reconcile,
3046 .cancel = task_cancel_reconcile,
3047 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_CONFIRM, p1, p2, rep,
3048 lead },
3049 });
3050 task.cls.setop.input_set = (struct SetKey) { SET_KIND_ECHO_RESULT, rep,
3051 lead };
3052 task.cls.setop.output_rfn = (struct RfnKey) { RFN_KIND_CONFIRM, rep, lead };
3053 /* If there was at least one element in the echo round that was
3054 contested (i.e. it had no n-t majority), then we let the other peers
3055 know, and other peers let us know. The contested flag for each peer is
3056 stored in the rfn. */
3057 task.cls.setop.transceive_contested = GNUNET_YES;
3058 put_task (session->taskmap, &task);
3059 }
3060
3061 prev_step = step;
3062 /* Same round, since step only has local tasks */
3063 step = create_step (session, round, GNUNET_YES);
3064#ifdef GNUNET_EXTRA_LOGGING
3065 GNUNET_asprintf (&step->debug_name, "confirm grade leader %u rep %u", lead,
3066 rep);
3067#endif
3068 step_depend_on (step, prev_step);
3069
3070 task = ((struct TaskEntry) {
3071 .step = step,
3072 .key = (struct TaskKey) { PHASE_KIND_GRADECAST_CONFIRM_GRADE, -1, -1, rep,
3073 lead },
3074 .start = task_start_grade,
3075 });
3076 put_task (session->taskmap, &task);
3077
3078 step_depend_on (step_after, step);
3079}
3080
3081
3082static void
3083construct_task_graph (struct ConsensusSession *session)
3084{
3085 uint16_t n = session->num_peers;
3086 uint16_t t = n / 3;
3087 uint16_t me = session->local_peer_idx;
3088 /* The task we're currently setting up. */
3089 struct TaskEntry task;
3090 /* Current leader */
3091 unsigned int lead;
3092 struct Step *step;
3093 struct Step *prev_step;
3094 unsigned int round = 0;
3095
3096 // XXX: introduce first step,
3097 // where we wait for all insert acks
3098 // from the set service
3099
3100 /* faster but brittle all-to-all */
3101
3102 // XXX: Not implemented yet
3103
3104 /* all-to-all step */
3105
3106 step = create_step (session, round, GNUNET_NO);
3107
3108#ifdef GNUNET_EXTRA_LOGGING
3109 step->debug_name = GNUNET_strdup ("all to all");
3110#endif
3111
3112 for (unsigned int i = 0; i < n; i++)
3113 {
3114 uint16_t p1;
3115 uint16_t p2;
3116
3117 p1 = me;
3118 p2 = i;
3119 arrange_peers (&p1, &p2, n);
3120 task = ((struct TaskEntry) {
3121 .key = (struct TaskKey) { PHASE_KIND_ALL_TO_ALL, p1, p2, -1, -1 },
3122 .step = step,
3123 .start = task_start_reconcile,
3124 .cancel = task_cancel_reconcile,
3125 });
3126 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, 0 };
3127 task.cls.setop.output_set = task.cls.setop.input_set;
3128 task.cls.setop.do_not_remove = GNUNET_YES;
3129 put_task (session->taskmap, &task);
3130 }
3131
3132 round += 1;
3133 prev_step = step;
3134 step = create_step (session, round, GNUNET_NO);;
3135#ifdef GNUNET_EXTRA_LOGGING
3136 step->debug_name = GNUNET_strdup ("all to all 2");
3137#endif
3138 step_depend_on (step, prev_step);
3139
3140
3141 for (unsigned int i = 0; i < n; i++)
3142 {
3143 uint16_t p1;
3144 uint16_t p2;
3145
3146 p1 = me;
3147 p2 = i;
3148 arrange_peers (&p1, &p2, n);
3149 task = ((struct TaskEntry) {
3150 .key = (struct TaskKey) { PHASE_KIND_ALL_TO_ALL_2, p1, p2, -1, -1 },
3151 .step = step,
3152 .start = task_start_reconcile,
3153 .cancel = task_cancel_reconcile,
3154 });
3155 task.cls.setop.input_set = (struct SetKey) { SET_KIND_CURRENT, 0 };
3156 task.cls.setop.output_set = task.cls.setop.input_set;
3157 task.cls.setop.do_not_remove = GNUNET_YES;
3158 put_task (session->taskmap, &task);
3159 }
3160
3161 round += 1;
3162
3163 prev_step = step;
3164 step = NULL;
3165
3166
3167 /* Byzantine union */
3168
3169 /* sequential repetitions of the gradecasts */
3170 for (unsigned int i = 0; i < t + 1; i++)
3171 {
3172 struct Step *step_rep_start;
3173 struct Step *step_rep_end;
3174
3175 /* Every repetition is in a separate round. */
3176 step_rep_start = create_step (session, round, GNUNET_YES);
3177#ifdef GNUNET_EXTRA_LOGGING
3178 GNUNET_asprintf (&step_rep_start->debug_name, "gradecast start rep %u", i);
3179#endif
3180
3181 step_depend_on (step_rep_start, prev_step);
3182
3183 /* gradecast has three rounds */
3184 round += 3;
3185 step_rep_end = create_step (session, round, GNUNET_YES);
3186#ifdef GNUNET_EXTRA_LOGGING
3187 GNUNET_asprintf (&step_rep_end->debug_name, "gradecast end rep %u", i);
3188#endif
3189
3190 /* parallel gradecasts */
3191 for (lead = 0; lead < n; lead++)
3192 construct_task_graph_gradecast (session, i, lead, step_rep_start,
3193 step_rep_end);
3194
3195 task = ((struct TaskEntry) {
3196 .step = step_rep_end,
3197 .key = (struct TaskKey) { PHASE_KIND_APPLY_REP, -1, -1, i, -1 },
3198 .start = task_start_apply_round,
3199 });
3200 put_task (session->taskmap, &task);
3201
3202 prev_step = step_rep_end;
3203 }
3204
3205 /* There is no next gradecast round, thus the final
3206 start step is the overall end step of the gradecasts */
3207 round += 1;
3208 step = create_step (session, round, GNUNET_NO);
3209#ifdef GNUNET_EXTRA_LOGGING
3210 GNUNET_asprintf (&step->debug_name, "finish");
3211#endif
3212 step_depend_on (step, prev_step);
3213
3214 task = ((struct TaskEntry) {
3215 .step = step,
3216 .key = (struct TaskKey) { PHASE_KIND_FINISH, -1, -1, -1, -1 },
3217 .start = task_start_finish,
3218 });
3219 task.cls.finish.input_set = (struct SetKey) { SET_KIND_LAST_GRADECAST };
3220
3221 put_task (session->taskmap, &task);
3222}
3223
3224
3225/**
3226 * Check join message.
3227 *
3228 * @param cls session of client that sent the message
3229 * @param m message sent by the client
3230 * @return #GNUNET_OK if @a m is well-formed
3231 */
3232static int
3233check_client_join (void *cls,
3234 const struct GNUNET_CONSENSUS_JoinMessage *m)
3235{
3236 uint32_t listed_peers = ntohl (m->num_peers);
3237
3238 if ((ntohs (m->header.size) - sizeof(*m)) !=
3239 listed_peers * sizeof(struct GNUNET_PeerIdentity))
3240 {
3241 GNUNET_break (0);
3242 return GNUNET_SYSERR;
3243 }
3244 return GNUNET_OK;
3245}
3246
3247
3248/**
3249 * Called when a client wants to join a consensus session.
3250 *
3251 * @param cls session of client that sent the message
3252 * @param m message sent by the client
3253 */
3254static void
3255handle_client_join (void *cls,
3256 const struct GNUNET_CONSENSUS_JoinMessage *m)
3257{
3258 struct ConsensusSession *session = cls;
3259 struct ConsensusSession *other_session;
3260
3261 initialize_session_peer_list (session,
3262 m);
3263 compute_global_id (session,
3264 &m->session_id);
3265
3266 /* Check if some local client already owns the session.
3267 It is only legal to have a session with an existing global id
3268 if all other sessions with this global id are finished.*/
3269 for (other_session = sessions_head;
3270 NULL != other_session;
3271 other_session = other_session->next)
3272 {
3273 if ( (other_session != session) &&
3274 (0 == GNUNET_CRYPTO_hash_cmp (&session->global_id,
3275 &other_session->global_id)) )
3276 break;
3277 }
3278
3279 session->conclude_deadline
3280 = GNUNET_TIME_absolute_ntoh (m->deadline);
3281 session->conclude_start
3282 = GNUNET_TIME_absolute_ntoh (m->start);
3283 session->local_peer_idx = get_peer_idx (&my_peer,
3284 session);
3285 GNUNET_assert (-1 != session->local_peer_idx);
3286
3287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3288 "Joining consensus session %s containing %u peers as %u with timeout %s\n",
3289 GNUNET_h2s (&m->session_id),
3290 session->num_peers,
3291 session->local_peer_idx,
3292 GNUNET_STRINGS_relative_time_to_string (
3293 GNUNET_TIME_absolute_get_difference (session->conclude_start,
3294 session->conclude_deadline),
3295 GNUNET_YES));
3296
3297 session->set_listener
3298 = GNUNET_SET_listen (cfg,
3299 GNUNET_SET_OPERATION_UNION,
3300 &session->global_id,
3301 &set_listen_cb,
3302 session);
3303
3304 session->setmap = GNUNET_CONTAINER_multihashmap_create (1,
3305 GNUNET_NO);
3306 session->taskmap = GNUNET_CONTAINER_multihashmap_create (1,
3307 GNUNET_NO);
3308 session->diffmap = GNUNET_CONTAINER_multihashmap_create (1,
3309 GNUNET_NO);
3310 session->rfnmap = GNUNET_CONTAINER_multihashmap_create (1,
3311 GNUNET_NO);
3312
3313 {
3314 struct SetEntry *client_set;
3315
3316 client_set = GNUNET_new (struct SetEntry);
3317 client_set->h = GNUNET_SET_create (cfg,
3318 GNUNET_SET_OPERATION_UNION);
3319 struct SetHandle *sh = GNUNET_new (struct SetHandle);
3320 sh->h = client_set->h;
3321 GNUNET_CONTAINER_DLL_insert (session->set_handles_head,
3322 session->set_handles_tail,
3323 sh);
3324 client_set->key = ((struct SetKey) { SET_KIND_CURRENT, 0, 0 });
3325 put_set (session,
3326 client_set);
3327 }
3328
3329 session->peers_blacklisted = GNUNET_new_array (session->num_peers,
3330 int);
3331
3332 /* Just construct the task graph,
3333 but don't run anything until the client calls conclude. */
3334 construct_task_graph (session);
3335 GNUNET_SERVICE_client_continue (session->client);
3336}
3337
3338
3339/**
3340 * Called when a client performs an insert operation.
3341 *
3342 * @param cls client handle
3343 * @param msg message sent by the client
3344 * @return #GNUNET_OK (always well-formed)
3345 */
3346static int
3347check_client_insert (void *cls,
3348 const struct GNUNET_CONSENSUS_ElementMessage *msg)
3349{
3350 return GNUNET_OK;
3351}
3352
3353
3354/**
3355 * Called when a client performs an insert operation.
3356 *
3357 * @param cls client handle
3358 * @param msg message sent by the client
3359 */
3360static void
3361handle_client_insert (void *cls,
3362 const struct GNUNET_CONSENSUS_ElementMessage *msg)
3363{
3364 struct ConsensusSession *session = cls;
3365 ssize_t element_size;
3366 struct GNUNET_SET_Handle *initial_set;
3367 struct ConsensusElement *ce;
3368
3369 if (GNUNET_YES == session->conclude_started)
3370 {
3371 GNUNET_break (0);
3372 GNUNET_SERVICE_client_drop (session->client);
3373 return;
3374 }
3375 element_size = ntohs (msg->header.size) - sizeof(*msg);
3376 ce = GNUNET_malloc (sizeof(struct ConsensusElement) + element_size);
3377 GNUNET_memcpy (&ce[1],
3378 &msg[1],
3379 element_size);
3380 ce->payload_type = msg->element_type;
3381
3382 {
3383 struct SetKey key = { SET_KIND_CURRENT, 0, 0 };
3384 struct SetEntry *entry;
3385
3386 entry = lookup_set (session,
3387 &key);
3388 GNUNET_assert (NULL != entry);
3389 initial_set = entry->h;
3390 }
3391
3392 session->num_client_insert_pending++;
3393
3394 {
3395 struct GNUNET_SET_Element element = {
3396 .element_type = GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT,
3397 .size = sizeof(struct ConsensusElement) + element_size,
3398 .data = ce,
3399 };
3400
3401 GNUNET_SET_add_element (initial_set,
3402 &element,
3403 NULL,
3404 NULL);
3405#ifdef GNUNET_EXTRA_LOGGING
3406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3407 "P%u: element %s added\n",
3408 session->local_peer_idx,
3409 debug_str_element (&element));
3410#endif
3411 }
3412 GNUNET_free (ce);
3413 GNUNET_SERVICE_client_continue (session->client);
3414}
3415
3416
3417/**
3418 * Called when a client performs the conclude operation.
3419 *
3420 * @param cls client handle
3421 * @param message message sent by the client
3422 */
3423static void
3424handle_client_conclude (void *cls,
3425 const struct GNUNET_MessageHeader *message)
3426{
3427 struct ConsensusSession *session = cls;
3428
3429 if (GNUNET_YES == session->conclude_started)
3430 {
3431 /* conclude started twice */
3432 GNUNET_break (0);
3433 GNUNET_SERVICE_client_drop (session->client);
3434 return;
3435 }
3436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3437 "conclude requested\n");
3438 session->conclude_started = GNUNET_YES;
3439 install_step_timeouts (session);
3440 run_ready_steps (session);
3441 GNUNET_SERVICE_client_continue (session->client);
3442}
3443
3444
3445/**
3446 * Called to clean up, after a shutdown has been requested.
3447 *
3448 * @param cls closure
3449 */
3450static void
3451shutdown_task (void *cls)
3452{
3453 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3454 "shutting down\n");
3455 GNUNET_STATISTICS_destroy (statistics,
3456 GNUNET_NO);
3457 statistics = NULL;
3458}
3459
3460
3461/**
3462 * Start processing consensus requests.
3463 *
3464 * @param cls closure
3465 * @param c configuration to use
3466 * @param service the initialized service
3467 */
3468static void
3469run (void *cls,
3470 const struct GNUNET_CONFIGURATION_Handle *c,
3471 struct GNUNET_SERVICE_Handle *service)
3472{
3473 cfg = c;
3474 if (GNUNET_OK !=
3475 GNUNET_CRYPTO_get_peer_identity (cfg,
3476 &my_peer))
3477 {
3478 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3479 "Could not retrieve host identity\n");
3480 GNUNET_SCHEDULER_shutdown ();
3481 return;
3482 }
3483 statistics = GNUNET_STATISTICS_create ("consensus",
3484 cfg);
3485 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
3486 NULL);
3487}
3488
3489
3490/**
3491 * Callback called when a client connects to the service.
3492 *
3493 * @param cls closure for the service
3494 * @param c the new client that connected to the service
3495 * @param mq the message queue used to send messages to the client
3496 * @return @a c
3497 */
3498static void *
3499client_connect_cb (void *cls,
3500 struct GNUNET_SERVICE_Client *c,
3501 struct GNUNET_MQ_Handle *mq)
3502{
3503 struct ConsensusSession *session = GNUNET_new (struct ConsensusSession);
3504
3505 session->client = c;
3506 session->client_mq = mq;
3507 GNUNET_CONTAINER_DLL_insert (sessions_head,
3508 sessions_tail,
3509 session);
3510 return session;
3511}
3512
3513
3514/**
3515 * Callback called when a client disconnected from the service
3516 *
3517 * @param cls closure for the service
3518 * @param c the client that disconnected
3519 * @param internal_cls should be equal to @a c
3520 */
3521static void
3522client_disconnect_cb (void *cls,
3523 struct GNUNET_SERVICE_Client *c,
3524 void *internal_cls)
3525{
3526 struct ConsensusSession *session = internal_cls;
3527
3528 if (NULL != session->set_listener)
3529 {
3530 GNUNET_SET_listen_cancel (session->set_listener);
3531 session->set_listener = NULL;
3532 }
3533 GNUNET_CONTAINER_DLL_remove (sessions_head,
3534 sessions_tail,
3535 session);
3536 while (session->set_handles_head)
3537 {
3538 struct SetHandle *sh = session->set_handles_head;
3539
3540 session->set_handles_head = sh->next;
3541 GNUNET_SET_destroy (sh->h);
3542 GNUNET_free (sh);
3543 }
3544 GNUNET_free (session);
3545}
3546
3547
3548/**
3549 * Define "main" method using service macro.
3550 */
3551GNUNET_SERVICE_MAIN (
3552 "consensus",
3553 GNUNET_SERVICE_OPTION_NONE,
3554 &run,
3555 &client_connect_cb,
3556 &client_disconnect_cb,
3557 NULL,
3558 GNUNET_MQ_hd_fixed_size (client_conclude,
3559 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_CONCLUDE,
3560 struct GNUNET_MessageHeader,
3561 NULL),
3562 GNUNET_MQ_hd_var_size (client_insert,
3563 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_INSERT,
3564 struct GNUNET_CONSENSUS_ElementMessage,
3565 NULL),
3566 GNUNET_MQ_hd_var_size (client_join,
3567 GNUNET_MESSAGE_TYPE_CONSENSUS_CLIENT_JOIN,
3568 struct GNUNET_CONSENSUS_JoinMessage,
3569 NULL),
3570 GNUNET_MQ_handler_end ());
3571
3572/* end of gnunet-service-consensus.c */
diff --git a/src/contrib/service/consensus/meson.build b/src/contrib/service/consensus/meson.build
new file mode 100644
index 000000000..86cc0663b
--- /dev/null
+++ b/src/contrib/service/consensus/meson.build
@@ -0,0 +1,66 @@
1libgnunetconsensus_src = ['consensus_api.c']
2
3gnunetserviceconsensus_src = ['gnunet-service-consensus.c']
4
5configure_file(input : 'consensus.conf.in',
6 output : 'consensus.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11# FIXME needs new seti/setu
12if get_option('monolith')
13 #foreach p : libgnunetconsensus_src + gnunetserviceconsensus_src
14 # gnunet_src += 'consensus/' + p
15 #endforeach
16endif
17
18libgnunetconsensus = shared_library('gnunetconsensus',
19 libgnunetconsensus_src,
20 soversion: '0',
21 version: '0.0.0',
22 dependencies: libgnunetutil_dep,
23 include_directories: [incdir, configuration_inc],
24 install: true,
25 install_dir: get_option('libdir'))
26pkg.generate(libgnunetconsensus, url: 'https://www.gnunet.org',
27 description : 'Provides API for accessing the consensus service')
28libgnunetarm_dep = declare_dependency(link_with : libgnunetarm)
29libgnunetconsensus_dep = declare_dependency(link_with : libgnunetconsensus)
30
31shared_module('gnunet_plugin_block_consensus',
32 ['plugin_block_consensus.c'],
33 dependencies: [libgnunetutil_dep,
34 libgnunetblock_dep],
35 include_directories: [incdir, configuration_inc],
36 install:true,
37 install_dir: get_option('libdir')/'gnunet')
38
39executable ('gnunet-service-consensus',
40 gnunetserviceconsensus_src,
41 dependencies: [libgnunetconsensus_dep,
42 libgnunetutil_dep,
43 libgnunetset_dep,
44 libgnunetstatistics_dep],
45 include_directories: [incdir, configuration_inc],
46 install: true,
47 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
48
49# FIXME do we need evil stuff and ENABLE_MALICIOUS?
50
51testconsensusapi = executable ('test_consensus_api',
52 ['test_consensus_api.c'],
53 dependencies: [libgnunetconsensus_dep,
54 libgnunetutil_dep,
55 libgnunettesting_dep],
56 include_directories: [incdir, configuration_inc],
57 install: false)
58
59configure_file(input : 'test_consensus.conf',
60 output : 'test_consensus.conf',
61 configuration : cdata,
62 install: false)
63
64test('test_consensus_api', testconsensusapi,
65 workdir: meson.current_build_dir(),
66 suite: ['consensus', 'contrib'])
diff --git a/src/contrib/service/consensus/plugin_block_consensus.c b/src/contrib/service/consensus/plugin_block_consensus.c
new file mode 100644
index 000000000..241d8fc7b
--- /dev/null
+++ b/src/contrib/service/consensus/plugin_block_consensus.c
@@ -0,0 +1,222 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/plugin_block_consensus.c
23 * @brief consensus block, either nested block or marker
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "consensus_protocol.h"
29#include "gnunet_block_plugin.h"
30#include "gnunet_block_group_lib.h"
31
32
33/**
34 * Our closure.
35 */
36struct BlockContext
37{
38 /**
39 * Configuration to use.
40 */
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Lazily initialized block context.
45 */
46 struct GNUNET_BLOCK_Context *bc;
47};
48
49
50
51/**
52 * Function called to validate a query.
53 *
54 * @param cls closure
55 * @param type block type
56 * @param query original query (hash)
57 * @param xquery extrended query data (can be NULL, depending on type)
58 * @param xquery_size number of bytes in @a xquery
59 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
60 */
61static enum GNUNET_GenericReturnValue
62block_plugin_consensus_check_query (void *cls,
63 enum GNUNET_BLOCK_Type type,
64 const struct GNUNET_HashCode *query,
65 const void *xquery,
66 size_t xquery_size)
67{
68 /* consensus does not use queries/DHT */
69 GNUNET_break (0);
70 return GNUNET_SYSERR;
71}
72
73
74/**
75 * Function called to validate a block for storage.
76 *
77 * @param cls closure
78 * @param type block type
79 * @param block block data to validate
80 * @param block_size number of bytes in @a block
81 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
82 */
83static enum GNUNET_GenericReturnValue
84block_plugin_consensus_check_block (void *cls,
85 enum GNUNET_BLOCK_Type type,
86 const void *block,
87 size_t block_size)
88{
89 struct BlockContext *ctx = cls;
90 const struct ConsensusElement *ce = block;
91
92 if (block_size < sizeof(*ce))
93 {
94 GNUNET_break_op (0);
95 return GNUNET_NO;
96 }
97 if ( (0 != ce->marker) ||
98 (0 == ce->payload_type) )
99 return GNUNET_OK;
100 if (NULL == ctx->bc)
101 ctx->bc = GNUNET_BLOCK_context_create (ctx->cfg);
102 return GNUNET_BLOCK_check_block (ctx->bc,
103 ntohl (ce->payload_type),
104 &ce[1],
105 block_size - sizeof(*ce));
106}
107
108
109/**
110 * Function called to validate a reply to a request. Note that it is assumed
111 * that the reply has already been matched to the key (and signatures checked)
112 * as it would be done with the GetKeyFunction and the
113 * BlockEvaluationFunction.
114 *
115 * @param cls closure
116 * @param type block type
117 * @param group which block group to use for evaluation
118 * @param query original query (hash)
119 * @param xquery extrended query data (can be NULL, depending on type)
120 * @param xquery_size number of bytes in @a xquery
121 * @param reply_block response to validate
122 * @param reply_block_size number of bytes in @a reply_block
123 * @return characterization of result
124 */
125static enum GNUNET_BLOCK_ReplyEvaluationResult
126block_plugin_consensus_check_reply (
127 void *cls,
128 enum GNUNET_BLOCK_Type type,
129 struct GNUNET_BLOCK_Group *group,
130 const struct GNUNET_HashCode *query,
131 const void *xquery,
132 size_t xquery_size,
133 const void *reply_block,
134 size_t reply_block_size)
135{
136 struct BlockContext *ctx = cls;
137 const struct ConsensusElement *ce = reply_block;
138
139 GNUNET_assert (reply_block_size >= sizeof(struct ConsensusElement));
140 if ( (0 != ce->marker) ||
141 (0 == ce->payload_type) )
142 return GNUNET_BLOCK_REPLY_OK_MORE;
143 if (NULL == ctx->bc)
144 ctx->bc = GNUNET_BLOCK_context_create (ctx->cfg);
145 return GNUNET_BLOCK_check_reply (ctx->bc,
146 ntohl (ce->payload_type),
147 group,
148 query,
149 xquery,
150 xquery_size,
151 &ce[1],
152 reply_block_size - sizeof(*ce));
153}
154
155
156/**
157 * Function called to obtain the key for a block.
158 *
159 * @param cls closure
160 * @param type block type
161 * @param block block to get the key for
162 * @param block_size number of bytes in block
163 * @param key set to the key (query) for the given block
164 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
165 * (or if extracting a key from a block of this type does not work)
166 */
167static enum GNUNET_GenericReturnValue
168block_plugin_consensus_get_key (void *cls,
169 enum GNUNET_BLOCK_Type type,
170 const void *block,
171 size_t block_size,
172 struct GNUNET_HashCode *key)
173{
174 return GNUNET_SYSERR;
175}
176
177
178/**
179 * Entry point for the plugin.
180 */
181void *
182libgnunet_plugin_block_consensus_init (void *cls)
183{
184 static const enum GNUNET_BLOCK_Type types[] = {
185 GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT,
186 GNUNET_BLOCK_TYPE_ANY /* end of list */
187 };
188 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
189 struct BlockContext *ctx;
190 struct GNUNET_BLOCK_PluginFunctions *api;
191
192 ctx = GNUNET_new (struct BlockContext);
193 ctx->cfg = cfg;
194 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
195 api->cls = ctx;
196 api->get_key = &block_plugin_consensus_get_key;
197 api->check_query = &block_plugin_consensus_check_query;
198 api->check_block = &block_plugin_consensus_check_block;
199 api->check_reply = &block_plugin_consensus_check_reply;
200 api->types = types;
201 return api;
202}
203
204
205/**
206 * Exit point from the plugin.
207 */
208void *
209libgnunet_plugin_block_consensus_done (void *cls)
210{
211 struct GNUNET_BLOCK_PluginFunctions *api = cls;
212 struct BlockContext *ctx = api->cls;
213
214 if (NULL != ctx->bc)
215 GNUNET_BLOCK_context_destroy (ctx->bc);
216 GNUNET_free (ctx);
217 GNUNET_free (api);
218 return NULL;
219}
220
221
222/* end of plugin_block_consensus.c */
diff --git a/src/contrib/service/consensus/test_consensus.conf b/src/contrib/service/consensus/test_consensus.conf
new file mode 100644
index 000000000..df7fb6861
--- /dev/null
+++ b/src/contrib/service/consensus/test_consensus.conf
@@ -0,0 +1,87 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-consensus/
3
4[consensus]
5#OPTIONS = -L INFO
6BINARY = gnunet-service-consensus
7
8#PREFIX = valgrind
9
10#EVIL_SPEC = 0;cram-all;noreplace;5
11#EVIL_SPEC = 0;cram;5/1;cram;5
12#EVIL_SPEC = 0;cram;5/1;cram;5/2;cram;5
13#EVIL_SPEC = 0;cram;5/1;cram;5/2;cram;5/3;cram;5
14
15
16[arm]
17RESOURCE_DIAGNOSTICS = resource.log.${PEERID:-master}
18
19[core]
20IMMEDIATE_START = YES
21
22[revocation]
23IMMEDIATE_START = NO
24
25[fs]
26IMMEDIATE_START = NO
27
28[gns]
29IMMEDIATE_START = NO
30
31[zonemaster]
32IMMEDIATE_START = NO
33
34[hostlist]
35IMMEDIATE_START = NO
36
37[cadet]
38#PREFIX = valgrind
39
40[transport]
41PLUGINS = unix
42OPTIONS = -LERROR
43
44[set]
45#OPTIONS = -L INFO
46#PREFIX = valgrind --leak-check=full
47#PREFIX = valgrind
48
49[testbed]
50OVERLAY_TOPOLOGY = CLIQUE
51MAX_PARALLEL_OPERATIONS = 1000
52MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 100
53OPERATION_TIMEOUT = 60 s
54MAX_OPEN_FDS = 4096
55
56[hostlist]
57START_ON_DEMAND = NO
58
59[fs]
60START_ON_DEMAND = NO
61
62[revocation]
63START_ON_DEMAND = NO
64
65[nat]
66# Use addresses from the local network interfaces (including loopback, but also others)
67USE_LOCALADDR = YES
68
69# Disable IPv6 support
70DISABLEV6 = NO
71
72# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
73RETURN_LOCAL_ADDRESSES = YES
74
75[nse]
76WORKBITS=0
77
78[ats]
79WAN_QUOTA_IN = unlimited
80WAN_QUOTA_OUT = unlimited
81
82[core]
83USE_EPHEMERAL_KEYS = NO
84
85[rps]
86START_ON_DEMAND = NO
87IMMEDIATE_START = NO
diff --git a/src/contrib/service/consensus/test_consensus_api.c b/src/contrib/service/consensus/test_consensus_api.c
new file mode 100644
index 000000000..235a67484
--- /dev/null
+++ b/src/contrib/service/consensus/test_consensus_api.c
@@ -0,0 +1,120 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file consensus/test_consensus_api.c
23 * @brief testcase for consensus_api.c
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_consensus_service.h"
28#include "gnunet_testing_lib.h"
29
30
31static struct GNUNET_CONSENSUS_Handle *consensus;
32
33static struct GNUNET_HashCode session_id;
34
35static unsigned int elements_received;
36
37
38static void
39conclude_done (void *cls)
40{
41 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "conclude over\n");
42 if (2 != elements_received)
43 GNUNET_assert (0);
44 GNUNET_SCHEDULER_shutdown ();
45}
46
47
48static void
49on_new_element (void *cls,
50 const struct GNUNET_SET_Element *element)
51{
52 elements_received++;
53}
54
55
56static void
57insert_done (void *cls, int success)
58{
59 /* make sure cb is only called once */
60 static int called = GNUNET_NO;
61
62 GNUNET_assert (GNUNET_NO == called);
63 called = GNUNET_YES;
64 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "insert done\n");
65 GNUNET_CONSENSUS_conclude (consensus, &conclude_done, NULL);
66}
67
68
69/**
70 * Signature of the main function of a task.
71 *
72 * @param cls closure
73 */
74static void
75on_shutdown (void *cls)
76{
77 if (NULL != consensus)
78 {
79 GNUNET_CONSENSUS_destroy (consensus);
80 consensus = NULL;
81 }
82}
83
84
85static void
86run (void *cls,
87 const struct GNUNET_CONFIGURATION_Handle *cfg,
88 struct GNUNET_TESTING_Peer *peer)
89{
90 char *str = "foo";
91
92 struct GNUNET_SET_Element el1 = { 4, 0, "foo" };
93 struct GNUNET_SET_Element el2 = { 5, 0, "quux" };
94
95 GNUNET_log_setup ("test_consensus_api",
96 "INFO",
97 NULL);
98 GNUNET_SCHEDULER_add_shutdown (&on_shutdown, NULL);
99
100 GNUNET_CRYPTO_hash (str, strlen (str), &session_id);
101 consensus = GNUNET_CONSENSUS_create (cfg, 0, NULL, &session_id,
102 GNUNET_TIME_relative_to_absolute (
103 GNUNET_TIME_UNIT_SECONDS),
104 GNUNET_TIME_relative_to_absolute (
105 GNUNET_TIME_UNIT_MINUTES),
106 on_new_element, &consensus);
107 GNUNET_assert (consensus != NULL);
108
109 GNUNET_CONSENSUS_insert (consensus, &el1, NULL, &consensus);
110 GNUNET_CONSENSUS_insert (consensus, &el2, &insert_done, &consensus);
111}
112
113
114int
115main (int argc, char **argv)
116{
117 return GNUNET_TESTING_peer_run ("test_consensus_api",
118 "test_consensus.conf",
119 &run, NULL);
120}
diff --git a/src/contrib/service/conversation/.gitignore b/src/contrib/service/conversation/.gitignore
new file mode 100644
index 000000000..e74e259bf
--- /dev/null
+++ b/src/contrib/service/conversation/.gitignore
@@ -0,0 +1,8 @@
1gnunet-service-conversation
2gnunet-conversation
3gnunet-conversation-test
4gnunet-helper-audio-playback
5gnunet-helper-audio-record
6test_conversation_api
7test_conversation_api_reject
8test_conversation_api_twocalls
diff --git a/src/contrib/service/conversation/Makefile.am b/src/contrib/service/conversation/Makefile.am
new file mode 100644
index 000000000..b573208e6
--- /dev/null
+++ b/src/contrib/service/conversation/Makefile.am
@@ -0,0 +1,263 @@
1# This Makefile.am is in the public domain
2SUBDIRS = .
3
4plugindir = $(libdir)/gnunet
5
6AM_CPPFLAGS = \
7 $(GNUNET_CPPFLAGS) \
8 -I$(top_srcdir)/src/include \
9 -I$(top_srcdir)
10
11
12if BUILD_CONVERSATION
13lib_LTLIBRARIES = \
14 libgnunetmicrophone.la \
15 libgnunetspeaker.la \
16 libgnunetconversation.la
17
18bin_PROGRAMS = \
19 gnunet-conversation-test \
20 gnunet-conversation
21
22libexec_PROGRAMS = \
23 gnunet-service-conversation
24
25check_PROGRAMS = \
26 test_conversation_api \
27 test_conversation_api_reject \
28 test_conversation_api_twocalls
29
30pkgcfg_DATA = conversation.conf
31endif
32
33pkgcfgdir= $(pkgdatadir)/config.d/
34
35libexecdir= $(pkglibdir)/libexec/
36
37plugin_LTLIBRARIES = \
38 libgnunet_plugin_gnsrecord_conversation.la
39
40
41libgnunet_plugin_gnsrecord_conversation_la_SOURCES = \
42 plugin_gnsrecord_conversation.c
43libgnunet_plugin_gnsrecord_conversation_la_LIBADD = \
44 $(top_builddir)/src/lib/util/libgnunetutil.la \
45 $(LTLIBINTL)
46libgnunet_plugin_gnsrecord_conversation_la_LDFLAGS = \
47 $(GN_PLUGIN_LDFLAGS)
48
49
50libgnunetmicrophone_la_SOURCES = \
51 microphone.c
52libgnunetmicrophone_la_LIBADD = \
53 $(top_builddir)/src/lib/util/libgnunetutil.la
54libgnunetmicrophone_la_LDFLAGS = \
55 $(GN_LIB_LDFLAGS) \
56 -version-info 0:0:0
57
58libgnunetspeaker_la_SOURCES = \
59 speaker.c
60libgnunetspeaker_la_LIBADD = \
61 $(top_builddir)/src/lib/util/libgnunetutil.la
62libgnunetspeaker_la_LDFLAGS = \
63 $(GN_LIB_LDFLAGS) \
64 -version-info 0:0:0
65
66
67libgnunetconversation_la_SOURCES = \
68 conversation_api.c \
69 conversation_api_call.c \
70 conversation.h
71libgnunetconversation_la_LIBADD = \
72 $(top_builddir)/src/service/gns/libgnunetgns.la \
73 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
74 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
75 $(top_builddir)/src/service/identity/libgnunetidentity.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la
77
78libgnunetconversation_la_LDFLAGS = \
79 $(GN_LIB_LDFLAGS) \
80 -version-info 0:0:0
81
82
83if BUILD_PULSE_HELPERS
84AUDIO_HELPER_RECD=gnunet-helper-audio-record
85AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
86AUDIO_TESTS=$(check_PROGRAMS)
87else
88if BUILD_GST_HELPERS
89AUDIO_HELPER_RECD=gnunet-helper-audio-record
90AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
91AUDIO_TESTS=$(check_PROGRAMS)
92else
93if BUILD_EXPERIMENTAL_HELPERS
94AUDIO_HELPER_RECD=gnunet-helper-audio-record
95AUDIO_HELPER_PLAY=gnunet-helper-audio-playback
96AUDIO_TESTS=$(check_PROGRAMS)
97endif
98endif
99endif
100
101if BUILD_CONVERSATION
102libexec_PROGRAMS += \
103 $(AUDIO_HELPER_RECD) \
104 $(AUDIO_HELPER_PLAY)
105endif
106
107AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
108if ENABLE_TEST_RUN
109TESTS = $(AUDIO_TESTS)
110endif
111
112if BUILD_PULSE_HELPERS
113gnunet_helper_audio_record_SOURCES = \
114 gnunet-helper-audio-record.c
115gnunet_helper_audio_record_LDADD = \
116 $(top_builddir)/src/lib/util/libgnunetutil.la \
117 -lpulse -lopus -logg \
118 $(INTLLIBS)
119#gnunet_helper_audio_record_LDFLAGS = \
120#
121
122gnunet_helper_audio_playback_SOURCES = \
123 gnunet-helper-audio-playback.c
124gnunet_helper_audio_playback_LDADD = \
125 $(top_builddir)/src/lib/util/libgnunetutil.la \
126 -lpulse -lopus -logg \
127 $(INTLLIBS)
128#gnunet_helper_audio_playback_LDFLAGS = \
129#
130else
131if BUILD_GST_HELPERS
132gnunet_helper_audio_record_SOURCES = \
133 gnunet-helper-audio-record-gst.c
134gnunet_helper_audio_record_LDADD = \
135 $(top_builddir)/src/lib/util/libgnunetutil.la \
136 $(GST_LIBS) \
137 $(INTLLIBS)
138gnunet_helper_audio_record_LDFLAGS = \
139 $(GST_LDFLAGS)
140gnunet_helper_audio_record_CFLAGS = \
141 $(GST_CFLAGS)
142
143gnunet_helper_audio_playback_SOURCES = \
144 gnunet-helper-audio-playback-gst.c
145gnunet_helper_audio_playback_LDADD = \
146 $(top_builddir)/src/lib/util/libgnunetutil.la \
147 $(GST_LIBS) \
148 $(INTLLIBS)
149gnunet_helper_audio_playback_LDFLAGS = \
150 $(GST_LDFLAGS)
151gnunet_helper_audio_playback_CFLAGS = \
152 $(GST_CFLAGS) -DIS_SPEAKER
153else
154if BUILD_EXPERIMENTAL_HELPERS
155gnunet_helper_audio_record_SOURCES = \
156 gnunet_gst_test.c gnunet_gst.c
157gnunet_helper_audio_record_LDADD = \
158 $(top_builddir)/src/lib/util/libgnunetutil.la \
159 $(GST_LIBS) \
160 $(INTLLIBS)
161gnunet_helper_audio_record_LDFLAGS = \
162 $(GST_LDFLAGS)
163gnunet_helper_audio_record_CFLAGS = \
164 $(GST_CFLAGS) -DIS_MIC
165
166gnunet_helper_audio_playback_SOURCES = \
167 gnunet_gst_test.c gnunet_gst.c
168gnunet_helper_audio_playback_LDADD = \
169 $(top_builddir)/src/lib/util/libgnunetutil.la \
170 $(GST_LIBS) \
171 $(INTLLIBS)
172gnunet_helper_audio_playback_LDFLAGS = \
173 $(GST_LDFLAGS)
174gnunet_helper_audio_playback_CFLAGS = \
175 $(GST_CFLAGS) -DIS_SPEAKER
176endif
177endif
178endif
179
180gnunet_service_conversation_SOURCES = \
181 gnunet-service-conversation.c
182gnunet_service_conversation_LDADD = \
183 libgnunetconversation.la \
184 libgnunetspeaker.la \
185 libgnunetmicrophone.la \
186 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
187 $(top_builddir)/src/lib/util/libgnunetutil.la \
188 $(top_builddir)/src/service/identity/libgnunetidentity.la \
189 $(INTLLIBS)
190#gnunet_service_conversation_LDFLAGS = \
191#
192
193gnunet_conversation_SOURCES = \
194 gnunet-conversation.c
195gnunet_conversation_LDADD = \
196 libgnunetmicrophone.la \
197 libgnunetspeaker.la \
198 libgnunetconversation.la \
199 $(top_builddir)/src/service/gns/libgnunetgns.la \
200 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
201 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
202 $(top_builddir)/src/service/identity/libgnunetidentity.la \
203 $(top_builddir)/src/lib/util/libgnunetutil.la \
204 $(INTLLIBS)
205#gnunet_conversation_LDFLAGS = \
206#
207
208gnunet_conversation_test_SOURCES = \
209 gnunet-conversation-test.c
210gnunet_conversation_test_LDADD = \
211 libgnunetmicrophone.la \
212 libgnunetspeaker.la \
213 $(top_builddir)/src/lib/util/libgnunetutil.la \
214 $(INTLLIBS)
215#gnunet_conversation_test_LDFLAGS = \
216#
217
218
219test_conversation_api_SOURCES = \
220 test_conversation_api.c
221test_conversation_api_LDADD = \
222 libgnunetconversation.la \
223 libgnunetspeaker.la \
224 libgnunetmicrophone.la \
225 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
226 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
227 $(top_builddir)/src/service/identity/libgnunetidentity.la \
228 $(top_builddir)/src/service/testing/libgnunettesting.la \
229 $(top_builddir)/src/lib/util/libgnunetutil.la
230test_conversation_api_LDFLAGS = \
231 -export-dynamic
232
233test_conversation_api_twocalls_SOURCES = \
234 test_conversation_api_twocalls.c
235test_conversation_api_twocalls_LDADD = \
236 libgnunetconversation.la \
237 libgnunetspeaker.la \
238 libgnunetmicrophone.la \
239 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
240 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
241 $(top_builddir)/src/service/identity/libgnunetidentity.la \
242 $(top_builddir)/src/service/testing/libgnunettesting.la \
243 $(top_builddir)/src/lib/util/libgnunetutil.la
244test_conversation_api_twocalls_LDFLAGS = \
245 -export-dynamic
246
247test_conversation_api_reject_SOURCES = \
248 test_conversation_api_reject.c
249test_conversation_api_reject_LDADD = \
250 libgnunetconversation.la \
251 libgnunetspeaker.la \
252 libgnunetmicrophone.la \
253 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
254 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
255 $(top_builddir)/src/service/identity/libgnunetidentity.la \
256 $(top_builddir)/src/service/testing/libgnunettesting.la \
257 $(top_builddir)/src/lib/util/libgnunetutil.la
258test_conversation_api_reject_LDFLAGS = \
259 -export-dynamic
260
261
262
263EXTRA_DIST = test_conversation.conf
diff --git a/src/contrib/service/conversation/build_gst_test.sh b/src/contrib/service/conversation/build_gst_test.sh
new file mode 100644
index 000000000..1feb9e1c4
--- /dev/null
+++ b/src/contrib/service/conversation/build_gst_test.sh
@@ -0,0 +1,9 @@
1#!/bin/bash
2
3colorgcc -DIS_MIC -g gnunet_gst_test.c gnunet_gst.c -o gnunet-helper-audio-record-experimental `pkg-config --cflags --libs gstreamer-app-1.0 gnunetutil gnunetconversation gnunetenv gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
4
5colorgcc -DIS_SPEAKER -g gnunet_gst_test.c gnunet_gst.c -o gnunet-helper-audio-playback-experimental `pkg-config --cflags --libs gstreamer-app-1.0 gnunetutil gnunetconversation gnunetenv gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
6
7
8
9#colorgcc -g gnunet_gst_test.c gnunet_gst.c -o gnunet_gst_test `pkg-config --cflags --libs gstreamer-app-1.0 gstreamer-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0 gstreamer-video-1.0` -O0 -march=native -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-conversion -Wpedantic -Wformat -Wformat-security -fstack-protector -D_FORTIFY_SOURCE=2 -std=c99 -D_GNU_SOURCE
diff --git a/src/contrib/service/conversation/conversation.conf.in b/src/contrib/service/conversation/conversation.conf.in
new file mode 100644
index 000000000..b28fb6e1f
--- /dev/null
+++ b/src/contrib/service/conversation/conversation.conf.in
@@ -0,0 +1,29 @@
1[conversation]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-conversation
4UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-conversation.sock
5HOSTNAME = localhost
6@UNIXONLY@ PORT = 2120
7
8# Desired phone line. Change if multiple users are using
9# the same peer and we thus need disjoint phone lines.
10LINE = 1
11
12# Should the phone record be private? (only set to YES if
13# you really know what you are doing, you will then likely
14# not be able to receive calls).
15RECORD_IS_PRIVATE = NO
16
17# How long should phone records remain published in GNS?
18# A long expiration time has the advantage that the phone
19# is more likely found and/or resolved faster, OTOH it
20# then takes longer to change the phone line if necessary.
21# The default should be fine for most users.
22RECORD_EXPIRATION = 1 day
23
24
25ACCEPT_FROM = 127.0.0.1;
26ACCEPT_FROM6 = ::1;
27UNIX_MATCH_UID = NO
28UNIX_MATCH_GID = YES
29
diff --git a/src/contrib/service/conversation/conversation.h b/src/contrib/service/conversation/conversation.h
new file mode 100644
index 000000000..ee4ca372c
--- /dev/null
+++ b/src/contrib/service/conversation/conversation.h
@@ -0,0 +1,407 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013-2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/conversation.h
23 * @brief constants for network protocols
24 * @author Siomon Dieterle
25 * @author Andreas Fuchs
26 */
27#ifndef CONVERSATION_H
28#define CONVERSATION_H
29
30#include "gnunet_identity_service.h"
31
32#ifdef __cplusplus
33extern "C"
34{
35#if 0 /* keep Emacsens' auto-indent happy */
36}
37#endif
38#endif
39
40
41#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply ( \
42 GNUNET_TIME_UNIT_SECONDS, 60)
43
44
45/**
46 * Highest bit in a 32-bit unsigned integer,
47 * bit set if we are making an outgoing call,
48 * bit unset for local lines.
49 */
50#define HIGH_BIT ((uint32_t) (1LL << 31))
51
52GNUNET_NETWORK_STRUCT_BEGIN
53
54
55/**
56 * Message to transmit the audio (between client and helpers).
57 */
58struct AudioMessage
59{
60 /**
61 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO
62 */
63 struct GNUNET_MessageHeader header;
64
65 /* followed by audio data */
66};
67
68
69/**
70 * Client -> Service message to register a phone.
71 */
72struct ClientPhoneRegisterMessage
73{
74 /**
75 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER
76 */
77 struct GNUNET_MessageHeader header;
78
79 /**
80 * Always zero.
81 */
82 uint32_t reserved GNUNET_PACKED;
83
84 /**
85 * Phone line / CADET port to register.
86 */
87 struct GNUNET_HashCode line_port;
88};
89
90
91/**
92 * Service -> Client message for phone is ringing.
93 */
94struct ClientPhoneRingMessage
95{
96 /**
97 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING
98 */
99 struct GNUNET_MessageHeader header;
100
101 /**
102 * CID, internal caller ID number used in the future to identify
103 * which active call we are talking about.
104 */
105 uint32_t cid GNUNET_PACKED;
106
107 /**
108 * The identity key length
109 */
110 uint32_t key_len;
111
112 /**
113 * followed by who is calling us?, a public key
114 */
115};
116
117
118/**
119 * Service <-> Client message for phone was suspended.
120 */
121struct ClientPhoneSuspendMessage
122{
123 /**
124 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND
125 */
126 struct GNUNET_MessageHeader header;
127
128 /**
129 * CID, internal caller ID to identify which active call we are
130 * talking about.
131 */
132 uint32_t cid GNUNET_PACKED;
133};
134
135
136/**
137 * Service <-> Client message for phone was resumed.
138 */
139struct ClientPhoneResumeMessage
140{
141 /**
142 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME
143 */
144 struct GNUNET_MessageHeader header;
145
146 /**
147 * CID, internal caller ID to identify which active call we are
148 * talking about.
149 */
150 uint32_t cid GNUNET_PACKED;
151};
152
153
154/**
155 * Client -> Service pick up phone that is ringing.
156 */
157struct ClientPhonePickupMessage
158{
159 /**
160 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP
161 */
162 struct GNUNET_MessageHeader header;
163
164 /**
165 * CID, internal caller ID to identify which active call we are
166 * talking about.
167 */
168 uint32_t cid GNUNET_PACKED;
169};
170
171
172/**
173 * Client <-> Service hang up phone that may or may not be ringing.
174 * Also sent in response to a (failed) `struct ClientCallMessage`.
175 */
176struct ClientPhoneHangupMessage
177{
178 /**
179 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP
180 */
181 struct GNUNET_MessageHeader header;
182
183 /**
184 * CID, internal caller ID to identify which active call we are
185 * talking about.
186 */
187 uint32_t cid GNUNET_PACKED;
188};
189
190
191/**
192 * Message Client <-> Service to transmit the audio.
193 */
194struct ClientAudioMessage
195{
196 /**
197 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO
198 */
199 struct GNUNET_MessageHeader header;
200
201 /**
202 * CID, internal caller ID to identify which active call we are
203 * sending data to.
204 */
205 uint32_t cid GNUNET_PACKED;
206
207 /* followed by audio data */
208};
209
210
211/**
212 * Client -> Service message to call a phone.
213 */
214struct ClientCallMessage
215{
216 /**
217 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL
218 */
219 struct GNUNET_MessageHeader header;
220
221 /**
222 * Always zero.
223 */
224 uint32_t reserved GNUNET_PACKED;
225
226 /**
227 * Which peer is hosting the line?
228 */
229 struct GNUNET_PeerIdentity target;
230
231 /**
232 * Which phone line to call at the peer?
233 */
234 struct GNUNET_HashCode line_port;
235
236 /**
237 * The identity key length
238 */
239 uint32_t key_len;
240
241 /**
242 * followed by the identity of the caller.
243 */
244};
245
246
247/**
248 * Service -> Client: other peer has picked up the phone, we are
249 * now talking.
250 */
251struct ClientPhonePickedupMessage
252{
253 /**
254 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP
255 */
256 struct GNUNET_MessageHeader header;
257
258 /**
259 * Call ID of the corresponding
260 * #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL
261 */
262 uint32_t cid GNUNET_PACKED;
263};
264
265
266/**
267 * Information signed in a `struct CadetPhoneRingMessage`
268 * whereby the caller self-identifies to the receiver.
269 */
270struct CadetPhoneRingInfoPS
271{
272 /**
273 * Purpose for the signature, must be
274 * #GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING.
275 */
276 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
277
278 /**
279 * Which port did the call go to?
280 */
281 struct GNUNET_HashCode line_port;
282
283 /**
284 * Which peer is the call for?
285 */
286 struct GNUNET_PeerIdentity target_peer;
287
288 /**
289 * When does the signature expire?
290 */
291 struct GNUNET_TIME_AbsoluteNBO expiration_time;
292};
293
294
295/**
296 * Cadet message to make a phone ring. Sent to the port
297 * of the respective phone.
298 */
299struct CadetPhoneRingMessage
300{
301 /**
302 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING
303 */
304 struct GNUNET_MessageHeader header;
305
306 /**
307 * Always zero.
308 */
309 uint32_t reserved GNUNET_PACKED;
310
311 /**
312 * When does the signature expire?
313 */
314 struct GNUNET_TIME_AbsoluteNBO expiration_time;
315
316 /**
317 * The length of the key
318 */
319 uint32_t key_len;
320
321 /**
322 * The length of the signature
323 */
324 uint32_t sig_len;
325
326 /**
327 * Followed by the public key of who is calling us? (also who is signing).
328 * followed by the signature over a `struct CadetPhoneRingInfoPS`
329 */
330};
331
332
333/**
334 * Cadet message for hanging up.
335 */
336struct CadetPhoneHangupMessage
337{
338 /**
339 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP
340 */
341 struct GNUNET_MessageHeader header;
342};
343
344
345/**
346 * Cadet message for picking up.
347 */
348struct CadetPhonePickupMessage
349{
350 /**
351 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP
352 */
353 struct GNUNET_MessageHeader header;
354};
355
356
357/**
358 * Cadet message for phone suspended.
359 */
360struct CadetPhoneSuspendMessage
361{
362 /**
363 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND
364 */
365 struct GNUNET_MessageHeader header;
366};
367
368
369/**
370 * Cadet message for phone resumed.
371 */
372struct CadetPhoneResumeMessage
373{
374 /**
375 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME
376 */
377 struct GNUNET_MessageHeader header;
378};
379
380
381/**
382 * Cadet message to transmit the audio.
383 */
384struct CadetAudioMessage
385{
386 /**
387 * Type is #GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO
388 */
389 struct GNUNET_MessageHeader header;
390
391 /* followed by audio data */
392};
393
394
395GNUNET_NETWORK_STRUCT_END
396
397
398#if 0 /* keep Emacsens' auto-indent happy */
399{
400#endif
401#ifdef __cplusplus
402}
403#endif
404
405/* ifndef GNUNET_PROTOCOLS_CONVERSATION_H */
406#endif
407/* end of gnunet_protocols_conversation.h */
diff --git a/src/contrib/service/conversation/conversation_api.c b/src/contrib/service/conversation/conversation_api.c
new file mode 100644
index 000000000..ed22bf9cf
--- /dev/null
+++ b/src/contrib/service/conversation/conversation_api.c
@@ -0,0 +1,897 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/conversation_api.c
23 * @brief phone and caller API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "conversation.h"
31
32
33/**
34 * Possible states of a caller.
35 */
36enum CallerState
37{
38 /**
39 * The phone is ringing (user knows about incoming call).
40 */
41 CS_RINGING,
42
43 /**
44 * The phone is in an active conversation.
45 */
46 CS_ACTIVE,
47
48 /**
49 * We suspended the conversation.
50 */
51 CS_CALLEE_SUSPENDED,
52
53 /**
54 * Caller suspended the conversation.
55 */
56 CS_CALLER_SUSPENDED,
57
58 /**
59 * Both sides suspended the conversation.
60 */
61 CS_BOTH_SUSPENDED
62};
63
64
65/**
66 * A caller is the handle we have for an incoming call.
67 */
68struct GNUNET_CONVERSATION_Caller
69{
70 /**
71 * We keep all callers in a DLL.
72 */
73 struct GNUNET_CONVERSATION_Caller *next;
74
75 /**
76 * We keep all callers in a DLL.
77 */
78 struct GNUNET_CONVERSATION_Caller *prev;
79
80 /**
81 * Our phone.
82 */
83 struct GNUNET_CONVERSATION_Phone *phone;
84
85 /**
86 * Function to call for phone events.
87 */
88 GNUNET_CONVERSATION_CallerEventHandler event_handler;
89
90 /**
91 * Closure for @e event_handler
92 */
93 void *event_handler_cls;
94
95 /**
96 * Speaker, or NULL if none is attached.
97 */
98 struct GNUNET_SPEAKER_Handle *speaker;
99
100 /**
101 * Microphone, or NULL if none is attached.
102 */
103 struct GNUNET_MICROPHONE_Handle *mic;
104
105 /**
106 * Identity of the person calling us.
107 */
108 struct GNUNET_CRYPTO_PublicKey caller_id;
109
110 /**
111 * Internal handle to identify the caller with the service.
112 */
113 uint32_t cid;
114
115 /**
116 * State machine for the phone.
117 */
118 enum CallerState state;
119};
120
121
122/**
123 * Possible states of a phone.
124 */
125enum PhoneState
126{
127 /**
128 * We still need to register the phone.
129 */
130 PS_REGISTER = 0,
131
132 /**
133 * We are waiting for calls.
134 */
135 PS_READY
136};
137
138
139/**
140 * A phone is a device that can ring to signal an incoming call and
141 * that you can pick up to answer the call and hang up to terminate
142 * the call. You can also hang up a ringing phone immediately
143 * (without picking it up) to stop it from ringing. Phones have
144 * caller ID. You can ask the phone for its record and make that
145 * record available (via GNS) to enable others to call you.
146 * Multiple phones maybe connected to the same line (the line is
147 * something rather internal to a phone and not obvious from it).
148 * You can only have one conversation per phone at any time.
149 */
150struct GNUNET_CONVERSATION_Phone
151{
152 /**
153 * Our configuration.
154 */
155 const struct GNUNET_CONFIGURATION_Handle *cfg;
156
157 /**
158 * We keep all callers in a DLL.
159 */
160 struct GNUNET_CONVERSATION_Caller *caller_head;
161
162 /**
163 * We keep all callers in a DLL.
164 */
165 struct GNUNET_CONVERSATION_Caller *caller_tail;
166
167 /**
168 * Function to call for phone events.
169 */
170 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
171
172 /**
173 * Closure for @e event_handler
174 */
175 void *event_handler_cls;
176
177 /**
178 * Connection to NAMESTORE (for reverse lookup).
179 */
180 struct GNUNET_NAMESTORE_Handle *ns;
181
182 /**
183 * Handle for transmitting to the CONVERSATION service.
184 */
185 struct GNUNET_MQ_Handle *mq;
186
187 /**
188 * This phone's record.
189 */
190 struct GNUNET_CONVERSATION_PhoneRecord my_record;
191
192 /**
193 * My GNS zone.
194 */
195 struct GNUNET_CRYPTO_PrivateKey my_zone;
196
197 /**
198 * State machine for the phone.
199 */
200 enum PhoneState state;
201};
202
203
204/**
205 * The phone got disconnected, reconnect to the service.
206 *
207 * @param phone phone to reconnect
208 */
209static void
210reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
211
212
213/**
214 * Process recorded audio data.
215 *
216 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
217 * @param data_size number of bytes in @a data
218 * @param data audio data to play
219 */
220static void
221transmit_phone_audio (void *cls,
222 size_t data_size,
223 const void *data)
224{
225 struct GNUNET_CONVERSATION_Caller *caller = cls;
226 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
227 struct GNUNET_MQ_Envelope *e;
228 struct ClientAudioMessage *am;
229
230 e = GNUNET_MQ_msg_extra (am,
231 data_size,
232 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
233 am->cid = caller->cid;
234 GNUNET_memcpy (&am[1],
235 data,
236 data_size);
237 GNUNET_MQ_send (phone->mq,
238 e);
239}
240
241/**
242 * We received a `struct ClientPhoneRingMessage`
243 *
244 * @param cls the `struct GNUNET_CONVERSATION_Phone`
245 * @param ring the message
246 */
247static enum GNUNET_GenericReturnValue
248check_phone_ring (void *cls,
249 const struct ClientPhoneRingMessage *ring)
250{
251 //FIXME
252 return GNUNET_OK;
253}
254/**
255 * We received a `struct ClientPhoneRingMessage`
256 *
257 * @param cls the `struct GNUNET_CONVERSATION_Phone`
258 * @param ring the message
259 */
260static void
261handle_phone_ring (void *cls,
262 const struct ClientPhoneRingMessage *ring)
263{
264 struct GNUNET_CONVERSATION_Phone *phone = cls;
265 struct GNUNET_CONVERSATION_Caller *caller;
266 struct GNUNET_CRYPTO_PublicKey caller_id;
267 size_t key_len;
268 size_t read;
269
270 key_len = ntohl (ring->key_len);
271 switch (phone->state)
272 {
273 case PS_REGISTER:
274 GNUNET_assert (0);
275 break;
276
277 case PS_READY:
278 if ((GNUNET_SYSERR ==
279 GNUNET_CRYPTO_read_public_key_from_buffer (&ring[1],
280 key_len,
281 &caller_id,
282 &read)) ||
283 (read != key_len))
284 {
285 GNUNET_break (0);
286 break;
287 }
288 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
289 caller->phone = phone;
290 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
291 phone->caller_tail,
292 caller);
293 caller->caller_id = caller_id;
294 caller->cid = ring->cid;
295 caller->state = CS_RINGING;
296 phone->event_handler (phone->event_handler_cls,
297 GNUNET_CONVERSATION_EC_PHONE_RING,
298 caller,
299 &caller->caller_id);
300 break;
301 }
302}
303
304
305/**
306 * Find the record of the caller matching the @a cid
307 *
308 * @param phone phone to search
309 * @param cid caller ID to search for (in NBO)
310 * @return NULL if @a cid was not found
311 */
312static struct GNUNET_CONVERSATION_Caller *
313find_caller (struct GNUNET_CONVERSATION_Phone *phone,
314 uint32_t cid)
315{
316 struct GNUNET_CONVERSATION_Caller *caller;
317
318 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
319 if (cid == caller->cid)
320 return caller;
321 return NULL;
322}
323
324
325/**
326 * We received a `struct ClientPhoneHangupMessage`.
327 *
328 * @param cls the `struct GNUNET_CONVERSATION_Phone *`
329 * @param hang the message
330 */
331static void
332handle_phone_hangup (void *cls,
333 const struct ClientPhoneHangupMessage *hang)
334{
335 struct GNUNET_CONVERSATION_Phone *phone = cls;
336 struct GNUNET_CONVERSATION_Caller *caller;
337
338 caller = find_caller (phone,
339 hang->cid);
340 if (NULL == caller)
341 {
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "Received HANG_UP message for unknown caller ID %u\n",
344 (unsigned int) hang->cid);
345 return;
346 }
347
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "Received HANG_UP message, terminating call with `%s'\n",
350 GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
351 switch (caller->state)
352 {
353 case CS_RINGING:
354 phone->event_handler (phone->event_handler_cls,
355 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
356 caller,
357 &caller->caller_id);
358 break;
359
360 case CS_ACTIVE:
361 caller->speaker->disable_speaker (caller->speaker->cls);
362 caller->mic->disable_microphone (caller->mic->cls);
363 phone->event_handler (phone->event_handler_cls,
364 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
365 caller,
366 &caller->caller_id);
367 break;
368
369 case CS_CALLEE_SUSPENDED:
370 case CS_CALLER_SUSPENDED:
371 case CS_BOTH_SUSPENDED:
372 phone->event_handler (phone->event_handler_cls,
373 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
374 caller,
375 &caller->caller_id);
376 break;
377 }
378 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
379 phone->caller_tail,
380 caller);
381 GNUNET_free (caller);
382}
383
384
385/**
386 * We received a `struct ClientPhoneSuspendMessage`.
387 *
388 * @param cls the `struct GNUNET_CONVERSATION_Phone`
389 * @param suspend the message
390 */
391static void
392handle_phone_suspend (void *cls,
393 const struct ClientPhoneSuspendMessage *suspend)
394{
395 struct GNUNET_CONVERSATION_Phone *phone = cls;
396 struct GNUNET_CONVERSATION_Caller *caller;
397
398 caller = find_caller (phone,
399 suspend->cid);
400 if (NULL == caller)
401 return;
402 switch (caller->state)
403 {
404 case CS_RINGING:
405 GNUNET_break_op (0);
406 break;
407
408 case CS_ACTIVE:
409 caller->state = CS_CALLER_SUSPENDED;
410 caller->speaker->disable_speaker (caller->speaker->cls);
411 caller->mic->disable_microphone (caller->mic->cls);
412 caller->event_handler (caller->event_handler_cls,
413 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
414 break;
415
416 case CS_CALLEE_SUSPENDED:
417 caller->state = CS_BOTH_SUSPENDED;
418 caller->event_handler (caller->event_handler_cls,
419 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
420 break;
421
422 case CS_CALLER_SUSPENDED:
423 case CS_BOTH_SUSPENDED:
424 GNUNET_break_op (0);
425 break;
426 }
427}
428
429
430/**
431 * We received a `struct ClientPhoneResumeMessage`.
432 *
433 * @param cls the `struct GNUNET_CONVERSATION_Phone`
434 * @param resume the message
435 */
436static void
437handle_phone_resume (void *cls,
438 const struct ClientPhoneResumeMessage *resume)
439{
440 struct GNUNET_CONVERSATION_Phone *phone = cls;
441 struct GNUNET_CONVERSATION_Caller *caller;
442
443 caller = find_caller (phone,
444 resume->cid);
445 if (NULL == caller)
446 return;
447 switch (caller->state)
448 {
449 case CS_RINGING:
450 GNUNET_break_op (0);
451 break;
452
453 case CS_ACTIVE:
454 case CS_CALLEE_SUSPENDED:
455 GNUNET_break_op (0);
456 break;
457
458 case CS_CALLER_SUSPENDED:
459 caller->state = CS_ACTIVE;
460 caller->speaker->enable_speaker (caller->speaker->cls);
461 caller->mic->enable_microphone (caller->mic->cls,
462 &transmit_phone_audio,
463 caller);
464 caller->event_handler (caller->event_handler_cls,
465 GNUNET_CONVERSATION_EC_CALLER_RESUME);
466 break;
467
468 case CS_BOTH_SUSPENDED:
469 caller->state = CS_CALLEE_SUSPENDED;
470 caller->event_handler (caller->event_handler_cls,
471 GNUNET_CONVERSATION_EC_CALLER_RESUME);
472 break;
473 }
474}
475
476
477/**
478 * We received a `struct ClientAudioMessage`, check it is well-formed.
479 *
480 * @param cls the `struct GNUNET_CONVERSATION_Phone`
481 * @param am the message
482 * @return #GNUNET_OK if @a am is well-formed
483 */
484static int
485check_phone_audio (void *cls,
486 const struct ClientAudioMessage *am)
487{
488 (void) cls;
489 (void) am;
490
491 /* any variable-size payload is OK */
492 return GNUNET_OK;
493}
494
495
496/**
497 * We received a `struct ClientAudioMessage`
498 *
499 * @param cls the `struct GNUNET_CONVERSATION_Phone`
500 * @param am the message
501 */
502static void
503handle_phone_audio (void *cls,
504 const struct ClientAudioMessage *am)
505{
506 struct GNUNET_CONVERSATION_Phone *phone = cls;
507 struct GNUNET_CONVERSATION_Caller *caller;
508
509 caller = find_caller (phone,
510 am->cid);
511 if (NULL == caller)
512 return;
513 switch (caller->state)
514 {
515 case CS_RINGING:
516 GNUNET_break_op (0);
517 break;
518
519 case CS_ACTIVE:
520 caller->speaker->play (caller->speaker->cls,
521 ntohs (am->header.size) - sizeof(struct
522 ClientAudioMessage),
523 &am[1]);
524 break;
525
526 case CS_CALLEE_SUSPENDED:
527 case CS_CALLER_SUSPENDED:
528 case CS_BOTH_SUSPENDED:
529 break;
530 }
531}
532
533
534/**
535 * We encountered an error talking with the conversation service.
536 *
537 * @param cls the `struct GNUNET_CONVERSATION_Phone`
538 * @param error details about the error
539 */
540static void
541phone_error_handler (void *cls,
542 enum GNUNET_MQ_Error error)
543{
544 struct GNUNET_CONVERSATION_Phone *phone = cls;
545
546 (void) error;
547 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
548 _ (
549 "Connection to conversation service lost, trying to reconnect\n"));
550 reconnect_phone (phone);
551}
552
553
554/**
555 * Clean up all callers of the given phone.
556 *
557 * @param phone phone to clean up callers for
558 */
559static void
560clean_up_callers (struct GNUNET_CONVERSATION_Phone *phone)
561{
562 struct GNUNET_CONVERSATION_Caller *caller;
563
564 while (NULL != (caller = phone->caller_head))
565 {
566 /* make sure mic/speaker are disabled *before* callback */
567 if (CS_ACTIVE == caller->state)
568 {
569 caller->speaker->disable_speaker (caller->speaker->cls);
570 caller->mic->disable_microphone (caller->mic->cls);
571 caller->state = CS_CALLER_SUSPENDED;
572 }
573 phone->event_handler (phone->event_handler_cls,
574 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
575 caller,
576 &caller->caller_id);
577 GNUNET_CONVERSATION_caller_hang_up (caller);
578 }
579}
580
581
582/**
583 * The phone got disconnected, reconnect to the service.
584 *
585 * @param phone phone to reconnect
586 */
587static void
588reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
589{
590 struct GNUNET_MQ_MessageHandler handlers[] = {
591 GNUNET_MQ_hd_var_size (phone_ring,
592 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING,
593 struct ClientPhoneRingMessage,
594 phone),
595 GNUNET_MQ_hd_fixed_size (phone_hangup,
596 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
597 struct ClientPhoneHangupMessage,
598 phone),
599 GNUNET_MQ_hd_fixed_size (phone_suspend,
600 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
601 struct ClientPhoneSuspendMessage,
602 phone),
603 GNUNET_MQ_hd_fixed_size (phone_resume,
604 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
605 struct ClientPhoneResumeMessage,
606 phone),
607 GNUNET_MQ_hd_var_size (phone_audio,
608 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
609 struct ClientAudioMessage,
610 phone),
611 GNUNET_MQ_handler_end ()
612 };
613 struct GNUNET_MQ_Envelope *e;
614 struct ClientPhoneRegisterMessage *reg;
615
616 clean_up_callers (phone);
617 if (NULL != phone->mq)
618 {
619 GNUNET_MQ_destroy (phone->mq);
620 phone->mq = NULL;
621 }
622 phone->state = PS_REGISTER;
623 phone->mq = GNUNET_CLIENT_connect (phone->cfg,
624 "conversation",
625 handlers,
626 &phone_error_handler,
627 phone);
628 if (NULL == phone->mq)
629 return;
630 e = GNUNET_MQ_msg (reg,
631 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
632 reg->line_port = phone->my_record.line_port;
633 GNUNET_MQ_send (phone->mq,
634 e);
635 phone->state = PS_READY;
636}
637
638
639/**
640 * Create a new phone.
641 *
642 * @param cfg configuration for the phone; specifies the phone service and
643 * which line the phone is to be connected to
644 * @param ego ego to use for name resolution (when determining caller ID)
645 * @param event_handler how to notify the owner of the phone about events
646 * @param event_handler_cls closure for @a event_handler
647 * @return NULL on error (no valid line configured)
648 */
649struct GNUNET_CONVERSATION_Phone *
650GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
651 const struct GNUNET_IDENTITY_Ego *ego,
652 GNUNET_CONVERSATION_PhoneEventHandler
653 event_handler,
654 void *event_handler_cls)
655{
656 struct GNUNET_CONVERSATION_Phone *phone;
657 char *line;
658 struct GNUNET_HashCode line_port;
659
660 if (GNUNET_OK !=
661 GNUNET_CONFIGURATION_get_value_string (cfg,
662 "CONVERSATION",
663 "LINE",
664 &line))
665 {
666 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
667 "CONVERSATION",
668 "LINE");
669 return NULL;
670 }
671 GNUNET_CRYPTO_hash (line,
672 strlen (line),
673 &line_port);
674 GNUNET_free (line);
675 phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone);
676 if (GNUNET_OK !=
677 GNUNET_CRYPTO_get_peer_identity (cfg,
678 &phone->my_record.peer))
679 {
680 GNUNET_break (0);
681 GNUNET_free (phone);
682 return NULL;
683 }
684 phone->cfg = cfg;
685 phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
686 phone->event_handler = event_handler;
687 phone->event_handler_cls = event_handler_cls;
688 phone->ns = GNUNET_NAMESTORE_connect (cfg);
689 phone->my_record.version = htonl (1);
690 phone->my_record.reserved = htonl (0);
691 phone->my_record.line_port = line_port;
692 reconnect_phone (phone);
693 if ((NULL == phone->mq) ||
694 (NULL == phone->ns))
695 {
696 GNUNET_break (0);
697 GNUNET_CONVERSATION_phone_destroy (phone);
698 return NULL;
699 }
700 return phone;
701}
702
703
704/**
705 * Fill in a namestore record with the contact information
706 * for this phone. Note that the filled in "data" value
707 * is only valid until the phone is destroyed.
708 *
709 * @param phone phone to create a record for
710 * @param rd namestore record to fill in
711 */
712void
713GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
714 struct GNUNET_GNSRECORD_Data *rd)
715{
716 rd->data = &phone->my_record;
717 rd->expiration_time = 0;
718 rd->data_size = sizeof(struct GNUNET_CONVERSATION_PhoneRecord);
719 rd->record_type = GNUNET_GNSRECORD_TYPE_PHONE;
720 rd->flags = GNUNET_GNSRECORD_RF_NONE;
721}
722
723
724/**
725 * Picks up a (ringing) phone. This will connect the speaker
726 * to the microphone of the other party, and vice versa.
727 *
728 * @param caller handle that identifies which caller should be answered
729 * @param event_handler how to notify about events by the caller
730 * @param event_handler_cls closure for @a event_handler
731 * @param speaker speaker to use
732 * @param mic microphone to use
733 */
734void
735GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
736 GNUNET_CONVERSATION_CallerEventHandler
737 event_handler,
738 void *event_handler_cls,
739 struct GNUNET_SPEAKER_Handle *speaker,
740 struct GNUNET_MICROPHONE_Handle *mic)
741{
742 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
743 struct GNUNET_MQ_Envelope *e;
744 struct ClientPhonePickupMessage *pick;
745
746 GNUNET_assert (CS_RINGING == caller->state);
747 caller->speaker = speaker;
748 caller->mic = mic;
749 e = GNUNET_MQ_msg (pick,
750 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
751 pick->cid = caller->cid;
752 GNUNET_MQ_send (phone->mq,
753 e);
754 caller->state = CS_ACTIVE;
755 caller->event_handler = event_handler;
756 caller->event_handler_cls = event_handler_cls;
757 caller->speaker->enable_speaker (caller->speaker->cls);
758 caller->mic->enable_microphone (caller->mic->cls,
759 &transmit_phone_audio,
760 caller);
761}
762
763
764/**
765 * Hang up up a (possibly ringing) phone. This will notify the other
766 * party that we are no longer interested in talking with them.
767 *
768 * @param caller conversation to hang up on
769 */
770void
771GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
772{
773 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
774 struct GNUNET_MQ_Envelope *e;
775 struct ClientPhoneHangupMessage *hang;
776
777 switch (caller->state)
778 {
779 case CS_ACTIVE:
780 caller->speaker->disable_speaker (caller->speaker->cls);
781 caller->mic->disable_microphone (caller->mic->cls);
782 break;
783
784 default:
785 break;
786 }
787 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
788 phone->caller_tail,
789 caller);
790 e = GNUNET_MQ_msg (hang,
791 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
792 hang->cid = caller->cid;
793 GNUNET_MQ_send (phone->mq,
794 e);
795 GNUNET_free (caller);
796}
797
798
799/**
800 * Destroys a phone.
801 *
802 * @param phone phone to destroy
803 */
804void
805GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
806{
807 clean_up_callers (phone);
808 if (NULL != phone->ns)
809 {
810 GNUNET_NAMESTORE_disconnect (phone->ns);
811 phone->ns = NULL;
812 }
813 if (NULL != phone->mq)
814 {
815 GNUNET_MQ_destroy (phone->mq);
816 phone->mq = NULL;
817 }
818 GNUNET_free (phone);
819}
820
821
822/**
823 * Pause conversation of an active call. This will disconnect the speaker
824 * and the microphone. The call can later be resumed with
825 * #GNUNET_CONVERSATION_caller_resume.
826 *
827 * @param caller call to suspend
828 */
829void
830GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
831{
832 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
833 struct GNUNET_MQ_Envelope *e;
834 struct ClientPhoneSuspendMessage *suspend;
835
836 GNUNET_assert ((CS_ACTIVE == caller->state) ||
837 (CS_CALLER_SUSPENDED == caller->state));
838 if (CS_ACTIVE == caller->state)
839 {
840 caller->speaker->disable_speaker (caller->speaker->cls);
841 caller->mic->disable_microphone (caller->mic->cls);
842 }
843 caller->speaker = NULL;
844 caller->mic = NULL;
845 e = GNUNET_MQ_msg (suspend,
846 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
847 suspend->cid = caller->cid;
848 GNUNET_MQ_send (phone->mq,
849 e);
850 if (CS_ACTIVE == caller->state)
851 caller->state = CS_CALLEE_SUSPENDED;
852 else
853 caller->state = CS_BOTH_SUSPENDED;
854}
855
856
857/**
858 * Resume suspended conversation of a phone.
859 *
860 * @param caller call to resume
861 * @param speaker speaker to use
862 * @param mic microphone to use
863 */
864void
865GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
866 struct GNUNET_SPEAKER_Handle *speaker,
867 struct GNUNET_MICROPHONE_Handle *mic)
868{
869 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
870 struct GNUNET_MQ_Envelope *e;
871 struct ClientPhoneResumeMessage *resume;
872
873 GNUNET_assert ((CS_CALLEE_SUSPENDED == caller->state) ||
874 (CS_BOTH_SUSPENDED == caller->state));
875 caller->speaker = speaker;
876 caller->mic = mic;
877 e = GNUNET_MQ_msg (resume,
878 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
879 resume->cid = caller->cid;
880 GNUNET_MQ_send (phone->mq,
881 e);
882 if (CS_CALLEE_SUSPENDED == caller->state)
883 {
884 caller->state = CS_ACTIVE;
885 caller->speaker->enable_speaker (caller->speaker->cls);
886 caller->mic->enable_microphone (caller->mic->cls,
887 &transmit_phone_audio,
888 caller);
889 }
890 else
891 {
892 caller->state = CS_CALLER_SUSPENDED;
893 }
894}
895
896
897/* end of conversation_api.c */
diff --git a/src/contrib/service/conversation/conversation_api_call.c b/src/contrib/service/conversation/conversation_api_call.c
new file mode 100644
index 000000000..cd59678d9
--- /dev/null
+++ b/src/contrib/service/conversation/conversation_api_call.c
@@ -0,0 +1,749 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/conversation_api_call.c
23 * @brief call API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_gns_service.h"
32#include "conversation.h"
33
34
35/**
36 * Possible states of the phone.
37 */
38enum CallState
39{
40 /**
41 * We still need to lookup the callee.
42 */
43 CS_LOOKUP = 0,
44
45 /**
46 * The call is ringing.
47 */
48 CS_RINGING,
49
50 /**
51 * The call is in an active conversation.
52 */
53 CS_ACTIVE,
54
55 /**
56 * The call is in termination.
57 */
58 CS_SHUTDOWN,
59
60 /**
61 * The call was suspended by the caller.
62 */
63 CS_SUSPENDED_CALLER,
64
65 /**
66 * The call was suspended by the callee.
67 */
68 CS_SUSPENDED_CALLEE,
69
70 /**
71 * The call was suspended by both caller and callee.
72 */
73 CS_SUSPENDED_BOTH
74};
75
76
77/**
78 * Handle for an outgoing call.
79 */
80struct GNUNET_CONVERSATION_Call
81{
82 /**
83 * Our configuration.
84 */
85 const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 /**
88 * Our caller identity.
89 */
90 struct GNUNET_IDENTITY_Ego *caller_id;
91
92 /**
93 * Target callee as a GNS address/name.
94 */
95 char *callee;
96
97 /**
98 * Our speaker.
99 */
100 struct GNUNET_SPEAKER_Handle *speaker;
101
102 /**
103 * Our microphone.
104 */
105 struct GNUNET_MICROPHONE_Handle *mic;
106
107 /**
108 * Function to call with events.
109 */
110 GNUNET_CONVERSATION_CallEventHandler event_handler;
111
112 /**
113 * Closure for @e event_handler
114 */
115 void *event_handler_cls;
116
117 /**
118 * Handle for transmitting to the CONVERSATION service.
119 */
120 struct GNUNET_MQ_Handle *mq;
121
122 /**
123 * Connection to GNS (can be NULL).
124 */
125 struct GNUNET_GNS_Handle *gns;
126
127 /**
128 * Active GNS lookup (or NULL).
129 */
130 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
131
132 /**
133 * Target phone record, only valid after the lookup is done.
134 */
135 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
136
137 /**
138 * State machine for the call.
139 */
140 enum CallState state;
141};
142
143
144/**
145 * The call got disconnected, reconnect to the service.
146 *
147 * @param call call to reconnect
148 */
149static void
150fail_call (struct GNUNET_CONVERSATION_Call *call);
151
152
153/**
154 * Process recorded audio data.
155 *
156 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
157 * @param data_size number of bytes in @a data
158 * @param data audio data to play
159 */
160static void
161transmit_call_audio (void *cls,
162 size_t data_size,
163 const void *data)
164{
165 struct GNUNET_CONVERSATION_Call *call = cls;
166 struct GNUNET_MQ_Envelope *e;
167 struct ClientAudioMessage *am;
168
169 GNUNET_assert (CS_ACTIVE == call->state);
170 e = GNUNET_MQ_msg_extra (am,
171 data_size,
172 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
173 GNUNET_memcpy (&am[1],
174 data,
175 data_size);
176 GNUNET_MQ_send (call->mq,
177 e);
178}
179
180
181/**
182 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
183 *
184 * @param cls the `struct GNUNET_CONVERSATION_Call`
185 * @param msg the message
186 */
187static void
188handle_call_suspend (void *cls,
189 const struct ClientPhoneSuspendMessage *msg)
190{
191 struct GNUNET_CONVERSATION_Call *call = cls;
192
193 (void) msg;
194 switch (call->state)
195 {
196 case CS_LOOKUP:
197 GNUNET_break (0);
198 fail_call (call);
199 break;
200
201 case CS_RINGING:
202 GNUNET_break_op (0);
203 fail_call (call);
204 break;
205
206 case CS_SUSPENDED_CALLER:
207 call->state = CS_SUSPENDED_BOTH;
208 call->event_handler (call->event_handler_cls,
209 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
210 break;
211
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
214 GNUNET_break_op (0);
215 break;
216
217 case CS_ACTIVE:
218 call->state = CS_SUSPENDED_CALLEE;
219 call->speaker->disable_speaker (call->speaker->cls);
220 call->mic->disable_microphone (call->mic->cls);
221 call->event_handler (call->event_handler_cls,
222 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
223 break;
224
225 case CS_SHUTDOWN:
226 GNUNET_CONVERSATION_call_stop (call);
227 break;
228 }
229}
230
231
232/**
233 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
234 *
235 * @param cls the `struct GNUNET_CONVERSATION_Call`
236 * @param msg the message
237 */
238static void
239handle_call_resume (void *cls,
240 const struct ClientPhoneResumeMessage *msg)
241{
242 struct GNUNET_CONVERSATION_Call *call = cls;
243
244 (void) msg;
245 switch (call->state)
246 {
247 case CS_LOOKUP:
248 GNUNET_break (0);
249 fail_call (call);
250 break;
251
252 case CS_RINGING:
253 GNUNET_break_op (0);
254 fail_call (call);
255 break;
256
257 case CS_SUSPENDED_CALLER:
258 GNUNET_break_op (0);
259 break;
260
261 case CS_SUSPENDED_CALLEE:
262 call->state = CS_ACTIVE;
263 call->speaker->enable_speaker (call->speaker->cls);
264 call->mic->enable_microphone (call->mic->cls,
265 &transmit_call_audio,
266 call);
267 call->event_handler (call->event_handler_cls,
268 GNUNET_CONVERSATION_EC_CALL_RESUMED);
269 break;
270
271 case CS_SUSPENDED_BOTH:
272 call->state = CS_SUSPENDED_CALLER;
273 call->event_handler (call->event_handler_cls,
274 GNUNET_CONVERSATION_EC_CALL_RESUMED);
275 break;
276
277 case CS_ACTIVE:
278 GNUNET_break_op (0);
279 break;
280
281 case CS_SHUTDOWN:
282 GNUNET_CONVERSATION_call_stop (call);
283 break;
284 }
285}
286
287
288/**
289 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
290 *
291 * @param cls the `struct GNUNET_CONVERSATION_Call`
292 * @param msg the message
293 */
294static void
295handle_call_picked_up (void *cls,
296 const struct ClientPhonePickedupMessage *msg)
297{
298 struct GNUNET_CONVERSATION_Call *call = cls;
299
300 (void) msg;
301 switch (call->state)
302 {
303 case CS_LOOKUP:
304 GNUNET_break (0);
305 fail_call (call);
306 break;
307
308 case CS_RINGING:
309 call->state = CS_ACTIVE;
310 call->speaker->enable_speaker (call->speaker->cls);
311 call->mic->enable_microphone (call->mic->cls,
312 &transmit_call_audio,
313 call);
314 call->event_handler (call->event_handler_cls,
315 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
316 break;
317
318 case CS_SUSPENDED_CALLER:
319 case CS_SUSPENDED_CALLEE:
320 case CS_SUSPENDED_BOTH:
321 case CS_ACTIVE:
322 GNUNET_break (0);
323 fail_call (call);
324 break;
325
326 case CS_SHUTDOWN:
327 GNUNET_CONVERSATION_call_stop (call);
328 break;
329 }
330}
331
332
333/**
334 * We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
335 *
336 * @param cls the `struct GNUNET_CONVERSATION_Call`
337 * @param msg the message
338 */
339static void
340handle_call_hangup (void *cls,
341 const struct ClientPhoneHangupMessage *msg)
342{
343 struct GNUNET_CONVERSATION_Call *call = cls;
344 GNUNET_CONVERSATION_CallEventHandler eh;
345 void *eh_cls;
346
347 (void) msg;
348 switch (call->state)
349 {
350 case CS_LOOKUP:
351 GNUNET_break (0);
352 fail_call (call);
353 break;
354
355 case CS_RINGING:
356 case CS_SUSPENDED_CALLER:
357 case CS_SUSPENDED_CALLEE:
358 case CS_SUSPENDED_BOTH:
359 case CS_ACTIVE:
360 eh = call->event_handler;
361 eh_cls = call->event_handler_cls;
362 GNUNET_CONVERSATION_call_stop (call);
363 eh (eh_cls,
364 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
365 return;
366
367 case CS_SHUTDOWN:
368 GNUNET_CONVERSATION_call_stop (call);
369 break;
370 }
371}
372
373
374/**
375 * We received a `struct ClientAudioMessage`, check it is well-formed.
376 *
377 * @param cls the `struct GNUNET_CONVERSATION_Call`
378 * @param am the message
379 * @return #GNUNET_OK (always well-formed)
380 */
381static int
382check_call_audio (void *cls,
383 const struct ClientAudioMessage *am)
384{
385 (void) cls;
386 (void) am;
387 /* any payload is OK */
388 return GNUNET_OK;
389}
390
391
392/**
393 * We received a `struct ClientAudioMessage`
394 *
395 * @param cls the `struct GNUNET_CONVERSATION_Call`
396 * @param am the message
397 */
398static void
399handle_call_audio (void *cls,
400 const struct ClientAudioMessage *am)
401{
402 struct GNUNET_CONVERSATION_Call *call = cls;
403
404 switch (call->state)
405 {
406 case CS_LOOKUP:
407 GNUNET_break (0);
408 fail_call (call);
409 break;
410
411 case CS_RINGING:
412 GNUNET_break (0);
413 fail_call (call);
414 break;
415
416 case CS_SUSPENDED_CALLER:
417 /* can happen: we suspended, other peer did not yet
418 learn about this. */
419 break;
420
421 case CS_SUSPENDED_CALLEE:
422 case CS_SUSPENDED_BOTH:
423 /* can (rarely) also happen: other peer suspended, but cadet might
424 have had delayed data on the unreliable channel */
425 break;
426
427 case CS_ACTIVE:
428 call->speaker->play (call->speaker->cls,
429 ntohs (am->header.size) - sizeof(struct
430 ClientAudioMessage),
431 &am[1]);
432 break;
433
434 case CS_SHUTDOWN:
435 GNUNET_CONVERSATION_call_stop (call);
436 break;
437 }
438}
439
440
441/**
442 * Iterator called on obtained result for a GNS lookup.
443 *
444 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
445 * @param was_gns #GNUNET_NO if name was not a GNS name
446 * @param rd_count number of records in @a rd
447 * @param rd the records in reply
448 */
449static void
450handle_gns_response (void *cls,
451 int was_gns,
452 uint32_t rd_count,
453 const struct GNUNET_GNSRECORD_Data *rd)
454{
455 struct GNUNET_CONVERSATION_Call *call = cls;
456 struct GNUNET_MQ_Envelope *e;
457 struct ClientCallMessage *ccm;
458 const struct GNUNET_CRYPTO_PrivateKey *caller_id;
459 size_t key_len;
460
461 (void) was_gns;
462 GNUNET_break (NULL != call->gns_lookup);
463 GNUNET_break (CS_LOOKUP == call->state);
464 call->gns_lookup = NULL;
465 for (uint32_t i = 0; i < rd_count; i++)
466 {
467 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
468 {
469 if (rd[i].data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
470 {
471 GNUNET_break_op (0);
472 continue;
473 }
474 GNUNET_memcpy (&call->phone_record,
475 rd[i].data,
476 rd[i].data_size);
477 caller_id = GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
478 key_len = GNUNET_CRYPTO_private_key_get_length (caller_id);
479 e = GNUNET_MQ_msg_extra (ccm, key_len,
480 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
481 ccm->line_port = call->phone_record.line_port;
482 ccm->target = call->phone_record.peer;
483 GNUNET_CRYPTO_write_private_key_to_buffer (caller_id,
484 &ccm[1], key_len);
485 ccm->key_len = htonl (key_len);
486 GNUNET_MQ_send (call->mq,
487 e);
488 call->state = CS_RINGING;
489 call->event_handler (call->event_handler_cls,
490 GNUNET_CONVERSATION_EC_CALL_RINGING);
491 return;
492 }
493 }
494 /* not found */
495 call->event_handler (call->event_handler_cls,
496 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
497 GNUNET_CONVERSATION_call_stop (call);
498}
499
500
501/**
502 * We encountered an error talking with the conversation service.
503 *
504 * @param cls the `struct GNUNET_CONVERSATION_Call`
505 * @param error details about the error
506 */
507static void
508call_error_handler (void *cls,
509 enum GNUNET_MQ_Error error)
510{
511 struct GNUNET_CONVERSATION_Call *call = cls;
512
513 (void) error;
514 if (CS_SHUTDOWN == call->state)
515 {
516 GNUNET_CONVERSATION_call_stop (call);
517 return;
518 }
519 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
520 _ (
521 "Connection to conversation service lost, trying to reconnect\n"));
522 fail_call (call);
523}
524
525
526/**
527 * The call got disconnected, destroy the handle.
528 *
529 * @param call call to reconnect
530 */
531static void
532fail_call (struct GNUNET_CONVERSATION_Call *call)
533{
534 if (CS_ACTIVE == call->state)
535 {
536 call->speaker->disable_speaker (call->speaker->cls);
537 call->mic->disable_microphone (call->mic->cls);
538 }
539 if (NULL != call->mq)
540 {
541 GNUNET_MQ_destroy (call->mq);
542 call->mq = NULL;
543 }
544 call->state = CS_SHUTDOWN;
545 call->event_handler (call->event_handler_cls,
546 GNUNET_CONVERSATION_EC_CALL_ERROR);
547 GNUNET_CONVERSATION_call_stop (call);
548}
549
550
551/**
552 * Call the phone of another user.
553 *
554 * @param cfg configuration to use, specifies our phone service
555 * @param caller_id identity of the caller
556 * @param callee GNS name of the callee (used to locate the callee's record)
557 * @param speaker speaker to use (will be used automatically immediately once the
558 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated); we will NOT generate
559 * a ring tone on the speaker
560 * @param mic microphone to use (will be used automatically immediately once the
561 * #GNUNET_CONVERSATION_EC_CALL_PICKED_UP event is generated)
562 * @param event_handler how to notify the owner of the phone about events
563 * @param event_handler_cls closure for @a event_handler
564 * @return handle for the call, NULL on hard errors
565 */
566struct GNUNET_CONVERSATION_Call *
567GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
568 struct GNUNET_IDENTITY_Ego *caller_id,
569 const char *callee,
570 struct GNUNET_SPEAKER_Handle *speaker,
571 struct GNUNET_MICROPHONE_Handle *mic,
572 GNUNET_CONVERSATION_CallEventHandler
573 event_handler,
574 void *event_handler_cls)
575{
576 struct GNUNET_CONVERSATION_Call *call
577 = GNUNET_new (struct GNUNET_CONVERSATION_Call);
578 struct GNUNET_MQ_MessageHandler handlers[] = {
579 GNUNET_MQ_hd_fixed_size (call_suspend,
580 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
581 struct ClientPhoneSuspendMessage,
582 call),
583 GNUNET_MQ_hd_fixed_size (call_resume,
584 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
585 struct ClientPhoneResumeMessage,
586 call),
587 GNUNET_MQ_hd_fixed_size (call_picked_up,
588 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
589 struct ClientPhonePickedupMessage,
590 call),
591 GNUNET_MQ_hd_fixed_size (call_hangup,
592 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
593 struct ClientPhoneHangupMessage,
594 call),
595 GNUNET_MQ_hd_var_size (call_audio,
596 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
597 struct ClientAudioMessage,
598 call),
599 GNUNET_MQ_handler_end ()
600 };
601
602 call->mq = GNUNET_CLIENT_connect (cfg,
603 "conversation",
604 handlers,
605 &call_error_handler,
606 call);
607 if (NULL == call->mq)
608 {
609 GNUNET_break (0);
610 GNUNET_free (call);
611 return NULL;
612 }
613 call->cfg = cfg;
614 call->caller_id = caller_id;
615 call->callee = GNUNET_strdup (callee);
616 call->speaker = speaker;
617 call->mic = mic;
618 call->event_handler = event_handler;
619 call->event_handler_cls = event_handler_cls;
620 call->gns = GNUNET_GNS_connect (cfg);
621 if (NULL == call->gns)
622 {
623 GNUNET_CONVERSATION_call_stop (call);
624 return NULL;
625 }
626 call->state = CS_LOOKUP;
627 call->gns_lookup = GNUNET_GNS_lookup_with_tld (call->gns,
628 call->callee,
629 GNUNET_GNSRECORD_TYPE_PHONE,
630 GNUNET_GNS_LO_DEFAULT,
631 &handle_gns_response,
632 call);
633 if (NULL == call->gns_lookup)
634 {
635 GNUNET_CONVERSATION_call_stop (call);
636 return NULL;
637 }
638 return call;
639}
640
641
642/**
643 * Terminate a call. The call may be ringing or ready at this time.
644 *
645 * @param call call to terminate
646 */
647void
648GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
649{
650 if ((NULL != call->speaker) &&
651 (CS_ACTIVE == call->state))
652 call->speaker->disable_speaker (call->speaker->cls);
653 if ((NULL != call->mic) &&
654 (CS_ACTIVE == call->state))
655 call->mic->disable_microphone (call->mic->cls);
656 if (CS_SHUTDOWN != call->state)
657 {
658 call->state = CS_SHUTDOWN;
659 }
660 if (NULL != call->mq)
661 {
662 GNUNET_MQ_destroy (call->mq);
663 call->mq = NULL;
664 }
665 if (NULL != call->gns_lookup)
666 {
667 GNUNET_GNS_lookup_with_tld_cancel (call->gns_lookup);
668 call->gns_lookup = NULL;
669 }
670 if (NULL != call->gns)
671 {
672 GNUNET_GNS_disconnect (call->gns);
673 call->gns = NULL;
674 }
675 GNUNET_free (call->callee);
676 GNUNET_free (call);
677}
678
679
680/**
681 * Pause a call. Temporarily suspends the use of speaker and
682 * microphone.
683 *
684 * @param call call to pause
685 */
686void
687GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
688{
689 struct GNUNET_MQ_Envelope *e;
690 struct ClientPhoneSuspendMessage *suspend;
691
692 GNUNET_assert ((CS_SUSPENDED_CALLEE == call->state) ||
693 (CS_ACTIVE == call->state));
694 if (CS_ACTIVE == call->state)
695 {
696 call->speaker->disable_speaker (call->speaker->cls);
697 call->mic->disable_microphone (call->mic->cls);
698 }
699 call->speaker = NULL;
700 call->mic = NULL;
701 e = GNUNET_MQ_msg (suspend,
702 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
703 GNUNET_MQ_send (call->mq,
704 e);
705 if (CS_SUSPENDED_CALLER == call->state)
706 call->state = CS_SUSPENDED_BOTH;
707 else
708 call->state = CS_SUSPENDED_CALLER;
709}
710
711
712/**
713 * Resumes a call after #GNUNET_CONVERSATION_call_suspend.
714 *
715 * @param call call to resume
716 * @param speaker speaker to use
717 * a ring tone on the speaker
718 * @param mic microphone to use
719 */
720void
721GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
722 struct GNUNET_SPEAKER_Handle *speaker,
723 struct GNUNET_MICROPHONE_Handle *mic)
724{
725 struct GNUNET_MQ_Envelope *e;
726 struct ClientPhoneResumeMessage *resume;
727
728 GNUNET_assert ((CS_SUSPENDED_CALLER == call->state) ||
729 (CS_SUSPENDED_BOTH == call->state));
730 e = GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
731 GNUNET_MQ_send (call->mq, e);
732 call->speaker = speaker;
733 call->mic = mic;
734 if (CS_SUSPENDED_CALLER == call->state)
735 {
736 call->state = CS_ACTIVE;
737 call->speaker->enable_speaker (call->speaker->cls);
738 call->mic->enable_microphone (call->mic->cls,
739 &transmit_call_audio,
740 call);
741 }
742 else
743 {
744 call->state = CS_SUSPENDED_CALLEE;
745 }
746}
747
748
749/* end of conversation_api_call.c */
diff --git a/src/contrib/service/conversation/displaydot.sh b/src/contrib/service/conversation/displaydot.sh
new file mode 100644
index 000000000..16ee23409
--- /dev/null
+++ b/src/contrib/service/conversation/displaydot.sh
@@ -0,0 +1,3 @@
1#!/bin/sh
2dot -Tpng `ls -tr1 /tmp/*rec*.dot | tail -1` | display /dev/stdin &
3dot -Tpng `ls -tr1 /tmp/*play*.dot | tail -1` | display /dev/stdin &
diff --git a/src/contrib/service/conversation/gnunet-conversation-test.c b/src/contrib/service/conversation/gnunet-conversation-test.c
new file mode 100644
index 000000000..5e6bc805f
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-conversation-test.c
@@ -0,0 +1,265 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/gnunet-conversation-test.c
23 * @brief tool to test speaker and microphone (for end users!)
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_speaker_lib.h"
29#include "gnunet_microphone_lib.h"
30
31/**
32 * How long do we record before we replay?
33 */
34#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
35
36
37/**
38 * A recording we made.
39 */
40struct Recording
41{
42 /**
43 * Kept in a DLL.
44 */
45 struct Recording *next;
46
47 /**
48 * Kept in a DLL.
49 */
50 struct Recording *prev;
51
52 /**
53 * Number of bytes that follow.
54 */
55 size_t size;
56};
57
58
59/**
60 * Final status code.
61 */
62static int ret;
63
64/**
65 * Handle to the microphone.
66 */
67static struct GNUNET_MICROPHONE_Handle *microphone;
68
69/**
70 * Handle to the speaker.
71 */
72static struct GNUNET_SPEAKER_Handle *speaker;
73
74/**
75 * Task scheduled to switch from recording to playback.
76 */
77static struct GNUNET_SCHEDULER_Task *switch_task;
78
79/**
80 * The shutdown task.
81 */
82static struct GNUNET_SCHEDULER_Task *st;
83
84/**
85 * Head of DLL with recorded frames.
86 */
87static struct Recording *rec_head;
88
89/**
90 * Tail of DLL with recorded frames.
91 */
92static struct Recording *rec_tail;
93
94
95/**
96 * Terminate test.
97 *
98 * @param cls NULL
99 */
100static void
101do_shutdown (void *cls)
102{
103 struct Recording *rec;
104
105 (void) cls;
106 if (NULL != switch_task)
107 GNUNET_SCHEDULER_cancel (switch_task);
108 if (NULL != microphone)
109 GNUNET_MICROPHONE_destroy (microphone);
110 if (NULL != speaker)
111 GNUNET_SPEAKER_destroy (speaker);
112 while (NULL != (rec = rec_head))
113 {
114 GNUNET_CONTAINER_DLL_remove (rec_head,
115 rec_tail,
116 rec);
117 GNUNET_free (rec);
118 }
119 fprintf (stderr,
120 _ ("\nEnd of transmission. Have a GNU day.\n"));
121}
122
123
124/**
125 * Terminate recording process and switch to playback.
126 *
127 * @param cls NULL
128 */
129static void
130switch_to_speaker (void *cls)
131{
132 (void) cls;
133 switch_task = NULL;
134 microphone->disable_microphone (microphone->cls);
135 if (GNUNET_OK !=
136 speaker->enable_speaker (speaker->cls))
137 {
138 fprintf (stderr,
139 "Failed to enable microphone\n");
140 ret = 1;
141 GNUNET_SCHEDULER_shutdown ();
142 return;
143 }
144 fprintf (stderr,
145 _ (
146 "\new are now playing your recording back. If you can hear it, your audio settings are working..."));
147 for (struct Recording *rec = rec_head; NULL != rec; rec = rec->next)
148 {
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150 "Replaying %u bytes\n",
151 (unsigned int) rec->size);
152 speaker->play (speaker->cls,
153 rec->size,
154 &rec[1]);
155 }
156 GNUNET_SCHEDULER_cancel (st);
157 st = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
158 &do_shutdown,
159 NULL);
160}
161
162
163/**
164 * Process recorded audio data.
165 *
166 * @param cls clsoure
167 * @param data_size number of bytes in @a data
168 * @param data audio data to play
169 */
170static void
171record (void *cls,
172 size_t data_size,
173 const void *data)
174{
175 struct Recording *rec;
176
177 (void) cls;
178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179 "Recorded %u bytes\n",
180 (unsigned int) data_size);
181 rec = GNUNET_malloc (sizeof(struct Recording) + data_size);
182 rec->size = data_size;
183 GNUNET_memcpy (&rec[1], data, data_size);
184 GNUNET_CONTAINER_DLL_insert_tail (rec_head,
185 rec_tail,
186 rec);
187}
188
189
190/**
191 * Main function that will be run by the scheduler.
192 *
193 * @param cls closure
194 * @param args remaining command-line arguments
195 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
196 * @param cfg configuration
197 */
198static void
199run (void *cls,
200 char *const *args,
201 const char *cfgfile,
202 const struct GNUNET_CONFIGURATION_Handle *cfg)
203{
204 (void) cls;
205 (void) args;
206 (void) cfgfile;
207 microphone = GNUNET_MICROPHONE_create_from_hardware (cfg);
208 GNUNET_assert (NULL != microphone);
209 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
210 GNUNET_assert (NULL != speaker);
211 switch_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
212 &switch_to_speaker,
213 NULL);
214 st = GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
215 NULL);
216 fprintf (stderr,
217 _ (
218 "We will now be recording you for %s. After that time, the recording will be played back to you..."),
219 GNUNET_STRINGS_relative_time_to_string (TIMEOUT, GNUNET_YES));
220 if (GNUNET_OK !=
221 microphone->enable_microphone (microphone->cls,
222 &record, NULL))
223 {
224 fprintf (stderr,
225 "Failed to enable microphone\n");
226 ret = 1;
227 GNUNET_SCHEDULER_shutdown ();
228 return;
229 }
230}
231
232
233/**
234 * The main function of our code to test microphone and speaker.
235 *
236 * @param argc number of arguments from the command line
237 * @param argv command line arguments
238 * @return 0 ok, 1 on error
239 */
240int
241main (int argc,
242 char *const *argv)
243{
244 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
245 GNUNET_GETOPT_OPTION_END
246 };
247
248 if (GNUNET_OK !=
249 GNUNET_STRINGS_get_utf8_args (argc, argv,
250 &argc, &argv))
251 return 2;
252
253 ret = (GNUNET_OK ==
254 GNUNET_PROGRAM_run (argc, argv,
255 "gnunet-conversation-test",
256 gettext_noop ("help text"),
257 options,
258 &run,
259 NULL)) ? ret : 1;
260 GNUNET_free_nz ((void *) argv);
261 return ret;
262}
263
264
265/* end of gnunet-conversation-test.c */
diff --git a/src/contrib/service/conversation/gnunet-conversation.c b/src/contrib/service/conversation/gnunet-conversation.c
new file mode 100644
index 000000000..41fc7bd0b
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-conversation.c
@@ -0,0 +1,1232 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-conversation.c
22 * @brief conversation implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_conversation_service.h"
31#include "gnunet_namestore_service.h"
32
33/**
34 * Maximum length allowed for the command line input.
35 */
36#define MAX_MESSAGE_LENGTH 1024
37
38#define XSTRINGIFY(x) STRINGIFY (x)
39
40#define STRINGIFY(x) (#x)
41
42/**
43 * Possible states of the phone.
44 */
45enum PhoneState
46{
47 /**
48 * We're waiting for our own idenitty.
49 */
50 PS_LOOKUP_EGO,
51
52 /**
53 * We're listening for calls
54 */
55 PS_LISTEN,
56
57 /**
58 * We accepted an incoming phone call.
59 */
60 PS_ACCEPTED,
61
62 /**
63 * Internal error
64 */
65 PS_ERROR
66};
67
68
69/**
70 * States for current outgoing call.
71 */
72enum CallState
73{
74 /**
75 * We are looking up some other participant.
76 */
77 CS_RESOLVING,
78
79 /**
80 * We are now ringing the other participant.
81 */
82 CS_RINGING,
83
84 /**
85 * The other party accepted our call and we are now connected.
86 */
87 CS_CONNECTED,
88
89 /**
90 * The call is currently suspended (by us).
91 */
92 CS_SUSPENDED
93};
94
95
96/**
97 * List of incoming calls
98 */
99struct CallList
100{
101 /**
102 * A DLL.
103 */
104 struct CallList *prev;
105
106 /**
107 * A DLL.
108 */
109 struct CallList *next;
110
111 /**
112 * Handle to hang up or activate.
113 */
114 struct GNUNET_CONVERSATION_Caller *caller;
115
116 /**
117 * Public key identifying the caller.
118 */
119 struct GNUNET_CRYPTO_PublicKey caller_id;
120
121 /**
122 * Unique number of the call.
123 */
124 unsigned int caller_num;
125};
126
127
128/**
129 * Phone handle
130 */
131static struct GNUNET_CONVERSATION_Phone *phone;
132
133/**
134 * Call handle (for active outgoing call).
135 */
136static struct GNUNET_CONVERSATION_Call *call;
137
138/**
139 * Caller handle (for active incoming call).
140 * This call handler is NOT in the #cl_head / #cl_tail list.
141 */
142static struct CallList *cl_active;
143
144/**
145 * Head of calls waiting to be accepted.
146 */
147static struct CallList *cl_head;
148
149/**
150 * Tail of calls waiting to be accepted.
151 */
152static struct CallList *cl_tail;
153
154/**
155 * Desired phone line (string to be converted to a hash).
156 */
157static char *line;
158
159/**
160 * Task which handles the commands
161 */
162static struct GNUNET_SCHEDULER_Task *handle_cmd_task;
163
164/**
165 * Our speaker.
166 */
167static struct GNUNET_SPEAKER_Handle *speaker;
168
169/**
170 * Our microphone.
171 */
172static struct GNUNET_MICROPHONE_Handle *mic;
173
174/**
175 * Our configuration.
176 */
177static struct GNUNET_CONFIGURATION_Handle *cfg;
178
179/**
180 * Our ego.
181 */
182static struct GNUNET_IDENTITY_Ego *my_caller_id;
183
184/**
185 * Handle to identity service.
186 */
187static struct GNUNET_IDENTITY_Handle *id;
188
189/**
190 * Name of our ego.
191 */
192static char *ego_name;
193
194/**
195 * Public key of active conversation partner (if any).
196 */
197static struct GNUNET_CRYPTO_PublicKey peer_key;
198
199/**
200 * Name of active conversation partner (if any).
201 */
202static char *peer_name;
203
204/**
205 * File handle for stdin.
206 */
207static struct GNUNET_DISK_FileHandle *stdin_fh;
208
209/**
210 * Our phone's current state.
211 */
212static enum PhoneState phone_state;
213
214/**
215 * Our call's current state.
216 */
217static enum CallState call_state;
218
219/**
220 * Counts the number of incoming calls we have had so far.
221 */
222static unsigned int caller_num_gen;
223
224/**
225 * GNS address for this phone.
226 */
227static char *address;
228
229/**
230 * Be verbose.
231 */
232static int verbose;
233
234
235/**
236 * Function called with an event emitted by a phone.
237 *
238 * @param cls closure
239 * @param code type of the event
240 * @param caller handle for the caller
241 * @param caller_id public key of the caller (in GNS)
242 */
243static void
244phone_event_handler (void *cls,
245 enum GNUNET_CONVERSATION_PhoneEventCode code,
246 struct GNUNET_CONVERSATION_Caller *caller,
247 const struct GNUNET_CRYPTO_PublicKey *caller_id)
248{
249 struct CallList *cl;
250
251 (void) cls;
252 switch (code)
253 {
254 case GNUNET_CONVERSATION_EC_PHONE_RING:
255 /*
256 * FIXME: we should be playing our ringtones from contrib/sounds now!
257 *
258 ring_my_bell();
259 *
260 * see https://gstreamer.freedesktop.org/documentation/application-development/highlevel/playback-components.html on how to play a wav using the gst framework being used here
261 */fprintf (
262 stdout,
263 _ (
264 "Incoming call from `%s'. Please /accept %u or /cancel %u the call.\n"),
265 GNUNET_GNSRECORD_pkey_to_zkey (caller_id),
266 caller_num_gen,
267 caller_num_gen);
268 cl = GNUNET_new (struct CallList);
269 cl->caller = caller;
270 cl->caller_id = *caller_id;
271 cl->caller_num = caller_num_gen++;
272 GNUNET_CONTAINER_DLL_insert (cl_head, cl_tail, cl);
273 break;
274
275 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
276 for (cl = cl_head; NULL != cl; cl = cl->next)
277 if (caller == cl->caller)
278 break;
279 if ((NULL == cl) && (caller == cl_active->caller))
280 cl = cl_active;
281 if (NULL == cl)
282 {
283 GNUNET_break (0);
284 return;
285 }
286 fprintf (stdout,
287 _ ("Call from `%s' terminated\n"),
288 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
289 if (cl == cl_active)
290 {
291 cl_active = NULL;
292 phone_state = PS_LISTEN;
293 }
294 else
295 {
296 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
297 }
298 GNUNET_free (cl);
299 break;
300 }
301}
302
303
304/**
305 * Function called with an event emitted by a caller.
306 *
307 * @param cls closure with the `struct CallList` of the caller
308 * @param code type of the event issued by the caller
309 */
310static void
311caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
312{
313 struct CallList *cl = cls;
314
315 switch (code)
316 {
317 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
318 fprintf (stdout,
319 _ ("Call from `%s' suspended by other user\n"),
320 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
321 break;
322
323 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
324 fprintf (stdout,
325 _ ("Call from `%s' resumed by other user\n"),
326 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
327 break;
328 }
329}
330
331
332/**
333 * Start our phone.
334 */
335static void
336start_phone ()
337{
338 struct GNUNET_GNSRECORD_Data rd;
339
340 if (NULL == my_caller_id)
341 {
342 fprintf (stderr,
343 _ ("Ego `%s' no longer available, phone is now down.\n"),
344 ego_name);
345 phone_state = PS_LOOKUP_EGO;
346 return;
347 }
348 GNUNET_assert (NULL == phone);
349 phone = GNUNET_CONVERSATION_phone_create (cfg,
350 my_caller_id,
351 &phone_event_handler,
352 NULL);
353 /* FIXME: get record and print full GNS record info later here... */
354 if (NULL == phone)
355 {
356 fprintf (stderr, "%s", _ ("Failed to setup phone (internal error)\n"));
357 phone_state = PS_ERROR;
358 }
359 else
360 {
361 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
362 GNUNET_free (address);
363 address =
364 GNUNET_GNSRECORD_value_to_string (rd.record_type, rd.data, rd.data_size);
365 fprintf (
366 stdout,
367 _ (
368 "Phone active at `%s'. Type `/help' for a list of available commands\n"),
369 address);
370 phone_state = PS_LISTEN;
371 }
372}
373
374
375/**
376 * Function called with an event emitted by a call.
377 *
378 * @param cls closure, NULL
379 * @param code type of the event on the call
380 */
381static void
382call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
383{
384 (void) cls;
385
386 switch (code)
387 {
388 case GNUNET_CONVERSATION_EC_CALL_RINGING:
389 GNUNET_break (CS_RESOLVING == call_state);
390 fprintf (stdout,
391 _ ("Resolved address of `%s'. Now ringing other party.\n"),
392 peer_name);
393 call_state = CS_RINGING;
394 break;
395
396 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
397 GNUNET_break (CS_RINGING == call_state);
398 fprintf (stdout, _ ("Connection established to `%s'\n"), peer_name);
399 call_state = CS_CONNECTED;
400 break;
401
402 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
403 GNUNET_break (CS_RESOLVING == call_state);
404 fprintf (stdout, _ ("Failed to resolve `%s'\n"), peer_name);
405 GNUNET_free (peer_name);
406 peer_name = NULL;
407 call = NULL;
408 break;
409
410 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
411 fprintf (stdout, _ ("Call to `%s' terminated\n"), peer_name);
412 GNUNET_free (peer_name);
413 peer_name = NULL;
414 call = NULL;
415 break;
416
417 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
418 GNUNET_break (CS_CONNECTED == call_state);
419 fprintf (stdout,
420 _ ("Connection to `%s' suspended (by other user)\n"),
421 peer_name);
422 break;
423
424 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
425 GNUNET_break (CS_CONNECTED == call_state);
426 fprintf (stdout,
427 _ ("Connection to `%s' resumed (by other user)\n"),
428 peer_name);
429 break;
430
431 case GNUNET_CONVERSATION_EC_CALL_ERROR:
432 fprintf (stdout, _ ("Error with the call, restarting it\n"));
433 GNUNET_free (peer_name);
434 peer_name = NULL;
435 call = NULL;
436 break;
437 }
438}
439
440
441/**
442 * Function declareation for executing a action
443 *
444 * @param arguments arguments given to the function
445 */
446typedef void (*ActionFunction) (const char *arguments);
447
448
449/**
450 * Structure which defines a command
451 */
452struct VoipCommand
453{
454 /**
455 * Command the user needs to enter.
456 */
457 const char *command;
458
459 /**
460 * Function to call on command.
461 */
462 ActionFunction Action;
463
464 /**
465 * Help text for the command.
466 */
467 const char *helptext;
468};
469
470
471/**
472 * Action function to print help for the command shell.
473 *
474 * @param args arguments given to the command
475 */
476static void
477do_help (const char *args);
478
479
480/**
481 * Terminate the client
482 *
483 * @param args arguments given to the command
484 */
485static void
486do_quit (const char *args)
487{
488 (void) args;
489 GNUNET_SCHEDULER_shutdown ();
490}
491
492
493/**
494 * Handler for unknown command.
495 *
496 * @param msg arguments given to the command
497 */
498static void
499do_unknown (const char *msg)
500{
501 fprintf (stderr, _ ("Unknown command `%s'\n"), msg);
502}
503
504
505/**
506 * Initiating a new call
507 *
508 * @param arg arguments given to the command
509 */
510static void
511do_call (const char *arg)
512{
513 if (NULL == my_caller_id)
514 {
515 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
516 return;
517 }
518 if (NULL != call)
519 {
520 fprintf (stderr,
521 _ ("You are calling someone else already, hang up first!\n"));
522 return;
523 }
524 switch (phone_state)
525 {
526 case PS_LOOKUP_EGO:
527 fprintf (stderr, _ ("Ego `%s' not available\n"), ego_name);
528 return;
529
530 case PS_LISTEN:
531 /* ok to call! */
532 break;
533
534 case PS_ACCEPTED:
535 fprintf (
536 stderr,
537 _ (
538 "You are answering call from `%s', hang up or suspend that call first!\n"),
539 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
540 return;
541
542 case PS_ERROR:
543 /* ok to call */
544 break;
545 }
546 if (NULL == arg)
547 {
548 fprintf (stderr, _ ("Call recipient missing.\n"));
549 do_help ("/call");
550 return;
551 }
552 peer_name = GNUNET_strdup (arg);
553 call_state = CS_RESOLVING;
554 GNUNET_assert (NULL == call);
555 call = GNUNET_CONVERSATION_call_start (cfg,
556 my_caller_id,
557 arg,
558 speaker,
559 mic,
560 &call_event_handler,
561 NULL);
562}
563
564
565/**
566 * Accepting an incoming call
567 *
568 * @param args arguments given to the command
569 */
570static void
571do_accept (const char *args)
572{
573 struct CallList *cl;
574 char buf[32];
575
576 if ((NULL != call) && (CS_SUSPENDED != call_state))
577 {
578 fprintf (stderr,
579 _ ("You are calling someone else already, hang up first!\n"));
580 return;
581 }
582 switch (phone_state)
583 {
584 case PS_LOOKUP_EGO:
585 GNUNET_break (0);
586 break;
587
588 case PS_LISTEN:
589 /* this is the expected state */
590 break;
591
592 case PS_ACCEPTED:
593 fprintf (
594 stderr,
595 _ (
596 "You are answering call from `%s', hang up or suspend that call first!\n"),
597 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
598 return;
599
600 case PS_ERROR:
601 GNUNET_break (0);
602 break;
603 }
604 cl = cl_head;
605 if (NULL == cl)
606 {
607 fprintf (stderr, _ ("There is no incoming call to accept here!\n"));
608 return;
609 }
610 if ((NULL != cl->next) || (NULL != args))
611 {
612 for (cl = cl_head; NULL != cl; cl = cl->next)
613 {
614 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
615 if (0 == strcmp (buf, args))
616 break;
617 }
618 }
619 if (NULL == cl)
620 {
621 fprintf (stderr,
622 _ ("There is no incoming call `%s' to accept right now!\n"),
623 args);
624 return;
625 }
626 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
627 cl_active = cl;
628 peer_key = cl->caller_id;
629 phone_state = PS_ACCEPTED;
630 GNUNET_CONVERSATION_caller_pick_up (cl->caller,
631 &caller_event_handler,
632 cl,
633 speaker,
634 mic);
635}
636
637
638/**
639 * Print address information for this phone.
640 *
641 * @param args arguments given to the command
642 */
643static void
644do_address (const char *args)
645{
646 (void) args;
647 if (NULL == address)
648 {
649 fprintf (stdout, "%s", _ ("We currently do not have an address.\n"));
650 return;
651 }
652 fprintf (stdout, "%s\n", address);
653}
654
655
656/**
657 * Accepting an incoming call
658 *
659 * @param args arguments given to the command
660 */
661static void
662do_status (const char *args)
663{
664 struct CallList *cl;
665
666 (void) args;
667 switch (phone_state)
668 {
669 case PS_LOOKUP_EGO:
670 fprintf (
671 stdout,
672 _ (
673 "We are currently trying to locate the private key for the ego `%s'.\n"),
674 ego_name);
675 break;
676
677 case PS_LISTEN:
678 fprintf (stdout,
679 _ (
680 "We are listening for incoming calls for ego `%s' on line `%s'.\n"),
681 ego_name,
682 line);
683 break;
684
685 case PS_ACCEPTED:
686 fprintf (stdout,
687 _ ("You are having a conversation with `%s'.\n"),
688 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
689 ;
690 break;
691
692 case PS_ERROR:
693 fprintf (
694 stdout,
695 _ (
696 "We had an internal error setting up our phone line. You can still make calls.\n"));
697 break;
698 }
699 if (NULL != call)
700 {
701 switch (call_state)
702 {
703 case CS_RESOLVING:
704 fprintf (stdout,
705 _ ("We are trying to find the network address to call `%s'.\n"),
706 peer_name);
707 break;
708
709 case CS_RINGING:
710 fprintf (stdout,
711 _ ("We are calling `%s', their phone should be ringing.\n"),
712 peer_name);
713 break;
714
715 case CS_CONNECTED:
716 fprintf (stdout,
717 _ ("You are having a conversation with `%s'.\n"),
718 peer_name);
719 break;
720
721 case CS_SUSPENDED:
722 /* ok to accept incoming call right now */
723 break;
724 }
725 }
726 if ((NULL != cl_head) && ((cl_head != cl_active) || (cl_head != cl_tail)))
727 {
728 fprintf (stdout, "%s", _ ("Calls waiting:\n"));
729 for (cl = cl_head; NULL != cl; cl = cl->next)
730 {
731 if (cl == cl_active)
732 continue;
733 fprintf (stdout,
734 _ ("#%u: `%s'\n"),
735 cl->caller_num,
736 GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
737 }
738 fprintf (stdout, "%s", "\n");
739 }
740}
741
742
743/**
744 * Suspending a call
745 *
746 * @param args arguments given to the command
747 */
748static void
749do_suspend (const char *args)
750{
751 (void) args;
752 if (NULL != call)
753 {
754 switch (call_state)
755 {
756 case CS_RESOLVING:
757 case CS_RINGING:
758 case CS_SUSPENDED:
759 fprintf (stderr,
760 "%s",
761 _ ("There is no call that could be suspended right now.\n"));
762 return;
763
764 case CS_CONNECTED:
765 call_state = CS_SUSPENDED;
766 GNUNET_CONVERSATION_call_suspend (call);
767 return;
768 }
769 }
770 switch (phone_state)
771 {
772 case PS_LOOKUP_EGO:
773 case PS_LISTEN:
774 case PS_ERROR:
775 fprintf (stderr,
776 "%s",
777 _ ("There is no call that could be suspended right now.\n"));
778 return;
779
780 case PS_ACCEPTED:
781 /* expected state, do rejection logic */
782 break;
783 }
784 GNUNET_assert (NULL != cl_active);
785 GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
786 cl_active = NULL;
787 phone_state = PS_LISTEN;
788}
789
790
791/**
792 * Resuming a call
793 *
794 * @param args arguments given to the command
795 */
796static void
797do_resume (const char *args)
798{
799 struct CallList *cl;
800 char buf[32];
801
802 if (NULL != call)
803 {
804 switch (call_state)
805 {
806 case CS_RESOLVING:
807 case CS_RINGING:
808 case CS_CONNECTED:
809 fprintf (stderr,
810 "%s",
811 _ ("There is no call that could be resumed right now.\n"));
812 return;
813
814 case CS_SUSPENDED:
815 call_state = CS_CONNECTED;
816 GNUNET_CONVERSATION_call_resume (call, speaker, mic);
817 return;
818 }
819 }
820 switch (phone_state)
821 {
822 case PS_LOOKUP_EGO:
823 case PS_ERROR:
824 fprintf (stderr,
825 "%s",
826 _ ("There is no call that could be resumed right now.\n"));
827 return;
828
829 case PS_LISTEN:
830 /* expected state, do resume logic */
831 break;
832
833 case PS_ACCEPTED:
834 fprintf (stderr,
835 _ ("Already talking with `%s', cannot resume a call right now.\n"),
836 GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
837 return;
838 }
839 GNUNET_assert (NULL == cl_active);
840 cl = cl_head;
841 if (NULL == cl)
842 {
843 fprintf (stderr, _ ("There is no incoming call to resume here!\n"));
844 return;
845 }
846 if ((NULL != cl->next) || (NULL != args))
847 {
848 for (cl = cl_head; NULL != cl; cl = cl->next)
849 {
850 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
851 if (0 == strcmp (buf, args))
852 break;
853 }
854 }
855 if (NULL == cl)
856 {
857 fprintf (stderr,
858 _ ("There is no incoming call `%s' to resume right now!\n"),
859 args);
860 return;
861 }
862 cl_active = cl;
863 GNUNET_CONVERSATION_caller_resume (cl_active->caller, speaker, mic);
864 phone_state = PS_ACCEPTED;
865}
866
867
868/**
869 * Rejecting a call
870 *
871 * @param args arguments given to the command
872 */
873static void
874do_reject (const char *args)
875{
876 struct CallList *cl;
877 char buf[32];
878
879 if (NULL != call)
880 {
881 GNUNET_CONVERSATION_call_stop (call);
882 call = NULL;
883 return;
884 }
885 switch (phone_state)
886 {
887 case PS_LOOKUP_EGO:
888 case PS_ERROR:
889 fprintf (stderr,
890 "%s",
891 _ ("There is no call that could be cancelled right now.\n"));
892 return;
893
894 case PS_LISTEN:
895 /* look for active incoming calls to refuse */
896 cl = cl_head;
897 if (NULL == cl)
898 {
899 fprintf (stderr, _ ("There is no incoming call to refuse here!\n"));
900 return;
901 }
902 if ((NULL != cl->next) || (NULL != args))
903 {
904 for (cl = cl_head; NULL != cl; cl = cl->next)
905 {
906 GNUNET_snprintf (buf, sizeof(buf), "%u", cl->caller_num);
907 if (0 == strcmp (buf, args))
908 break;
909 }
910 }
911 if (NULL == cl)
912 {
913 fprintf (stderr,
914 _ ("There is no incoming call `%s' to refuse right now!\n"),
915 args);
916 return;
917 }
918 GNUNET_CONVERSATION_caller_hang_up (cl->caller);
919 GNUNET_CONTAINER_DLL_remove (cl_head, cl_tail, cl);
920 GNUNET_free (cl);
921 break;
922
923 case PS_ACCEPTED:
924 /* expected state, do rejection logic */
925 GNUNET_assert (NULL != cl_active);
926 GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
927 cl_active = NULL;
928 phone_state = PS_LISTEN;
929 break;
930 }
931}
932
933
934/**
935 * List of supported commands.
936 */
937static struct VoipCommand commands[] = {
938 { "/address",
939 &do_address,
940 gettext_noop (
941 "Use `/address' to find out which address this phone should have in GNS") },
942 { "/call", &do_call, gettext_noop ("Use `/call USER.gnu' to call USER") },
943 { "/accept",
944 &do_accept,
945 gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM") },
946 { "/suspend",
947 &do_suspend,
948 gettext_noop ("Use `/suspend' to suspend the active call") },
949 { "/resume",
950 &do_resume,
951 gettext_noop (
952 "Use `/resume [#NUM]' to resume a call, #NUM is needed to resume incoming calls, no argument is needed to resume the current outgoing call.") },
953 { "/cancel",
954 &do_reject,
955 gettext_noop ("Use `/cancel' to reject or terminate a call") },
956 { "/status",
957 &do_status,
958 gettext_noop ("Use `/status' to print status information") },
959 { "/quit",
960 &do_quit,
961 gettext_noop ("Use `/quit' to terminate gnunet-conversation") },
962 { "/help",
963 &do_help,
964 gettext_noop ("Use `/help command' to get help for a specific command") },
965 { "", &do_unknown, NULL },
966 { NULL, NULL, NULL },
967};
968
969
970/**
971 * Action function to print help for the command shell.
972 *
973 * @param args arguments given to the command
974 */
975static void
976do_help (const char *args)
977{
978 unsigned int i;
979
980 i = 0;
981 while ((NULL != args) && (0 != strlen (args)) &&
982 (commands[i].Action != &do_help))
983 {
984 if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1))
985 {
986 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
987 return;
988 }
989 i++;
990 }
991 i = 0;
992 fprintf (stdout, "%s", "Available commands:\n");
993 while (commands[i].Action != &do_help)
994 {
995 fprintf (stdout, "%s\n", gettext (commands[i].command));
996 i++;
997 }
998 fprintf (stdout, "%s", "\n");
999 fprintf (stdout, "%s\n", gettext (commands[i].helptext));
1000}
1001
1002
1003/**
1004 * Task run during shutdown.
1005 *
1006 * @param cls NULL
1007 */
1008static void
1009do_stop_task (void *cls)
1010{
1011 (void) cls;
1012 if (NULL != call)
1013 {
1014 GNUNET_CONVERSATION_call_stop (call);
1015 call = NULL;
1016 }
1017 if (NULL != phone)
1018 {
1019 GNUNET_CONVERSATION_phone_destroy (phone);
1020 phone = NULL;
1021 }
1022 if (NULL != handle_cmd_task)
1023 {
1024 GNUNET_SCHEDULER_cancel (handle_cmd_task);
1025 handle_cmd_task = NULL;
1026 }
1027 if (NULL != id)
1028 {
1029 GNUNET_IDENTITY_disconnect (id);
1030 id = NULL;
1031 }
1032 GNUNET_SPEAKER_destroy (speaker);
1033 speaker = NULL;
1034 GNUNET_MICROPHONE_destroy (mic);
1035 mic = NULL;
1036 GNUNET_free (ego_name);
1037 ego_name = NULL;
1038 GNUNET_free (peer_name);
1039 peer_name = NULL;
1040 phone_state = PS_ERROR;
1041}
1042
1043
1044/**
1045 * Handle user command.
1046 *
1047 * @param message command the user typed in
1048 * @param str_len number of bytes to process in @a message
1049 */
1050static void
1051handle_command_string (char *message, size_t str_len)
1052{
1053 size_t i;
1054 const char *ptr;
1055
1056 if (0 == str_len)
1057 return;
1058 if (message[str_len - 1] == '\n')
1059 message[str_len - 1] = '\0';
1060 if (message[str_len - 2] == '\r')
1061 message[str_len - 2] = '\0';
1062 if (0 == strlen (message))
1063 return;
1064 i = 0;
1065 while (
1066 (NULL != commands[i].command) &&
1067 (0 !=
1068 strncasecmp (commands[i].command, message, strlen (commands[i].command))))
1069 i++;
1070 ptr = &message[strlen (commands[i].command)];
1071 while (isspace ((unsigned char) *ptr))
1072 ptr++;
1073 if ('\0' == *ptr)
1074 ptr = NULL;
1075 commands[i].Action (ptr);
1076}
1077
1078
1079/**
1080 * Task to handle commands from the terminal.
1081 *
1082 * @param cls NULL
1083 */
1084static void
1085handle_command (void *cls)
1086{
1087 char message[MAX_MESSAGE_LENGTH + 1];
1088
1089 (void) cls;
1090 handle_cmd_task =
1091 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1092 stdin_fh,
1093 &handle_command,
1094 NULL);
1095 /* read message from command line and handle it */
1096 memset (message, 0, MAX_MESSAGE_LENGTH + 1);
1097 if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
1098 return;
1099 handle_command_string (message, strlen (message));
1100}
1101
1102
1103/**
1104 * Function called by identity service with information about egos.
1105 *
1106 * @param cls NULL
1107 * @param ego ego handle
1108 * @param ctx unused
1109 * @param name name of the ego
1110 */
1111static void
1112identity_cb (void *cls,
1113 struct GNUNET_IDENTITY_Ego *ego,
1114 void **ctx,
1115 const char *name)
1116{
1117 (void) cls;
1118 (void) ctx;
1119 if (NULL == name)
1120 return;
1121 if (ego == my_caller_id)
1122 {
1123 if (verbose)
1124 fprintf (stdout, _ ("Name of our ego changed to `%s'\n"), name);
1125 GNUNET_free (ego_name);
1126 ego_name = GNUNET_strdup (name);
1127 return;
1128 }
1129 if (0 != strcmp (name, ego_name))
1130 return;
1131 if (NULL == ego)
1132 {
1133 if (verbose)
1134 fprintf (stdout, _ ("Our ego `%s' was deleted!\n"), ego_name);
1135 my_caller_id = NULL;
1136 return;
1137 }
1138 my_caller_id = ego;
1139 GNUNET_CONFIGURATION_set_value_string (cfg, "CONVERSATION", "LINE", line);
1140 start_phone ();
1141}
1142
1143
1144/**
1145 * Main function that will be run by the scheduler.
1146 *
1147 * @param cls closure
1148 * @param args remaining command-line arguments
1149 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1150 * @param c configuration
1151 */
1152static void
1153run (void *cls,
1154 char *const *args,
1155 const char *cfgfile,
1156 const struct GNUNET_CONFIGURATION_Handle *c)
1157{
1158 (void) cls;
1159 (void) args;
1160 (void) cfgfile;
1161 cfg = GNUNET_CONFIGURATION_dup (c);
1162 speaker = GNUNET_SPEAKER_create_from_hardware (cfg);
1163 mic = GNUNET_MICROPHONE_create_from_hardware (cfg);
1164 if (NULL == ego_name)
1165 {
1166 fprintf (stderr, "%s", _ ("You must specify the NAME of an ego to use\n"));
1167 return;
1168 }
1169 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1170 handle_cmd_task =
1171 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
1172 &handle_command,
1173 NULL);
1174 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
1175}
1176
1177
1178/**
1179 * The main function to conversation.
1180 *
1181 * @param argc number of arguments from the command line
1182 * @param argv command line arguments
1183 * @return 0 ok, 1 on error
1184 */
1185int
1186main (int argc, char *const *argv)
1187{
1188 struct GNUNET_GETOPT_CommandLineOption options[] =
1189 { GNUNET_GETOPT_option_string (
1190 'e',
1191 "ego",
1192 "NAME",
1193 gettext_noop ("sets the NAME of the ego to use for the caller ID"),
1194 &ego_name),
1195 GNUNET_GETOPT_option_string ('p',
1196 "phone",
1197 "LINE",
1198 gettext_noop (
1199 "sets the LINE to use for the phone"),
1200 &line),
1201 GNUNET_GETOPT_OPTION_END };
1202 int ret;
1203
1204 int flags;
1205 flags = fcntl (0, F_GETFL, 0);
1206 flags |= O_NONBLOCK;
1207 if (0 != fcntl (0, F_SETFL, flags))
1208 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl");
1209 stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
1210
1211 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1212 return 2;
1213 ret =
1214 GNUNET_PROGRAM_run (argc,
1215 argv,
1216 "gnunet-conversation",
1217 gettext_noop (
1218 "Enables having a conversation with other GNUnet users."),
1219 options,
1220 &run,
1221 NULL);
1222 GNUNET_free_nz ((void *) argv);
1223 if (NULL != cfg)
1224 {
1225 GNUNET_CONFIGURATION_destroy (cfg);
1226 cfg = NULL;
1227 }
1228 return (GNUNET_OK == ret) ? 0 : 1;
1229}
1230
1231
1232/* end of gnunet-conversation.c */
diff --git a/src/contrib/service/conversation/gnunet-helper-audio-playback-gst.c b/src/contrib/service/conversation/gnunet-helper-audio-playback-gst.c
new file mode 100644
index 000000000..f1e1e773d
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-helper-audio-playback-gst.c
@@ -0,0 +1,405 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-helper-audio-playback-gst.c
22 * @brief program to playback audio data to the speaker (GStreamer version)
23 * @author LRN
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_protocols.h"
28#include "conversation.h"
29#include "gnunet_constants.h"
30#include "gnunet_core_service.h"
31
32#include <gst/gst.h>
33#include <gst/audio/gstaudiobasesrc.h>
34#include <gst/app/gstappsrc.h>
35#include <glib.h>
36
37#define DEBUG_READ_PURE_OGG 1
38
39/**
40 * How much data to read in one go
41 */
42#define MAXLINE 4096
43
44/**
45 * Max number of microseconds to buffer in audiosink.
46 * Default is 1000
47 */
48#define BUFFER_TIME 1000
49
50/**
51 * Min number of microseconds to buffer in audiosink.
52 * Default is 1000
53 */
54#define LATENCY_TIME 1000
55
56/**
57 * Tokenizer for the data we get from stdin
58 */
59struct GNUNET_MessageStreamTokenizer *stdin_mst;
60
61/**
62 * Main pipeline.
63 */
64static GstElement *pipeline;
65
66/**
67 * Appsrc instance into which we write data for the pipeline.
68 */
69static GstElement *source;
70
71static GstElement *demuxer;
72static GstElement *decoder;
73static GstElement *conv;
74static GstElement *resampler;
75static GstElement *sink;
76
77/**
78 * Set to 1 to break the reading loop
79 */
80static int abort_read;
81
82
83static void
84sink_child_added (GstChildProxy *child_proxy,
85 GObject *object,
86 gchar *name,
87 gpointer user_data)
88{
89 if (GST_IS_AUDIO_BASE_SRC (object))
90 g_object_set (object,
91 "buffer-time", (gint64) BUFFER_TIME,
92 "latency-time", (gint64) LATENCY_TIME,
93 NULL);
94}
95
96
97static void
98ogg_pad_added (GstElement *element,
99 GstPad *pad,
100 gpointer data)
101{
102 GstPad *sinkpad;
103 GstElement *decoder = (GstElement *) data;
104
105 /* We can now link this pad with the opus-decoder sink pad */
106 sinkpad = gst_element_get_static_pad (decoder, "sink");
107
108 gst_pad_link (pad, sinkpad);
109
110 gst_element_link_many (decoder, conv, resampler, sink, NULL);
111
112 gst_object_unref (sinkpad);
113}
114
115
116static void
117quit ()
118{
119 if (NULL != source)
120 gst_app_src_end_of_stream (GST_APP_SRC (source));
121 if (NULL != pipeline)
122 gst_element_set_state (pipeline, GST_STATE_NULL);
123 abort_read = 1;
124}
125
126
127static gboolean
128bus_call (GstBus *bus, GstMessage *msg, gpointer data)
129{
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Bus message\n");
132 switch (GST_MESSAGE_TYPE (msg))
133 {
134 case GST_MESSAGE_EOS:
135 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
136 "End of stream\n");
137 quit ();
138 break;
139
140 case GST_MESSAGE_ERROR:
141 {
142 gchar *debug;
143 GError *error;
144
145 gst_message_parse_error (msg, &error, &debug);
146 g_free (debug);
147
148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 "Error: %s\n",
150 error->message);
151 g_error_free (error);
152
153 quit ();
154 break;
155 }
156
157 default:
158 break;
159 }
160
161 return TRUE;
162}
163
164
165static void
166signalhandler (int s)
167{
168 quit ();
169}
170
171
172static int
173feed_buffer_to_gst (const char *audio, size_t b_len)
174{
175 GstBuffer *b;
176 gchar *bufspace;
177 GstFlowReturn flow;
178
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "Feeding %u bytes to GStreamer\n",
181 (unsigned int) b_len);
182
183 bufspace = g_memdup2 (audio, b_len);
184 b = gst_buffer_new_wrapped (bufspace, b_len);
185 if (NULL == b)
186 {
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "Failed to wrap a buffer\n");
189 g_free (bufspace);
190 return GNUNET_SYSERR;
191 }
192 flow = gst_app_src_push_buffer (GST_APP_SRC (source), b);
193 /* They all return GNUNET_OK, because currently player stops when
194 * data stops coming. This might need to be changed for the player
195 * to also stop when pipeline breaks.
196 */
197 switch (flow)
198 {
199 case GST_FLOW_OK:
200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201 "Fed %u bytes to the pipeline\n",
202 (unsigned int) b_len);
203 break;
204
205 case GST_FLOW_FLUSHING:
206 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
207 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208 "Dropped a buffer\n");
209 break;
210
211 case GST_FLOW_EOS:
212 /* end of stream */
213 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
214 "EOS\n");
215 break;
216
217 default:
218 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
219 "Unexpected push result\n");
220 break;
221 }
222 return GNUNET_OK;
223}
224
225
226/**
227 * Message callback
228 *
229 * @param msg message we received.
230 * @return #GNUNET_OK on success,
231 * #GNUNET_NO to stop further processing due to disconnect (no error)
232 * #GNUNET_SYSERR to stop further processing due to error
233 */
234static int
235stdin_receiver (void *cls,
236 const struct GNUNET_MessageHeader *msg)
237{
238 struct AudioMessage *audio;
239 size_t b_len;
240
241 switch (ntohs (msg->type))
242 {
243 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
244 audio = (struct AudioMessage *) msg;
245
246 b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
247 feed_buffer_to_gst ((const char *) &audio[1], b_len);
248 break;
249
250 default:
251 break;
252 }
253 return GNUNET_OK;
254}
255
256
257int
258main (int argc, char **argv)
259{
260 GstBus *bus;
261 guint bus_watch_id;
262 uint64_t toff;
263
264 typedef void (*SignalHandlerPointer) (int);
265
266 SignalHandlerPointer inthandler, termhandler;
267#ifdef DEBUG_READ_PURE_OGG
268 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
269#endif
270
271 inthandler = signal (SIGINT,
272 &signalhandler);
273 termhandler = signal (SIGTERM,
274 &signalhandler);
275
276 /* Initialisation */
277 gst_init (&argc, &argv);
278
279 GNUNET_assert (GNUNET_OK ==
280 GNUNET_log_setup ("gnunet-helper-audio-playback-gst",
281 "WARNING",
282 NULL));
283
284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
285 "Audio sink starts\n");
286
287 stdin_mst = GNUNET_MST_create (&stdin_receiver,
288 NULL);
289
290 /* Create gstreamer elements */
291 pipeline = gst_pipeline_new ("audio-player");
292 source = gst_element_factory_make ("appsrc", "audio-input");
293 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
294 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
295 conv = gst_element_factory_make ("audioconvert", "converter");
296 resampler = gst_element_factory_make ("audioresample", "resampler");
297 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
298
299 if (! pipeline || ! source || ! conv || ! resampler || ! decoder ||
300 ! demuxer || ! sink)
301 {
302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
303 "One element could not be created. Exiting.\n");
304 return -1;
305 }
306
307 g_signal_connect (sink,
308 "child-added",
309 G_CALLBACK (sink_child_added),
310 NULL);
311 g_signal_connect (demuxer,
312 "pad-added",
313 G_CALLBACK (ogg_pad_added),
314 decoder);
315
316 /* Keep a reference to it, we operate on it */
317 gst_object_ref (GST_OBJECT (source));
318
319 /* Set up the pipeline */
320
321 /* we feed appsrc as fast as possible, it just blocks when it's full */
322 g_object_set (G_OBJECT (source),
323/* "format", GST_FORMAT_TIME,*/
324 "block", TRUE,
325 "is-live", TRUE,
326 NULL);
327
328 g_object_set (G_OBJECT (decoder),
329/* "plc", FALSE,*/
330/* "apply-gain", TRUE,*/
331 "use-inband-fec", TRUE,
332 NULL);
333
334 /* we add a message handler */
335 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
336 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
337 gst_object_unref (bus);
338
339 /* we add all elements into the pipeline */
340 /* audio-input | ogg-demuxer | opus-decoder | converter | resampler | audiosink */
341 gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv,
342 resampler, sink, NULL);
343
344 /* we link the elements together */
345 gst_element_link_many (source, demuxer, NULL);
346
347 /* Set the pipeline to "playing" state*/
348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
349 gst_element_set_state (pipeline, GST_STATE_PLAYING);
350
351 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
352 /* Iterate */
353 toff = 0;
354 while (! abort_read)
355 {
356 char readbuf[MAXLINE];
357 int ret;
358
359 ret = read (0, readbuf, sizeof(readbuf));
360 if (0 > ret)
361 {
362 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363 _ ("Read error from STDIN: %d %s\n"),
364 ret, strerror (errno));
365 break;
366 }
367 toff += ret;
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Received %d bytes of audio data (total: %llu)\n",
370 (int) ret,
371 (unsigned long long) toff);
372 if (0 == ret)
373 break;
374#ifdef DEBUG_READ_PURE_OGG
375 if (read_pure_ogg)
376 {
377 feed_buffer_to_gst (readbuf, ret);
378 }
379 else
380#endif
381 GNUNET_MST_from_buffer (stdin_mst,
382 readbuf,
383 ret,
384 GNUNET_NO,
385 GNUNET_NO);
386 }
387 GNUNET_MST_destroy (stdin_mst);
388
389 signal (SIGINT, inthandler);
390 signal (SIGINT, termhandler);
391
392 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
393 "Returned, stopping playback\n");
394 quit ();
395
396 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
397 "Deleting pipeline\n");
398 gst_object_unref (GST_OBJECT (source));
399 source = NULL;
400 gst_object_unref (GST_OBJECT (pipeline));
401 pipeline = NULL;
402 g_source_remove (bus_watch_id);
403
404 return 0;
405}
diff --git a/src/contrib/service/conversation/gnunet-helper-audio-playback.c b/src/contrib/service/conversation/gnunet-helper-audio-playback.c
new file mode 100644
index 000000000..dfa400d71
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-helper-audio-playback.c
@@ -0,0 +1,888 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-helper-audio-playback.c
22 * @brief program to playback audio data to the speaker
23 * @author Siomon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "conversation.h"
31#include "gnunet_constants.h"
32#include "gnunet_core_service.h"
33
34#include <pulse/simple.h>
35#include <pulse/error.h>
36#include <pulse/rtclock.h>
37
38#include <pulse/pulseaudio.h>
39#include <opus/opus.h>
40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
42
43#define DEBUG_READ_PURE_OGG 1
44#define DEBUG_DUMP_DECODED_OGG 1
45
46#define MAXLINE 4096
47
48#define SAMPLING_RATE 48000
49
50#define CHANNELS 1
51
52/* 120ms at 48000 */
53#define MAX_FRAME_SIZE (960 * 6)
54
55/**
56 * Pulseaudio specification. May change in the future.
57 */
58static pa_sample_spec sample_spec = {
59 .format = PA_SAMPLE_FLOAT32LE,
60 .rate = SAMPLING_RATE,
61 .channels = CHANNELS
62};
63
64#ifdef DEBUG_DUMP_DECODED_OGG
65static int dump_to_stdout;
66#endif
67
68/**
69 * Pulseaudio mainloop api
70 */
71static pa_mainloop_api *mainloop_api;
72
73/**
74 * Pulseaudio threaded mainloop
75 */
76static pa_threaded_mainloop *m;
77
78/**
79 * Pulseaudio context
80 */
81static pa_context *context;
82
83/**
84 * Pulseaudio output stream
85 */
86static pa_stream *stream_out;
87
88/**
89 * OPUS decoder
90 */
91static OpusDecoder *dec;
92
93/**
94 * PCM data buffer
95 */
96static float *pcm_buffer;
97
98/**
99 * Number of samples for one frame
100 */
101static int frame_size;
102
103/**
104 * Pipe we use to signal the main loop that we are ready to receive.
105 */
106static int ready_pipe[2];
107
108/**
109 * Ogg I/O state.
110 */
111static ogg_sync_state oy;
112
113/**
114 * Ogg stream state.
115 */
116static ogg_stream_state os;
117
118static int channels;
119
120static int preskip;
121
122static float gain;
123
124GNUNET_NETWORK_STRUCT_BEGIN
125
126/* OggOpus spec says the numbers must be in little-endian order */
127struct OpusHeadPacket
128{
129 uint8_t magic[8];
130 uint8_t version;
131 uint8_t channels;
132 uint16_t preskip GNUNET_PACKED;
133 uint32_t sampling_rate GNUNET_PACKED;
134 uint16_t gain GNUNET_PACKED;
135 uint8_t channel_mapping;
136};
137
138GNUNET_NETWORK_STRUCT_END
139
140/**
141 * Process an Opus header and setup the opus decoder based on it.
142 * It takes several pointers for header values which are needed
143 * elsewhere in the code.
144 */
145static OpusDecoder *
146process_header (ogg_packet *op)
147{
148 int err;
149 OpusDecoder *dec;
150 struct OpusHeadPacket header;
151
152 if (((unsigned int) op->bytes) < sizeof(header))
153 return NULL;
154 GNUNET_memcpy (&header,
155 op->packet,
156 sizeof(header));
157 header.preskip = GNUNET_le16toh (header.preskip);
158 header.sampling_rate = GNUNET_le32toh (header.sampling_rate);
159 header.gain = GNUNET_le16toh (header.gain);
160
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162 "Header: v%u, %u-ch, skip %u, %uHz, %u gain\n",
163 header.version,
164 header.channels,
165 header.preskip,
166 header.sampling_rate,
167 header.gain);
168 channels = header.channels;
169 preskip = header.preskip;
170
171 if (header.channel_mapping != 0)
172 {
173 fprintf (stderr,
174 "This implementation does not support non-mono streams\n");
175 return NULL;
176 }
177
178 dec = opus_decoder_create (SAMPLING_RATE, channels, &err);
179 if (OPUS_OK != err)
180 {
181 fprintf (stderr,
182 "Cannot create encoder: %s\n",
183 opus_strerror (err));
184 return NULL;
185 }
186 if (! dec)
187 {
188 fprintf (stderr,
189 "Decoder initialization failed: %s\n",
190 opus_strerror (err));
191 return NULL;
192 }
193
194 if (0 != header.gain)
195 {
196 /*Gain API added in a newer libopus version, if we don't have it
197 we apply the gain ourselves. We also add in a user provided
198 manual gain at the same time.*/
199 int gainadj = (int) header.gain;
200 err = opus_decoder_ctl (dec, OPUS_SET_GAIN (gainadj));
201 if (OPUS_UNIMPLEMENTED == err)
202 {
203 gain = pow (10.0, gainadj / 5120.0);
204 }
205 else if (OPUS_OK != err)
206 {
207 fprintf (stderr, "Error setting gain: %s\n", opus_strerror (err));
208 return NULL;
209 }
210 }
211
212 return dec;
213}
214
215
216#ifdef DEBUG_DUMP_DECODED_OGG
217static size_t
218fwrite_le32 (opus_int32 i32, FILE *file)
219{
220 unsigned char buf[4];
221
222 buf[0] = (unsigned char) (i32 & 0xFF);
223 buf[1] = (unsigned char) (i32 >> 8 & 0xFF);
224 buf[2] = (unsigned char) (i32 >> 16 & 0xFF);
225 buf[3] = (unsigned char) (i32 >> 24 & 0xFF);
226 return fwrite (buf, 4, 1, file);
227}
228
229
230static size_t
231fwrite_le16 (int i16, FILE *file)
232{
233 unsigned char buf[2];
234
235 buf[0] = (unsigned char) (i16 & 0xFF);
236 buf[1] = (unsigned char) (i16 >> 8 & 0xFF);
237 return fwrite (buf, 2, 1, file);
238}
239
240
241static int
242write_wav_header ()
243{
244 int ret;
245 FILE *file = stdout;
246
247 ret = fprintf (file, "RIFF") >= 0;
248 ret &= fwrite_le32 (0x7fffffff, file);
249
250 ret &= fprintf (file, "WAVEfmt ") >= 0;
251 ret &= fwrite_le32 (16, file);
252 ret &= fwrite_le16 (1, file);
253 ret &= fwrite_le16 (channels, file);
254 ret &= fwrite_le32 (SAMPLING_RATE, file);
255 ret &= fwrite_le32 (2 * channels * SAMPLING_RATE, file);
256 ret &= fwrite_le16 (2 * channels, file);
257 ret &= fwrite_le16 (16, file);
258
259 ret &= fprintf (file, "data") >= 0;
260 ret &= fwrite_le32 (0x7fffffff, file);
261
262 return ! ret ? -1 : 16;
263}
264
265
266#endif
267
268
269static int64_t
270audio_write (int64_t maxout)
271{
272 int64_t sampout = 0;
273 int tmp_skip;
274 unsigned out_len;
275 unsigned to_write;
276 float *output;
277
278#ifdef DEBUG_DUMP_DECODED_OGG
279 static int wrote_wav_header;
280
281 if (dump_to_stdout && ! wrote_wav_header)
282 {
283 write_wav_header ();
284 wrote_wav_header = 1;
285 }
286#endif
287 maxout = 0 > maxout ? 0 : maxout;
288 do
289 {
290 tmp_skip = (preskip > frame_size) ? (int) frame_size : preskip;
291 preskip -= tmp_skip;
292 output = pcm_buffer + channels * tmp_skip;
293 out_len = frame_size - tmp_skip;
294 if (out_len > MAX_FRAME_SIZE)
295 exit (6);
296 frame_size = 0;
297
298 to_write = out_len < maxout ? out_len : (unsigned) maxout;
299 if (0 < maxout)
300 {
301 int64_t wrote = 0;
302 wrote = to_write;
303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304 "Writing %u * %u * %u = %llu bytes into PA\n",
305 to_write,
306 channels,
307 (unsigned int) sizeof(float),
308 (unsigned long long) (to_write * channels * sizeof(float)));
309#ifdef DEBUG_DUMP_DECODED_OGG
310 if (dump_to_stdout)
311 {
312# define fminf(_x, _y) ((_x) < (_y) ? (_x) : (_y))
313# define fmaxf(_x, _y) ((_x) > (_y) ? (_x) : (_y))
314# define float2int(flt) ((int) (floor (.5 + flt)))
315 int i;
316 int16_t *out = alloca (sizeof(short) * MAX_FRAME_SIZE * channels);
317 for (i = 0; i < (int) out_len * channels; i++)
318 out[i] = (short) float2int (fmaxf (-32768, fminf (output[i] * 32768.f,
319 32767)));
320
321 fwrite (out, 2 * channels, out_len < maxout ? out_len : maxout, stdout);
322 }
323 else
324#endif
325 if (pa_stream_write
326 (stream_out, output, to_write * channels * sizeof(float), NULL, 0,
327 PA_SEEK_RELATIVE) < 0)
328 {
329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330 _ ("pa_stream_write() failed: %s\n"),
331 pa_strerror (pa_context_errno (context)));
332 }
333 sampout += wrote;
334 maxout -= wrote;
335 }
336 }
337 while (0 < frame_size && 0 < maxout);
338
339 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340 "Wrote %" PRId64 " samples\n",
341 sampout);
342 return sampout;
343}
344
345
346/**
347 * Pulseaudio shutdown task
348 */
349static void
350quit (int ret)
351{
352 mainloop_api->quit (mainloop_api,
353 ret);
354 exit (ret);
355}
356
357
358static void
359ogg_demux_and_decode ()
360{
361 ogg_page og;
362 static int stream_init;
363 int64_t page_granule = 0;
364 ogg_packet op;
365 static int has_opus_stream;
366 static int has_tags_packet;
367 static int32_t opus_serialno;
368 static int64_t link_out;
369 static int64_t packet_count;
370 int eos = 0;
371 static int total_links;
372 static int gran_offset;
373
374 while (1 == ogg_sync_pageout (&oy, &og))
375 {
376 if (0 == stream_init)
377 {
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "Initialized the stream\n");
380 ogg_stream_init (&os, ogg_page_serialno (&og));
381 stream_init = 1;
382 }
383 if (ogg_page_serialno (&og) != os.serialno)
384 {
385 /* so all streams are read. */
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 "Re-set serial number\n");
388 ogg_stream_reset_serialno (&os, ogg_page_serialno (&og));
389 }
390 /*Add page to the bitstream*/
391 ogg_stream_pagein (&os, &og);
392 page_granule = ogg_page_granulepos (&og);
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Reading page that ends at %" PRId64 "\n",
395 page_granule);
396 /*Extract all available packets*/
397 while (1 == ogg_stream_packetout (&os, &op))
398 {
399 /*OggOpus streams are identified by a magic string in the initial
400 stream header.*/
401 if (op.b_o_s && (op.bytes >= 8) && ! memcmp (op.packet, "OpusHead", 8))
402 {
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404 "Got Opus Header\n");
405 if (has_opus_stream && has_tags_packet)
406 {
407 /*If we're seeing another BOS OpusHead now it means
408 the stream is chained without an EOS.
409 This can easily happen if record helper is terminated unexpectedly.
410 */
411 has_opus_stream = 0;
412 if (dec)
413 opus_decoder_destroy (dec);
414 dec = NULL;
415 fprintf (stderr,
416 "\nWarning: stream %" PRId64
417 " ended without EOS and a new stream began.\n",
418 (int64_t) os.serialno);
419 }
420 if (! has_opus_stream)
421 {
422 if ((packet_count > 0) && (opus_serialno == os.serialno) )
423 {
424 fprintf (stderr,
425 "\nError: Apparent chaining without changing serial number (%"
426 PRId64 "==%" PRId64 ").\n",
427 (int64_t) opus_serialno, (int64_t) os.serialno);
428 quit (1);
429 }
430 opus_serialno = os.serialno;
431 has_opus_stream = 1;
432 has_tags_packet = 0;
433 link_out = 0;
434 packet_count = 0;
435 eos = 0;
436 total_links++;
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438 "Got header for stream %" PRId64 ", this is %dth link\n",
439 (int64_t) opus_serialno, total_links);
440 }
441 else
442 {
443 fprintf (stderr, "\nWarning: ignoring opus stream %" PRId64 "\n",
444 (int64_t) os.serialno);
445 }
446 }
447 if (! has_opus_stream || (os.serialno != opus_serialno) )
448 {
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 "breaking out\n");
451 break;
452 }
453 /*If first packet in a logical stream, process the Opus header*/
454 if (0 == packet_count)
455 {
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "Decoding header\n");
458 dec = process_header (&op);
459 if (! dec)
460 quit (1);
461
462 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
463 og.header[og.header_len
464 - 1]) )
465 {
466 /*The format specifies that the initial header and tags packets are on their
467 own pages. To aid implementors in discovering that their files are wrong
468 we reject them explicitly here. In some player designs files like this would
469 fail even without an explicit test.*/
470 fprintf (stderr,
471 "Extra packets on initial header page. Invalid stream.\n");
472 quit (1);
473 }
474
475 /*Remember how many samples at the front we were told to skip
476 so that we can adjust the timestamp counting.*/
477 gran_offset = preskip;
478
479 if (! pcm_buffer)
480 {
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Allocating %u * %u * %u = %llu bytes of buffer space\n",
483 MAX_FRAME_SIZE,
484 channels,
485 (unsigned int) sizeof(float),
486 (unsigned long long) (MAX_FRAME_SIZE * channels
487 * sizeof(float)));
488 pcm_buffer = pa_xmalloc (sizeof(float) * MAX_FRAME_SIZE * channels);
489 }
490 }
491 else if (1 == packet_count)
492 {
493 has_tags_packet = 1;
494 if ((0 != ogg_stream_packetout (&os, &op)) || (255 ==
495 og.header[og.header_len
496 - 1]) )
497 {
498 fprintf (stderr,
499 "Extra packets on initial tags page. Invalid stream.\n");
500 quit (1);
501 }
502 }
503 else
504 {
505 int ret;
506 int64_t maxout;
507 int64_t outsamp;
508
509 /*End of stream condition*/
510 if (op.e_o_s && (os.serialno == opus_serialno) )
511 {
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 "Got EOS\n");
514 eos = 1; /* don't care for anything except opus eos */
515 }
516
517 /*Decode Opus packet*/
518 ret = opus_decode_float (dec,
519 (const unsigned char *) op.packet,
520 op.bytes,
521 pcm_buffer,
522 MAX_FRAME_SIZE, 0);
523
524 /*If the decoder returned less than zero, we have an error.*/
525 if (0 > ret)
526 {
527 fprintf (stderr, "Decoding error: %s\n", opus_strerror (ret));
528 break;
529 }
530 frame_size = ret;
531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532 "Decoded %d bytes/channel (%d bytes) from %u compressed bytes\n",
533 ret,
534 ret * channels,
535 (unsigned int) op.bytes);
536
537 /*Apply header gain, if we're not using an opus library new
538 enough to do this internally.*/
539 if (0 != gain)
540 {
541 int i;
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543 "Applying gain %f\n",
544 gain);
545 for (i = 0; i < frame_size * channels; i++)
546 pcm_buffer[i] *= gain;
547 }
548
549 /*This handles making sure that our output duration respects
550 the final end-trim by not letting the output sample count
551 get ahead of the granpos indicated value.*/
552 maxout = ((page_granule - gran_offset) * SAMPLING_RATE / 48000)
553 - link_out;
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "Writing audio packet %" PRId64 ", at most %" PRId64
556 " samples\n",
557 packet_count, maxout);
558
559 outsamp = audio_write (0 > maxout ? 0 : maxout);
560 link_out += outsamp;
561 }
562 packet_count++;
563 }
564 if (eos)
565 {
566 has_opus_stream = 0;
567 if (dec)
568 opus_decoder_destroy (dec);
569 dec = NULL;
570 }
571 }
572}
573
574
575/**
576 * Message callback
577 *
578 * @param msg message we received.
579 * @return #GNUNET_OK on success,
580 * #GNUNET_NO to stop further processing due to disconnect (no error)
581 * #GNUNET_SYSERR to stop further processing due to error
582 */
583static int
584stdin_receiver (void *cls,
585 const struct GNUNET_MessageHeader *msg)
586{
587 struct AudioMessage *audio;
588 char *data;
589 size_t payload_len;
590
591 (void) cls;
592 switch (ntohs (msg->type))
593 {
594 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
595 audio = (struct AudioMessage *) msg;
596 payload_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
597
598 /*Get the ogg buffer for writing*/
599 data = ogg_sync_buffer (&oy, payload_len);
600 /*Read bitstream from input file*/
601 GNUNET_memcpy (data, (const unsigned char *) &audio[1], payload_len);
602 ogg_sync_wrote (&oy, payload_len);
603
604 ogg_demux_and_decode ();
605 break;
606
607 default:
608 break;
609 }
610 return GNUNET_OK;
611}
612
613
614/**
615 * Callback when data is there for playback
616 */
617static void
618stream_write_callback (pa_stream *s,
619 size_t length,
620 void *userdata)
621{
622 /* unblock 'main' */
623 (void) userdata;
624 (void) length;
625 (void) s;
626 if (-1 != ready_pipe[1])
627 {
628 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629 "Unblocking main loop!\n");
630 (void) write (ready_pipe[1], "r", 1);
631 }
632}
633
634
635/**
636 * Exit callback for SIGTERM and SIGINT
637 */
638static void
639exit_signal_callback (pa_mainloop_api *m,
640 pa_signal_event *e,
641 int sig,
642 void *userdata)
643{
644 (void) m;
645 (void) e;
646 (void) sig;
647 (void) userdata;
648 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
649 _ ("gnunet-helper-audio-playback - Got signal, exiting\n"));
650 quit (1);
651}
652
653
654/**
655 * Pulseaudio stream state callback
656 */
657static void
658context_state_callback (pa_context *c,
659 void *userdata)
660{
661 int p;
662
663 (void) userdata;
664 GNUNET_assert (NULL != c);
665 switch (pa_context_get_state (c))
666 {
667 case PA_CONTEXT_CONNECTING:
668 case PA_CONTEXT_AUTHORIZING:
669 case PA_CONTEXT_SETTING_NAME:
670 break;
671
672 case PA_CONTEXT_READY:
673 {
674 GNUNET_assert (! stream_out);
675 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
676 _ ("Connection established.\n"));
677 if (! (stream_out =
678 pa_stream_new (c, "GNUNET VoIP playback", &sample_spec, NULL)))
679 {
680 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681 _ ("pa_stream_new() failed: %s\n"),
682 pa_strerror (pa_context_errno (c)));
683 goto fail;
684 }
685 pa_stream_set_write_callback (stream_out,
686 &stream_write_callback,
687 NULL);
688 if ((p =
689 pa_stream_connect_playback (stream_out, NULL,
690 NULL,
691 PA_STREAM_ADJUST_LATENCY
692 | PA_STREAM_INTERPOLATE_TIMING
693 | PA_STREAM_AUTO_TIMING_UPDATE,
694 NULL, NULL)) < 0)
695 {
696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
697 _ ("pa_stream_connect_playback() failed: %s\n"),
698 pa_strerror (pa_context_errno (c)));
699 goto fail;
700 }
701 break;
702 }
703
704 case PA_CONTEXT_TERMINATED:
705 quit (0);
706 break;
707
708 case PA_CONTEXT_FAILED:
709 default:
710 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
711 _ ("Connection failure: %s\n"),
712 pa_strerror (pa_context_errno (c)));
713 goto fail;
714 }
715 return;
716fail:
717 quit (1);
718}
719
720
721/**
722 * Pulseaudio initialization
723 */
724static void
725pa_init ()
726{
727 int r;
728
729 if (! pa_sample_spec_valid (&sample_spec))
730 {
731 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
732 _ ("Wrong Spec\n"));
733 }
734 /* set up threaded playback mainloop */
735 if (! (m = pa_threaded_mainloop_new ()))
736 {
737 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
738 _ ("pa_mainloop_new() failed.\n"));
739 }
740 mainloop_api = pa_threaded_mainloop_get_api (m);
741 /* listen to signals */
742 r = pa_signal_init (mainloop_api);
743 GNUNET_assert (r == 0);
744 pa_signal_new (SIGINT, exit_signal_callback, NULL);
745 pa_signal_new (SIGTERM, exit_signal_callback, NULL);
746
747
748 /* connect to the main pulseaudio context */
749 if (! (context = pa_context_new (mainloop_api, "GNUnet VoIP")))
750 {
751 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
752 _ ("pa_context_new() failed.\n"));
753 }
754 pa_context_set_state_callback (context, context_state_callback, NULL);
755
756 if (pa_context_connect (context, NULL, 0, NULL) < 0)
757 {
758 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
759 _ ("pa_context_connect() failed: %s\n"),
760 pa_strerror (pa_context_errno (context)));
761 }
762 if (pa_threaded_mainloop_start (m) < 0)
763 {
764 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
765 _ ("pa_mainloop_run() failed.\n"));
766 }
767}
768
769
770static void
771ogg_init ()
772{
773 ogg_sync_init (&oy);
774}
775
776
777static void
778drain_callback (pa_stream*s, int success, void *userdata)
779{
780 (void) s;
781 (void) success;
782 (void) userdata;
783 pa_threaded_mainloop_signal (m,
784 0);
785}
786
787
788/**
789 * The main function for the playback helper.
790 *
791 * @param argc number of arguments from the command line
792 * @param argv command line arguments
793 * @return 0 ok, 1 on error
794 */
795int
796main (int argc, char *argv[])
797{
798 static unsigned long long toff;
799 char readbuf[MAXLINE];
800 struct GNUNET_MessageStreamTokenizer *stdin_mst;
801 char c;
802 ssize_t ret;
803
804#ifdef DEBUG_READ_PURE_OGG
805 int read_pure_ogg = getenv ("GNUNET_READ_PURE_OGG") ? 1 : 0;
806#endif
807
808 (void) argc;
809 (void) argv;
810 GNUNET_assert (GNUNET_OK ==
811 GNUNET_log_setup ("gnunet-helper-audio-playback",
812 "WARNING",
813 NULL));
814 if (0 != pipe (ready_pipe))
815 {
816 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
817 return 1;
818 }
819 stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL);
820 ogg_init ();
821 pa_init ();
822 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823 "Waiting for PulseAudio to be ready.\n");
824 GNUNET_assert (1 == read (ready_pipe[0], &c, 1));
825 close (ready_pipe[0]);
826 close (ready_pipe[1]);
827 ready_pipe[0] = -1;
828 ready_pipe[1] = -1;
829#ifdef DEBUG_DUMP_DECODED_OGG
830 dump_to_stdout = getenv ("GNUNET_DUMP_DECODED_OGG") ? 1 : 0;
831#endif
832 while (1)
833 {
834 ret = read (STDIN_FILENO,
835 readbuf,
836 sizeof(readbuf));
837 toff += ret;
838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
839 "Received %d bytes of audio data (total: %llu)\n",
840 (int) ret,
841 toff);
842 if (0 > ret)
843 {
844 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
845 _ ("Read error from STDIN: %s\n"),
846 strerror (errno));
847 break;
848 }
849 if (0 == ret)
850 break;
851#ifdef DEBUG_READ_PURE_OGG
852 if (read_pure_ogg)
853 {
854 char *data = ogg_sync_buffer (&oy, ret);
855 GNUNET_memcpy (data, readbuf, ret);
856 ogg_sync_wrote (&oy, ret);
857 ogg_demux_and_decode ();
858 }
859 else
860#endif
861 GNUNET_MST_from_buffer (stdin_mst,
862 readbuf, ret,
863 GNUNET_NO, GNUNET_NO);
864 }
865 GNUNET_MST_destroy (stdin_mst);
866 if (stream_out)
867 {
868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869 "Locking\n");
870 pa_threaded_mainloop_lock (m);
871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872 "Draining\n");
873 pa_operation *o = pa_stream_drain (stream_out, drain_callback, NULL);
874 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING)
875 {
876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877 "Waiting\n");
878 pa_threaded_mainloop_wait (m);
879 }
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881 "Unreffing\n");
882 pa_operation_unref (o);
883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884 "Unlocking\n");
885 pa_threaded_mainloop_unlock (m);
886 }
887 return 0;
888}
diff --git a/src/contrib/service/conversation/gnunet-helper-audio-record-gst.c b/src/contrib/service/conversation/gnunet-helper-audio-record-gst.c
new file mode 100644
index 000000000..883dd9eea
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-helper-audio-record-gst.c
@@ -0,0 +1,393 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-helper-audio-record-gst.c
22 * @brief program to record audio data from the microphone (GStreamer version)
23 * @author LRN
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_protocols.h"
28#include "conversation.h"
29#include "gnunet_constants.h"
30#include "gnunet_core_service.h"
31
32#include <gst/gst.h>
33#include <gst/app/gstappsink.h>
34#include <gst/audio/gstaudiobasesrc.h>
35#include <glib.h>
36
37#define DEBUG_RECORD_PURE_OGG 1
38
39/**
40 * Number of channels.
41 * Must be one of the following (from libopusenc documentation):
42 * 1, 2
43 */
44#define OPUS_CHANNELS 1
45
46/**
47 * Maximal size of a single opus packet.
48 */
49#define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
50
51/**
52 * Size of a single frame fed to the encoder, in ms.
53 * Must be one of the following (from libopus documentation):
54 * 2.5, 5, 10, 20, 40 or 60
55 */
56#define OPUS_FRAME_SIZE 40
57
58/**
59 * Expected packet loss to prepare for, in percents.
60 */
61#define PACKET_LOSS_PERCENTAGE 1
62
63/**
64 * Set to 1 to enable forward error correction.
65 * Set to 0 to disable.
66 */
67#define INBAND_FEC_MODE 1
68
69/**
70 * Max number of microseconds to buffer in audiosource.
71 * Default is 200000
72 */
73#define BUFFER_TIME 1000 /* 1ms */
74
75/**
76 * Min number of microseconds to buffer in audiosource.
77 * Default is 10000
78 */
79#define LATENCY_TIME 1000 /* 1ms */
80
81/**
82 * Maximum delay in multiplexing streams, in ns.
83 * Setting this to 0 forces page flushing, which
84 * decreases delay, but increases overhead.
85 */
86#define OGG_MAX_DELAY 0
87
88/**
89 * Maximum delay for sending out a page, in ns.
90 * Setting this to 0 forces page flushing, which
91 * decreases delay, but increases overhead.
92 */
93#define OGG_MAX_PAGE_DELAY 0
94
95/**
96 * Main pipeline.
97 */
98static GstElement *pipeline;
99
100#ifdef DEBUG_RECORD_PURE_OGG
101static int dump_pure_ogg;
102#endif
103
104static void
105quit ()
106{
107 if (NULL != pipeline)
108 gst_element_set_state (pipeline, GST_STATE_NULL);
109}
110
111
112static gboolean
113bus_call (GstBus *bus, GstMessage *msg, gpointer data)
114{
115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bus message\n");
116 switch (GST_MESSAGE_TYPE (msg))
117 {
118 case GST_MESSAGE_EOS:
119 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "End of stream\n");
120 quit ();
121 break;
122
123 case GST_MESSAGE_ERROR:
124 {
125 gchar *debug;
126 GError *error;
127
128 gst_message_parse_error (msg, &error, &debug);
129 g_free (debug);
130
131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: %s\n", error->message);
132 g_error_free (error);
133
134 quit ();
135 break;
136 }
137
138 default:
139 break;
140 }
141
142 return TRUE;
143}
144
145
146void
147source_child_added (GstChildProxy *child_proxy, GObject *object, gchar *name,
148 gpointer user_data)
149{
150 if (GST_IS_AUDIO_BASE_SRC (object))
151 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time",
152 (gint64) LATENCY_TIME, NULL);
153}
154
155
156static void
157signalhandler (int s)
158{
159 quit ();
160}
161
162
163int
164main (int argc, char **argv)
165{
166 GstElement *source, *filter, *encoder, *conv, *resampler, *sink, *oggmux;
167 GstCaps *caps;
168 GstBus *bus;
169 guint bus_watch_id;
170 struct AudioMessage audio_message;
171 int abort_send = 0;
172
173 typedef void (*SignalHandlerPointer) (int);
174
175 SignalHandlerPointer inthandler, termhandler;
176 inthandler = signal (SIGINT, signalhandler);
177 termhandler = signal (SIGTERM, signalhandler);
178
179#ifdef DEBUG_RECORD_PURE_OGG
180 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
181#endif
182
183 /* Initialisation */
184 gst_init (&argc, &argv);
185
186 GNUNET_assert (GNUNET_OK ==
187 GNUNET_log_setup ("gnunet-helper-audio-record",
188 "WARNING",
189 NULL));
190
191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192 "Audio source starts\n");
193
194 audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
195
196 /* Create gstreamer elements */
197 pipeline = gst_pipeline_new ("audio-recorder");
198 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
199 filter = gst_element_factory_make ("capsfilter", "filter");
200 conv = gst_element_factory_make ("audioconvert", "converter");
201 resampler = gst_element_factory_make ("audioresample", "resampler");
202 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
203 oggmux = gst_element_factory_make ("oggmux", "ogg-muxer");
204 sink = gst_element_factory_make ("appsink", "audio-output");
205
206 if (! pipeline || ! filter || ! source || ! conv || ! resampler ||
207 ! encoder || ! oggmux || ! sink)
208 {
209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210 "One element could not be created. Exiting.\n");
211 return -1;
212 }
213
214 g_signal_connect (source, "child-added", G_CALLBACK (source_child_added),
215 NULL);
216
217 /* Set up the pipeline */
218
219 caps = gst_caps_new_simple ("audio/x-raw",
220 "format", G_TYPE_STRING, "S16LE",
221/* "rate", G_TYPE_INT, SAMPLING_RATE,*/
222 "channels", G_TYPE_INT, OPUS_CHANNELS,
223/* "layout", G_TYPE_STRING, "interleaved",*/
224 NULL);
225 g_object_set (G_OBJECT (filter),
226 "caps", caps,
227 NULL);
228 gst_caps_unref (caps);
229
230 g_object_set (G_OBJECT (encoder),
231/* "bitrate", 64000, */
232/* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
233 "inband-fec", INBAND_FEC_MODE,
234 "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
235 "max-payload-size", MAX_PAYLOAD_SIZE,
236 "audio", FALSE, /* VoIP, not audio */
237 "frame-size", OPUS_FRAME_SIZE,
238 NULL);
239
240 g_object_set (G_OBJECT (oggmux),
241 "max-delay", OGG_MAX_DELAY,
242 "max-page-delay", OGG_MAX_PAGE_DELAY,
243 NULL);
244
245 /* we add a message handler */
246 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
247 bus_watch_id = gst_bus_add_watch (bus, bus_call, pipeline);
248 gst_object_unref (bus);
249
250 /* we add all elements into the pipeline */
251 /* audiosource | converter | resampler | opus-encoder | audio-output */
252 gst_bin_add_many (GST_BIN (pipeline), source, filter, conv, resampler,
253 encoder,
254 oggmux, sink, NULL);
255
256 /* we link the elements together */
257 gst_element_link_many (source, filter, conv, resampler, encoder, oggmux, sink,
258 NULL);
259
260 /* Set the pipeline to "playing" state*/
261 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Now playing\n");
262 gst_element_set_state (pipeline, GST_STATE_PLAYING);
263
264
265 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running...\n");
266 /* Iterate */
267 while (! abort_send)
268 {
269 GstSample *s;
270 GstBuffer *b;
271 GstMapInfo m;
272 size_t len, msg_size;
273 const char *ptr;
274 int phase;
275
276 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
277 s = gst_app_sink_pull_sample (GST_APP_SINK (sink));
278 if (NULL == s)
279 {
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
281 break;
282 }
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
284 {
285 const GstStructure *si;
286 char *si_str;
287 GstCaps *s_caps;
288 char *caps_str;
289 si = gst_sample_get_info (s);
290 if (si)
291 {
292 si_str = gst_structure_to_string (si);
293 if (si_str)
294 {
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
296 g_free (si_str);
297 }
298 }
299 else
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
301 s_caps = gst_sample_get_caps (s);
302 if (s_caps)
303 {
304 caps_str = gst_caps_to_string (s_caps);
305 if (caps_str)
306 {
307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n",
308 caps_str);
309 g_free (caps_str);
310 }
311 }
312 else
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
314 }
315 b = gst_sample_get_buffer (s);
316 if ((NULL == b) || ! gst_buffer_map (b, &m, GST_MAP_READ))
317 {
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319 "got NULL buffer %p or failed to map the buffer\n", b);
320 gst_sample_unref (s);
321 continue;
322 }
323
324 len = m.size;
325 if (len > UINT16_MAX - sizeof(struct AudioMessage))
326 {
327 GNUNET_break (0);
328 len = UINT16_MAX - sizeof(struct AudioMessage);
329 }
330 msg_size = sizeof(struct AudioMessage) + len;
331 audio_message.header.size = htons ((uint16_t) msg_size);
332
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "Sending %u bytes of audio data\n", (unsigned int) msg_size);
335 for (phase = 0; phase < 2; phase++)
336 {
337 size_t offset;
338 size_t to_send;
339 ssize_t ret;
340 if (0 == phase)
341 {
342#ifdef DEBUG_RECORD_PURE_OGG
343 if (dump_pure_ogg)
344 continue;
345#endif
346 ptr = (const char *) &audio_message;
347 to_send = sizeof(audio_message);
348 }
349 else
350 {
351 ptr = (const char *) m.data;
352 to_send = len;
353 }
354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355 "Sending %u bytes on phase %d\n", (unsigned int) to_send,
356 phase);
357 for (offset = 0; offset < to_send; offset += ret)
358 {
359 ret = write (1, &ptr[offset], to_send - offset);
360 if (0 >= ret)
361 {
362 if (-1 == ret)
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
365 (unsigned int) (to_send - offset),
366 (unsigned int) offset,
367 (unsigned int) (to_send + offset),
368 phase,
369 strerror (errno));
370 abort_send = 1;
371 break;
372 }
373 }
374 if (abort_send)
375 break;
376 }
377 gst_buffer_unmap (b, &m);
378 gst_sample_unref (s);
379 }
380
381 signal (SIGINT, inthandler);
382 signal (SIGINT, termhandler);
383
384 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returned, stopping playback\n");
385 quit ();
386
387 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Deleting pipeline\n");
388 gst_object_unref (GST_OBJECT (pipeline));
389 pipeline = NULL;
390 g_source_remove (bus_watch_id);
391
392 return 0;
393}
diff --git a/src/contrib/service/conversation/gnunet-helper-audio-record.c b/src/contrib/service/conversation/gnunet-helper-audio-record.c
new file mode 100644
index 000000000..060a7c779
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-helper-audio-record.c
@@ -0,0 +1,807 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-helper-audio-record.c
22 * @brief program to record audio data from the microphone
23 * @author Siomon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "conversation.h"
31#include "gnunet_constants.h"
32#include "gnunet_core_service.h"
33
34#include <pulse/simple.h>
35#include <pulse/error.h>
36#include <pulse/rtclock.h>
37
38#include <pulse/pulseaudio.h>
39#include <opus/opus.h>
40#include <opus/opus_types.h>
41#include <ogg/ogg.h>
42
43#define DEBUG_RECORD_PURE_OGG 1
44
45/**
46 * Sampling rate
47 */
48#define SAMPLING_RATE 48000
49
50/**
51 * How many ms of audio to buffer before encoding them.
52 * Possible values:
53 * 60, 40, 20, 10, 5, 2.5
54 */
55#define FRAME_SIZE_MS 40
56
57/**
58 * How many samples to buffer before encoding them.
59 */
60#define FRAME_SIZE (SAMPLING_RATE / 1000 * FRAME_SIZE_MS)
61
62/**
63 * Pages are committed when their size goes over this value.
64 * Note that in practice we flush pages VERY often (every frame),
65 * which means that pages NEVER really get to be this big.
66 * With one-packet-per-page, pages are roughly 100-300 bytes each.
67 *
68 * This value is chosen to make MAX_PAYLOAD_BYTES=1024 fit
69 * into a single page.
70 */
71#define PAGE_WATERLINE 800
72
73/**
74 * Maximum length of opus payload
75 */
76#define MAX_PAYLOAD_BYTES 1024
77
78/**
79 * Number of channels
80 */
81#define CHANNELS 1
82
83/**
84 * Configures the encoder's expected packet loss percentage.
85 *
86 * Higher values will trigger progressively more loss resistant behavior
87 * in the encoder at the expense of quality at a given bitrate
88 * in the lossless case, but greater quality under loss.
89 */
90#define CONV_OPUS_PACKET_LOSS_PERCENTAGE 1
91
92/**
93 * Configures the encoder's computational complexity.
94 *
95 * The supported range is 0-10 inclusive with 10 representing
96 * the highest complexity.
97 */
98#define CONV_OPUS_ENCODING_COMPLEXITY 10
99
100/**
101 * Configures the encoder's use of inband forward error correction (FEC).
102 *
103 * Note: This is only applicable to the LPC layer.
104 */
105#define CONV_OPUS_INBAND_FEC 1
106
107/**
108 * Configures the type of signal being encoded.
109 *
110 * This is a hint which helps the encoder's mode selection.
111 *
112 * Possible values:
113 * OPUS_AUTO - (default) Encoder detects the type automatically.
114 * OPUS_SIGNAL_VOICE - Bias thresholds towards choosing LPC or Hybrid modes.
115 * OPUS_SIGNAL_MUSIC - Bias thresholds towards choosing MDCT modes.
116 */
117#define CONV_OPUS_SIGNAL OPUS_SIGNAL_VOICE
118
119/**
120 * Coding mode.
121 *
122 * Possible values:
123 * OPUS_APPLICATION_VOIP - gives best quality at a given bitrate for voice
124 * signals. It enhances the input signal by high-pass filtering and
125 * emphasizing formants and harmonics. Optionally it includes in-band forward
126 * error correction to protect against packet loss. Use this mode for typical
127 * VoIP applications. Because of the enhancement, even at high bitrates
128 * the output may sound different from the input.
129 * OPUS_APPLICATION_AUDIO - gives best quality at a given bitrate for most
130 * non-voice signals like music. Use this mode for music and mixed
131 * (music/voice) content, broadcast, and applications requiring less than
132 * 15 ms of coding delay.
133 * OPUS_APPLICATION_RESTRICTED_LOWDELAY - configures low-delay mode that
134 * disables the speech-optimized mode in exchange for slightly reduced delay.
135 * This mode can only be set on an newly initialized or freshly reset encoder
136 * because it changes the codec delay.
137 */
138#define CONV_OPUS_APP_TYPE OPUS_APPLICATION_VOIP
139
140/**
141 * Specification for recording. May change in the future to spec negotiation.
142 */
143static pa_sample_spec sample_spec = {
144 .format = PA_SAMPLE_FLOAT32LE,
145 .rate = SAMPLING_RATE,
146 .channels = CHANNELS
147};
148
149GNUNET_NETWORK_STRUCT_BEGIN
150
151/* OggOpus spec says the numbers must be in little-endian order */
152struct OpusHeadPacket
153{
154 uint8_t magic[8];
155 uint8_t version;
156 uint8_t channels;
157 uint16_t preskip GNUNET_PACKED;
158 uint32_t sampling_rate GNUNET_PACKED;
159 uint16_t gain GNUNET_PACKED;
160 uint8_t channel_mapping;
161};
162
163struct OpusCommentsPacket
164{
165 uint8_t magic[8];
166 uint32_t vendor_length;
167 /* followed by:
168 char vendor[vendor_length];
169 uint32_t string_count;
170 followed by @a string_count pairs of:
171 uint32_t string_length;
172 char string[string_length];
173 */
174};
175
176GNUNET_NETWORK_STRUCT_END
177
178/**
179 * Pulseaudio mainloop api
180 */
181static pa_mainloop_api *mainloop_api;
182
183/**
184 * Pulseaudio mainloop
185 */
186static pa_mainloop *m;
187
188/**
189 * Pulseaudio context
190 */
191static pa_context *context;
192
193/**
194 * Pulseaudio recording stream
195 */
196static pa_stream *stream_in;
197
198/**
199 * Pulseaudio io events
200 */
201static pa_io_event *stdio_event;
202
203/**
204 * OPUS encoder
205 */
206static OpusEncoder *enc;
207
208/**
209 * Buffer for encoded data
210 */
211static unsigned char *opus_data;
212
213/**
214 * PCM data buffer for one OPUS frame
215 */
216static float *pcm_buffer;
217
218/**
219 * Length of the pcm data needed for one OPUS frame
220 */
221static int pcm_length;
222
223/**
224 * Audio buffer
225 */
226static char *transmit_buffer;
227
228/**
229 * Length of audio buffer
230 */
231static size_t transmit_buffer_length;
232
233/**
234 * Read index for transmit buffer
235 */
236static size_t transmit_buffer_index;
237
238/**
239 * Audio message skeleton
240 */
241static struct AudioMessage *audio_message;
242
243/**
244 * Ogg muxer state
245 */
246static ogg_stream_state os;
247
248/**
249 * Ogg packet id
250 */
251static int32_t packet_id;
252
253/**
254 * Ogg granule for current packet
255 */
256static int64_t enc_granulepos;
257
258#ifdef DEBUG_RECORD_PURE_OGG
259/**
260 * 1 to not to write GNUnet message headers,
261 * producing pure playable ogg output
262 */
263static int dump_pure_ogg;
264#endif
265
266/**
267 * Pulseaudio shutdown task
268 */
269static void
270quit (int ret)
271{
272 mainloop_api->quit (mainloop_api,
273 ret);
274 exit (ret);
275}
276
277
278static void
279write_data (const char *ptr,
280 size_t msg_size)
281{
282 ssize_t ret;
283 size_t off;
284
285 off = 0;
286 while (off < msg_size)
287 {
288 ret = write (STDOUT_FILENO,
289 &ptr[off],
290 msg_size - off);
291 if (0 >= ret)
292 {
293 if (-1 == ret)
294 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
295 "write");
296 quit (2);
297 }
298 off += ret;
299 }
300}
301
302
303static void
304write_page (ogg_page *og)
305{
306 static unsigned long long toff;
307 size_t msg_size;
308
309 msg_size = sizeof(struct AudioMessage) + og->header_len + og->body_len;
310 audio_message->header.size = htons ((uint16_t) msg_size);
311 GNUNET_memcpy (&audio_message[1], og->header, og->header_len);
312 GNUNET_memcpy (((char *) &audio_message[1]) + og->header_len, og->body,
313 og->body_len);
314
315 toff += msg_size;
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Sending %u bytes of audio data (total: %llu)\n",
318 (unsigned int) msg_size,
319 toff);
320#ifdef DEBUG_RECORD_PURE_OGG
321 if (dump_pure_ogg)
322 write_data ((const char *) &audio_message[1],
323 og->header_len + og->body_len);
324 else
325#endif
326 write_data ((const char *) audio_message,
327 msg_size);
328}
329
330
331/**
332 * Creates OPUS packets from PCM data
333 */
334static void
335packetizer ()
336{
337 char *nbuf;
338 size_t new_size;
339 int32_t len;
340 ogg_packet op;
341 ogg_page og;
342
343 while (transmit_buffer_length >= transmit_buffer_index + pcm_length)
344 {
345 GNUNET_memcpy (pcm_buffer,
346 &transmit_buffer[transmit_buffer_index],
347 pcm_length);
348 transmit_buffer_index += pcm_length;
349 len =
350 opus_encode_float (enc, pcm_buffer, FRAME_SIZE, opus_data,
351 MAX_PAYLOAD_BYTES);
352
353 if (len < 0)
354 {
355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356 _ ("opus_encode_float() failed: %s. Aborting\n"),
357 opus_strerror (len));
358 quit (5);
359 }
360 if (((uint32_t) len) > UINT16_MAX - sizeof(struct AudioMessage))
361 {
362 GNUNET_break (0);
363 continue;
364 }
365
366 /* As per OggOpus spec, granule is calculated as if the audio
367 had 48kHz sampling rate. */
368 enc_granulepos += FRAME_SIZE * 48000 / SAMPLING_RATE;
369
370 op.packet = (unsigned char *) opus_data;
371 op.bytes = len;
372 op.b_o_s = 0;
373 op.e_o_s = 0;
374 op.granulepos = enc_granulepos;
375 op.packetno = packet_id++;
376 ogg_stream_packetin (&os, &op);
377
378 while (ogg_stream_flush_fill (&os, &og, PAGE_WATERLINE))
379 {
380 if (((unsigned long long) og.header_len)
381 + ((unsigned long long) og.body_len) >
382 UINT16_MAX - sizeof(struct AudioMessage))
383 {
384 GNUNET_assert (0);
385 continue;
386 }
387 write_page (&og);
388 }
389 }
390
391 new_size = transmit_buffer_length - transmit_buffer_index;
392 if (0 != new_size)
393 {
394 nbuf = pa_xmalloc (new_size);
395 memmove (nbuf,
396 &transmit_buffer[transmit_buffer_index],
397 new_size);
398 pa_xfree (transmit_buffer);
399 transmit_buffer = nbuf;
400 }
401 else
402 {
403 pa_xfree (transmit_buffer);
404 transmit_buffer = NULL;
405 }
406 transmit_buffer_index = 0;
407 transmit_buffer_length = new_size;
408}
409
410
411/**
412 * Pulseaudio callback when new data is available.
413 */
414static void
415stream_read_callback (pa_stream *s,
416 size_t length,
417 void *userdata)
418{
419 const void *data;
420
421 (void) userdata;
422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423 "Got %u/%d bytes of PCM data\n",
424 (unsigned int) length,
425 pcm_length);
426
427 GNUNET_assert (NULL != s);
428 GNUNET_assert (length > 0);
429 if (stdio_event)
430 mainloop_api->io_enable (stdio_event, PA_IO_EVENT_OUTPUT);
431
432 if (pa_stream_peek (s, (const void **) &data, &length) < 0)
433 {
434 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
435 _ ("pa_stream_peek() failed: %s\n"),
436 pa_strerror (pa_context_errno (context)));
437 quit (1);
438 return;
439 }
440 GNUNET_assert (NULL != data);
441 GNUNET_assert (length > 0);
442 if (NULL != transmit_buffer)
443 {
444 transmit_buffer = pa_xrealloc (transmit_buffer,
445 transmit_buffer_length + length);
446 GNUNET_memcpy (&transmit_buffer[transmit_buffer_length],
447 data,
448 length);
449 transmit_buffer_length += length;
450 }
451 else
452 {
453 transmit_buffer = pa_xmalloc (length);
454 GNUNET_memcpy (transmit_buffer, data, length);
455 transmit_buffer_length = length;
456 transmit_buffer_index = 0;
457 }
458 pa_stream_drop (s);
459 packetizer ();
460}
461
462
463/**
464 * Exit callback for SIGTERM and SIGINT
465 */
466static void
467exit_signal_callback (pa_mainloop_api *m,
468 pa_signal_event *e,
469 int sig,
470 void *userdata)
471{
472 (void) m;
473 (void) e;
474 (void) sig;
475 (void) userdata;
476 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
477 _ ("Got signal, exiting.\n"));
478 quit (1);
479}
480
481
482/**
483 * Pulseaudio stream state callback
484 */
485static void
486stream_state_callback (pa_stream *s,
487 void *userdata)
488{
489 (void) userdata;
490 GNUNET_assert (NULL != s);
491 switch (pa_stream_get_state (s))
492 {
493 case PA_STREAM_CREATING:
494 case PA_STREAM_TERMINATED:
495 break;
496
497 case PA_STREAM_READY:
498 {
499 const pa_buffer_attr *a;
500 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
501 char sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
502
503 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
504 _ ("Stream successfully created.\n"));
505
506 if (! (a = pa_stream_get_buffer_attr (s)))
507 {
508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
509 _ ("pa_stream_get_buffer_attr() failed: %s\n"),
510 pa_strerror (pa_context_errno
511 (pa_stream_get_context (s))));
512 }
513 else
514 {
515 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
516 _ ("Buffer metrics: maxlength=%u, fragsize=%u\n"),
517 a->maxlength, a->fragsize);
518 }
519 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520 _ ("Using sample spec '%s', channel map '%s'.\n"),
521 pa_sample_spec_snprint (sst, sizeof(sst),
522 pa_stream_get_sample_spec (s)),
523 pa_channel_map_snprint (cmt, sizeof(cmt),
524 pa_stream_get_channel_map (s)));
525
526 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
527 _ ("Connected to device %s (%u, %ssuspended).\n"),
528 pa_stream_get_device_name (s),
529 pa_stream_get_device_index (s),
530 pa_stream_is_suspended (s) ? "" : "not ");
531 }
532 break;
533
534 case PA_STREAM_FAILED:
535 default:
536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 _ ("Stream error: %s\n"),
538 pa_strerror (pa_context_errno (pa_stream_get_context (s))));
539 quit (1);
540 }
541}
542
543
544/**
545 * Pulseaudio context state callback
546 */
547static void
548context_state_callback (pa_context *c,
549 void *userdata)
550{
551 (void) userdata;
552 GNUNET_assert (c);
553
554 switch (pa_context_get_state (c))
555 {
556 case PA_CONTEXT_CONNECTING:
557 case PA_CONTEXT_AUTHORIZING:
558 case PA_CONTEXT_SETTING_NAME:
559 break;
560
561 case PA_CONTEXT_READY:
562 {
563 int r;
564 pa_buffer_attr na;
565
566 GNUNET_assert (! stream_in);
567 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
568 _ ("Connection established.\n"));
569 if (! (stream_in =
570 pa_stream_new (c, "GNUNET_VoIP recorder", &sample_spec, NULL)))
571 {
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _ ("pa_stream_new() failed: %s\n"),
574 pa_strerror (pa_context_errno (c)));
575 goto fail;
576 }
577 pa_stream_set_state_callback (stream_in, &stream_state_callback, NULL);
578 pa_stream_set_read_callback (stream_in, &stream_read_callback, NULL);
579 memset (&na, 0, sizeof(na));
580 na.maxlength = UINT32_MAX;
581 na.fragsize = pcm_length;
582 if ((r = pa_stream_connect_record (stream_in, NULL, &na,
583 PA_STREAM_ADJUST_LATENCY)) < 0)
584 {
585 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586 _ ("pa_stream_connect_record() failed: %s\n"),
587 pa_strerror (pa_context_errno (c)));
588 goto fail;
589 }
590
591 break;
592 }
593
594 case PA_CONTEXT_TERMINATED:
595 quit (0);
596 break;
597
598 case PA_CONTEXT_FAILED:
599 default:
600 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
601 _ ("Connection failure: %s\n"),
602 pa_strerror (pa_context_errno (c)));
603 goto fail;
604 }
605 return;
606
607fail:
608 quit (1);
609}
610
611
612/**
613 * Pulsaudio init
614 */
615static void
616pa_init ()
617{
618 int r;
619 int i;
620
621 if (! pa_sample_spec_valid (&sample_spec))
622 {
623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
624 _ ("Wrong Spec\n"));
625 }
626 /* set up main record loop */
627 if (! (m = pa_mainloop_new ()))
628 {
629 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
630 _ ("pa_mainloop_new() failed.\n"));
631 }
632 mainloop_api = pa_mainloop_get_api (m);
633
634 /* listen to signals */
635 r = pa_signal_init (mainloop_api);
636 GNUNET_assert (r == 0);
637 pa_signal_new (SIGINT, &exit_signal_callback, NULL);
638 pa_signal_new (SIGTERM, &exit_signal_callback, NULL);
639
640 /* connect to the main pulseaudio context */
641
642 if (! (context = pa_context_new (mainloop_api, "GNUNET VoIP")))
643 {
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 _ ("pa_context_new() failed.\n"));
646 }
647 pa_context_set_state_callback (context, &context_state_callback, NULL);
648 if (pa_context_connect (context, NULL, 0, NULL) < 0)
649 {
650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651 _ ("pa_context_connect() failed: %s\n"),
652 pa_strerror (pa_context_errno (context)));
653 }
654 if (pa_mainloop_run (m, &i) < 0)
655 {
656 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
657 _ ("pa_mainloop_run() failed.\n"));
658 }
659}
660
661
662/**
663 * OPUS init
664 */
665static void
666opus_init ()
667{
668 int err;
669
670 pcm_length = FRAME_SIZE * CHANNELS * sizeof(float);
671 pcm_buffer = pa_xmalloc (pcm_length);
672 opus_data = GNUNET_malloc (MAX_PAYLOAD_BYTES);
673 enc = opus_encoder_create (SAMPLING_RATE,
674 CHANNELS,
675 CONV_OPUS_APP_TYPE,
676 &err);
677 opus_encoder_ctl (enc,
678 OPUS_SET_PACKET_LOSS_PERC (
679 CONV_OPUS_PACKET_LOSS_PERCENTAGE));
680 opus_encoder_ctl (enc,
681 OPUS_SET_COMPLEXITY (CONV_OPUS_ENCODING_COMPLEXITY));
682 opus_encoder_ctl (enc,
683 OPUS_SET_INBAND_FEC (CONV_OPUS_INBAND_FEC));
684 opus_encoder_ctl (enc,
685 OPUS_SET_SIGNAL (CONV_OPUS_SIGNAL));
686}
687
688
689static void
690ogg_init ()
691{
692 int serialno;
693 struct OpusHeadPacket headpacket;
694 struct OpusCommentsPacket *commentspacket;
695 size_t commentspacket_len;
696
697 serialno = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
698 0x7FFFFFFF);
699 /*Initialize Ogg stream struct*/
700 if (-1 == ogg_stream_init (&os, serialno))
701 {
702 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
703 _ ("ogg_stream_init() failed.\n"));
704 exit (3);
705 }
706
707 packet_id = 0;
708
709 /*Write header*/
710 {
711 ogg_packet op;
712 ogg_page og;
713 const char *opusver;
714 int vendor_length;
715
716 GNUNET_memcpy (headpacket.magic, "OpusHead", 8);
717 headpacket.version = 1;
718 headpacket.channels = CHANNELS;
719 headpacket.preskip = GNUNET_htole16 (0);
720 headpacket.sampling_rate = GNUNET_htole32 (SAMPLING_RATE);
721 headpacket.gain = GNUNET_htole16 (0);
722 headpacket.channel_mapping = 0; /* Mono or stereo */
723
724 op.packet = (unsigned char *) &headpacket;
725 op.bytes = sizeof(headpacket);
726 op.b_o_s = 1;
727 op.e_o_s = 0;
728 op.granulepos = 0;
729 op.packetno = packet_id++;
730 ogg_stream_packetin (&os, &op);
731
732 /* Head packet must be alone on its page */
733 while (ogg_stream_flush (&os, &og))
734 {
735 write_page (&og);
736 }
737
738 commentspacket_len = sizeof(*commentspacket);
739 opusver = opus_get_version_string ();
740 vendor_length = strlen (opusver);
741 commentspacket_len += vendor_length;
742 commentspacket_len += sizeof(uint32_t);
743
744 commentspacket = (struct OpusCommentsPacket *) malloc (commentspacket_len);
745 if (NULL == commentspacket)
746 {
747 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
748 _ ("Failed to allocate %u bytes for second packet\n"),
749 (unsigned int) commentspacket_len);
750 exit (5);
751 }
752
753 GNUNET_memcpy (commentspacket->magic, "OpusTags", 8);
754 commentspacket->vendor_length = GNUNET_htole32 (vendor_length);
755 GNUNET_memcpy (&commentspacket[1], opusver, vendor_length);
756 *(uint32_t *) &((char *) &commentspacket[1])[vendor_length] = \
757 GNUNET_htole32 (0); /* no tags */
758
759 op.packet = (unsigned char *) commentspacket;
760 op.bytes = commentspacket_len;
761 op.b_o_s = 0;
762 op.e_o_s = 0;
763 op.granulepos = 0;
764 op.packetno = packet_id++;
765 ogg_stream_packetin (&os, &op);
766
767 /* Comment packets must not be mixed with audio packets on their pages */
768 while (ogg_stream_flush (&os, &og))
769 {
770 write_page (&og);
771 }
772
773 free (commentspacket);
774 }
775}
776
777
778/**
779 * The main function for the record helper.
780 *
781 * @param argc number of arguments from the command line
782 * @param argv command line arguments
783 * @return 0 ok, 1 on error
784 */
785int
786main (int argc,
787 char *argv[])
788{
789 (void) argc;
790 (void) argv;
791 GNUNET_assert (GNUNET_OK ==
792 GNUNET_log_setup ("gnunet-helper-audio-record",
793 "WARNING",
794 NULL));
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 "Audio source starts\n");
797 audio_message = GNUNET_malloc (UINT16_MAX);
798 audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
799
800#ifdef DEBUG_RECORD_PURE_OGG
801 dump_pure_ogg = getenv ("GNUNET_RECORD_PURE_OGG") ? 1 : 0;
802#endif
803 ogg_init ();
804 opus_init ();
805 pa_init ();
806 return 0;
807}
diff --git a/src/contrib/service/conversation/gnunet-service-conversation.c b/src/contrib/service/conversation/gnunet-service-conversation.c
new file mode 100644
index 000000000..06721e535
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet-service-conversation.c
@@ -0,0 +1,1461 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016, 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet-service-conversation.c
22 * @brief conversation service implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_applications.h"
31#include "gnunet_constants.h"
32#include "gnunet_signatures.h"
33#include "gnunet_cadet_service.h"
34#include "gnunet_conversation_service.h"
35#include "conversation.h"
36
37
38/**
39 * How long is our signature on a call valid? Needs to be long enough for time zone
40 * differences and network latency to not matter. No strong need for it to be short,
41 * but we simply like all signatures to eventually expire.
42 */
43#define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
44
45
46/**
47 * A line connects a local client with a cadet channel (or, if it is an
48 * open line, is waiting for a cadet channel).
49 */
50struct Line;
51
52/**
53 * The possible connection status
54 */
55enum ChannelStatus
56{
57 /**
58 * We just got the connection, but no introduction yet.
59 */
60 CS_CALLEE_INIT,
61
62 /**
63 * Our phone is ringing, waiting for the client to pick up.
64 */
65 CS_CALLEE_RINGING,
66
67 /**
68 * We are talking!
69 */
70 CS_CALLEE_CONNECTED,
71
72 /**
73 * We're in shutdown, sending hangup messages before cleaning up.
74 */
75 CS_CALLEE_SHUTDOWN,
76
77 /**
78 * We are waiting for the phone to be picked up.
79 */
80 CS_CALLER_CALLING,
81
82 /**
83 * We are talking!
84 */
85 CS_CALLER_CONNECTED,
86
87 /**
88 * We're in shutdown, sending hangup messages before cleaning up.
89 */
90 CS_CALLER_SHUTDOWN
91};
92
93
94/**
95 * A `struct Channel` represents a cadet channel, which is a P2P
96 * connection to another conversation service. Multiple channels can
97 * be attached the the same `struct Line`, which represents a local
98 * client. We keep them in a linked list.
99 */
100struct Channel
101{
102 /**
103 * This is a DLL.
104 */
105 struct Channel *next;
106
107 /**
108 * This is a DLL.
109 */
110 struct Channel *prev;
111
112 /**
113 * Line associated with the channel.
114 */
115 struct Line *line;
116
117 /**
118 * Handle for the channel.
119 */
120 struct GNUNET_CADET_Channel *channel;
121
122 /**
123 * Message queue for control messages
124 */
125 struct GNUNET_MQ_Handle *mq;
126
127 /**
128 * Temporary buffer for audio data in the @e mq.
129 */
130 struct GNUNET_MQ_Envelope *env;
131
132 /**
133 * Channel identifier we use for this call with the client.
134 */
135 uint32_t cid;
136
137 /**
138 * Current status of this line.
139 */
140 enum ChannelStatus status;
141
142 /**
143 * #GNUNET_YES if the channel was suspended by the other peer.
144 */
145 int8_t suspended_remote;
146
147 /**
148 * #GNUNET_YES if the channel was suspended by the local client.
149 */
150 int8_t suspended_local;
151};
152
153
154/**
155 * A `struct Line` connects a local client with cadet channels.
156 */
157struct Line
158{
159 /**
160 * This is a DLL.
161 */
162 struct Channel *channel_head;
163
164 /**
165 * This is a DLL.
166 */
167 struct Channel *channel_tail;
168
169 /**
170 * Handle to the line client.
171 */
172 struct GNUNET_SERVICE_Client *client;
173
174 /**
175 * Message queue for @e client.
176 */
177 struct GNUNET_MQ_Handle *mq;
178
179 /**
180 * Our open port.
181 */
182 struct GNUNET_CADET_Port *port;
183
184 /**
185 * Port number we are listening on (to verify signatures).
186 * Only valid if @e port is non-NULL.
187 */
188 struct GNUNET_HashCode line_port;
189
190 /**
191 * Generator for channel IDs.
192 */
193 uint32_t cid_gen;
194};
195
196
197/**
198 * Our configuration.
199 */
200static const struct GNUNET_CONFIGURATION_Handle *cfg;
201
202/**
203 * Handle for cadet
204 */
205static struct GNUNET_CADET_Handle *cadet;
206
207/**
208 * Identity of this peer.
209 */
210static struct GNUNET_PeerIdentity my_identity;
211
212
213/**
214 * Given a @a cid, find the corresponding channel given
215 * a @a line.
216 *
217 * @param line a line to search
218 * @param cid what to search for
219 * @return NULL for not found
220 */
221static struct Channel *
222find_channel_by_line (struct Line *line, uint32_t cid)
223{
224 for (struct Channel *ch = line->channel_head; NULL != ch; ch = ch->next)
225 if (cid == ch->cid)
226 return ch;
227 return NULL;
228}
229
230
231/**
232 * Function to handle a pickup request message from the client
233 *
234 * @param cls the `struct Line` of the client from which the message is
235 * @param msg the message from the client
236 */
237static void
238handle_client_pickup_message (void *cls,
239 const struct ClientPhonePickupMessage *msg)
240{
241 struct Line *line = cls;
242 struct CadetPhonePickupMessage *mppm;
243 struct GNUNET_MQ_Envelope *env;
244 struct Channel *ch;
245
246 if (NULL == line->port)
247 {
248 /* we never opened the port, bad client! */
249 GNUNET_break_op (0);
250 GNUNET_SERVICE_client_drop (line->client);
251 return;
252 }
253 for (ch = line->channel_head; NULL != ch; ch = ch->next)
254 if (msg->cid == ch->cid)
255 break;
256 if (NULL == ch)
257 {
258 /* could have been destroyed asynchronously, ignore message */
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
260 GNUNET_SERVICE_client_continue (line->client);
261 return;
262 }
263 switch (ch->status)
264 {
265 case CS_CALLEE_INIT:
266 GNUNET_break (0);
267 GNUNET_SERVICE_client_drop (line->client);
268 return;
269
270 case CS_CALLEE_RINGING:
271 ch->status = CS_CALLEE_CONNECTED;
272 break;
273
274 case CS_CALLEE_CONNECTED:
275 GNUNET_break (0);
276 GNUNET_SERVICE_client_drop (line->client);
277 return;
278
279 case CS_CALLEE_SHUTDOWN:
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
282 break;
283
284 case CS_CALLER_CALLING:
285 case CS_CALLER_CONNECTED:
286 case CS_CALLER_SHUTDOWN:
287 GNUNET_break (0);
288 GNUNET_SERVICE_client_drop (line->client);
289 return;
290 }
291 GNUNET_break (CS_CALLEE_CONNECTED == ch->status);
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending PICK_UP message to cadet\n");
293 env =
294 GNUNET_MQ_msg (mppm, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP);
295 GNUNET_MQ_send (ch->mq, env);
296 GNUNET_SERVICE_client_continue (line->client);
297}
298
299
300/**
301 * Channel went down, notify client and free data
302 * structure.
303 *
304 * @param ch channel that went down
305 */
306static void
307clean_up_channel (struct Channel *ch)
308{
309 struct Line *line = ch->line;
310 struct GNUNET_MQ_Envelope *env;
311 struct ClientPhoneHangupMessage *hup;
312
313 switch (ch->status)
314 {
315 case CS_CALLEE_INIT:
316 case CS_CALLEE_SHUTDOWN:
317 case CS_CALLER_SHUTDOWN:
318 break;
319
320 case CS_CALLEE_RINGING:
321 case CS_CALLEE_CONNECTED:
322 case CS_CALLER_CALLING:
323 case CS_CALLER_CONNECTED:
324 if (NULL != line)
325 {
326 env =
327 GNUNET_MQ_msg (hup, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
328 hup->cid = ch->cid;
329 GNUNET_MQ_send (line->mq, env);
330 }
331 break;
332 }
333 if (NULL != line)
334 GNUNET_CONTAINER_DLL_remove (line->channel_head, line->channel_tail, ch);
335 GNUNET_free (ch);
336}
337
338
339/**
340 * Destroy a channel.
341 *
342 * @param ch channel to destroy.
343 */
344static void
345destroy_line_cadet_channels (struct Channel *ch)
346{
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying cadet channels\n");
348 if (NULL != ch->channel)
349 {
350 GNUNET_CADET_channel_destroy (ch->channel);
351 ch->channel = NULL;
352 }
353 clean_up_channel (ch);
354}
355
356
357/**
358 * We are done signalling shutdown to the other peer. Close down
359 * the channel.
360 *
361 * @param cls the `struct Channel` to reset/terminate
362 */
363static void
364mq_done_finish_caller_shutdown (void *cls)
365{
366 struct Channel *ch = cls;
367
368 switch (ch->status)
369 {
370 case CS_CALLEE_INIT:
371 GNUNET_break (0);
372 break;
373
374 case CS_CALLEE_RINGING:
375 GNUNET_break (0);
376 break;
377
378 case CS_CALLEE_CONNECTED:
379 GNUNET_break (0);
380 break;
381
382 case CS_CALLEE_SHUTDOWN:
383 destroy_line_cadet_channels (ch);
384 break;
385
386 case CS_CALLER_CALLING:
387 GNUNET_break (0);
388 break;
389
390 case CS_CALLER_CONNECTED:
391 GNUNET_break (0);
392 break;
393
394 case CS_CALLER_SHUTDOWN:
395 destroy_line_cadet_channels (ch);
396 break;
397 }
398}
399
400
401/**
402 * Function to handle a hangup request message from the client
403 *
404 * @param cls the `struct Line` the hangup is for
405 * @param msg the message from the client
406 */
407static void
408handle_client_hangup_message (void *cls,
409 const struct ClientPhoneHangupMessage *msg)
410{
411 struct Line *line = cls;
412 struct GNUNET_MQ_Envelope *e;
413 struct CadetPhoneHangupMessage *mhum;
414 struct Channel *ch;
415
416 for (ch = line->channel_head; NULL != ch; ch = ch->next)
417 if (msg->cid == ch->cid)
418 break;
419 if (NULL == ch)
420 {
421 /* could have been destroyed asynchronously, ignore message */
422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
423 GNUNET_SERVICE_client_continue (line->client);
424 return;
425 }
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427 "Received HANGUP for channel %u which is in state %d\n",
428 msg->cid,
429 ch->status);
430 switch (ch->status)
431 {
432 case CS_CALLEE_INIT:
433 GNUNET_break (0);
434 GNUNET_SERVICE_client_drop (line->client);
435 return;
436
437 case CS_CALLEE_RINGING:
438 ch->status = CS_CALLEE_SHUTDOWN;
439 break;
440
441 case CS_CALLEE_CONNECTED:
442 ch->status = CS_CALLEE_SHUTDOWN;
443 break;
444
445 case CS_CALLEE_SHUTDOWN:
446 /* maybe the other peer closed asynchronously... */
447 GNUNET_SERVICE_client_continue (line->client);
448 return;
449
450 case CS_CALLER_CALLING:
451 ch->status = CS_CALLER_SHUTDOWN;
452 break;
453
454 case CS_CALLER_CONNECTED:
455 ch->status = CS_CALLER_SHUTDOWN;
456 break;
457
458 case CS_CALLER_SHUTDOWN:
459 /* maybe the other peer closed asynchronously... */
460 GNUNET_SERVICE_client_continue (line->client);
461 return;
462 }
463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending HANG_UP message via cadet\n");
464 e =
465 GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP);
466 GNUNET_MQ_notify_sent (e, &mq_done_finish_caller_shutdown, ch);
467 GNUNET_MQ_send (ch->mq, e);
468 GNUNET_SERVICE_client_continue (line->client);
469}
470
471
472/**
473 * Function to handle a suspend request message from the client
474 *
475 * @param cls the `struct Line` the message is about
476 * @param msg the message from the client
477 */
478static void
479handle_client_suspend_message (void *cls,
480 const struct ClientPhoneSuspendMessage *msg)
481{
482 struct Line *line = cls;
483 struct GNUNET_MQ_Envelope *e;
484 struct CadetPhoneSuspendMessage *mhum;
485 struct Channel *ch;
486
487 for (ch = line->channel_head; NULL != ch; ch = ch->next)
488 if (msg->cid == ch->cid)
489 break;
490 if (NULL == ch)
491 {
492 /* could have been destroyed asynchronously, ignore message */
493 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
494 GNUNET_SERVICE_client_continue (line->client);
495 return;
496 }
497 if (GNUNET_YES == ch->suspended_local)
498 {
499 GNUNET_break (0);
500 GNUNET_SERVICE_client_drop (line->client);
501 return;
502 }
503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504 "Received SUSPEND for channel %u which is in state %d\n",
505 msg->cid,
506 ch->status);
507 switch (ch->status)
508 {
509 case CS_CALLEE_INIT:
510 GNUNET_break (0);
511 GNUNET_SERVICE_client_drop (line->client);
512 return;
513
514 case CS_CALLEE_RINGING:
515 GNUNET_break (0);
516 GNUNET_SERVICE_client_drop (line->client);
517 return;
518
519 case CS_CALLEE_CONNECTED:
520 ch->suspended_local = GNUNET_YES;
521 break;
522
523 case CS_CALLEE_SHUTDOWN:
524 /* maybe the other peer closed asynchronously... */
525 GNUNET_SERVICE_client_continue (line->client);
526 return;
527
528 case CS_CALLER_CALLING:
529 GNUNET_break (0);
530 GNUNET_SERVICE_client_drop (line->client);
531 return;
532
533 case CS_CALLER_CONNECTED:
534 ch->suspended_local = GNUNET_YES;
535 break;
536
537 case CS_CALLER_SHUTDOWN:
538 /* maybe the other peer closed asynchronously... */
539 GNUNET_SERVICE_client_continue (line->client);
540 return;
541 }
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SUSPEND message via cadet\n");
543 e =
544 GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND);
545 GNUNET_MQ_send (ch->mq, e);
546 GNUNET_SERVICE_client_continue (line->client);
547}
548
549
550/**
551 * Function to handle a resume request message from the client
552 *
553 * @param cls the `struct Line` the message is about
554 * @param msg the message from the client
555 */
556static void
557handle_client_resume_message (void *cls,
558 const struct ClientPhoneResumeMessage *msg)
559{
560 struct Line *line = cls;
561 struct GNUNET_MQ_Envelope *e;
562 struct CadetPhoneResumeMessage *mhum;
563 struct Channel *ch;
564
565 for (ch = line->channel_head; NULL != ch; ch = ch->next)
566 if (msg->cid == ch->cid)
567 break;
568 if (NULL == ch)
569 {
570 /* could have been destroyed asynchronously, ignore message */
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
572 GNUNET_SERVICE_client_continue (line->client);
573 return;
574 }
575 if (GNUNET_YES != ch->suspended_local)
576 {
577 GNUNET_break (0);
578 GNUNET_SERVICE_client_drop (line->client);
579 return;
580 }
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582 "Received RESUME for channel %u which is in state %d\n",
583 msg->cid,
584 ch->status);
585 switch (ch->status)
586 {
587 case CS_CALLEE_INIT:
588 GNUNET_break (0);
589 GNUNET_SERVICE_client_drop (line->client);
590 return;
591
592 case CS_CALLEE_RINGING:
593 GNUNET_break (0);
594 GNUNET_SERVICE_client_drop (line->client);
595 return;
596
597 case CS_CALLEE_CONNECTED:
598 ch->suspended_local = GNUNET_NO;
599 break;
600
601 case CS_CALLEE_SHUTDOWN:
602 /* maybe the other peer closed asynchronously... */
603 GNUNET_SERVICE_client_continue (line->client);
604 return;
605
606 case CS_CALLER_CALLING:
607 GNUNET_break (0);
608 GNUNET_SERVICE_client_drop (line->client);
609 return;
610
611 case CS_CALLER_CONNECTED:
612 ch->suspended_local = GNUNET_NO;
613 break;
614
615 case CS_CALLER_SHUTDOWN:
616 /* maybe the other peer closed asynchronously... */
617 GNUNET_SERVICE_client_drop (line->client);
618 return;
619 }
620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RESUME message via cadet\n");
621 e = GNUNET_MQ_msg (mhum, GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME);
622 GNUNET_MQ_send (ch->mq, e);
623 GNUNET_SERVICE_client_continue (line->client);
624}
625
626
627/**
628 * Transmission of audio data via cadet channel finished.
629 *
630 * @param cls the `struct Channel` we are transmitting for
631 */
632static void
633channel_audio_sent_notify (void *cls)
634{
635 struct Channel *ch = cls;
636
637 ch->env = NULL;
638}
639
640
641/**
642 * Function to check audio data from the client
643 *
644 * @param cls the `struct Line` the message is about
645 * @param msg the message from the client
646 * @return #GNUNET_OK (any data is ok)
647 */
648static int
649check_client_audio_message (void *cls, const struct ClientAudioMessage *msg)
650{
651 (void) cls;
652 (void) msg;
653 return GNUNET_OK;
654}
655
656
657/**
658 * Function to handle audio data from the client
659 *
660 * @param cls the `struct Line` the message is about
661 * @param msg the message from the client
662 */
663static void
664handle_client_audio_message (void *cls, const struct ClientAudioMessage *msg)
665{
666 struct Line *line = cls;
667 struct CadetAudioMessage *mam;
668 struct Channel *ch;
669 size_t size;
670
671 size = ntohs (msg->header.size) - sizeof(struct ClientAudioMessage);
672 ch = find_channel_by_line (line, msg->cid);
673 if (NULL == ch)
674 {
675 /* could have been destroyed asynchronously, ignore message */
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Channel %u not found\n", msg->cid);
677 GNUNET_SERVICE_client_continue (line->client);
678 return;
679 }
680
681 switch (ch->status)
682 {
683 case CS_CALLEE_INIT:
684 case CS_CALLEE_RINGING:
685 case CS_CALLER_CALLING:
686 GNUNET_break (0);
687 GNUNET_SERVICE_client_drop (line->client);
688 return;
689
690 case CS_CALLEE_CONNECTED:
691 case CS_CALLER_CONNECTED:
692 /* common case, handled below */
693 break;
694
695 case CS_CALLEE_SHUTDOWN:
696 case CS_CALLER_SHUTDOWN:
697 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
698 "Cadet audio channel in shutdown; audio data dropped\n");
699 GNUNET_SERVICE_client_continue (line->client);
700 return;
701 }
702 if (GNUNET_YES == ch->suspended_local)
703 {
704 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
705 "This channel is suspended locally\n");
706 GNUNET_SERVICE_client_drop (line->client);
707 return;
708 }
709 if (NULL != ch->env)
710 {
711 /* NOTE: we may want to not do this and instead combine the data */
712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713 "Bandwidth insufficient; dropping previous audio data segment\n");
714 GNUNET_MQ_send_cancel (ch->env);
715 ch->env = NULL;
716 }
717
718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719 "Received %u bytes of AUDIO data from client CID %u\n",
720 (unsigned int) size,
721 msg->cid);
722 ch->env = GNUNET_MQ_msg_extra (mam,
723 size,
724 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO);
725 GNUNET_memcpy (&mam[1], &msg[1], size);
726 /* FIXME: set options for unreliable transmission */
727 GNUNET_MQ_notify_sent (ch->env, &channel_audio_sent_notify, ch);
728 GNUNET_MQ_send (ch->mq, ch->env);
729 GNUNET_SERVICE_client_continue (line->client);
730}
731
732/**
733 * Function to handle a ring message incoming over cadet
734 *
735 * @param cls closure, NULL
736 * @param msg the incoming message
737 */
738static enum GNUNET_GenericReturnValue
739check_cadet_ring_message (void *cls, const struct CadetPhoneRingMessage *msg)
740{
741 // FIXME
742 return GNUNET_OK;
743}
744
745/**
746 * Function to handle a ring message incoming over cadet
747 *
748 * @param cls closure, NULL
749 * @param msg the incoming message
750 */
751static void
752handle_cadet_ring_message (void *cls, const struct CadetPhoneRingMessage *msg)
753{
754 struct Channel *ch = cls;
755 struct Line *line = ch->line;
756 struct GNUNET_MQ_Envelope *env;
757 struct ClientPhoneRingMessage *cring;
758 struct CadetPhoneRingInfoPS rs;
759 struct GNUNET_CRYPTO_PublicKey identity;
760 struct GNUNET_CRYPTO_Signature sig;
761 size_t key_len;
762 size_t sig_len;
763 size_t read;
764
765 rs.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
766 rs.purpose.size = htonl (sizeof(struct CadetPhoneRingInfoPS));
767 rs.line_port = line->line_port;
768 rs.target_peer = my_identity;
769 rs.expiration_time = msg->expiration_time;
770 key_len = ntohl (msg->key_len);
771 sig_len = ntohl (msg->sig_len);
772
773 if ((GNUNET_SYSERR ==
774 GNUNET_CRYPTO_read_public_key_from_buffer (&msg[1],
775 key_len,
776 &identity,
777 &read)) ||
778 (read != key_len))
779 {
780 GNUNET_break_op (0);
781 destroy_line_cadet_channels (ch);
782 return;
783 }
784 GNUNET_CRYPTO_read_signature_from_buffer (&sig,
785 (char*) &msg[1] + read,
786 sig_len);
787 if (GNUNET_OK !=
788 GNUNET_CRYPTO_signature_verify (
789 GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
790 &rs,
791 &sig,
792 &identity))
793 {
794 GNUNET_break_op (0);
795 destroy_line_cadet_channels (ch);
796 return;
797 }
798 if (0 == GNUNET_TIME_absolute_get_remaining (
799 GNUNET_TIME_absolute_ntoh (msg->expiration_time))
800 .rel_value_us)
801 {
802 /* ancient call, replay? */
803 GNUNET_break_op (0);
804 /* Note that our reliance on time here is awkward; better would be
805 to use a more complex challenge-response protocol against
806 replay attacks. Left for future work ;-). */
807 destroy_line_cadet_channels (ch);
808 return;
809 }
810 if (CS_CALLEE_INIT != ch->status)
811 {
812 GNUNET_break_op (0);
813 destroy_line_cadet_channels (ch);
814 return;
815 }
816 GNUNET_CADET_receive_done (ch->channel);
817 ch->status = CS_CALLEE_RINGING;
818 env = GNUNET_MQ_msg_extra (cring,
819 key_len,
820 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
821 cring->cid = ch->cid;
822 memcpy (&cring[1], &msg[1], key_len);
823 cring->key_len = msg->key_len;
824 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825 "Sending RING message to client. CID is %u\n",
826 (unsigned int) ch->cid);
827 GNUNET_MQ_send (line->mq, env);
828}
829
830
831/**
832 * Function to handle a hangup message incoming over cadet
833 *
834 * @param cls closure, our `struct Channel *`
835 * @param message the incoming message
836 */
837static void
838handle_cadet_hangup_message (void *cls,
839 const struct CadetPhoneHangupMessage *message)
840{
841 struct Channel *ch = cls;
842 struct Line *line = ch->line;
843 struct GNUNET_MQ_Envelope *env;
844 struct ClientPhoneHangupMessage *hup;
845 enum ChannelStatus status;
846 uint32_t cid;
847
848 (void) message;
849 GNUNET_CADET_receive_done (ch->channel);
850 cid = ch->cid;
851 status = ch->status;
852 destroy_line_cadet_channels (ch);
853 switch (status)
854 {
855 case CS_CALLEE_INIT:
856 GNUNET_break_op (0);
857 return;
858
859 case CS_CALLEE_RINGING:
860 case CS_CALLEE_CONNECTED:
861 break;
862
863 case CS_CALLEE_SHUTDOWN:
864 return;
865
866 case CS_CALLER_CALLING:
867 case CS_CALLER_CONNECTED:
868 break;
869
870 case CS_CALLER_SHUTDOWN:
871 return;
872 }
873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending HANG UP message to client\n");
874 env = GNUNET_MQ_msg (hup, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
875 hup->cid = cid;
876 GNUNET_MQ_send (line->mq, env);
877}
878
879
880/**
881 * Function to handle a pickup message incoming over cadet
882 *
883 * @param cls closure, our `struct Channel *`
884 * @param message the incoming message
885 */
886static void
887handle_cadet_pickup_message (void *cls,
888 const struct CadetPhonePickupMessage *message)
889{
890 struct Channel *ch = cls;
891 struct Line *line = ch->line;
892 struct GNUNET_MQ_Envelope *env;
893 struct ClientPhonePickedupMessage *pick;
894
895 (void) message;
896 GNUNET_CADET_receive_done (ch->channel);
897 switch (ch->status)
898 {
899 case CS_CALLEE_INIT:
900 case CS_CALLEE_RINGING:
901 case CS_CALLEE_CONNECTED:
902 GNUNET_break_op (0);
903 destroy_line_cadet_channels (ch);
904 return;
905
906 case CS_CALLEE_SHUTDOWN:
907 GNUNET_break_op (0);
908 destroy_line_cadet_channels (ch);
909 return;
910
911 case CS_CALLER_CALLING:
912 ch->status = CS_CALLER_CONNECTED;
913 break;
914
915 case CS_CALLER_CONNECTED:
916 GNUNET_break_op (0);
917 return;
918
919 case CS_CALLER_SHUTDOWN:
920 GNUNET_break_op (0);
921 mq_done_finish_caller_shutdown (ch);
922 return;
923 }
924 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending PICKED UP message to client\n");
925 env =
926 GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
927 pick->cid = ch->cid;
928 GNUNET_MQ_send (line->mq, env);
929}
930
931
932/**
933 * Function to handle a suspend message incoming over cadet
934 *
935 * @param cls closure, our `struct Channel *`
936 * @param message the incoming message
937 */
938static void
939handle_cadet_suspend_message (void *cls,
940 const struct CadetPhoneSuspendMessage *message)
941{
942 struct Channel *ch = cls;
943 struct Line *line = ch->line;
944 struct GNUNET_MQ_Envelope *env;
945 struct ClientPhoneSuspendMessage *suspend;
946
947 (void) message;
948 GNUNET_CADET_receive_done (ch->channel);
949 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending channel CID: %u\n", ch->cid);
950 switch (ch->status)
951 {
952 case CS_CALLEE_INIT:
953 GNUNET_break_op (0);
954 break;
955
956 case CS_CALLEE_RINGING:
957 GNUNET_break_op (0);
958 break;
959
960 case CS_CALLEE_CONNECTED:
961 ch->suspended_remote = GNUNET_YES;
962 break;
963
964 case CS_CALLEE_SHUTDOWN:
965 return;
966
967 case CS_CALLER_CALLING:
968 GNUNET_break_op (0);
969 break;
970
971 case CS_CALLER_CONNECTED:
972 ch->suspended_remote = GNUNET_YES;
973 break;
974
975 case CS_CALLER_SHUTDOWN:
976 return;
977 }
978 env =
979 GNUNET_MQ_msg (suspend, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
980 suspend->cid = ch->cid;
981 GNUNET_MQ_send (line->mq, env);
982}
983
984
985/**
986 * Function to handle a resume message incoming over cadet
987 *
988 * @param cls closure, our `struct Channel *`
989 * @param msg the incoming message
990 */
991static void
992handle_cadet_resume_message (void *cls,
993 const struct CadetPhoneResumeMessage *msg)
994{
995 struct Channel *ch = cls;
996 struct Line *line;
997 struct GNUNET_MQ_Envelope *env;
998 struct ClientPhoneResumeMessage *resume;
999
1000 (void) msg;
1001 line = ch->line;
1002 GNUNET_CADET_receive_done (ch->channel);
1003 if (GNUNET_YES != ch->suspended_remote)
1004 {
1005 GNUNET_log (
1006 GNUNET_ERROR_TYPE_DEBUG,
1007 "RESUME message received for non-suspended channel, dropping channel.\n");
1008 destroy_line_cadet_channels (ch);
1009 return;
1010 }
1011 switch (ch->status)
1012 {
1013 case CS_CALLEE_INIT:
1014 GNUNET_break (0);
1015 break;
1016
1017 case CS_CALLEE_RINGING:
1018 GNUNET_break (0);
1019 break;
1020
1021 case CS_CALLEE_CONNECTED:
1022 ch->suspended_remote = GNUNET_NO;
1023 break;
1024
1025 case CS_CALLEE_SHUTDOWN:
1026 return;
1027
1028 case CS_CALLER_CALLING:
1029 GNUNET_break (0);
1030 break;
1031
1032 case CS_CALLER_CONNECTED:
1033 ch->suspended_remote = GNUNET_NO;
1034 break;
1035
1036 case CS_CALLER_SHUTDOWN:
1037 return;
1038 }
1039 env =
1040 GNUNET_MQ_msg (resume, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
1041 resume->cid = ch->cid;
1042 GNUNET_MQ_send (line->mq, env);
1043}
1044
1045
1046/**
1047 * Function to check an audio message incoming over cadet
1048 *
1049 * @param cls closure, our `struct Channel *`
1050 * @param msg the incoming message
1051 * @return #GNUNET_OK (always)
1052 */
1053static int
1054check_cadet_audio_message (void *cls, const struct CadetAudioMessage *msg)
1055{
1056 (void) cls;
1057 (void) msg;
1058 return GNUNET_OK; /* any payload is fine */
1059}
1060
1061
1062/**
1063 * Function to handle an audio message incoming over cadet
1064 *
1065 * @param cls closure, our `struct Channel *`
1066 * @param msg the incoming message
1067 */
1068static void
1069handle_cadet_audio_message (void *cls, const struct CadetAudioMessage *msg)
1070{
1071 struct Channel *ch = cls;
1072 size_t msize = ntohs (msg->header.size) - sizeof(struct CadetAudioMessage);
1073 struct GNUNET_MQ_Envelope *env;
1074 struct ClientAudioMessage *cam;
1075
1076 GNUNET_CADET_receive_done (ch->channel);
1077 if ((GNUNET_YES == ch->suspended_local) ||
1078 (GNUNET_YES == ch->suspended_remote))
1079 {
1080 GNUNET_log (
1081 GNUNET_ERROR_TYPE_DEBUG,
1082 "Received %u bytes of AUDIO data on suspended channel CID %u; dropping\n",
1083 (unsigned int) msize,
1084 ch->cid);
1085 return;
1086 }
1087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088 "Forwarding %u bytes of AUDIO data to client CID %u\n",
1089 (unsigned int) msize,
1090 ch->cid);
1091 env =
1092 GNUNET_MQ_msg_extra (cam, msize, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1093 cam->cid = ch->cid;
1094 GNUNET_memcpy (&cam[1], &msg[1], msize);
1095 GNUNET_MQ_send (ch->line->mq, env);
1096}
1097
1098
1099/**
1100 * Function called whenever an inbound channel is destroyed. Should clean up
1101 * any associated state.
1102 *
1103 * @param cls closure (set from #GNUNET_CADET_connect)
1104 * @param channel connection to the other end (henceforth invalid)
1105 */
1106static void
1107inbound_end (void *cls, const struct GNUNET_CADET_Channel *channel)
1108{
1109 struct Channel *ch = cls;
1110
1111 GNUNET_assert (channel == ch->channel);
1112 ch->channel = NULL;
1113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1114 "Channel destroyed by CADET in state %d\n",
1115 ch->status);
1116 clean_up_channel (ch);
1117}
1118
1119/**
1120 * Function to handle call request from the client
1121 *
1122 * @param cls the `struct Line` the message is about
1123 * @param msg the message from the client
1124 */
1125static enum GNUNET_GenericReturnValue
1126check_client_call_message (void *cls, const struct ClientCallMessage *msg)
1127{
1128 // FIXME
1129 return GNUNET_OK;
1130}
1131
1132/**
1133 * Function to handle call request from the client
1134 *
1135 * @param cls the `struct Line` the message is about
1136 * @param msg the message from the client
1137 */
1138static void
1139handle_client_call_message (void *cls, const struct ClientCallMessage *msg)
1140{
1141 struct Line *line = cls;
1142 struct Channel *ch = GNUNET_new (struct Channel);
1143 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1144 { GNUNET_MQ_hd_fixed_size (cadet_hangup_message,
1145 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP,
1146 struct CadetPhoneHangupMessage,
1147 ch),
1148 GNUNET_MQ_hd_fixed_size (cadet_pickup_message,
1149 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP,
1150 struct CadetPhonePickupMessage,
1151 ch),
1152 GNUNET_MQ_hd_fixed_size (cadet_suspend_message,
1153 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND,
1154 struct CadetPhoneSuspendMessage,
1155 ch),
1156 GNUNET_MQ_hd_fixed_size (cadet_resume_message,
1157 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME,
1158 struct CadetPhoneResumeMessage,
1159 ch),
1160 GNUNET_MQ_hd_var_size (cadet_audio_message,
1161 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO,
1162 struct CadetAudioMessage,
1163 ch),
1164 GNUNET_MQ_handler_end () };
1165 struct GNUNET_MQ_Envelope *e;
1166 struct CadetPhoneRingMessage *ring;
1167 struct CadetPhoneRingInfoPS rs;
1168 struct GNUNET_CRYPTO_PrivateKey caller_id;
1169 struct GNUNET_CRYPTO_PublicKey caller_id_pub;
1170 struct GNUNET_CRYPTO_Signature sig;
1171 ssize_t written;
1172 size_t key_len;
1173 size_t pkey_len;
1174 size_t sig_len;
1175 size_t read;
1176
1177 line->line_port = msg->line_port;
1178 rs.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
1179 rs.purpose.size = htonl (sizeof(struct CadetPhoneRingInfoPS));
1180 rs.line_port = line->line_port;
1181 rs.target_peer = msg->target;
1182 rs.expiration_time =
1183 GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
1184 key_len = ntohl (msg->key_len);
1185 if (GNUNET_SYSERR ==
1186 GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1],
1187 key_len,
1188 &caller_id,
1189 &read))
1190 {
1191 GNUNET_break_op (0);
1192 GNUNET_free (ch);
1193 GNUNET_SERVICE_client_drop (line->client);
1194 return;
1195 }
1196 ch->line = line;
1197 GNUNET_CONTAINER_DLL_insert (line->channel_head, line->channel_tail, ch);
1198 ch->status = CS_CALLER_CALLING;
1199 ch->channel = GNUNET_CADET_channel_create (cadet,
1200 ch,
1201 &msg->target,
1202 &msg->line_port,
1203 NULL,
1204 &inbound_end,
1205 cadet_handlers);
1206 ch->mq = GNUNET_CADET_get_mq (ch->channel);
1207 GNUNET_assert (read == key_len);
1208 GNUNET_CRYPTO_sign (&caller_id, &rs, &sig);
1209 sig_len = GNUNET_CRYPTO_signature_get_length (&sig);
1210 GNUNET_CRYPTO_key_get_public (&caller_id, &caller_id_pub);
1211 pkey_len = GNUNET_CRYPTO_public_key_get_length (&caller_id_pub);
1212 e = GNUNET_MQ_msg_extra (ring, pkey_len + sig_len,
1213 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING);
1214 written = GNUNET_CRYPTO_write_public_key_to_buffer (&caller_id_pub,
1215 &ring[1],
1216 pkey_len);
1217 ring->expiration_time = rs.expiration_time;
1218 ring->key_len = htonl (pkey_len);
1219 ring->sig_len = htonl (sig_len);
1220 GNUNET_CRYPTO_write_signature_to_buffer (&sig,
1221 (char *) &ring[1] + written,
1222 sig_len);
1223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending RING message via CADET\n");
1224 GNUNET_MQ_send (ch->mq, e);
1225 GNUNET_SERVICE_client_continue (line->client);
1226}
1227
1228
1229/**
1230 * Method called whenever another peer has added us to a channel
1231 * the other peer initiated.
1232 *
1233 * @param cls the `struct Line` receiving a connection
1234 * @param channel new handle to the channel
1235 * @param initiator peer that started the channel
1236 * @return initial channel context for the channel
1237 */
1238static void *
1239inbound_channel (void *cls,
1240 struct GNUNET_CADET_Channel *channel,
1241 const struct GNUNET_PeerIdentity *initiator)
1242{
1243 struct Line *line = cls;
1244 struct Channel *ch;
1245
1246 (void) initiator;
1247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1248 "Received incoming cadet channel on line %p\n",
1249 line);
1250 ch = GNUNET_new (struct Channel);
1251 ch->status = CS_CALLEE_INIT;
1252 ch->line = line;
1253 ch->channel = channel;
1254 ch->mq = GNUNET_CADET_get_mq (ch->channel);
1255 ch->cid = line->cid_gen++;
1256 GNUNET_CONTAINER_DLL_insert (line->channel_head, line->channel_tail, ch);
1257 return ch;
1258}
1259
1260
1261/**
1262 * A client connected. Initialize the `struct Line` data structure.
1263 *
1264 * @param cls closure, NULL
1265 * @param client identification of the client
1266 * @param mq message queue for @a client
1267 * @return the `struct Line` for the client
1268 */
1269static void *
1270client_connect_cb (void *cls,
1271 struct GNUNET_SERVICE_Client *client,
1272 struct GNUNET_MQ_Handle *mq)
1273{
1274 struct Line *line;
1275
1276 (void) cls;
1277 line = GNUNET_new (struct Line);
1278 line->client = client;
1279 line->mq = mq;
1280 return line;
1281}
1282
1283
1284/**
1285 * A client disconnected. Remove all of its data structure entries.
1286 *
1287 * @param cls closure, NULL
1288 * @param client identification of the client
1289 * @param app_ctx our `struct Line *` for @a client
1290 */
1291static void
1292client_disconnect_cb (void *cls,
1293 struct GNUNET_SERVICE_Client *client,
1294 void *app_ctx)
1295{
1296 struct Line *line = app_ctx;
1297 struct Channel *chn;
1298
1299 (void) cls;
1300 (void) client;
1301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected, closing line\n");
1302 if (NULL != line->port)
1303 {
1304 GNUNET_CADET_close_port (line->port);
1305 line->port = NULL;
1306 }
1307 for (struct Channel *ch = line->channel_head; NULL != ch; ch = chn)
1308 {
1309 chn = ch->next;
1310 ch->line = NULL;
1311 destroy_line_cadet_channels (ch);
1312 }
1313 GNUNET_free (line);
1314}
1315
1316
1317/**
1318 * Function to register a phone.
1319 *
1320 * @param cls the `struct Line` of the client from which the message is
1321 * @param msg the message from the client
1322 */
1323static void
1324handle_client_register_message (void *cls,
1325 const struct ClientPhoneRegisterMessage *msg)
1326{
1327 struct Line *line = cls;
1328 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1329 { GNUNET_MQ_hd_var_size (cadet_ring_message,
1330 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RING,
1331 struct CadetPhoneRingMessage,
1332 NULL),
1333 GNUNET_MQ_hd_fixed_size (cadet_hangup_message,
1334 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_HANG_UP,
1335 struct CadetPhoneHangupMessage,
1336 NULL),
1337 GNUNET_MQ_hd_fixed_size (cadet_pickup_message,
1338 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_PICK_UP,
1339 struct CadetPhonePickupMessage,
1340 NULL),
1341 GNUNET_MQ_hd_fixed_size (cadet_suspend_message,
1342 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_SUSPEND,
1343 struct CadetPhoneSuspendMessage,
1344 NULL),
1345 GNUNET_MQ_hd_fixed_size (cadet_resume_message,
1346 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_PHONE_RESUME,
1347 struct CadetPhoneResumeMessage,
1348 NULL),
1349 GNUNET_MQ_hd_var_size (cadet_audio_message,
1350 GNUNET_MESSAGE_TYPE_CONVERSATION_CADET_AUDIO,
1351 struct CadetAudioMessage,
1352 NULL),
1353 GNUNET_MQ_handler_end () };
1354
1355 line->line_port = msg->line_port;
1356 line->port = GNUNET_CADET_open_port (cadet,
1357 &msg->line_port,
1358 &inbound_channel,
1359 line,
1360 NULL,
1361 &inbound_end,
1362 cadet_handlers);
1363 if (NULL == line->port)
1364 {
1365 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1366 _ ("Could not open line, port %s already in use!\n"),
1367 GNUNET_h2s (&msg->line_port));
1368 GNUNET_SERVICE_client_drop (line->client);
1369 return;
1370 }
1371 GNUNET_SERVICE_client_continue (line->client);
1372}
1373
1374
1375/**
1376 * Shutdown nicely
1377 *
1378 * @param cls closure, NULL
1379 */
1380static void
1381do_shutdown (void *cls)
1382{
1383 (void) cls;
1384 if (NULL != cadet)
1385 {
1386 GNUNET_CADET_disconnect (cadet);
1387 cadet = NULL;
1388 }
1389}
1390
1391
1392/**
1393 * Main function that will be run by the scheduler.
1394 *
1395 * @param cls closure
1396 * @param c configuration
1397 * @param service service handle
1398 */
1399static void
1400run (void *cls,
1401 const struct GNUNET_CONFIGURATION_Handle *c,
1402 struct GNUNET_SERVICE_Handle *service)
1403{
1404 (void) cls;
1405 (void) service;
1406 cfg = c;
1407 GNUNET_assert (GNUNET_OK ==
1408 GNUNET_CRYPTO_get_peer_identity (cfg, &my_identity));
1409 cadet = GNUNET_CADET_connect (cfg);
1410 if (NULL == cadet)
1411 {
1412 GNUNET_break (0);
1413 GNUNET_SCHEDULER_shutdown ();
1414 return;
1415 }
1416 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1417}
1418
1419
1420/**
1421 * Define "main" method using service macro.
1422 */
1423GNUNET_SERVICE_MAIN (
1424 "conversation",
1425 GNUNET_SERVICE_OPTION_NONE,
1426 &run,
1427 &client_connect_cb,
1428 &client_disconnect_cb,
1429 NULL,
1430 GNUNET_MQ_hd_fixed_size (client_register_message,
1431 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1432 struct ClientPhoneRegisterMessage,
1433 NULL),
1434 GNUNET_MQ_hd_fixed_size (client_pickup_message,
1435 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1436 struct ClientPhonePickupMessage,
1437 NULL),
1438 GNUNET_MQ_hd_fixed_size (client_suspend_message,
1439 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
1440 struct ClientPhoneSuspendMessage,
1441 NULL),
1442 GNUNET_MQ_hd_fixed_size (client_resume_message,
1443 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
1444 struct ClientPhoneResumeMessage,
1445 NULL),
1446 GNUNET_MQ_hd_fixed_size (client_hangup_message,
1447 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1448 struct ClientPhoneHangupMessage,
1449 NULL),
1450 GNUNET_MQ_hd_var_size (client_call_message,
1451 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1452 struct ClientCallMessage,
1453 NULL),
1454 GNUNET_MQ_hd_var_size (client_audio_message,
1455 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1456 struct ClientAudioMessage,
1457 NULL),
1458 GNUNET_MQ_handler_end ());
1459
1460
1461/* end of gnunet-service-conversation.c */
diff --git a/src/contrib/service/conversation/gnunet_gst.c b/src/contrib/service/conversation/gnunet_gst.c
new file mode 100644
index 000000000..48d41a457
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet_gst.c
@@ -0,0 +1,1154 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet_gst.c
22 * @brief FIXME
23 * @author Hark
24 */
25#include "platform.h"
26#include "gnunet_gst_def.h"
27
28/**
29 * Our configuration.
30 */
31static struct GNUNET_CONFIGURATION_Handle *cfg;
32
33
34void
35dump_buffer (unsigned n, const unsigned char*buf)
36{
37 const unsigned char *p, *end;
38 unsigned i, j;
39
40 end = buf + n;
41
42 for (i = 0;; i += 16)
43 {
44 p = buf + i;
45 for (j = 0; j < 16; j++)
46 {
47 fprintf (stderr, "%02X ", p[j]);
48 if (p + j >= end)
49 goto BREAKOUT;
50 }
51 fprintf (stderr, " ");
52 p = buf + i;
53 for (j = 0; j < 16; j++)
54 {
55 fprintf (stderr, "%c", isprint (p[j]) ? p[j] :
56 '.');
57 if (p + j >= end)
58 goto BREAKOUT;
59 }
60 fprintf (stderr, "\n");
61 }
62BREAKOUT:
63 return;
64}
65
66
67/***
68 * load gnunet configuration
69 */
70void
71gg_load_configuration (GNUNET_gstData *d)
72{
73 char *audiobackend_string;
74
75 cfg = GNUNET_CONFIGURATION_create ();
76 GNUNET_CONFIGURATION_load (cfg, "mediahelper.conf");
77
78 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_IN",
79 &d->jack_pp_in);
80 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "JACK_PP_OUT",
81 &d->jack_pp_out);
82
83 GNUNET_CONFIGURATION_get_value_string (cfg, "MEDIAHELPER", "AUDIOBACKEND",
84 &audiobackend_string);
85
86 // printf("abstring: %s \n", audiobackend_string);
87
88 if (0 == strcasecmp (audiobackend_string, "AUTO"))
89 {
90 d->audiobackend = AUTO;
91 }
92 else if (0 == strcasecmp (audiobackend_string, "JACK"))
93 {
94 d->audiobackend = JACK;
95 }
96 else if (0 == strcasecmp (audiobackend_string, "ALSA"))
97 {
98 d->audiobackend = ALSA;
99 }
100 else if (0 == strcasecmp (audiobackend_string, "FAKE"))
101 {
102 d->audiobackend = FAKE;
103 }
104 else if (0 == strcasecmp (audiobackend_string, "TEST"))
105 {
106 d->audiobackend = TEST;
107 }
108 else
109 {
110 d->audiobackend = AUTO;
111 }
112
113 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER",
114 "REMOVESILENCE") == GNUNET_YES)
115 {
116 d->dropsilence = TRUE;
117 }
118 else
119 {
120 d->dropsilence = FALSE;
121 }
122
123 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER",
124 "NO_GN_HEADERS") == GNUNET_YES)
125 {
126 d->pure_ogg = TRUE;
127 }
128 else
129 {
130 d->pure_ogg = FALSE;
131 }
132
133
134 if (GNUNET_CONFIGURATION_get_value_yesno (cfg, "MEDIAHELPER", "USERTP") ==
135 GNUNET_YES)
136 {
137 d->usertp = TRUE;
138 }
139 else
140 {
141 d->usertp = FALSE;
142 }
143
144// GNUNET_CONFIGURATION_write(cfg, "mediahelper.conf");
145}
146
147
148static void
149write_data (const char *ptr, size_t msg_size)
150{
151 ssize_t ret;
152 size_t off;
153
154 off = 0;
155 while (off < msg_size)
156 {
157 ret = write (1, &ptr[off], msg_size - off);
158 if (0 >= ret)
159 {
160 if (-1 == ret)
161 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "write");
162// quit (2);
163 }
164 off += ret;
165 }
166}
167
168
169extern GstFlowReturn
170on_appsink_new_sample (GstElement *element, GNUNET_gstData *d)
171{
172 // size of message including gnunet header
173 size_t msg_size;
174
175 GstSample *s;
176 GstBuffer *b;
177 GstMapInfo map;
178
179/*
180 const GstStructure *si;
181 char *si_str;
182 GstCaps *s_caps;
183 char *caps_str;
184 */if (gst_app_sink_is_eos (GST_APP_SINK (element)))
185 return GST_FLOW_OK;
186
187 // pull sample from appsink
188 s = gst_app_sink_pull_sample (GST_APP_SINK (element));
189
190 if (s == NULL)
191 return GST_FLOW_OK;
192
193 if (! GST_IS_SAMPLE (s))
194 return GST_FLOW_OK;
195
196 b = gst_sample_get_buffer (s);
197
198 GST_WARNING ("caps are %" GST_PTR_FORMAT, gst_sample_get_caps (s));
199
200
201 gst_buffer_map (b, &map, GST_MAP_READ);
202
203 size_t len;
204 len = map.size;
205 if (len > UINT16_MAX - sizeof(struct AudioMessage))
206 {
207 // this should never happen?
208 printf ("GSTREAMER sample too big! \n");
209 exit (20);
210 len = UINT16_MAX - sizeof(struct AudioMessage);
211 }
212
213 msg_size = sizeof(struct AudioMessage) + len;
214
215 // copy the data into audio_message
216 GNUNET_memcpy (((char *) &(d->audio_message)[1]), map.data, len);
217 (d->audio_message)->header.size = htons ((uint16_t) msg_size);
218 if (d->pure_ogg)
219 // write the audio_message without the gnunet headers
220 write_data ((const char *) &(d->audio_message)[1], len);
221 else
222 write_data ((const char *) d->audio_message, msg_size);
223
224 gst_sample_unref (s);
225 return GST_FLOW_OK;
226}
227
228
229/***
230 * Dump a pipeline graph
231 */
232extern void
233pl_graph (GstElement *pipeline)
234{
235#ifdef IS_SPEAKER
236 gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline),
237 GST_DEBUG_GRAPH_SHOW_ALL,
238 "playback_helper.dot");
239#endif
240#ifdef IS_MIC
241 gst_debug_bin_to_dot_file_with_ts (GST_BIN (pipeline),
242 GST_DEBUG_GRAPH_SHOW_ALL,
243 "record_helper.dot");
244#endif
245
246
247 // load_configuration();
248}
249
250
251extern gboolean
252gnunet_gst_bus_call (GstBus *bus, GstMessage *msg, gpointer data)
253{
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "Bus message\n");
256 switch (GST_MESSAGE_TYPE (msg))
257 {
258 case GST_MESSAGE_EOS:
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
260 "End of stream\n");
261 exit (10);
262 break;
263
264 case GST_MESSAGE_ERROR:
265 {
266 gchar *debug;
267 GError *error;
268
269 gst_message_parse_error (msg, &error, &debug);
270 g_free (debug);
271
272 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
273 "Error: %s\n",
274 error->message);
275 g_error_free (error);
276
277 exit (10);
278 break;
279 }
280
281 default:
282 break;
283 }
284
285 return TRUE;
286}
287
288
289/* called when pipeline changes state */
290extern void
291state_changed_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *d)
292{
293 GstState old_state, new_state, pending_state;
294
295 gst_message_parse_state_changed (msg, &old_state, &new_state,
296 &pending_state);
297 switch (new_state)
298 {
299 case GST_STATE_READY:
300// printf("ready.... \n");
301 // pl_graph(GST_ELEMENT(d->pipeline));
302 break;
303
304 case GST_STATE_PLAYING:
305
306 // GST_LOG ("caps are %" GST_PTR_FORMAT, caps);
307
308 // printf("Playing.... \n");
309 pl_graph (GST_ELEMENT (d->pipeline));
310 break;
311
312 case GST_STATE_VOID_PENDING:
313 // printf("void_pending.... \n");
314 // pl_graph(GST_ELEMENT(d->pipeline));
315 break;
316
317 case GST_STATE_NULL:
318 // printf("null.... \n");
319 // pl_graph(GST_ELEMENT(d->pipeline));
320 break;
321
322 case GST_STATE_PAUSED:
323 // printf("paused.... \n");
324 // pl_graph(GST_ELEMENT(d->pipeline));
325 break;
326 }
327}
328
329
330static void
331application_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
332{
333 // printf("application cb");
334 return;
335}
336
337
338static void
339error_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
340{
341 // printf("error cb");
342 return;
343}
344
345
346static void
347eos_cb (GstBus *bus, GstMessage *msg, GNUNET_gstData *data)
348{
349 // printf("eos cb");
350 return;
351}
352
353
354extern void
355gg_setup_gst_bus (GNUNET_gstData *d)
356{
357 GstBus *bus;
358
359 bus = gst_element_get_bus (GST_ELEMENT (d->pipeline));
360 gst_bus_add_signal_watch (bus);
361 g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb,
362 d);
363 g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb,
364 d);
365 g_signal_connect (G_OBJECT (bus), "message::state-changed",
366 (GCallback) state_changed_cb, d);
367 g_signal_connect (G_OBJECT (bus), "message::application",
368 (GCallback) application_cb, d);
369 g_signal_connect (G_OBJECT (bus), "message::about-to-finish",
370 (GCallback) application_cb, d);
371 gst_object_unref (bus);
372}
373
374
375/*
376 * take buffer from gstreamer and feed it to gnunet
377 */
378/*
379 extern int
380 feed_buffer_to_gnunet (GNUNET_gstData * d)
381 {
382 GstSample *s;
383 GstBuffer *b;
384 GstMapInfo m;
385 size_t len, msg_size;
386 const char *ptr;
387 int phase;
388
389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulling...\n");
390 s = gst_app_sink_pull_sample (GST_APP_SINK(d->appsink));
391 if (NULL == s)
392 {
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pulled NULL\n");
394 return OK;
395 }
396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "...pulled!\n");
397
398 const GstStructure *si;
399 char *si_str;
400 GstCaps *s_caps;
401 char *caps_str;
402 si = gst_sample_get_info (s);
403 if (si)
404 {
405 si_str = gst_structure_to_string (si);
406 if (si_str)
407 {
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample %s\n", si_str);
409 g_free (si_str);
410 }
411 }
412 else
413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no info\n");
414 s_caps = gst_sample_get_caps (s);
415 if (s_caps)
416 {
417 caps_str = gst_caps_to_string (s_caps);
418 if (caps_str)
419 {
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with caps %s\n", caps_str);
421 g_free (caps_str);
422 }
423 }
424 else
425 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got sample with no caps\n");
426
427 b = gst_sample_get_buffer (s);
428 if (NULL == b || !gst_buffer_map (b, &m, GST_MAP_READ))
429 {
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got NULL buffer %p or failed to map the buffer\n", b);
431 gst_sample_unref (s);
432 return FAIL;
433 }
434
435 len = m.size;
436 if (len > UINT16_MAX - sizeof (struct AudioMessage))
437 {
438 GNUNET_break (0);
439 len = UINT16_MAX - sizeof (struct AudioMessage);
440 }
441 msg_size = sizeof (struct AudioMessage) + len;
442 audio_message.header.size = htons ((uint16_t) msg_size);
443
444
445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446 "Sending %u bytes of audio data\n", (unsigned int) msg_size);
447 for (phase = 0; phase < 2; phase++)
448 {
449 size_t offset;
450 size_t to_send;
451 ssize_t ret;
452 if (0 == phase && !d->pure_ogg)
453 {
454 //#ifdef DEBUG_RECORD_PURE_OGG
455
456 // if (d->pure_ogg)
457 // break;
458
459 //#endif
460 ptr = (const char *) &audio_message;
461 to_send = sizeof (audio_message);
462 }
463 else
464 {
465 ptr = (const char *) m.data;
466 to_send = len;
467 }
468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469 "Sending %u bytes on phase %d\n", (unsigned int) to_send, phase);
470 for (offset = 0; offset < to_send; offset += ret)
471 {
472 ret = write (1, &ptr[offset], to_send - offset);
473 if (0 >= ret)
474 {
475 if (-1 == ret)
476 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477 "Failed to write %u bytes at offset %u (total %u) in phase %d: %s\n",
478 (unsigned int) to_send - offset, (unsigned int) offset,
479 (unsigned int) (to_send + offset), phase, strerror (errno));
480 // abort_send = 1;
481 return FAIL;
482 }
483 }
484
485 // if (abort_send)
486 // break;
487
488 }
489 gst_buffer_unmap (b, &m);
490 gst_sample_unref (s);
491 }
492 */
493
494
495extern int
496feed_buffer_to_gst (const char *audio, size_t b_len, GNUNET_gstData *d)
497{
498 GstBuffer *b;
499 gchar *bufspace;
500 GstFlowReturn flow;
501
502 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503 "Feeding %u bytes to GStreamer\n",
504 (unsigned int) b_len);
505
506 bufspace = g_memdup (audio, b_len);
507 b = gst_buffer_new_wrapped (bufspace, b_len);
508 if (NULL == b)
509 {
510 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511 "Failed to wrap a buffer\n");
512 g_free (bufspace);
513 return GNUNET_SYSERR;
514 }
515 if (GST_APP_SRC (d->appsrc) == NULL)
516 exit (10);
517 flow = gst_app_src_push_buffer (GST_APP_SRC (d->appsrc), b);
518 /* They all return GNUNET_OK, because currently player stops when
519 * data stops coming. This might need to be changed for the player
520 * to also stop when pipeline breaks.
521 */
522 switch (flow)
523 {
524 case GST_FLOW_OK:
525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
526 "Fed %u bytes to the pipeline\n",
527 (unsigned int) b_len);
528 break;
529
530 case GST_FLOW_FLUSHING:
531 /* buffer was dropped, because pipeline state is not PAUSED or PLAYING */
532 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
533 "Dropped a buffer\n");
534 break;
535
536 case GST_FLOW_EOS:
537 /* end of stream */
538 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
539 "EOS\n");
540 break;
541
542 default:
543 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
544 "Unexpected push result\n");
545 break;
546 }
547 return GNUNET_OK;
548}
549
550
551/**
552 * debug making elements
553 */
554extern GstElement *
555gst_element_factory_make_debug (gchar *factoryname, gchar *name)
556{
557 GstElement *element;
558
559 element = gst_element_factory_make (factoryname, name);
560
561 if (element == NULL)
562 {
563 printf ("\n Failed to create element - type: %s name: %s \n", factoryname,
564 name);
565 exit (10);
566 return element;
567 }
568 else
569 {
570 return element;
571 }
572}
573
574
575/*
576 static gboolean
577 gst_element_link_many_debug(...)
578 {
579 va_list arguments;
580 gst_element_link_many(argptr);
581 }
582
583 #define gst_element_link_many(...) \
584 gst_element_link_many_debug(__VA_ARGS__)
585 */
586extern void
587lf (char *msg)
588{
589 printf ("linking elements failed: %s", msg);
590 exit (10);
591}
592
593
594/***
595 * used to set properties on autoaudiosink's chosen sink
596 */
597static void
598autoaudiosink_child_added (GstChildProxy *child_proxy,
599 GObject *object,
600 gchar *name,
601 gpointer user_data)
602{
603 if (GST_IS_AUDIO_BASE_SRC (object))
604 g_object_set (object,
605 "buffer-time", (gint64) BUFFER_TIME,
606 "latency-time", (gint64) LATENCY_TIME,
607 NULL);
608}
609
610
611/***
612 * used to set properties on autoaudiosource's chosen sink
613 */
614static void
615autoaudiosource_child_added (GstChildProxy *child_proxy, GObject *object,
616 gchar *name, gpointer user_data)
617{
618 if (GST_IS_AUDIO_BASE_SRC (object))
619 g_object_set (object, "buffer-time", (gint64) BUFFER_TIME, "latency-time",
620 (gint64) LATENCY_TIME, NULL);
621}
622
623
624GstElement *
625get_pipeline (GstElement *element)
626{
627 GstPipeline *p;
628
629 p = GST_PIPELINE (gst_object_get_parent (GST_OBJECT (element)));
630
631 return GST_ELEMENT (p);
632}
633
634
635static void
636decoder_ogg_pad_added (GstElement *element,
637 GstPad *pad,
638 gpointer data)
639{
640 GstPad *sinkpad;
641 GstElement *decoder = (GstElement *) data;
642
643 printf ("==== ogg pad added callback \n");
644 /* We can now link this pad with the opus-decoder sink pad */
645// pl_graph(get_pipeline(element));
646 sinkpad = gst_element_get_static_pad (decoder, "sink");
647
648 gst_pad_link (pad, sinkpad);
649 gst_element_link_many (element, decoder, NULL);
650 gst_object_unref (sinkpad);
651}
652
653
654int
655gnunet_read (GNUNET_gstData *d)
656{
657 char readbuf[MAXLINE];
658 int ret;
659
660 printf ("read \n");
661 ret = read (0, readbuf, sizeof(readbuf));
662 if (0 > ret)
663 {
664 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
665 _ ("Read error from STDIN: %d %s\n"),
666 ret, strerror (errno));
667 return FAIL;
668 }
669 // toff += ret;
670 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671 "Received %d bytes of audio data\n",
672 (int) ret);
673 if (0 == ret)
674 return FAIL;
675 // #ifdef DEBUG_READ_PURE_OGG
676
677 if (d->pure_ogg)
678 {
679 feed_buffer_to_gst (readbuf, ret, d);
680 }
681 else
682 {
683 // #endif
684 GNUNET_MST_from_buffer (d->stdin_mst,
685 readbuf,
686 ret,
687 GNUNET_NO,
688 GNUNET_NO);
689 }
690 return 0;
691}
692
693
694/**
695 * Message callback
696 *
697 * @param msg message we received.
698 * @return #GNUNET_OK on success,
699 * #GNUNET_NO to stop further processing due to disconnect (no error)
700 * #GNUNET_SYSERR to stop further processing due to error
701 */
702static int
703stdin_receiver (void *cls,
704 const struct GNUNET_MessageHeader *msg)
705{
706 struct AudioMessage *audio;
707 size_t b_len;
708
709 printf ("stdin receiver \n ");
710 dump_buffer (sizeof(msg),
711 (const unsigned char *) msg);
712
713 switch (ntohs (msg->type))
714 {
715 case GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO:
716 audio = (struct AudioMessage *) msg;
717
718 b_len = ntohs (audio->header.size) - sizeof(struct AudioMessage);
719 printf ("feeding buffer to gst \n ");
720 feed_buffer_to_gst ((const char *) &audio[1], b_len, cls);
721 break;
722
723 default:
724 printf ("No audio message: %u \n ", ntohs (msg->type));
725 break;
726 }
727 return GNUNET_OK;
728}
729
730
731GstBin *
732get_app (GNUNET_gstData *d, int type)
733{
734 GstBin *bin;
735 GstPad *pad, *ghostpad;
736
737 if (type == SOURCE)
738 {
739 bin = GST_BIN (gst_bin_new ("Gnunet appsrc"));
740
741
742 GNUNET_assert (GNUNET_OK ==
743 GNUNET_log_setup ("gnunet-helper-audio-playback",
744 "WARNING",
745 NULL));
746
747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748 "Audio playback starts\n");
749 printf (" creating appsrc \n ");
750 // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
751
752// d->audio_message = GNUNET_malloc (UINT16_MAX);
753// d->audio_message = (AudioMessage*)malloc(sizeof(struct AudioMessage));
754// d->audio_message = GNUNET_malloc(sizeof(struct AudioMessage));
755
756
757 // d->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
758
759
760 d->stdin_mst = GNUNET_MST_create (&stdin_receiver, d);
761
762 if (d->stdin_mst == NULL)
763 printf ("stdin_mst = NULL");
764
765 d->appsrc = gst_element_factory_make ("appsrc", "appsrc");
766
767 gst_bin_add_many (bin, d->appsrc, NULL);
768// gst_element_link_many ( encoder, muxer, NULL);
769
770 pad = gst_element_get_static_pad (d->appsrc, "src");
771 ghostpad = gst_ghost_pad_new ("src", pad);
772 }
773 if (type == SINK)
774 {
775 bin = GST_BIN (gst_bin_new ("Gnunet appsink"));
776
777
778 GNUNET_assert (GNUNET_OK ==
779 GNUNET_log_setup ("gnunet-helper-audio-record",
780 "WARNING",
781 NULL));
782
783 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
784 "Audio source starts\n");
785
786 d->appsink = gst_element_factory_make ("appsink", "appsink");
787
788 // Move this out of here!
789 d->audio_message = GNUNET_malloc (UINT16_MAX);
790 (d->audio_message)->header.type = htons (
791 GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
792 g_object_set (G_OBJECT (d->appsink), "emit-signals", TRUE, "sync", TRUE,
793 NULL);
794
795 g_signal_connect (d->appsink, "new-sample",
796 G_CALLBACK (on_appsink_new_sample), &d);
797
798 gst_bin_add_many (bin, d->appsink, NULL);
799// gst_element_link_many ( encoder, muxer, NULL);
800
801 pad = gst_element_get_static_pad (d->appsink, "sink");
802 ghostpad = gst_ghost_pad_new ("sink", pad);
803 }
804
805 /* set the bin pads */
806 gst_pad_set_active (ghostpad, TRUE);
807 gst_element_add_pad (GST_ELEMENT (bin), ghostpad);
808
809 gst_object_unref (pad);
810
811 return bin;
812}
813
814
815extern GstBin *
816get_coder (GNUNET_gstData *d, int type)
817{
818 GstBin *bin;
819 GstPad *srcpad, *sinkpad, *srcghostpad, *sinkghostpad;
820 GstCaps *rtpcaps;
821 GstElement *encoder, *muxer, *decoder, *demuxer, *jitterbuffer,
822 *rtpcapsfilter;
823
824 if (d->usertp == TRUE)
825 {
826 /*
827 * application/x-rtp, media=(string)audio, clock-rate=(int)48000, encoding-name=(string)OPUS, sprop-maxcapturerate=(string)48000, sprop-stereo=(string)0, payload=(int)96, encoding-params=(string)2, ssrc=(uint)630297634, timestamp-offset=(uint)678334141, seqnum-offset=(uint)16938 */
828/*
829 rtpcaps = gst_caps_new_simple ("application/x-rtp",
830 "media", G_TYPE_STRING, "audio",
831 "clock-rate", G_TYPE_INT, SAMPLING_RATE,
832 "encoding-name", G_TYPE_STRING, "OPUS",
833 "payload", G_TYPE_INT, 96,
834 "sprop-stereo", G_TYPE_STRING, "0",
835 "encoding-params", G_TYPE_STRING, "2",
836 NULL);
837 */ rtpcaps = gst_caps_new_simple ("application/x-rtp",
838 "media", G_TYPE_STRING, "audio",
839 "clock-rate", G_TYPE_INT, SAMPLING_RATE,
840 "encoding-name", G_TYPE_STRING, "OPUS",
841 "payload", G_TYPE_INT, 96,
842 "sprop-stereo", G_TYPE_STRING, "0",
843 "encoding-params", G_TYPE_STRING, "2",
844 NULL);
845
846
847 rtpcapsfilter = gst_element_factory_make ("capsfilter", "rtpcapsfilter");
848
849 g_object_set (G_OBJECT (rtpcapsfilter),
850 "caps", rtpcaps,
851 NULL);
852 gst_caps_unref (rtpcaps);
853 }
854
855
856 if (type == ENCODER)
857 {
858 bin = GST_BIN (gst_bin_new ("Gnunet audioencoder"));
859
860 encoder = gst_element_factory_make ("opusenc", "opus-encoder");
861 if (d->usertp == TRUE)
862 {
863 muxer = gst_element_factory_make ("rtpopuspay", "rtp-payloader");
864 }
865 else
866 {
867 muxer = gst_element_factory_make ("oggmux", "ogg-muxer");
868 }
869 g_object_set (G_OBJECT (encoder),
870 /* "bitrate", 64000, */
871 /* "bandwidth", OPUS_BANDWIDTH_FULLBAND, */
872 "inband-fec", INBAND_FEC_MODE,
873 "packet-loss-percentage", PACKET_LOSS_PERCENTAGE,
874 "max-payload-size", MAX_PAYLOAD_SIZE,
875 "audio", TRUE, /* VoIP, not audio */
876 "frame-size", OPUS_FRAME_SIZE,
877 NULL);
878
879 if (d->usertp != TRUE)
880 {
881 g_object_set (G_OBJECT (muxer),
882 "max-delay", OGG_MAX_DELAY,
883 "max-page-delay", OGG_MAX_PAGE_DELAY,
884 NULL);
885 }
886
887 gst_bin_add_many (bin, encoder, muxer, NULL);
888 gst_element_link_many (encoder, muxer, NULL);
889 sinkpad = gst_element_get_static_pad (encoder, "sink");
890 sinkghostpad = gst_ghost_pad_new ("sink", sinkpad);
891
892 srcpad = gst_element_get_static_pad (muxer, "src");
893 srcghostpad = gst_ghost_pad_new ("src", srcpad);
894 }
895 if (type == DECODER)
896 {
897 bin = GST_BIN (gst_bin_new ("Gnunet audiodecoder"));
898
899 // decoder
900 if (d->usertp == TRUE)
901 {
902 demuxer = gst_element_factory_make ("rtpopusdepay", "ogg-demuxer");
903 jitterbuffer = gst_element_factory_make ("rtpjitterbuffer",
904 "rtpjitterbuffer");
905 }
906 else
907 {
908 demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
909 }
910 decoder = gst_element_factory_make ("opusdec", "opus-decoder");
911
912 if (d->usertp == TRUE)
913 {
914 gst_bin_add_many (bin, rtpcapsfilter, jitterbuffer, demuxer, decoder,
915 NULL);
916 gst_element_link_many (rtpcapsfilter, jitterbuffer, demuxer, decoder,
917 NULL);
918 sinkpad = gst_element_get_static_pad (rtpcapsfilter, "sink");
919 }
920 else
921 {
922 gst_bin_add_many (bin, demuxer, decoder, NULL);
923
924 g_signal_connect (demuxer,
925 "pad-added",
926 G_CALLBACK (decoder_ogg_pad_added),
927 decoder);
928
929 sinkpad = gst_element_get_static_pad (demuxer, "sink");
930 }
931 sinkghostpad = gst_ghost_pad_new ("sink", sinkpad);
932
933 srcpad = gst_element_get_static_pad (decoder, "src");
934 srcghostpad = gst_ghost_pad_new ("src", srcpad);
935 }
936
937 // add pads to the bin
938 gst_pad_set_active (sinkghostpad, TRUE);
939 gst_element_add_pad (GST_ELEMENT (bin), sinkghostpad);
940
941 gst_pad_set_active (srcghostpad, TRUE);
942 gst_element_add_pad (GST_ELEMENT (bin), srcghostpad);
943
944
945 return bin;
946}
947
948
949extern GstBin *
950get_audiobin (GNUNET_gstData *d, int type)
951{
952 GstBin *bin;
953 GstElement *sink, *source, *queue, *conv, *resampler, *removesilence, *filter;
954 GstPad *pad, *ghostpad;
955 GstCaps *caps;
956
957 if (type == SINK)
958 {
959 bin = GST_BIN (gst_bin_new ("Gnunet audiosink"));
960
961 /* Create all the elements */
962 if (d->dropsilence == TRUE)
963 {
964 queue = gst_element_factory_make ("queue", "queue");
965 removesilence = gst_element_factory_make ("removesilence",
966 "removesilence");
967 }
968
969 conv = gst_element_factory_make ("audioconvert", "converter");
970 resampler = gst_element_factory_make ("audioresample", "resampler");
971
972 if (d->audiobackend == AUTO)
973 {
974 sink = gst_element_factory_make ("autoaudiosink", "audiosink");
975 g_signal_connect (sink, "child-added", G_CALLBACK (
976 autoaudiosink_child_added), NULL);
977 }
978
979 if (d->audiobackend == ALSA)
980 {
981 sink = gst_element_factory_make ("alsaaudiosink", "audiosink");
982 }
983
984 if (d->audiobackend == JACK)
985 {
986 sink = gst_element_factory_make ("jackaudiosink", "audiosink");
987
988 g_object_set (G_OBJECT (sink), "client-name", "gnunet", NULL);
989
990 if (g_object_class_find_property
991 (G_OBJECT_GET_CLASS (sink), "port-pattern"))
992 {
993// char *portpattern = "system";
994
995 g_object_set (G_OBJECT (sink), "port-pattern", d->jack_pp_out,
996 NULL);
997 }
998 }
999
1000 if (d->audiobackend == FAKE)
1001 {
1002 sink = gst_element_factory_make ("fakesink", "audiosink");
1003 }
1004
1005 g_object_set (sink,
1006 "buffer-time", (gint64) BUFFER_TIME,
1007 "latency-time", (gint64) LATENCY_TIME,
1008 NULL);
1009
1010 if (d->dropsilence == TRUE)
1011 {
1012 // Do not remove silence by default
1013 g_object_set (removesilence, "remove", FALSE, NULL);
1014 g_object_set (queue, "max-size-buffers", 12, NULL);
1015 /*
1016 g_signal_connect (source,
1017 "need-data",
1018 G_CALLBACK(appsrc_need_data),
1019 NULL);
1020
1021 g_signal_connect (source,
1022 "enough-data",
1023 G_CALLBACK(appsrc_enough_data),
1024 NULL);
1025 *//*
1026 g_signal_connect (queue,
1027 "notify::current-level-bytes",
1028 G_CALLBACK(queue_current_level),
1029 NULL);
1030
1031 g_signal_connect (queue,
1032 "underrun",
1033 G_CALLBACK(queue_underrun),
1034 NULL);
1035
1036 g_signal_connect (queue,
1037 "running",
1038 G_CALLBACK(queue_running),
1039 NULL);
1040
1041 g_signal_connect (queue,
1042 "overrun",
1043 G_CALLBACK(queue_overrun),
1044 NULL);
1045
1046 g_signal_connect (queue,
1047 "pushing",
1048 G_CALLBACK(queue_pushing),
1049 NULL);
1050 */ }
1051
1052
1053 gst_bin_add_many (bin, conv, resampler, sink, NULL);
1054 gst_element_link_many (conv, resampler, sink, NULL);
1055
1056 if (d->dropsilence == TRUE)
1057 {
1058 gst_bin_add_many (bin, queue, removesilence, NULL);
1059
1060 if (! gst_element_link_many (queue, removesilence, conv, NULL))
1061 lf ("queue, removesilence, conv ");
1062
1063 pad = gst_element_get_static_pad (queue, "sink");
1064 }
1065 else
1066 {
1067 pad = gst_element_get_static_pad (conv, "sink");
1068 }
1069
1070 ghostpad = gst_ghost_pad_new ("sink", pad);
1071 }
1072 else
1073 {
1074 // SOURCE
1075
1076 bin = GST_BIN (gst_bin_new ("Gnunet audiosource"));
1077
1078 // source = gst_element_factory_make("audiotestsrc", "audiotestsrcbla");
1079
1080 if (d->audiobackend == AUTO)
1081 {
1082 source = gst_element_factory_make ("autoaudiosrc", "audiosource");
1083 }
1084 if (d->audiobackend == ALSA)
1085 {
1086 source = gst_element_factory_make ("alsasrc", "audiosource");
1087 }
1088 if (d->audiobackend == JACK)
1089 {
1090 source = gst_element_factory_make ("jackaudiosrc", "audiosource");
1091 }
1092 if (d->audiobackend == TEST)
1093 {
1094 source = gst_element_factory_make ("audiotestsrc", "audiosource");
1095 }
1096
1097 filter = gst_element_factory_make ("capsfilter", "filter");
1098 conv = gst_element_factory_make ("audioconvert", "converter");
1099 resampler = gst_element_factory_make ("audioresample", "resampler");
1100
1101 if (d->audiobackend == AUTO)
1102 {
1103 g_signal_connect (source, "child-added", G_CALLBACK (
1104 autoaudiosource_child_added), NULL);
1105 }
1106 else
1107 {
1108 if (GST_IS_AUDIO_BASE_SRC (source))
1109 g_object_set (source, "buffer-time", (gint64) BUFFER_TIME,
1110 "latency-time", (gint64) LATENCY_TIME, NULL);
1111 if (d->audiobackend == JACK)
1112 {
1113 g_object_set (G_OBJECT (source), "client-name", "gnunet", NULL);
1114 if (g_object_class_find_property
1115 (G_OBJECT_GET_CLASS (source), "port-pattern"))
1116 {
1117 char *portpattern = "moc";
1118
1119 g_object_set (G_OBJECT (source), "port-pattern", portpattern,
1120 NULL);
1121 }
1122 }
1123 }
1124
1125 caps = gst_caps_new_simple ("audio/x-raw",
1126 /* "format", G_TYPE_STRING, "S16LE", */
1127 /* "rate", G_TYPE_INT, SAMPLING_RATE,*/
1128 "channels", G_TYPE_INT, OPUS_CHANNELS,
1129 /* "layout", G_TYPE_STRING, "interleaved",*/
1130 NULL);
1131
1132 g_object_set (G_OBJECT (filter),
1133 "caps", caps,
1134 NULL);
1135 gst_caps_unref (caps);
1136
1137 gst_bin_add_many (bin, source, filter, conv, resampler, NULL);
1138 gst_element_link_many (source, filter, conv, resampler, NULL);
1139
1140 pad = gst_element_get_static_pad (resampler, "src");
1141
1142
1143 /* pads */
1144 ghostpad = gst_ghost_pad_new ("src", pad);
1145 }
1146
1147 /* set the bin pads */
1148 gst_pad_set_active (ghostpad, TRUE);
1149 gst_element_add_pad (GST_ELEMENT (bin), ghostpad);
1150
1151 gst_object_unref (pad);
1152
1153 return bin;
1154}
diff --git a/src/contrib/service/conversation/gnunet_gst.h b/src/contrib/service/conversation/gnunet_gst.h
new file mode 100644
index 000000000..479ae14b3
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet_gst.h
@@ -0,0 +1,62 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet_gst.c
22 * @brief FIXME
23 * @author Hark
24 */
25
26// which audiobackend we use
27//
28
29/*
30 int audiobackend = JACK;
31 int dropsilence = TRUE;
32 int enough = 0;
33 int usertp = TRUE;
34 */
35
36#define gst_element_factory_make(element, name) gst_element_factory_make_debug ( \
37 element, name);
38
39extern void pl_graph ();
40
41
42extern GstElement *
43gst_element_factory_make_debug (gchar *, gchar *);
44
45extern GstBin *
46get_audiobin (GNUNET_gstData *, int);
47
48extern GstBin *
49get_coder (GNUNET_gstData *, int);
50
51
52extern gboolean
53gnunet_gst_bus_call (GstBus *bus, GstMessage *msg, gpointer data);
54
55extern void
56gg_setup_gst_bus (GNUNET_gstData *d);
57
58extern void
59gg_load_configuration (GNUNET_gstData *d);
60
61extern GstFlowReturn
62on_appsink_new_sample (GstElement *, GNUNET_gstData *);
diff --git a/src/contrib/service/conversation/gnunet_gst_def.h b/src/contrib/service/conversation/gnunet_gst_def.h
new file mode 100644
index 000000000..7bfcc5e53
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet_gst_def.h
@@ -0,0 +1,219 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet_gst_def.h
22 * @brief FIXME
23 * @author Hark
24 */
25
26#include <getopt.h>
27#include <string.h>
28#include <stdio.h>
29#include <ctype.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <time.h>
33#include <regex.h>
34
35
36#include "platform.h"
37#include "gnunet_util_lib.h"
38#include "gnunet_protocols.h"
39// #include "gnunet/conversation.h" doesn't get installed
40#include "conversation.h"
41#include "gnunet_constants.h"
42#include "gnunet_core_service.h"
43#include "gnunet_common.h"
44
45/*
46 #include <gst/gst.h>
47 #include <gst/audio/gstaudiobasesrc.h>
48 #include <gst/app/gstappsrc.h>
49 */
50
51/* huh
52 #include <glib-2.0/glib.h>
53
54 #include <gstreamer-1.0/gst/gst.h>
55 #include <gstreamer-1.0/gst/pbutils/pbutils.h>
56 #include <gstreamer-1.0/gst/video/videooverlay.h>
57 #include <gstreamer-1.0/gst/audio/gstaudiobasesrc.h>
58 #include <gstreamer-1.0/gst/app/gstappsrc.h>
59 */
60
61#include <gst/gst.h>
62#include <gst/audio/gstaudiobasesrc.h>
63#include <gst/app/gstappsrc.h>
64#include <glib.h>
65#include <gst/app/gstappsink.h>
66
67// sockets
68#if HAVE_NETINET_IN_H
69#include <netinet/in.h>
70#endif
71#include <sys/socket.h>
72#include <arpa/inet.h>
73#include <netdb.h>
74
75#include <sys/types.h>
76#include <fcntl.h>
77
78
79// glib stuff
80// #include <glib.h>
81#include <glib-2.0/glib/gprintf.h>
82// #include <glib-unix.h>
83
84// static struct AudioMessage *audio_message;
85
86
87typedef struct GNUNET_gstData GNUNET_gstData;
88struct GNUNET_gstData
89{
90 // general
91 GstPipeline *pipeline;
92
93 // things
94 struct AudioMessage *audio_message;
95 struct GNUNET_MessageStreamTokenizer *stdin_mst;
96 GstElement *appsrc;
97 GstElement *appsink;
98 // settings
99 int audiobackend;
100 int dropsilence;
101 int usertp;
102 int pure_ogg;
103 char *jack_pp_in;
104 char *jack_pp_out;
105};
106
107
108#define DEBUG_READ_PURE_OGG 1
109#define DEBUG_RECORD_PURE_OGG 1
110
111
112/**
113 * How much data to read in one go
114 */
115#define MAXLINE 4096
116
117/**
118 * Max number of microseconds to buffer in audiosink.
119 * Default is 1000
120 */
121#define BUFFER_TIME 1000
122
123/**
124 * Min number of microseconds to buffer in audiosink.
125 * Default is 1000
126 */
127#define LATENCY_TIME 1000
128
129
130/**
131 * Number of channels.
132 * Must be one of the following (from libopusenc documentation):
133 * 1, 2
134 */
135#define OPUS_CHANNELS 1
136
137/**
138 * Maximal size of a single opus packet.
139 */
140#define MAX_PAYLOAD_SIZE (1024 / OPUS_CHANNELS)
141
142/**
143 * Size of a single frame fed to the encoder, in ms.
144 * Must be one of the following (from libopus documentation):
145 * 2.5, 5, 10, 20, 40 or 60
146 */
147#define OPUS_FRAME_SIZE 40
148
149/**
150 * Expected packet loss to prepare for, in percents.
151 */
152#define PACKET_LOSS_PERCENTAGE 1
153
154/**
155 * Set to 1 to enable forward error correction.
156 * Set to 0 to disable.
157 */
158#define INBAND_FEC_MODE 1
159
160/**
161 * Max number of microseconds to buffer in audiosource.
162 * Default is 200000
163 */
164#define BUFFER_TIME 1000 /* 1ms */
165
166/**
167 * Min number of microseconds to buffer in audiosource.
168 * Default is 10000
169 */
170#define LATENCY_TIME 1000 /* 1ms */
171
172/**
173 * Maximum delay in multiplexing streams, in ns.
174 * Setting this to 0 forces page flushing, which
175 * decreases delay, but increases overhead.
176 */
177#define OGG_MAX_DELAY 0
178
179/**
180 * Maximum delay for sending out a page, in ns.
181 * Setting this to 0 forces page flushing, which
182 * decreases delay, but increases overhead.
183 */
184#define OGG_MAX_PAGE_DELAY 0
185
186#define SAMPLING_RATE 48000
187
188enum
189{
190 AUTO,
191 JACK,
192 ALSA,
193 FAKE,
194 TEST
195};
196
197enum
198{
199 SOURCE,
200 SINK
201};
202
203enum
204{
205 ENCODER,
206 DECODER
207};
208
209enum
210{
211 FAIL,
212 OK
213};
214
215enum
216{
217 SPEAKER,
218 MICROPHONE
219};
diff --git a/src/contrib/service/conversation/gnunet_gst_test.c b/src/contrib/service/conversation/gnunet_gst_test.c
new file mode 100644
index 000000000..dd2ef5a38
--- /dev/null
+++ b/src/contrib/service/conversation/gnunet_gst_test.c
@@ -0,0 +1,135 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/gnunet_gst_test.c
22 * @brief FIXME
23 * @author Hark
24 */
25
26#include "platform.h"
27#include "gnunet_gst_def.h"
28#include "gnunet_gst.h"
29
30int
31main (int argc, char *argv[])
32{
33 struct GNUNET_gstData *gst;
34 // GstBus *bus;
35 GstElement *gnunetsrc, *gnunetsink, *source, *sink, *encoder, *decoder;
36
37
38 // audio_message = GNUNET_malloc (UINT16_MAX);
39 // audio_message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
40
41
42 // GstPipeline *pipeline;
43
44 gst = (GNUNET_gstData *) malloc (sizeof(struct GNUNET_gstData));
45
46 // gst->audio_message.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
47
48
49 gg_load_configuration (gst);
50/*
51 gst->audiobackend = JACK;
52 gst->dropsilence = TRUE;
53 gst->usertp = FALSE;
54 *//* Initialize GStreamer */gst_init (&argc, &argv);
55
56 gst->pipeline = GST_PIPELINE (gst_pipeline_new ("gnunet-media-helper"));
57
58#ifdef IS_SPEAKER
59 int type = SPEAKER;
60 printf ("this is the speaker \n");
61#endif
62#ifdef IS_MIC
63 int type = MICROPHONE;
64 printf ("this is the microphone \n");
65#endif
66 if (type == SPEAKER)
67 {
68 gnunetsrc = GST_ELEMENT (get_app (gst, SOURCE));
69
70 sink = GST_ELEMENT (get_audiobin (gst, SINK));
71 decoder = GST_ELEMENT (get_coder (gst, DECODER));
72 gst_bin_add_many (GST_BIN (gst->pipeline), gnunetsrc, decoder, sink, NULL);
73 gst_element_link_many (gnunetsrc, decoder, sink, NULL);
74 }
75 if (type == MICROPHONE)
76 {
77 source = GST_ELEMENT (get_audiobin (gst, SOURCE));
78
79 encoder = GST_ELEMENT (get_coder (gst, ENCODER));
80
81 gnunetsink = GST_ELEMENT (get_app (gst, SINK));
82
83 gst_bin_add_many (GST_BIN (gst->pipeline), source, encoder, gnunetsink,
84 NULL);
85 gst_element_link_many (source, encoder, gnunetsink, NULL);
86 }
87 /*
88 gst_bin_add_many( GST_BIN(gst->pipeline), appsource, appsink, source, encoder, decoder, sink, NULL);
89 gst_element_link_many( source, encoder, decoder, sink , NULL);
90 */
91 pl_graph (gst->pipeline);
92 /* Start playing */
93 gst_element_set_state (GST_ELEMENT (gst->pipeline), GST_STATE_PLAYING);
94
95 // pl_graph(gst->pipeline);
96
97 /* Wait until error or EOS */
98 // bus = gst_element_get_bus (GST_ELEMENT(gst->pipeline));
99 // bus_watch_id = gst_bus_add_watch (bus, gnunet_gst_bus_call, pipeline);
100
101 gg_setup_gst_bus (gst);
102// g_print ("Running...\n");
103
104
105 // start pushing buffers
106 if (type == MICROPHONE)
107 {
108 GMainLoop *loop;
109 loop = g_main_loop_new (NULL, FALSE);
110
111 g_main_loop_run (loop);
112
113/*
114 while ( 1 )
115 {
116 GstFlowReturn flow;
117 flow = on_appsink_new_sample (gst->appsink, gst);
118 }
119 */}
120 if (type == SPEAKER)
121 {
122 while (1)
123 {
124// printf("read.. \n");
125 gnunet_read (gst);
126 }
127 }
128 g_print ("Returned, stopping playback\n");
129
130 // gst_object_unref (bus);
131 gst_element_set_state (GST_ELEMENT (gst->pipeline), GST_STATE_NULL);
132 gst_object_unref (gst->pipeline);
133
134 return 0;
135}
diff --git a/src/contrib/service/conversation/mediahelper.conf b/src/contrib/service/conversation/mediahelper.conf
new file mode 100644
index 000000000..85c051107
--- /dev/null
+++ b/src/contrib/service/conversation/mediahelper.conf
@@ -0,0 +1,7 @@
1[MEDIAHELPER]
2AUDIOBACKEND = JACK
3REMOVESILENCE = NO
4USERTP = NO
5NO_GN_HEADERS = NO
6JACK_PP_IN = mocp
7JACK_PP_OUT = system
diff --git a/src/contrib/service/conversation/meson.build b/src/contrib/service/conversation/meson.build
new file mode 100644
index 000000000..4ecc0fc93
--- /dev/null
+++ b/src/contrib/service/conversation/meson.build
@@ -0,0 +1,203 @@
1libgnunetconversation_src = ['conversation_api.c', 'conversation_api_call.c']
2
3gnunetserviceconversation_src = ['gnunet-service-conversation.c']
4
5gnunetconversationproxy_src = ['gnunet-conversation-proxy.c']
6
7configure_file(input : 'conversation.conf.in',
8 output : 'conversation.conf',
9 configuration : cdata,
10 install: true,
11 install_dir: pkgcfgdir)
12
13
14if get_option('monolith')
15 foreach p : libgnunetconversation_src + gnunetserviceconversation_src
16 gnunet_src += 'conversation/' + p
17 endforeach
18endif
19
20libgnunetmicrophone = library('gnunetmicrophone',
21 ['microphone.c'],
22 soversion: '0',
23 version: '0.0.0',
24 dependencies: [libgnunetutil_dep],
25 include_directories: [incdir, configuration_inc],
26 install: true,
27 install_dir: get_option('libdir'))
28libgnunetmicrophone_dep = declare_dependency(link_with : libgnunetmicrophone)
29pkg.generate(libgnunetmicrophone, url: 'https://www.gnunet.org',
30 description : 'Provides API to access to microphone')
31
32libgnunetspeaker = library('gnunetspeaker',
33 ['speaker.c'],
34 soversion: '0',
35 version: '0.0.0',
36 dependencies: [libgnunetutil_dep],
37 include_directories: [incdir, configuration_inc],
38 install: true,
39 install_dir: get_option('libdir'))
40libgnunetspeaker_dep = declare_dependency(link_with : libgnunetspeaker)
41pkg.generate(libgnunetspeaker, url: 'https://www.gnunet.org',
42 description : 'Provides API to access to speaker')
43
44libgnunetconversation = library('gnunetconversation',
45 libgnunetconversation_src,
46 soversion: '0',
47 version: '0.0.0',
48 dependencies: [libgnunetutil_dep,
49 libgnunetgnsrecord_dep,
50 libgnunetgns_dep,
51 libgnunetnamestore_dep,
52 libgnunetidentity_dep],
53 include_directories: [incdir, configuration_inc],
54 install: true,
55 install_dir: get_option('libdir'))
56libgnunetconversation_dep = declare_dependency(link_with : libgnunetconversation)
57pkg.generate(libgnunetconversation, url: 'https://www.gnunet.org',
58 description : 'Provides API to access to conversation')
59
60shared_module('gnunet_plugin_gnsrecord_conversation',
61 ['plugin_gnsrecord_conversation.c'],
62 dependencies: [libgnunetutil_dep,
63 libgnunetconversation_dep,
64 libgnunetgns_dep,
65 libgnunetgnsrecord_dep,
66 libgnunetidentity_dep],
67 include_directories: [incdir, configuration_inc],
68 install: true,
69 install_dir: get_option('libdir')/'gnunet')
70
71executable ('gnunet-conversation',
72 'gnunet-conversation.c',
73 dependencies: [libgnunetconversation_dep,
74 libgnunetgns_dep,
75 libgnunetgnsrecord_dep,
76 libgnunetnamestore_dep,
77 libgnunetspeaker_dep,
78 libgnunetmicrophone_dep,
79 libgnunetidentity_dep,
80 libgnunetutil_dep],
81 include_directories: [incdir, configuration_inc],
82 install: true,
83 install_dir: get_option('bindir'))
84
85executable ('gnunet-conversation-test',
86 'gnunet-conversation-test.c',
87 dependencies: [libgnunetconversation_dep,
88 libgnunetspeaker_dep,
89 libgnunetmicrophone_dep,
90 libgnunetutil_dep],
91 include_directories: [incdir, configuration_inc],
92 install: true,
93 install_dir: get_option('bindir'))
94
95executable ('gnunet-service-conversation',
96 gnunetserviceconversation_src,
97 dependencies: [libgnunetconversation_dep,
98 libgnunetutil_dep,
99 libgnunetspeaker_dep,
100 libgnunetmicrophone_dep,
101 libgnunetidentity_dep,
102 libgnunetcadet_dep],
103 include_directories: [incdir, configuration_inc],
104 install: true,
105 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
106
107helperrecord_src = ['gnunet-helper-audio-record.c']
108helperplayback_src = ['gnunet-helper-audio-record.c']
109if conversation_backend == 'gst'
110 helperrecord_src = ['gnunet-helper-audio-record-gst.c']
111 helperplayback_src = ['gnunet-helper-audio-playback-gst.c']
112endif
113
114executable ('gnunet-helper-audio-record',
115 helperrecord_src,
116 dependencies: [libgnunetconversation_dep,
117 libgnunetutil_dep,
118 libgnunetspeaker_dep,
119 libgnunetmicrophone_dep,
120 gst_dep,
121 gst_app_dep,
122 gst_audio_dep,
123 ogg_dep,
124 pulse_dep,
125 opus_dep
126 ],
127 include_directories: [incdir, configuration_inc],
128 install: true,
129 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
130
131executable ('gnunet-helper-audio-playback',
132 helperplayback_src,
133 dependencies: [libgnunetconversation_dep,
134 libgnunetutil_dep,
135 libgnunetspeaker_dep,
136 libgnunetmicrophone_dep,
137 gst_dep,
138 gst_app_dep,
139 gst_audio_dep,
140 ogg_dep,
141 pulse_dep,
142 opus_dep
143 ],
144 include_directories: [incdir, configuration_inc],
145 install: true,
146 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
147
148testconvapi = executable ('test_conversation_api',
149 ['test_conversation_api.c'],
150 dependencies: [libgnunetconversation_dep,
151 libgnunetutil_dep,
152 libgnunetidentity_dep,
153 libgnunettesting_dep,
154 libgnunetgnsrecord_dep,
155 libgnunetnamestore_dep,
156 libgnunetspeaker_dep,
157 libgnunetmicrophone_dep,
158 ],
159 include_directories: [incdir, configuration_inc],
160 install: false)
161
162testconvapitwo = executable ('test_conversation_api_twocalls',
163 ['test_conversation_api_twocalls.c'],
164 dependencies: [libgnunetconversation_dep,
165 libgnunetutil_dep,
166 libgnunetidentity_dep,
167 libgnunettesting_dep,
168 libgnunetgnsrecord_dep,
169 libgnunetnamestore_dep,
170 libgnunetspeaker_dep,
171 libgnunetmicrophone_dep,
172 ],
173 include_directories: [incdir, configuration_inc],
174 install: false)
175
176testconvapireject = executable ('test_conversation_api_reject',
177 ['test_conversation_api_reject.c'],
178 dependencies: [libgnunetconversation_dep,
179 libgnunetutil_dep,
180 libgnunetidentity_dep,
181 libgnunettesting_dep,
182 libgnunetgnsrecord_dep,
183 libgnunetnamestore_dep,
184 libgnunetspeaker_dep,
185 libgnunetmicrophone_dep,
186 ],
187 include_directories: [incdir, configuration_inc],
188 install: false)
189
190configure_file(input : 'test_conversation.conf',
191 output : 'test_conversation.conf',
192 copy: true,
193 install: false)
194
195test('test_conversation_api', testconvapi, workdir: meson.current_build_dir(),
196 suite: ['conversation', 'contrib'])
197
198test('test_conversation_api_twocalls', testconvapitwo, workdir: meson.current_build_dir(),
199 suite: ['conversation', 'contrib'])
200
201test('test_conversation_api_reject', testconvapireject, workdir: meson.current_build_dir(),
202 suite: ['conversation', 'contrib'])
203
diff --git a/src/contrib/service/conversation/microphone.c b/src/contrib/service/conversation/microphone.c
new file mode 100644
index 000000000..a4a40796f
--- /dev/null
+++ b/src/contrib/service/conversation/microphone.c
@@ -0,0 +1,200 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/microphone.c
23 * @brief API to access an audio microphone; provides access to hardware microphones;
24 * actually just wraps the gnunet-helper-audio-record
25 * @author Simon Dieterle
26 * @author Andreas Fuchs
27 * @author Christian Grothoff
28 */
29#include "platform.h"
30#include "gnunet_microphone_lib.h"
31#include "conversation.h"
32
33
34/**
35 * Internal data structures for the microphone.
36 */
37struct Microphone
38{
39 /**
40 * Our configuration.
41 */
42 const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44 /**
45 * Handle for the record helper
46 */
47 struct GNUNET_HELPER_Handle *record_helper;
48
49 /**
50 * Function to call with audio data (if we are enabled).
51 */
52 GNUNET_MICROPHONE_RecordedDataCallback rdc;
53
54 /**
55 * Closure for @e rdc.
56 */
57 void *rdc_cls;
58};
59
60
61/**
62 * Function to process the audio from the record helper
63 *
64 * @param cls clsoure with our `struct Microphone`
65 * @param msg the message from the helper
66 * @return #GNUNET_OK on success,
67 * #GNUNET_NO to stop further processing (no error)
68 * #GNUNET_SYSERR to stop further processing with error
69 */
70static int
71process_record_messages (void *cls,
72 const struct GNUNET_MessageHeader *msg)
73{
74 struct Microphone *mic = cls;
75 const struct AudioMessage *am;
76
77 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO)
78 {
79 GNUNET_break (0);
80 return GNUNET_SYSERR;
81 }
82 am = (const struct AudioMessage *) msg;
83 mic->rdc (mic->rdc_cls,
84 ntohs (msg->size) - sizeof(struct AudioMessage),
85 &am[1]);
86 return GNUNET_OK;
87}
88
89
90/**
91 * Enable a microphone.
92 *
93 * @param cls clsoure with our `struct Microphone`
94 * @param rdc function to call with recorded data
95 * @param rdc_cls closure for @a dc
96 */
97static int
98enable (void *cls,
99 GNUNET_MICROPHONE_RecordedDataCallback rdc,
100 void *rdc_cls)
101{
102 struct Microphone *mic = cls;
103 static char *const record_helper_argv[] = {
104 "gnunet-helper-audio-record",
105 NULL
106 };
107
108 mic->rdc = rdc;
109 mic->rdc_cls = rdc_cls;
110 mic->record_helper = GNUNET_HELPER_start (GNUNET_NO,
111 "gnunet-helper-audio-record",
112 record_helper_argv,
113 &process_record_messages,
114 NULL, mic);
115 if (NULL == mic->record_helper)
116 {
117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
118 _ ("Could not start record audio helper\n"));
119 return GNUNET_SYSERR;
120 }
121 return GNUNET_OK;
122}
123
124
125/**
126 * Function that disables a microphone.
127 *
128 * @param cls clsoure
129 */
130static void
131disable (void *cls)
132{
133 struct Microphone *mic = cls;
134
135 if (NULL == mic->record_helper)
136 {
137 GNUNET_break (0);
138 return;
139 }
140 GNUNET_break (GNUNET_OK ==
141 GNUNET_HELPER_kill (mic->record_helper, GNUNET_NO));
142 GNUNET_HELPER_destroy (mic->record_helper);
143 mic->record_helper = NULL;
144}
145
146
147/**
148 * Function to destroy a microphone.
149 *
150 * @param cls clsoure
151 */
152static void
153destroy (void *cls)
154{
155 struct Microphone *mic = cls;
156
157 if (NULL != mic->record_helper)
158 disable (mic);
159}
160
161
162/**
163 * Create a microphone that corresponds to the microphone hardware
164 * of our system.
165 *
166 * @param cfg configuration to use
167 * @return NULL on error
168 */
169struct GNUNET_MICROPHONE_Handle *
170GNUNET_MICROPHONE_create_from_hardware (const struct
171 GNUNET_CONFIGURATION_Handle *cfg)
172{
173 struct GNUNET_MICROPHONE_Handle *microphone;
174 struct Microphone *mic;
175
176 mic = GNUNET_new (struct Microphone);
177 mic->cfg = cfg;
178 microphone = GNUNET_new (struct GNUNET_MICROPHONE_Handle);
179 microphone->cls = mic;
180 microphone->enable_microphone = &enable;
181 microphone->disable_microphone = &disable;
182 microphone->destroy_microphone = &destroy;
183 return microphone;
184}
185
186
187/**
188 * Destroy a microphone.
189 *
190 * @param microphone microphone to destroy
191 */
192void
193GNUNET_MICROPHONE_destroy (struct GNUNET_MICROPHONE_Handle *microphone)
194{
195 microphone->destroy_microphone (microphone->cls);
196 GNUNET_free (microphone);
197}
198
199
200/* end of microphone.c */
diff --git a/src/contrib/service/conversation/plugin_gnsrecord_conversation.c b/src/contrib/service/conversation/plugin_gnsrecord_conversation.c
new file mode 100644
index 000000000..802732cfe
--- /dev/null
+++ b/src/contrib/service/conversation/plugin_gnsrecord_conversation.c
@@ -0,0 +1,262 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/plugin_gnsrecord_conversation.c
23 * @brief gnsrecord plugin to provide the API for fundamental GNS records
24 * This includes the VPN record because GNS resolution
25 * is expected to understand VPN records and (if needed)
26 * map the result to A/AAAA.
27 * @author Christian Grothoff
28 */
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_conversation_service.h"
34#include "gnunet_gnsrecord_plugin.h"
35
36
37/**
38 * Convert the 'value' of a record to a string.
39 *
40 * @param cls closure, unused
41 * @param type type of the record
42 * @param data value in binary encoding
43 * @param data_size number of bytes in @a data
44 * @return NULL on error, otherwise human-readable representation of the value
45 */
46static char *
47conversation_value_to_string (void *cls,
48 uint32_t type,
49 const void *data,
50 size_t data_size)
51{
52 char *s;
53
54 (void) cls;
55 switch (type)
56 {
57 case GNUNET_GNSRECORD_TYPE_PHONE:
58 {
59 const struct GNUNET_CONVERSATION_PhoneRecord *pr;
60 char *ret;
61 char *pkey;
62
63 if (data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
64 {
65 GNUNET_break_op (0);
66 return NULL;
67 }
68 pr = data;
69 if (1 != ntohl (pr->version))
70 {
71 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
72 _ ("PHONE version %u not supported\n"),
73 ntohl (pr->version));
74 return NULL;
75 }
76 pkey = GNUNET_CRYPTO_eddsa_public_key_to_string (&pr->peer.public_key);
77 s = GNUNET_STRINGS_data_to_string_alloc (&pr->line_port,
78 sizeof(struct GNUNET_HashCode));
79
80 GNUNET_asprintf (&ret,
81 "%s-%s",
82 s,
83 pkey);
84 GNUNET_free (s);
85 GNUNET_free (pkey);
86 return ret;
87 }
88
89 default:
90 return NULL;
91 }
92}
93
94
95/**
96 * Convert human-readable version of a 'value' of a record to the binary
97 * representation.
98 *
99 * @param cls closure, unused
100 * @param type type of the record
101 * @param s human-readable string
102 * @param data set to value in binary encoding (will be allocated)
103 * @param data_size set to number of bytes in @a data
104 * @return #GNUNET_OK on success
105 */
106static int
107conversation_string_to_value (void *cls,
108 uint32_t type,
109 const char *s,
110 void **data,
111 size_t *data_size)
112{
113 (void) cls;
114 if (NULL == s)
115 {
116 GNUNET_break (0);
117 return GNUNET_SYSERR;
118 }
119 switch (type)
120 {
121 case GNUNET_GNSRECORD_TYPE_PHONE:
122 {
123 struct GNUNET_CONVERSATION_PhoneRecord *pr;
124 char line_port[104];
125 const char *dash;
126 struct GNUNET_PeerIdentity peer;
127
128 if ((NULL == (dash = strchr (s, '-'))) ||
129 (1 != sscanf (s, "%103s-", line_port)) ||
130 (GNUNET_OK !=
131 GNUNET_CRYPTO_eddsa_public_key_from_string (dash + 1,
132 strlen (dash + 1),
133 &peer.public_key)))
134 {
135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
136 _ ("Unable to parse PHONE record `%s'\n"),
137 s);
138 return GNUNET_SYSERR;
139 }
140 pr = GNUNET_new (struct GNUNET_CONVERSATION_PhoneRecord);
141 pr->version = htonl (1);
142 pr->reserved = htonl (0);
143 if (GNUNET_OK !=
144 GNUNET_STRINGS_string_to_data (line_port,
145 strlen (line_port),
146 &pr->line_port,
147 sizeof(struct GNUNET_HashCode)))
148 {
149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 _ ("Unable to parse PHONE record `%s'\n"),
151 s);
152 GNUNET_free (pr);
153 return GNUNET_SYSERR;
154 }
155 pr->peer = peer;
156 *data = pr;
157 *data_size = sizeof(struct GNUNET_CONVERSATION_PhoneRecord);
158 return GNUNET_OK;
159 }
160
161 default:
162 return GNUNET_SYSERR;
163 }
164}
165
166
167/**
168 * Mapping of record type numbers to human-readable
169 * record type names.
170 */
171static struct
172{
173 const char *name;
174 uint32_t number;
175} name_map[] = {
176 { "PHONE", GNUNET_GNSRECORD_TYPE_PHONE },
177 { NULL, UINT32_MAX }
178};
179
180
181/**
182 * Convert a type name (e.g. "AAAA") to the corresponding number.
183 *
184 * @param cls closure, unused
185 * @param gns_typename name to convert
186 * @return corresponding number, UINT32_MAX on error
187 */
188static uint32_t
189conversation_typename_to_number (void *cls,
190 const char *gns_typename)
191{
192 unsigned int i;
193
194 (void) cls;
195 i = 0;
196 while ((name_map[i].name != NULL) &&
197 (0 != strcasecmp (gns_typename, name_map[i].name)))
198 i++;
199 return name_map[i].number;
200}
201
202
203/**
204 * Convert a type number to the corresponding type string (e.g. 1 to "A")
205 *
206 * @param cls closure, unused
207 * @param type number of a type to convert
208 * @return corresponding typestring, NULL on error
209 */
210static const char *
211conversation_number_to_typename (void *cls,
212 uint32_t type)
213{
214 unsigned int i;
215
216 (void) cls;
217 i = 0;
218 while ((name_map[i].name != NULL) &&
219 (type != name_map[i].number))
220 i++;
221 return name_map[i].name;
222}
223
224
225/**
226 * Entry point for the plugin.
227 *
228 * @param cls NULL
229 * @return the exported block API
230 */
231void *
232libgnunet_plugin_gnsrecord_conversation_init (void *cls)
233{
234 struct GNUNET_GNSRECORD_PluginFunctions *api;
235
236 (void) cls;
237 api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
238 api->value_to_string = &conversation_value_to_string;
239 api->string_to_value = &conversation_string_to_value;
240 api->typename_to_number = &conversation_typename_to_number;
241 api->number_to_typename = &conversation_number_to_typename;
242 return api;
243}
244
245
246/**
247 * Exit point from the plugin.
248 *
249 * @param cls the return value from #libgnunet_plugin_block_test_init
250 * @return NULL
251 */
252void *
253libgnunet_plugin_gnsrecord_conversation_done (void *cls)
254{
255 struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
256
257 GNUNET_free (api);
258 return NULL;
259}
260
261
262/* end of plugin_gnsrecord_conversation.c */
diff --git a/src/contrib/service/conversation/speaker.c b/src/contrib/service/conversation/speaker.c
new file mode 100644
index 000000000..38eb1159c
--- /dev/null
+++ b/src/contrib/service/conversation/speaker.c
@@ -0,0 +1,189 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file conversation/speaker.c
23 * @brief API to access an audio speaker; provides access to hardware speakers
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_speaker_lib.h"
30#include "conversation.h"
31
32
33/**
34 * Internal data structures for the speaker.
35 */
36struct Speaker
37{
38 /**
39 * Our configuration.
40 */
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42
43 /**
44 * Handle for the playback helper
45 */
46 struct GNUNET_HELPER_Handle *playback_helper;
47};
48
49
50/**
51 * Function that enables a speaker.
52 *
53 * @param cls closure with the `struct Speaker`
54 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
55 */
56static int
57enable (void *cls)
58{
59 struct Speaker *spe = cls;
60 static char *playback_helper_argv[] = {
61 "gnunet-helper-audio-playback",
62 NULL
63 };
64
65 spe->playback_helper = GNUNET_HELPER_start (GNUNET_NO,
66 "gnunet-helper-audio-playback",
67 playback_helper_argv,
68 NULL,
69 NULL, spe);
70 if (NULL == spe->playback_helper)
71 {
72 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
73 _ ("Could not start playback audio helper.\n"));
74 return GNUNET_SYSERR;
75 }
76 return GNUNET_OK;
77}
78
79
80/**
81 * Function that disables a speaker.
82 *
83 * @param cls closure with the `struct Speaker`
84 */
85static void
86disable (void *cls)
87{
88 struct Speaker *spe = cls;
89
90 if (NULL == spe->playback_helper)
91 {
92 GNUNET_break (0);
93 return;
94 }
95 GNUNET_break (GNUNET_OK ==
96 GNUNET_HELPER_kill (spe->playback_helper, GNUNET_NO));
97 GNUNET_HELPER_destroy (spe->playback_helper);
98 spe->playback_helper = NULL;
99}
100
101
102/**
103 * Function to destroy a speaker.
104 *
105 * @param cls closure with the `struct Speaker`
106 */
107static void
108destroy (void *cls)
109{
110 struct Speaker *spe = cls;
111
112 if (NULL != spe->playback_helper)
113 disable (spe);
114}
115
116
117/**
118 * Function to cause a speaker to play audio data.
119 *
120 * @param cls clsoure with the `struct Speaker`
121 * @param data_size number of bytes in @a data
122 * @param data audio data to play, format is
123 * opaque to the API but should be OPUS.
124 */
125static void
126play (void *cls,
127 size_t data_size,
128 const void *data)
129{
130 struct Speaker *spe = cls;
131 char buf[sizeof(struct AudioMessage) + data_size];
132 struct AudioMessage *am;
133
134 if (NULL == spe->playback_helper)
135 {
136 GNUNET_break (0);
137 return;
138 }
139 am = (struct AudioMessage *) buf;
140 am->header.size = htons (sizeof(struct AudioMessage) + data_size);
141 am->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO);
142 GNUNET_memcpy (&am[1], data, data_size);
143 (void) GNUNET_HELPER_send (spe->playback_helper,
144 &am->header,
145 GNUNET_NO,
146 NULL, NULL);
147}
148
149
150/**
151 * Create a speaker that corresponds to the speaker hardware
152 * of our system.
153 *
154 * @param cfg configuration to use
155 * @return NULL on error
156 */
157struct GNUNET_SPEAKER_Handle *
158GNUNET_SPEAKER_create_from_hardware (const struct
159 GNUNET_CONFIGURATION_Handle *cfg)
160{
161 struct GNUNET_SPEAKER_Handle *speaker;
162 struct Speaker *spe;
163
164 spe = GNUNET_new (struct Speaker);
165 spe->cfg = cfg;
166 speaker = GNUNET_new (struct GNUNET_SPEAKER_Handle);
167 speaker->cls = spe;
168 speaker->enable_speaker = &enable;
169 speaker->play = &play;
170 speaker->disable_speaker = &disable;
171 speaker->destroy_speaker = &destroy;
172 return speaker;
173}
174
175
176/**
177 * Destroy a speaker.
178 *
179 * @param speaker speaker to destroy
180 */
181void
182GNUNET_SPEAKER_destroy (struct GNUNET_SPEAKER_Handle *speaker)
183{
184 speaker->destroy_speaker (speaker->cls);
185 GNUNET_free (speaker);
186}
187
188
189/* end of speaker.c */
diff --git a/src/contrib/service/conversation/test.sh b/src/contrib/service/conversation/test.sh
new file mode 100644
index 000000000..20e82bc88
--- /dev/null
+++ b/src/contrib/service/conversation/test.sh
@@ -0,0 +1,4 @@
1#!/bin/sh
2
3export GST_DEBUG_DUMP_DOT_DIR=/tmp/
4GST_DEBUG_DUMP_DOT_DIR=/tmp/ ./gnunet-helper-audio-record |GST_DEBUG_DUMP_DOT_DIR=/tmp/ ./gnunet-helper-audio-playback
diff --git a/src/contrib/service/conversation/test_conversation.conf b/src/contrib/service/conversation/test_conversation.conf
new file mode 100644
index 000000000..097aed592
--- /dev/null
+++ b/src/contrib/service/conversation/test_conversation.conf
@@ -0,0 +1,12 @@
1@INLINE@ ../../../../contrib/conf/gnunet/no_forcestart.conf
2
3[conversation]
4LINE=1
5#PREFIX = valgrind
6
7[zonemaster]
8IMMEDIATE_START = YES
9START_ON_DEMAND = YES
10
11[nse]
12WORKBITS = 0
diff --git a/src/contrib/service/conversation/test_conversation_api.c b/src/contrib/service/conversation/test_conversation_api.c
new file mode 100644
index 000000000..decc40b64
--- /dev/null
+++ b/src/contrib/service/conversation/test_conversation_api.c
@@ -0,0 +1,510 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/test_conversation_api.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of a call to a phone
25 * where the phone user picks up and then the call is
26 * terminated by the party that initiated the call.
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_conversation_service.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_namestore_service.h"
35
36#define FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
37
38#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
39
40static int ok = 1;
41
42static const struct GNUNET_CONFIGURATION_Handle *cfg;
43
44static struct GNUNET_IDENTITY_Handle *id;
45
46static struct GNUNET_IDENTITY_Operation *op;
47
48static struct GNUNET_CONVERSATION_Phone *phone;
49
50static struct GNUNET_NAMESTORE_Handle *ns;
51
52static struct GNUNET_CONVERSATION_Call *call;
53
54static struct GNUNET_NAMESTORE_QueueEntry *qe;
55
56static struct GNUNET_CONVERSATION_Caller *active_caller;
57
58static char *gns_name;
59
60static char *gns_caller_id;
61
62static GNUNET_MICROPHONE_RecordedDataCallback phone_rdc;
63
64static void *phone_rdc_cls;
65
66static GNUNET_MICROPHONE_RecordedDataCallback call_rdc;
67
68static void *call_rdc_cls;
69
70static struct GNUNET_SCHEDULER_Task *phone_task;
71
72static struct GNUNET_SCHEDULER_Task *call_task;
73
74
75static void
76phone_send (void *cls)
77{
78 static unsigned int i;
79 char buf[32];
80
81 (void) cls;
82 GNUNET_assert (NULL != phone_rdc);
83 GNUNET_snprintf (buf, sizeof(buf), "phone-%u", i++);
84 phone_rdc (phone_rdc_cls, strlen (buf) + 1, buf);
85 phone_task = GNUNET_SCHEDULER_add_delayed (FREQ, &phone_send, NULL);
86}
87
88
89static void
90call_send (void *cls)
91{
92 static unsigned int i;
93 char buf[32];
94
95 (void) cls;
96 GNUNET_assert (NULL != call_rdc);
97 GNUNET_snprintf (buf, sizeof(buf), "call-%u", i++);
98 call_rdc (call_rdc_cls, strlen (buf) + 1, buf);
99 call_task = GNUNET_SCHEDULER_add_delayed (FREQ, &call_send, NULL);
100}
101
102
103static int
104enable_speaker (void *cls)
105{
106 const char *origin = cls;
107
108 fprintf (stderr, "Speaker %s enabled\n", origin);
109 return GNUNET_OK;
110}
111
112
113static void
114disable_speaker (void *cls)
115{
116 const char *origin = cls;
117
118 fprintf (stderr, "Speaker %s disabled\n", origin);
119}
120
121
122static void
123play (void *cls, size_t data_size, const void *data)
124{
125 const char *origin = cls;
126 static unsigned int phone_i = 1;
127 static unsigned int call_i;
128 char buf[32];
129
130 if (0 == strcmp (origin, "phone"))
131 GNUNET_snprintf (buf, sizeof(buf), "call-%u", call_i++);
132 else
133 GNUNET_snprintf (buf, sizeof(buf), "phone-%u", phone_i++);
134 if ((data_size != strlen (buf) + 1) || (0 != strncmp (buf, data, data_size)))
135 {
136 fprintf (stderr,
137 "Expected %s, received %.*s\n",
138 buf,
139 (int) data_size,
140 (const char *) data);
141 }
142 else
143 {
144 fprintf (stderr, ".");
145 }
146 if ((20 < call_i) && (20 < phone_i) && (NULL != call))
147 {
148 /* time to hang up ... */
149 GNUNET_CONVERSATION_call_stop (call);
150 call = NULL;
151 }
152}
153
154
155static void
156destroy_speaker (void *cls)
157{
158 const char *origin = cls;
159
160 fprintf (stderr, "Speaker %s destroyed\n", origin);
161}
162
163
164static struct GNUNET_SPEAKER_Handle call_speaker = { &enable_speaker,
165 &play,
166 &disable_speaker,
167 &destroy_speaker,
168 "caller" };
169
170
171static struct GNUNET_SPEAKER_Handle phone_speaker = { &enable_speaker,
172 &play,
173 &disable_speaker,
174 &destroy_speaker,
175 "phone" };
176
177
178static int
179enable_mic (void *cls,
180 GNUNET_MICROPHONE_RecordedDataCallback rdc,
181 void *rdc_cls)
182{
183 const char *origin = cls;
184
185 fprintf (stderr, "Mic %s enabled\n", origin);
186 if (0 == strcmp (origin, "phone"))
187 {
188 phone_rdc = rdc;
189 phone_rdc_cls = rdc_cls;
190 phone_task = GNUNET_SCHEDULER_add_now (&phone_send, NULL);
191 }
192 else
193 {
194 call_rdc = rdc;
195 call_rdc_cls = rdc_cls;
196 call_task = GNUNET_SCHEDULER_add_now (&call_send, NULL);
197 }
198 return GNUNET_OK;
199}
200
201
202static void
203disable_mic (void *cls)
204{
205 const char *origin = cls;
206
207 fprintf (stderr, "Mic %s disabled\n", origin);
208 if (0 == strcmp (origin, "phone"))
209 {
210 phone_rdc = NULL;
211 phone_rdc_cls = NULL;
212 GNUNET_SCHEDULER_cancel (phone_task);
213 phone_task = NULL;
214 }
215 else
216 {
217 call_rdc = NULL;
218 call_rdc_cls = NULL;
219 GNUNET_SCHEDULER_cancel (call_task);
220 call_task = NULL;
221 }
222}
223
224
225static void
226destroy_mic (void *cls)
227{
228 const char *origin = cls;
229
230 fprintf (stderr, "Mic %s destroyed\n", origin);
231}
232
233
234static struct GNUNET_MICROPHONE_Handle call_mic = { &enable_mic,
235 &disable_mic,
236 &destroy_mic,
237 "caller" };
238
239
240static struct GNUNET_MICROPHONE_Handle phone_mic = { &enable_mic,
241 &disable_mic,
242 &destroy_mic,
243 "phone" };
244
245
246/**
247 * Signature of the main function of a task.
248 *
249 * @param cls closure
250 */
251static void
252end_test (void *cls)
253{
254 (void) cls;
255 GNUNET_SCHEDULER_shutdown ();
256 if (NULL != op)
257 {
258 GNUNET_IDENTITY_cancel (op);
259 op = NULL;
260 }
261 if (NULL != call)
262 {
263 GNUNET_CONVERSATION_call_stop (call);
264 call = NULL;
265 }
266 if (NULL != phone)
267 {
268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
269 GNUNET_CONVERSATION_phone_destroy (phone);
270 phone = NULL;
271 }
272 if (NULL != id)
273 {
274 GNUNET_IDENTITY_disconnect (id);
275 id = NULL;
276 }
277 if (NULL != qe)
278 {
279 GNUNET_NAMESTORE_cancel (qe);
280 qe = NULL;
281 }
282 if (NULL != ns)
283 {
284 GNUNET_NAMESTORE_disconnect (ns);
285 ns = NULL;
286 }
287}
288
289
290static void
291caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
292{
293 (void) cls;
294 switch (code)
295 {
296 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
297 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
298 fprintf (stderr, "Unexpected caller code: %d\n", code);
299 break;
300 }
301}
302
303
304static void
305phone_event_handler (void *cls,
306 enum GNUNET_CONVERSATION_PhoneEventCode code,
307 struct GNUNET_CONVERSATION_Caller *caller,
308 const struct GNUNET_CRYPTO_PublicKey *caller_id)
309{
310 static enum GNUNET_CONVERSATION_PhoneEventCode expect =
311 GNUNET_CONVERSATION_EC_PHONE_RING;
312
313 (void) cls;
314 (void) caller_id;
315 GNUNET_break (code == expect);
316 switch (code)
317 {
318 case GNUNET_CONVERSATION_EC_PHONE_RING:
319 active_caller = caller;
320 GNUNET_CONVERSATION_caller_pick_up (caller,
321 &caller_event_handler,
322 NULL,
323 &phone_speaker,
324 &phone_mic);
325 expect = GNUNET_CONVERSATION_EC_PHONE_HUNG_UP;
326 break;
327
328 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
329 GNUNET_break (caller == active_caller);
330 active_caller = NULL;
331 if (1 == ok)
332 ok = 0;
333 GNUNET_SCHEDULER_shutdown ();
334 break;
335
336 default:
337 fprintf (stderr, "Unexpected phone code: %d\n", code);
338 break;
339 }
340}
341
342
343static void
344call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
345{
346 static enum GNUNET_CONVERSATION_CallEventCode expect =
347 GNUNET_CONVERSATION_EC_CALL_RINGING;
348
349 (void) cls;
350 GNUNET_break (code == expect);
351 switch (code)
352 {
353 case GNUNET_CONVERSATION_EC_CALL_RINGING:
354 expect = GNUNET_CONVERSATION_EC_CALL_PICKED_UP;
355 break;
356
357 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
358 expect = -1;
359 break;
360
361 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
362 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
363 call = NULL;
364 ok = 2;
365 GNUNET_break (0);
366 fprintf (stderr, "Unexpected call code: %d\n", code);
367 break;
368
369 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
370 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
371 GNUNET_break (0);
372 fprintf (stderr, "Unexpected call code: %d\n", code);
373 ok = 2;
374 break;
375
376 case GNUNET_CONVERSATION_EC_CALL_ERROR:
377 GNUNET_break (0);
378 fprintf (stderr, "Unexpected call code: %d\n", code);
379 call = NULL;
380 ok = 2;
381 break;
382 }
383}
384
385
386static void
387caller_ego_create_cont (void *cls,
388 const struct GNUNET_CRYPTO_PrivateKey *pk,
389 enum GNUNET_ErrorCode ec)
390{
391 (void) cls;
392 op = NULL;
393 GNUNET_assert (GNUNET_EC_NONE == ec);
394}
395
396
397static void
398namestore_put_cont (void *cls, enum GNUNET_ErrorCode ec)
399{
400 (void) cls;
401 qe = NULL;
402 GNUNET_assert (GNUNET_EC_NONE == ec);
403 GNUNET_assert (NULL == op);
404 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
405 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
406 &caller_ego_create_cont,
407 NULL);
408}
409
410
411static void
412identity_cb (void *cls,
413 struct GNUNET_IDENTITY_Ego *ego,
414 void **ctx,
415 const char *name)
416{
417 struct GNUNET_GNSRECORD_Data rd;
418 struct GNUNET_CRYPTO_PublicKey pub;
419
420 (void) cls;
421 (void) ctx;
422 if (NULL == name)
423 return;
424 if (NULL == ego)
425 return;
426 if (0 == strcmp (name, "phone-ego"))
427 {
428 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
429 GNUNET_asprintf (&gns_name,
430 "phone.%s",
431 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
432 phone =
433 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
434 GNUNET_assert (NULL != phone);
435 memset (&rd, 0, sizeof(rd));
436 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
437 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
438 rd.expiration_time = UINT64_MAX;
439 qe =
440 GNUNET_NAMESTORE_record_set_store (ns,
441 GNUNET_IDENTITY_ego_get_private_key (ego),
442 "phone" /* GNS label */,
443 1,
444 &rd,
445 &namestore_put_cont,
446 NULL);
447 return;
448 }
449 if (0 == strcmp (name, "caller-ego"))
450 {
451 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
452 GNUNET_asprintf (&gns_caller_id,
453 "%s",
454 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
455 call = GNUNET_CONVERSATION_call_start (cfg,
456 ego,
457 gns_name,
458 &call_speaker,
459 &call_mic,
460 &call_event_handler,
461 NULL);
462 return;
463 }
464}
465
466
467static void
468phone_ego_create_cont (void *cls,
469 const struct GNUNET_CRYPTO_PrivateKey *pk,
470 enum GNUNET_ErrorCode ec)
471{
472 (void) cls;
473 op = NULL;
474 GNUNET_assert (GNUNET_EC_NONE == ec);
475}
476
477
478static void
479run (void *cls,
480 const struct GNUNET_CONFIGURATION_Handle *c,
481 struct GNUNET_TESTING_Peer *peer)
482{
483 (void) cls;
484 (void) peer;
485 cfg = c;
486 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
487 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
488 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
489 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
490 &phone_ego_create_cont,
491 NULL);
492 ns = GNUNET_NAMESTORE_connect (cfg);
493}
494
495
496int
497main (int argc, char *argv[])
498{
499 (void) argc;
500 (void) argv;
501 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api",
502 "test_conversation.conf",
503 &run,
504 NULL))
505 return 1;
506 return ok;
507}
508
509
510/* end of test_conversation_api.c */
diff --git a/src/contrib/service/conversation/test_conversation_api_reject.c b/src/contrib/service/conversation/test_conversation_api_reject.c
new file mode 100644
index 000000000..248c7835d
--- /dev/null
+++ b/src/contrib/service/conversation/test_conversation_api_reject.c
@@ -0,0 +1,363 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/test_conversation_api_reject.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of a call to a phone
25 * where the phone user immediately hangs up (rejecting the
26 * call).
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_conversation_service.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_namestore_service.h"
35
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
37
38static int ok = 1;
39
40static const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42static struct GNUNET_IDENTITY_Handle *id;
43
44static struct GNUNET_IDENTITY_Operation *op;
45
46static struct GNUNET_CONVERSATION_Phone *phone;
47
48static struct GNUNET_NAMESTORE_Handle *ns;
49
50static struct GNUNET_CONVERSATION_Call *call;
51
52static struct GNUNET_NAMESTORE_QueueEntry *qe;
53
54static char *gns_name;
55
56static char *gns_caller_id;
57
58
59static int
60enable_speaker (void *cls)
61{
62 (void) cls;
63 GNUNET_break (0);
64 return GNUNET_SYSERR;
65}
66
67
68static void
69disable_speaker (void *cls)
70{
71 (void) cls;
72 GNUNET_break (0);
73}
74
75
76static void
77play (void *cls, size_t data_size, const void *data)
78{
79 (void) cls;
80 (void) data_size;
81 (void) data;
82 GNUNET_break (0);
83}
84
85
86static void
87destroy_speaker (void *cls)
88{
89 (void) cls;
90}
91
92
93static struct GNUNET_SPEAKER_Handle call_speaker = { &enable_speaker,
94 &play,
95 &disable_speaker,
96 &destroy_speaker,
97 "caller" };
98
99
100static int
101enable_mic (void *cls,
102 GNUNET_MICROPHONE_RecordedDataCallback rdc,
103 void *rdc_cls)
104{
105 (void) cls;
106 (void) rdc;
107 (void) rdc_cls;
108 GNUNET_break (0);
109 return GNUNET_SYSERR;
110}
111
112
113static void
114disable_mic (void *cls)
115{
116 (void) cls;
117 GNUNET_break (0);
118}
119
120
121static void
122destroy_mic (void *cls)
123{
124 (void) cls;
125}
126
127
128static struct GNUNET_MICROPHONE_Handle call_mic = { &enable_mic,
129 &disable_mic,
130 &destroy_mic,
131 "caller" };
132
133
134/**
135 * Signature of the main function of a task.
136 *
137 * @param cls closure
138 */
139static void
140end_test (void *cls)
141{
142 (void) cls;
143 GNUNET_SCHEDULER_shutdown ();
144 if (NULL != op)
145 {
146 GNUNET_IDENTITY_cancel (op);
147 op = NULL;
148 }
149 if (NULL != call)
150 {
151 GNUNET_CONVERSATION_call_stop (call);
152 call = NULL;
153 }
154 if (NULL != phone)
155 {
156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
157 GNUNET_CONVERSATION_phone_destroy (phone);
158 phone = NULL;
159 }
160 if (NULL != id)
161 {
162 GNUNET_IDENTITY_disconnect (id);
163 id = NULL;
164 }
165 if (NULL != qe)
166 {
167 GNUNET_NAMESTORE_cancel (qe);
168 qe = NULL;
169 }
170 if (NULL != ns)
171 {
172 GNUNET_NAMESTORE_disconnect (ns);
173 ns = NULL;
174 }
175}
176
177
178static void
179phone_event_handler (void *cls,
180 enum GNUNET_CONVERSATION_PhoneEventCode code,
181 struct GNUNET_CONVERSATION_Caller *caller,
182 const struct GNUNET_CRYPTO_PublicKey *caller_id)
183{
184 static enum GNUNET_CONVERSATION_PhoneEventCode expect =
185 GNUNET_CONVERSATION_EC_PHONE_RING;
186
187 (void) cls;
188 (void) caller_id;
189 GNUNET_break (code == expect);
190 switch (code)
191 {
192 case GNUNET_CONVERSATION_EC_PHONE_RING:
193 GNUNET_CONVERSATION_caller_hang_up (caller);
194 break;
195
196 default:
197 fprintf (stderr, "Unexpected phone code: %d\n", code);
198 break;
199 }
200}
201
202
203static void
204call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
205{
206 static enum GNUNET_CONVERSATION_CallEventCode expect =
207 GNUNET_CONVERSATION_EC_CALL_RINGING;
208
209 (void) cls;
210 GNUNET_break (code == expect);
211 switch (code)
212 {
213 case GNUNET_CONVERSATION_EC_CALL_RINGING:
214 expect = GNUNET_CONVERSATION_EC_CALL_HUNG_UP;
215 break;
216
217 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
218 call = NULL;
219 ok = 0;
220 GNUNET_SCHEDULER_shutdown ();
221 expect = -1;
222 break;
223
224 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
225 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
226 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
227 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
228 fprintf (stderr, "Unexpected call code: %d\n", code);
229 break;
230
231 case GNUNET_CONVERSATION_EC_CALL_ERROR:
232 fprintf (stderr, "Unexpected call code: %d\n", code);
233 call = NULL;
234 break;
235 }
236}
237
238
239static void
240caller_ego_create_cont (void *cls,
241 const struct GNUNET_CRYPTO_PrivateKey *pk,
242 enum GNUNET_ErrorCode ec)
243{
244 (void) cls;
245 op = NULL;
246 GNUNET_assert (GNUNET_EC_NONE == ec);
247}
248
249
250static void
251namestore_put_cont (void *cls, enum GNUNET_ErrorCode ec)
252{
253 (void) cls;
254 qe = NULL;
255 GNUNET_assert (GNUNET_EC_NONE == ec);
256 GNUNET_assert (NULL == op);
257 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
258 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
259 &caller_ego_create_cont,
260 NULL);
261}
262
263
264static void
265identity_cb (void *cls,
266 struct GNUNET_IDENTITY_Ego *ego,
267 void **ctx,
268 const char *name)
269{
270 struct GNUNET_GNSRECORD_Data rd;
271 struct GNUNET_CRYPTO_PublicKey pub;
272
273 (void) cls;
274 (void) ctx;
275 if (NULL == name)
276 return;
277 if (NULL == ego)
278 return;
279 if (0 == strcmp (name, "phone-ego"))
280 {
281 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
282 GNUNET_asprintf (&gns_name,
283 "phone.%s",
284 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
285 phone =
286 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
287 GNUNET_assert (NULL != phone);
288 memset (&rd, 0, sizeof(rd));
289 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
290 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
291 rd.expiration_time = UINT64_MAX;
292 qe =
293 GNUNET_NAMESTORE_record_set_store (ns,
294 GNUNET_IDENTITY_ego_get_private_key (ego),
295 "phone" /* GNS label */,
296 1,
297 &rd,
298 &namestore_put_cont,
299 NULL);
300 return;
301 }
302 if (0 == strcmp (name, "caller-ego"))
303 {
304 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
305 GNUNET_asprintf (&gns_caller_id,
306 "%s",
307 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
308 call = GNUNET_CONVERSATION_call_start (cfg,
309 ego,
310 gns_name,
311 &call_speaker,
312 &call_mic,
313 &call_event_handler,
314 NULL);
315 return;
316 }
317}
318
319
320static void
321phone_ego_create_cont (void *cls,
322 const struct GNUNET_CRYPTO_PrivateKey *pk,
323 enum GNUNET_ErrorCode ec)
324{
325 (void) cls;
326 op = NULL;
327 GNUNET_assert (GNUNET_EC_NONE == ec);
328}
329
330
331static void
332run (void *cls,
333 const struct GNUNET_CONFIGURATION_Handle *c,
334 struct GNUNET_TESTING_Peer *peer)
335{
336 (void) cls;
337 (void) peer;
338 cfg = c;
339 GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
340 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
341 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
342 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
343 &phone_ego_create_cont,
344 NULL);
345 ns = GNUNET_NAMESTORE_connect (cfg);
346}
347
348
349int
350main (int argc, char *argv[])
351{
352 (void) argc;
353 (void) argv;
354 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api",
355 "test_conversation.conf",
356 &run,
357 NULL))
358 return 1;
359 return ok;
360}
361
362
363/* end of test_conversation_api_reject.c */
diff --git a/src/contrib/service/conversation/test_conversation_api_twocalls.c b/src/contrib/service/conversation/test_conversation_api_twocalls.c
new file mode 100644
index 000000000..cc4969e09
--- /dev/null
+++ b/src/contrib/service/conversation/test_conversation_api_twocalls.c
@@ -0,0 +1,642 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2018x GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file conversation/test_conversation_api_twocalls.c
22 * @brief testcase for conversation_api.c
23 *
24 * This test performs the operations of TWO calls made to a phone
25 * where the phone user picks up one, suspends it, picks up the
26 * second one; eventually, the initiator hangs up, the callee
27 * resumes the first call, and then the initiator hangs up the
28 * second call.
29 */
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_testing_lib.h"
33#include "gnunet_gnsrecord_lib.h"
34#include "gnunet_conversation_service.h"
35#include "gnunet_identity_service.h"
36#include "gnunet_namestore_service.h"
37
38#define FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
39
40#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 25)
41
42#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
43
44#define LOG_DEBUG(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
45
46static const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48static struct GNUNET_IDENTITY_Handle *id;
49
50static struct GNUNET_IDENTITY_Operation *op;
51
52static struct GNUNET_CONVERSATION_Phone *phone;
53
54static struct GNUNET_NAMESTORE_Handle *ns;
55
56static struct GNUNET_CONVERSATION_Call *call1;
57
58static struct GNUNET_CONVERSATION_Call *call2;
59
60static struct GNUNET_NAMESTORE_QueueEntry *qe;
61
62static struct GNUNET_CONVERSATION_Caller *active_caller1;
63
64static struct GNUNET_CONVERSATION_Caller *active_caller2;
65
66static char *gns_name;
67
68static char *gns_caller_id;
69
70static GNUNET_MICROPHONE_RecordedDataCallback phone_rdc;
71
72static void *phone_rdc_cls;
73
74static struct GNUNET_SCHEDULER_Task *phone_task;
75
76static struct GNUNET_SCHEDULER_Task *timeout_task;
77
78/**
79 * Variable for recognizing caller1
80 */
81static const char *caller1 = "caller1";
82
83/**
84 * Variable for recognizing caller2
85 */
86static const char *caller2 = "caller2";
87
88/**
89 * Variable for recognizing callee
90 */
91static const char *phone0 = "phone";
92
93
94#define CALLER1 &caller1
95#define CALLER2 &caller2
96#define PHONE0 &phone0
97
98#define CLS_STR(caller) (*((char **) caller))
99
100
101/**
102 * Did caller1 call finish successfully
103 */
104static int call1_finished;
105
106/**
107 * Did caller2 call finish successfully
108 */
109static int call2_finished;
110
111struct MicContext
112{
113 GNUNET_MICROPHONE_RecordedDataCallback rdc;
114
115 void *rdc_cls;
116
117 struct GNUNET_SCHEDULER_Task *call_task;
118};
119
120static struct MicContext call1_mic_ctx;
121static struct MicContext call2_mic_ctx;
122// static struct MicContext phone_mic_ctx;
123
124
125static void
126phone_send (void *cls)
127{
128 char buf[32];
129
130 (void) cls;
131 GNUNET_assert (NULL != phone_rdc);
132 GNUNET_snprintf (buf, sizeof(buf), "phone");
133 phone_rdc (phone_rdc_cls, strlen (buf) + 1, buf);
134 phone_task = GNUNET_SCHEDULER_add_delayed (FREQ, &phone_send, NULL);
135}
136
137
138static void
139call_send (void *cls)
140{
141 struct MicContext *mc = cls;
142 char buf[32];
143
144 (void) cls;
145 GNUNET_assert (NULL != mc->rdc);
146 GNUNET_snprintf (buf, sizeof(buf), "call");
147 mc->rdc (mc->rdc_cls, strlen (buf) + 1, buf);
148 mc->call_task = GNUNET_SCHEDULER_add_delayed (FREQ, &call_send, mc);
149}
150
151
152static int
153enable_speaker (void *cls)
154{
155 const char *origin = CLS_STR (cls);
156
157 (void) cls;
158 LOG_DEBUG ("Speaker %s enabled\n", origin);
159 return GNUNET_OK;
160}
161
162
163static void
164disable_speaker (void *cls)
165{
166 const char *origin = CLS_STR (cls);
167
168 (void) cls;
169 LOG_DEBUG ("Speaker %s disabled\n", origin);
170}
171
172
173static void
174play (void *cls, size_t data_size, const void *data)
175{
176 static unsigned int phone_i;
177 static unsigned int call_i;
178
179 (void) cls;
180 if (0 == strncmp ("call", data, data_size))
181 call_i++;
182 else if (0 == strncmp ("phone", data, data_size))
183 phone_i++;
184 else
185 {
186 LOG_DEBUG ("Received %u bytes of unexpected data `%.*s'\n",
187 (unsigned int) data_size,
188 (int) data_size,
189 (const char *) data);
190 }
191
192 if ((20 < call_i) && (20 < phone_i) && (CALLER2 == cls))
193 {
194 /* time to hang up ... */
195 GNUNET_CONVERSATION_call_stop (call2);
196 call2 = NULL;
197 /* reset counters */
198 call_i = 0;
199 phone_i = 0;
200 call2_finished = GNUNET_YES;
201 }
202 if ((20 < call_i) && (20 < phone_i) && (CALLER1 == cls))
203 {
204 /* time to hang up ... */
205 GNUNET_CONVERSATION_call_stop (call1);
206 call1 = NULL;
207 call_i = 0;
208 phone_i = 0;
209 call1_finished = GNUNET_YES;
210 }
211}
212
213
214static void
215destroy_speaker (void *cls)
216{
217 const char *origin = CLS_STR (cls);
218
219 LOG_DEBUG ("Speaker %s destroyed\n", origin);
220}
221
222
223static struct GNUNET_SPEAKER_Handle call1_speaker = { &enable_speaker,
224 &play,
225 &disable_speaker,
226 &destroy_speaker,
227 CALLER1 };
228
229
230static struct GNUNET_SPEAKER_Handle call2_speaker = { &enable_speaker,
231 &play,
232 &disable_speaker,
233 &destroy_speaker,
234 CALLER2 };
235
236
237static struct GNUNET_SPEAKER_Handle phone_speaker = { &enable_speaker,
238 &play,
239 &disable_speaker,
240 &destroy_speaker,
241 PHONE0 };
242
243
244static int
245enable_mic (void *cls,
246 GNUNET_MICROPHONE_RecordedDataCallback rdc,
247 void *rdc_cls)
248{
249 const char *origin = CLS_STR (cls);
250 struct MicContext *mc;
251
252 LOG_DEBUG ("Mic %s enabled\n", origin);
253 if (PHONE0 == cls)
254 {
255 phone_rdc = rdc;
256 phone_rdc_cls = rdc_cls;
257 GNUNET_break (NULL == phone_task);
258 phone_task = GNUNET_SCHEDULER_add_now (&phone_send, NULL);
259 return GNUNET_OK;
260 }
261 mc = (CALLER1 == cls) ? &call1_mic_ctx : &call2_mic_ctx;
262 mc->rdc = rdc;
263 mc->rdc_cls = rdc_cls;
264 GNUNET_break (NULL == mc->call_task);
265 mc->call_task = GNUNET_SCHEDULER_add_now (&call_send, mc);
266 return GNUNET_OK;
267}
268
269
270static void
271disable_mic (void *cls)
272{
273 const char *origin = CLS_STR (cls);
274 struct MicContext *mc;
275
276 LOG_DEBUG ("Mic %s disabled\n", origin);
277 if (PHONE0 == cls)
278 {
279 phone_rdc = NULL;
280 phone_rdc_cls = NULL;
281 GNUNET_SCHEDULER_cancel (phone_task);
282 phone_task = NULL;
283 return;
284 }
285 mc = (CALLER1 == cls) ? &call1_mic_ctx : &call2_mic_ctx;
286 mc->rdc = NULL;
287 mc->rdc_cls = NULL;
288 GNUNET_SCHEDULER_cancel (mc->call_task);
289 mc->call_task = NULL;
290}
291
292
293static void
294destroy_mic (void *cls)
295{
296 const char *origin = CLS_STR (cls);
297
298 LOG_DEBUG ("Mic %s destroyed\n", origin);
299}
300
301
302static struct GNUNET_MICROPHONE_Handle call1_mic = { &enable_mic,
303 &disable_mic,
304 &destroy_mic,
305 CALLER1 };
306
307
308static struct GNUNET_MICROPHONE_Handle call2_mic = { &enable_mic,
309 &disable_mic,
310 &destroy_mic,
311 CALLER2 };
312
313
314static struct GNUNET_MICROPHONE_Handle phone_mic = { &enable_mic,
315 &disable_mic,
316 &destroy_mic,
317 PHONE0 };
318
319
320/**
321 * Function run on timeout.
322 *
323 * @param cls closure
324 */
325static void
326end_test (void *cls)
327{
328 (void) cls;
329 timeout_task = NULL;
330 fprintf (stderr, "Timeout!\n");
331 GNUNET_SCHEDULER_shutdown ();
332}
333
334
335/**
336 * Function run on shutdown.
337 *
338 * @param cls closure
339 */
340static void
341do_shutdown (void *cls)
342{
343 (void) cls;
344 if (NULL != timeout_task)
345 {
346 GNUNET_SCHEDULER_cancel (timeout_task);
347 timeout_task = NULL;
348 }
349 if (NULL != op)
350 {
351 GNUNET_IDENTITY_cancel (op);
352 op = NULL;
353 }
354 if (NULL != call1)
355 {
356 GNUNET_CONVERSATION_call_stop (call1);
357 call1 = NULL;
358 }
359 if (NULL != call2)
360 {
361 GNUNET_CONVERSATION_call_stop (call2);
362 call2 = NULL;
363 }
364 if (NULL != phone)
365 {
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from PHONE service.\n");
367 GNUNET_CONVERSATION_phone_destroy (phone);
368 phone = NULL;
369 }
370 if (NULL != id)
371 {
372 GNUNET_IDENTITY_disconnect (id);
373 id = NULL;
374 }
375 if (NULL != qe)
376 {
377 GNUNET_NAMESTORE_cancel (qe);
378 qe = NULL;
379 }
380 if (NULL != ns)
381 {
382 GNUNET_NAMESTORE_disconnect (ns);
383 ns = NULL;
384 }
385}
386
387
388static void
389caller_event_handler (void *cls, enum GNUNET_CONVERSATION_CallerEventCode code)
390{
391 (void) cls;
392 switch (code)
393 {
394 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
395 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
396 LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected caller code: %d\n", code);
397 break;
398 }
399}
400
401
402static void
403phone_event_handler (void *cls,
404 enum GNUNET_CONVERSATION_PhoneEventCode code,
405 struct GNUNET_CONVERSATION_Caller *caller,
406 const struct GNUNET_CRYPTO_PublicKey *caller_id)
407{
408 const char *cid;
409
410 (void) cls;
411 (void) caller_id;
412
413 switch (code)
414 {
415 case GNUNET_CONVERSATION_EC_PHONE_RING:
416 if (NULL == active_caller1)
417 {
418 active_caller1 = caller;
419 cid = "caller1";
420 GNUNET_CONVERSATION_caller_pick_up (caller,
421 &caller_event_handler,
422 (void *) cid,
423 &phone_speaker,
424 &phone_mic);
425 }
426 else
427 {
428 GNUNET_CONVERSATION_caller_suspend (active_caller1);
429 active_caller2 = caller;
430 cid = "caller2";
431 GNUNET_CONVERSATION_caller_pick_up (caller,
432 &caller_event_handler,
433 (void *) cid,
434 &phone_speaker,
435 &phone_mic);
436 }
437 break;
438
439 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
440 if (caller == active_caller2)
441 {
442 active_caller2 = NULL;
443 GNUNET_CONVERSATION_caller_resume (active_caller1,
444 &phone_speaker,
445 &phone_mic);
446 }
447 else if (caller == active_caller1)
448 {
449 active_caller1 = NULL;
450 GNUNET_break (NULL == active_caller2);
451 GNUNET_SCHEDULER_shutdown ();
452 }
453 break;
454
455 default:
456 LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected phone code: %d\n", code);
457 break;
458 }
459}
460
461
462static void
463call_event_handler (void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
464{
465 const char *cid = cls;
466
467 switch (code)
468 {
469 case GNUNET_CONVERSATION_EC_CALL_RINGING:
470 break;
471
472 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
473 LOG_DEBUG ("Call %s picked\n", cid);
474 break;
475
476 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
477 LOG_DEBUG ("Call %s GNS lookup failed \n", cid);
478 break;
479
480 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
481 LOG_DEBUG ("Call %s hungup\n", cid);
482 if (0 == strcmp (cid, "call1"))
483 call1 = NULL;
484 else
485 call2 = NULL;
486 break;
487
488 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
489 LOG_DEBUG ("Call %s suspended\n", cid);
490 break;
491
492 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
493 LOG_DEBUG ("Call %s resumed\n", cid);
494 break;
495
496 case GNUNET_CONVERSATION_EC_CALL_ERROR:
497 GNUNET_break (0);
498 if (0 == strcmp (cid, "call1"))
499 call1 = NULL;
500 else
501 call2 = NULL;
502 GNUNET_SCHEDULER_shutdown ();
503 break;
504 }
505}
506
507
508static void
509caller_ego_create_cont (void *cls,
510 const struct GNUNET_CRYPTO_PrivateKey *pk,
511 enum GNUNET_ErrorCode ec)
512{
513 (void) cls;
514 op = NULL;
515 GNUNET_assert (GNUNET_EC_NONE == ec);
516}
517
518
519static void
520namestore_put_cont (void *cls, enum GNUNET_ErrorCode ec)
521{
522 (void) cls;
523 qe = NULL;
524 GNUNET_assert (GNUNET_EC_NONE == ec);
525 GNUNET_assert (NULL == op);
526 op = GNUNET_IDENTITY_create (id, "caller-ego", NULL,
527 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
528 &caller_ego_create_cont,
529 NULL);
530}
531
532
533static void
534identity_cb (void *cls,
535 struct GNUNET_IDENTITY_Ego *ego,
536 void **ctx,
537 const char *name)
538{
539 struct GNUNET_GNSRECORD_Data rd;
540 struct GNUNET_CRYPTO_PublicKey pub;
541
542 (void) cls;
543 (void) ctx;
544 if (NULL == name)
545 return;
546 if (NULL == ego)
547 return;
548 if (0 == strcmp (name, "phone-ego"))
549 {
550 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
551 GNUNET_asprintf (&gns_name,
552 "phone.%s",
553 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
554 phone =
555 GNUNET_CONVERSATION_phone_create (cfg, ego, &phone_event_handler, NULL);
556 GNUNET_assert (NULL != phone);
557 memset (&rd, 0, sizeof(rd));
558 GNUNET_CONVERSATION_phone_get_record (phone, &rd);
559 GNUNET_assert (rd.record_type == GNUNET_GNSRECORD_TYPE_PHONE);
560 rd.expiration_time = UINT64_MAX;
561 qe =
562 GNUNET_NAMESTORE_record_set_store (ns,
563 GNUNET_IDENTITY_ego_get_private_key (ego),
564 "phone" /* GNS label */,
565 1,
566 &rd,
567 &namestore_put_cont,
568 NULL);
569 return;
570 }
571 if (0 == strcmp (name, "caller-ego"))
572 {
573 GNUNET_IDENTITY_ego_get_public_key (ego, &pub);
574 GNUNET_asprintf (&gns_caller_id,
575 "%s",
576 GNUNET_GNSRECORD_pkey_to_zkey (&pub));
577 call1 = GNUNET_CONVERSATION_call_start (cfg,
578 ego,
579 gns_name,
580 &call1_speaker,
581 &call1_mic,
582 &call_event_handler,
583 (void *) "call1");
584 call2 = GNUNET_CONVERSATION_call_start (cfg,
585 ego,
586 gns_name,
587 &call2_speaker,
588 &call2_mic,
589 &call_event_handler,
590 (void *) "call2");
591 return;
592 }
593}
594
595
596static void
597phone_ego_create_cont (void *cls,
598 const struct GNUNET_CRYPTO_PrivateKey *pk,
599 enum GNUNET_ErrorCode ec)
600{
601 (void) cls;
602 op = NULL;
603 GNUNET_assert (GNUNET_EC_NONE == ec);
604}
605
606
607static void
608run (void *cls,
609 const struct GNUNET_CONFIGURATION_Handle *c,
610 struct GNUNET_TESTING_Peer *peer)
611{
612 (void) cls;
613 (void) peer;
614 cfg = c;
615 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_test, NULL);
616 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
617 id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
618 op = GNUNET_IDENTITY_create (id, "phone-ego", NULL,
619 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
620 &phone_ego_create_cont,
621 NULL);
622 ns = GNUNET_NAMESTORE_connect (cfg);
623}
624
625
626int
627main (int argc, char *argv[])
628{
629 (void) argc;
630 (void) argv;
631 if (0 != GNUNET_TESTING_peer_run ("test_conversation_api_twocalls",
632 "test_conversation.conf",
633 &run,
634 NULL))
635 return 1;
636 if (call1_finished && call2_finished)
637 return 0;
638 return 1;
639}
640
641
642/* end of test_conversation_api_twocalls.c */
diff --git a/src/contrib/service/meson.build b/src/contrib/service/meson.build
new file mode 100644
index 000000000..eae7b7c8e
--- /dev/null
+++ b/src/contrib/service/meson.build
@@ -0,0 +1,11 @@
1subdir('template')
2subdir('set')
3subdir('consensus')
4#subdir('auction')
5#subdir('abd')
6#subdir('rps')
7subdir('scalarproduct')
8if conversation_backend != 'none'
9 subdir('conversation')
10endif
11subdir('secretsharing')
diff --git a/src/contrib/service/rps/.gitignore b/src/contrib/service/rps/.gitignore
new file mode 100644
index 000000000..9e78e2ca0
--- /dev/null
+++ b/src/contrib/service/rps/.gitignore
@@ -0,0 +1,16 @@
1gnunet-service-rps
2gnunet-rps
3gnunet-rps-profiler
4test_rps_single_req
5test_rps_req_cancel
6test_rps_sub
7test_rps_seed_big
8test_rps_seed_request
9test_service_rps_custommap
10test_service_rps_sampler_elem
11test_service_rps_view
12test_rps_churn
13test_service_rps_peers
14test_rps_malicious_1
15test_rps_malicious_2
16test_rps_malicious_3
diff --git a/src/contrib/service/rps/Makefile.am b/src/contrib/service/rps/Makefile.am
new file mode 100644
index 000000000..1a3c9fa3a
--- /dev/null
+++ b/src/contrib/service/rps/Makefile.am
@@ -0,0 +1,149 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13pkgcfg_DATA = \
14 rps.conf
15
16bin_PROGRAMS = gnunet-rps
17
18gnunet_rps_SOURCES = \
19 gnunet-rps.c
20
21gnunet_rps_LDADD = \
22 libgnunetrps.la \
23 $(top_builddir)/src/lib/util/libgnunetutil.la \
24 $(XLIB) $(GN_LIBINTL)
25
26lib_LTLIBRARIES = libgnunetrps.la
27
28libgnunetrps_la_SOURCES = \
29 gnunet-service-rps_sampler_elem.h gnunet-service-rps_sampler_elem.c \
30 rps-test_util.h rps-test_util.c \
31 rps-sampler_common.h rps-sampler_common.c \
32 rps-sampler_client.h rps-sampler_client.c \
33 rps_api.c rps.h
34libgnunetrps_la_LIBADD = \
35 $(top_builddir)/src/service/nse/libgnunetnse.la \
36 $(top_builddir)/src/lib/util/libgnunetutil.la \
37 $(GN_LIBINTL) $(XLIB)
38libgnunetrps_la_LDFLAGS = \
39 $(GN_LIB_LDFLAGS) \
40 -version-info 0:0:0
41# Fix 'created both with libtool and without' error:
42libgnunetrps_la_CFLAGS = $(AM_CFLAGS)
43
44
45libexec_PROGRAMS = \
46 gnunet-service-rps
47
48gnunet_service_rps_SOURCES = \
49 gnunet-service-rps_sampler_elem.h gnunet-service-rps_sampler_elem.c \
50 rps-sampler_common.h rps-sampler_common.c \
51 gnunet-service-rps_sampler.h gnunet-service-rps_sampler.c \
52 gnunet-service-rps_custommap.h gnunet-service-rps_custommap.c \
53 gnunet-service-rps_view.h gnunet-service-rps_view.c \
54 gnunet-service-rps.c
55
56
57gnunet_service_rps_LDADD = \
58 libgnunetrps.la \
59 $(top_builddir)/src/lib/util/libgnunetutil.la \
60 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
61 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
62 $(top_builddir)/src/service/nse/libgnunetnse.la \
63 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
64 $(top_builddir)/src/service/core/libgnunetcore.la \
65 $(LIBGCRYPT_LIBS) \
66 -lm -lgcrypt \
67 $(GN_LIBINTL)
68
69
70check_PROGRAMS = \
71 test_service_rps_view \
72 test_service_rps_custommap \
73 test_service_rps_sampler_elem
74 # test_rps_single_req \
75 # test_rps_churn
76 # test_rps_sub \
77 # test_rps_seed_request
78#if ENABLE_MALICIOUS
79#check_PROGRAMS += \
80# test_rps_malicious_1 \
81# test_rps_malicious_2 \
82# test_rps_malicious_3
83#endif
84
85rps_test_src = \
86 test_rps.c \
87 rps-test_util.h rps-test_util.c \
88 gnunet-service-rps_sampler_elem.h gnunet-service-rps_sampler_elem.c \
89 rps-sampler_common.h rps-sampler_common.c \
90 gnunet-service-rps_sampler.h gnunet-service-rps_sampler.c
91
92#ld_rps_test_lib = \
93# libgnunetrps.la \
94# $(top_builddir)/src/lib/util/libgnunetutil.la \
95# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
96# $(top_builddir)/src/testbed/libgnunettestbed.la \
97# -lm
98
99if ENABLE_TEST_RUN
100AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
101TESTS = $(check_PROGRAMS)
102endif
103
104test_service_rps_view_SOURCES = \
105 gnunet-service-rps_view.h gnunet-service-rps_view.c \
106 test_service_rps_view.c
107test_service_rps_view_LDADD = $(top_builddir)/src/lib/util/libgnunetutil.la
108
109test_service_rps_custommap_SOURCES = \
110 gnunet-service-rps_custommap.h gnunet-service-rps_custommap.c \
111 test_service_rps_custommap.c
112test_service_rps_custommap_LDADD = \
113 $(top_builddir)/src/lib/util/libgnunetutil.la
114
115test_service_rps_sampler_elem_SOURCES = \
116 gnunet-service-rps_sampler_elem.h gnunet-service-rps_sampler_elem.c \
117 rps-test_util.h rps-test_util.c \
118 test_service_rps_sampler_elem.c
119test_service_rps_sampler_elem_LDADD = $(top_builddir)/src/lib/util/libgnunetutil.la
120
121#test_rps_single_req_SOURCES = $(rps_test_src)
122#test_rps_single_req_LDADD = $(ld_rps_test_lib)
123#
124#test_rps_seed_request_SOURCES = $(rps_test_src)
125#test_rps_seed_request_LDADD = $(ld_rps_test_lib)
126#
127#test_rps_req_cancel_SOURCES = $(rps_test_src)
128#test_rps_req_cancel_LDADD = $(ld_rps_test_lib)
129#
130#test_rps_sub_SOURCES = $(rps_test_src)
131#test_rps_sub_LDADD = $(ld_rps_test_lib)
132#
133#test_rps_seed_big_SOURCES = $(rps_test_src)
134#test_rps_seed_big_LDADD = $(ld_rps_test_lib)
135#
136#test_rps_churn_SOURCES = $(rps_test_src)
137#test_rps_churn_LDADD = $(ld_rps_test_lib)
138#
139#test_rps_malicious_1_SOURCES = $(rps_test_src)
140#test_rps_malicious_1_LDADD = $(ld_rps_test_lib)
141#
142#test_rps_malicious_2_SOURCES = $(rps_test_src)
143#test_rps_malicious_2_LDADD = $(ld_rps_test_lib)
144#
145#test_rps_malicious_3_SOURCES = $(rps_test_src)
146#test_rps_malicious_3_LDADD = $(ld_rps_test_lib)
147
148EXTRA_DIST = \
149 test_rps.conf
diff --git a/src/contrib/service/rps/gnunet-rps.c b/src/contrib/service/rps/gnunet-rps.c
new file mode 100644
index 000000000..f90fc0d88
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-rps.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-rps.c
23 * @brief random peer sampling
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_rps_service.h"
29#include <inttypes.h>
30
31static int ret;
32
33/**
34 * RPS handle
35 */
36static struct GNUNET_RPS_Handle *rps_handle;
37
38/**
39 * Request handle
40 */
41static struct GNUNET_RPS_Request_Handle *req_handle;
42
43/**
44 * PeerID (Option --seed)
45 */
46static struct GNUNET_PeerIdentity peer_id;
47
48/**
49 * @brief Do we want to receive updates of the view? (Option --view)
50 */
51static int view_update;
52
53/**
54 * @brief Do we want to receive updates of the view? (Option --view)
55 */
56static int stream_input;
57
58/**
59 * @brief Number of updates we want to receive
60 */
61static uint64_t num_view_updates;
62
63
64/**
65 * Task run when user presses CTRL-C to abort.
66 * Cancels pending request and disconnects.
67 *
68 * @param cls NULL
69 */
70static void
71do_shutdown (void *cls)
72{
73 (void) cls;
74
75 if (NULL != req_handle)
76 GNUNET_RPS_request_cancel (req_handle);
77 GNUNET_RPS_disconnect (rps_handle);
78}
79
80
81/**
82 * Callback called on receipt of reply.
83 * Prints replied PeerIDs.
84 *
85 * @param cls closure
86 * @param n number of peers
87 * @param recv_peers the received peers
88 */
89static void
90reply_handle (void *cls,
91 uint64_t n,
92 const struct GNUNET_PeerIdentity *recv_peers)
93{
94 uint64_t i;
95
96 (void) cls;
97
98 req_handle = NULL;
99 for (i = 0; i < n; i++)
100 {
101 fprintf (stdout, "%s\n",
102 GNUNET_i2s_full (&recv_peers[i]));
103 }
104 ret = 0;
105
106 GNUNET_SCHEDULER_shutdown ();
107}
108
109
110/**
111 * Callback called on receipt view update.
112 * Prints view.
113 *
114 * @param n number of peers
115 * @param recv_peers the received peers
116 */
117static void
118view_update_handle (void *cls,
119 uint64_t n,
120 const struct GNUNET_PeerIdentity *recv_peers)
121{
122 uint64_t i;
123
124 (void) cls;
125
126 if (0 == n)
127 {
128 fprintf (stdout, "Empty view\n");
129 }
130 req_handle = NULL;
131 for (i = 0; i < n; i++)
132 {
133 fprintf (stdout, "%s\n",
134 GNUNET_i2s_full (&recv_peers[i]));
135 }
136
137 if (1 == num_view_updates)
138 {
139 ret = 0;
140 GNUNET_SCHEDULER_shutdown ();
141 }
142 else if (1 < num_view_updates)
143 {
144 num_view_updates--;
145 }
146}
147
148
149/**
150 * Callback called on receipt of peer from biased stream
151 *
152 * @param num_peers number of peers
153 * @param recv_peers the received peers
154 */
155static void
156stream_input_handle (void *cls,
157 uint64_t num_peers,
158 const struct GNUNET_PeerIdentity *recv_peers)
159{
160 uint64_t i;
161
162 (void) cls;
163
164 if (0 == num_peers)
165 {
166 fprintf (stdout, "No peer was returned\n");
167 }
168 req_handle = NULL;
169 for (i = 0; i < num_peers; i++)
170 {
171 fprintf (stdout, "%s\n",
172 GNUNET_i2s_full (&recv_peers[i]));
173 }
174}
175
176
177/**
178 * Main function that will be run by the scheduler.
179 *
180 * @param cls closure
181 * @param args remaining command-line arguments
182 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
183 * @param cfg configuration
184 */
185static void
186run (void *cls,
187 char *const *args,
188 const char *cfgfile,
189 const struct GNUNET_CONFIGURATION_Handle *cfg)
190{
191 static uint64_t num_peers;
192 static struct GNUNET_PeerIdentity zero_pid;
193
194 (void) cls;
195 (void) cfgfile;
196
197 rps_handle = GNUNET_RPS_connect (cfg);
198 if (NULL == rps_handle)
199 {
200 fprintf (stderr, "Failed to connect to the rps service\n");
201 return;
202 }
203
204 if ((0 == memcmp (&zero_pid, &peer_id, sizeof(peer_id))) &&
205 (! view_update) &&
206 (! stream_input))
207 { /* Request n PeerIDs */
208 /* If number was specified use it, else request single peer. */
209 if ((NULL == args[0]) ||
210 (0 == sscanf (args[0], "%lu", &num_peers)) )
211 {
212 num_peers = 1;
213 }
214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215 "Requesting %" PRIu64 " PeerIDs\n", num_peers);
216 req_handle = GNUNET_RPS_request_peers (rps_handle, num_peers, reply_handle,
217 NULL);
218 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
219 }
220 else if (view_update)
221 {
222 /* Get updates of view */
223 if ((NULL == args[0]) ||
224 (0 == sscanf (args[0], "%lu", &num_view_updates)) )
225 {
226 num_view_updates = 0;
227 }
228 GNUNET_RPS_view_request (rps_handle, num_view_updates, view_update_handle,
229 NULL);
230 if (0 != num_view_updates)
231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232 "Requesting %" PRIu64 " view updates\n", num_view_updates);
233 else
234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235 "Requesting continuous view updates\n");
236 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
237 }
238 else if (stream_input)
239 {
240 /* Get updates of view */
241 GNUNET_RPS_stream_request (rps_handle, stream_input_handle, NULL);
242 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
243 }
244 else
245 { /* Seed PeerID */
246 GNUNET_RPS_seed_ids (rps_handle, 1, &peer_id);
247 fprintf (stdout, "Seeded PeerID %s\n", GNUNET_i2s_full (&peer_id));
248 ret = 0;
249 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
250 }
251}
252
253
254/**
255 * The main function to rps.
256 *
257 * @param argc number of arguments from the command line
258 * @param argv command line arguments
259 * @return 0 ok, 1 on error
260 */
261int
262main (int argc, char *const *argv)
263{
264 const char helpstr[] =
265 "Get random GNUnet peers. If none is specified a single is requested.";
266 struct GNUNET_GETOPT_CommandLineOption options[] = {
267 GNUNET_GETOPT_option_base32_auto ('s',
268 "seed",
269 "PEER_ID",
270 gettext_noop ("Seed a PeerID"),
271 &peer_id),
272 GNUNET_GETOPT_option_flag ('V',
273 "view",
274 gettext_noop (
275 "Get updates of view (0 for infinite updates)"),
276 &view_update),
277 GNUNET_GETOPT_option_flag ('S',
278 "stream",
279 gettext_noop ("Get peers from biased stream"),
280 &stream_input),
281 GNUNET_GETOPT_OPTION_END
282 };
283
284 return (GNUNET_OK ==
285 GNUNET_PROGRAM_run (argc,
286 argv,
287 "gnunet-rps [NUMBER_OF_PEERS]",
288 gettext_noop
289 (helpstr),
290 options, &run, NULL)) ? ret : 1;
291}
292
293
294/* end of gnunet-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps.c b/src/contrib/service/rps/gnunet-service-rps.c
new file mode 100644
index 000000000..1ea59104b
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps.c
@@ -0,0 +1,5028 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps.c
23 * @brief rps service implementation
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_applications.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_cadet_service.h"
30#include "gnunet_core_service.h"
31#include "gnunet_peerstore_service.h"
32#include "gnunet_nse_service.h"
33#include "gnunet_statistics_service.h"
34#include "rps.h"
35#include "rps-test_util.h"
36#include "gnunet-service-rps_sampler.h"
37#include "gnunet-service-rps_custommap.h"
38#include "gnunet-service-rps_view.h"
39#include "gnunet_constants.h"
40
41#include <math.h>
42#include <inttypes.h>
43#include <string.h>
44
45#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
46
47// TODO check for overflows
48
49// TODO align message structs
50
51// TODO connect to friends
52
53// TODO blacklist? (-> mal peer detection on top of brahms)
54
55// hist_size_init, hist_size_max
56
57/***********************************************************************
58* Old gnunet-service-rps_peers.c
59***********************************************************************/
60
61/**
62 * Set a peer flag of given peer context.
63 */
64#define SET_PEER_FLAG(peer_ctx, mask) ((peer_ctx->peer_flags) |= (mask))
65
66/**
67 * Get peer flag of given peer context.
68 */
69#define check_peer_flag_set(peer_ctx, mask) \
70 ((peer_ctx->peer_flags) & (mask) ? GNUNET_YES : GNUNET_NO)
71
72/**
73 * Unset flag of given peer context.
74 */
75#define UNSET_PEER_FLAG(peer_ctx, mask) ((peer_ctx->peer_flags) &= ~(mask))
76
77/**
78 * Get channel flag of given channel context.
79 */
80#define check_channel_flag_set(channel_flags, mask) \
81 ((*channel_flags) & (mask) ? GNUNET_YES : GNUNET_NO)
82
83/**
84 * Unset flag of given channel context.
85 */
86#define unset_channel_flag(channel_flags, mask) ((*channel_flags) &= ~(mask))
87
88
89/**
90 * Pending operation on peer consisting of callback and closure
91 *
92 * When an operation cannot be executed right now this struct is used to store
93 * the callback and closure for later execution.
94 */
95struct PeerPendingOp
96{
97 /**
98 * Callback
99 */
100 PeerOp op;
101
102 /**
103 * Closure
104 */
105 void *op_cls;
106};
107
108/**
109 * List containing all messages that are yet to be send
110 *
111 * This is used to keep track of all messages that have not been sent yet. When
112 * a peer is to be removed the pending messages can be removed properly.
113 */
114struct PendingMessage
115{
116 /**
117 * DLL next, prev
118 */
119 struct PendingMessage *next;
120 struct PendingMessage *prev;
121
122 /**
123 * The envelope to the corresponding message
124 */
125 struct GNUNET_MQ_Envelope *ev;
126
127 /**
128 * The corresponding context
129 */
130 struct PeerContext *peer_ctx;
131
132 /**
133 * The message type
134 */
135 const char *type;
136};
137
138/**
139 * @brief Context for a channel
140 */
141struct ChannelCtx;
142
143/**
144 * Struct used to keep track of other peer's status
145 *
146 * This is stored in a multipeermap.
147 * It contains information such as cadet channels, a message queue for sending,
148 * status about the channels, the pending operations on this peer and some flags
149 * about the status of the peer itself. (online, valid, ...)
150 */
151struct PeerContext
152{
153 /**
154 * The Sub this context belongs to.
155 */
156 struct Sub *sub;
157
158 /**
159 * Message queue open to client
160 */
161 struct GNUNET_MQ_Handle *mq;
162
163 /**
164 * Channel open to client.
165 */
166 struct ChannelCtx *send_channel_ctx;
167
168 /**
169 * Channel open from client.
170 */
171 struct ChannelCtx *recv_channel_ctx;
172
173 /**
174 * Array of pending operations on this peer.
175 */
176 struct PeerPendingOp *pending_ops;
177
178 /**
179 * Handle to the callback given to cadet_ntfy_tmt_rdy()
180 *
181 * To be canceled on shutdown.
182 */
183 struct PendingMessage *online_check_pending;
184
185 /**
186 * Number of pending operations.
187 */
188 unsigned int num_pending_ops;
189
190 /**
191 * Identity of the peer
192 */
193 struct GNUNET_PeerIdentity peer_id;
194
195 /**
196 * Flags indicating status of peer
197 */
198 uint32_t peer_flags;
199
200 /**
201 * Last time we received something from that peer.
202 */
203 struct GNUNET_TIME_Absolute last_message_recv;
204
205 /**
206 * Last time we received a keepalive message.
207 */
208 struct GNUNET_TIME_Absolute last_keepalive;
209
210 /**
211 * DLL with all messages that are yet to be sent
212 */
213 struct PendingMessage *pending_messages_head;
214 struct PendingMessage *pending_messages_tail;
215
216 /**
217 * This is pobably followed by 'statistical' data (when we first saw
218 * it, how did we get its ID, how many pushes (in a timeinterval),
219 * ...)
220 */
221 uint32_t round_pull_req;
222};
223
224/**
225 * @brief Closure to #valid_peer_iterator
226 */
227struct PeersIteratorCls
228{
229 /**
230 * Iterator function
231 */
232 PeersIterator iterator;
233
234 /**
235 * Closure to iterator
236 */
237 void *cls;
238};
239
240/**
241 * @brief Context for a channel
242 */
243struct ChannelCtx
244{
245 /**
246 * @brief The channel itself
247 */
248 struct GNUNET_CADET_Channel *channel;
249
250 /**
251 * @brief The peer context associated with the channel
252 */
253 struct PeerContext *peer_ctx;
254
255 /**
256 * @brief When channel destruction needs to be delayed (because it is called
257 * from within the cadet routine of another channel destruction) this task
258 * refers to the respective _SCHEDULER_Task.
259 */
260 struct GNUNET_SCHEDULER_Task *destruction_task;
261};
262
263
264#if ENABLE_MALICIOUS
265
266/**
267 * If type is 2 This struct is used to store the attacked peers in a DLL
268 */
269struct AttackedPeer
270{
271 /**
272 * DLL
273 */
274 struct AttackedPeer *next;
275 struct AttackedPeer *prev;
276
277 /**
278 * PeerID
279 */
280 struct GNUNET_PeerIdentity peer_id;
281};
282
283#endif /* ENABLE_MALICIOUS */
284
285/**
286 * @brief This number determines the number of slots for files that represent
287 * histograms
288 */
289#define HISTOGRAM_FILE_SLOTS 32
290
291/**
292 * @brief The size (in bytes) a file needs to store the histogram
293 *
294 * Per slot: 1 newline, up to 4 chars,
295 * Additionally: 1 null termination
296 */
297#define SIZE_DUMP_FILE (HISTOGRAM_FILE_SLOTS * 5) + 1
298
299/**
300 * @brief One Sub.
301 *
302 * Essentially one instance of brahms that only connects to other instances
303 * with the same (secret) value.
304 */
305struct Sub
306{
307 /**
308 * @brief Hash of the shared value that defines Subs.
309 */
310 struct GNUNET_HashCode hash;
311
312 /**
313 * @brief Port to communicate to other peers.
314 */
315 struct GNUNET_CADET_Port *cadet_port;
316
317 /**
318 * @brief Hashmap of valid peers.
319 */
320 struct GNUNET_CONTAINER_MultiPeerMap *valid_peers;
321
322 /**
323 * @brief Filename of the file that stores the valid peers persistently.
324 */
325 char *filename_valid_peers;
326
327 /**
328 * Set of all peers to keep track of them.
329 */
330 struct GNUNET_CONTAINER_MultiPeerMap *peer_map;
331
332 /**
333 * @brief This is the minimum estimate used as sampler size.
334 *
335 * It is configured by the user.
336 */
337 unsigned int sampler_size_est_min;
338
339 /**
340 * The size of sampler we need to be able to satisfy the Brahms protocol's
341 * need of random peers.
342 *
343 * This is one minimum size the sampler grows to.
344 */
345 unsigned int sampler_size_est_need;
346
347 /**
348 * Time interval the do_round task runs in.
349 */
350 struct GNUNET_TIME_Relative round_interval;
351
352 /**
353 * Sampler used for the Brahms protocol itself.
354 */
355 struct RPS_Sampler *sampler;
356
357#ifdef TO_FILE_FULL
358 /**
359 * Name to log view to
360 */
361 char *file_name_view_log;
362#endif /* TO_FILE_FULL */
363
364#ifdef TO_FILE
365#ifdef TO_FILE_FULL
366 /**
367 * Name to log number of observed peers to
368 */
369 char *file_name_observed_log;
370#endif /* TO_FILE_FULL */
371
372 /**
373 * @brief Count the observed peers
374 */
375 uint32_t num_observed_peers;
376
377 /**
378 * @brief Multipeermap (ab-) used to count unique peer_ids
379 */
380 struct GNUNET_CONTAINER_MultiPeerMap *observed_unique_peers;
381#endif /* TO_FILE */
382
383 /**
384 * List to store peers received through pushes temporary.
385 */
386 struct CustomPeerMap *push_map;
387
388 /**
389 * List to store peers received through pulls temporary.
390 */
391 struct CustomPeerMap *pull_map;
392
393 /**
394 * @brief This is the estimate used as view size.
395 *
396 * It is initialised with the minimum
397 */
398 unsigned int view_size_est_need;
399
400 /**
401 * @brief This is the minimum estimate used as view size.
402 *
403 * It is configured by the user.
404 */
405 unsigned int view_size_est_min;
406
407 /**
408 * @brief The view.
409 */
410 struct View *view;
411
412 /**
413 * Identifier for the main task that runs periodically.
414 */
415 struct GNUNET_SCHEDULER_Task *do_round_task;
416
417 /* === stats === */
418
419 /**
420 * @brief Counts the executed rounds.
421 */
422 uint32_t num_rounds;
423
424 /**
425 * @brief This array accumulates the number of received pushes per round.
426 *
427 * Number at index i represents the number of rounds with i observed pushes.
428 */
429 uint32_t push_recv[HISTOGRAM_FILE_SLOTS];
430
431 /**
432 * @brief Histogram of deltas between the expected and actual number of
433 * received pushes.
434 *
435 * As half of the entries are expected to be negative, this is shifted by
436 * #HISTOGRAM_FILE_SLOTS/2.
437 */
438 uint32_t push_delta[HISTOGRAM_FILE_SLOTS];
439
440 /**
441 * @brief Number of pull replies with this delay measured in rounds.
442 *
443 * Number at index i represents the number of pull replies with a delay of i
444 * rounds.
445 */
446 uint32_t pull_delays[HISTOGRAM_FILE_SLOTS];
447};
448
449
450/***********************************************************************
451* Globals
452***********************************************************************/
453
454/**
455 * Our configuration.
456 */
457static const struct GNUNET_CONFIGURATION_Handle *cfg;
458
459/**
460 * Handle to the statistics service.
461 */
462struct GNUNET_STATISTICS_Handle *stats;
463
464/**
465 * Handler to CADET.
466 */
467struct GNUNET_CADET_Handle *cadet_handle;
468
469/**
470 * Handle to CORE
471 */
472struct GNUNET_CORE_Handle *core_handle;
473
474/**
475 * @brief PeerMap to keep track of connected peers.
476 */
477struct GNUNET_CONTAINER_MultiPeerMap *map_single_hop;
478
479/**
480 * Our own identity.
481 */
482static struct GNUNET_PeerIdentity own_identity;
483
484/**
485 * Percentage of total peer number in the view
486 * to send random PUSHes to
487 */
488static float alpha;
489
490/**
491 * Percentage of total peer number in the view
492 * to send random PULLs to
493 */
494static float beta;
495
496/**
497 * Handler to NSE.
498 */
499static struct GNUNET_NSE_Handle *nse;
500
501/**
502 * Handle to the PEERSTORE service.
503 */
504static struct GNUNET_PEERSTORE_Handle *peerstore;
505
506/**
507 * Our peerstore notification context. We use notification
508 * to instantly learn about new peers as they are discovered.
509 */
510static struct GNUNET_PEERSTORE_Monitor *peerstore_notify;
511
512
513#if ENABLE_MALICIOUS
514/**
515 * Type of malicious peer
516 *
517 * 0 Don't act malicious at all - Default
518 * 1 Try to maximise representation
519 * 2 Try to partition the network
520 * 3 Combined attack
521 */
522static uint32_t mal_type;
523
524/**
525 * Other malicious peers
526 */
527static struct GNUNET_PeerIdentity *mal_peers;
528
529/**
530 * Hashmap of malicious peers used as set.
531 * Used to more efficiently check whether we know that peer.
532 */
533static struct GNUNET_CONTAINER_MultiPeerMap *mal_peer_set;
534
535/**
536 * Number of other malicious peers
537 */
538static uint32_t num_mal_peers;
539
540
541/**
542 * If type is 2 this is the DLL of attacked peers
543 */
544static struct AttackedPeer *att_peers_head;
545static struct AttackedPeer *att_peers_tail;
546
547/**
548 * This index is used to point to an attacked peer to
549 * implement the round-robin-ish way to select attacked peers.
550 */
551static struct AttackedPeer *att_peer_index;
552
553/**
554 * Hashmap of attacked peers used as set.
555 * Used to more efficiently check whether we know that peer.
556 */
557static struct GNUNET_CONTAINER_MultiPeerMap *att_peer_set;
558
559/**
560 * Number of attacked peers
561 */
562static uint32_t num_attacked_peers;
563
564/**
565 * If type is 1 this is the attacked peer
566 */
567static struct GNUNET_PeerIdentity attacked_peer;
568
569/**
570 * The limit of PUSHes we can send in one round.
571 * This is an assumption of the Brahms protocol and either implemented
572 * via proof of work
573 * or
574 * assumed to be the bandwidth limitation.
575 */
576static uint32_t push_limit = 10000;
577#endif /* ENABLE_MALICIOUS */
578
579/**
580 * @brief Main Sub.
581 *
582 * This is run in any case by all peers and connects to all peers without
583 * specifying a shared value.
584 */
585static struct Sub *msub;
586
587/**
588 * @brief Maximum number of valid peers to keep.
589 * TODO read from config
590 */
591static const uint32_t num_valid_peers_max = UINT32_MAX;
592
593/***********************************************************************
594* /Globals
595***********************************************************************/
596
597
598static void
599do_round (void *cls);
600
601#if ENABLE_MALICIOUS
602static void
603do_mal_round (void *cls);
604
605#endif /* ENABLE_MALICIOUS */
606
607
608/**
609 * @brief Get the #PeerContext associated with a peer
610 *
611 * @param peer_map The peer map containing the context
612 * @param peer the peer id
613 *
614 * @return the #PeerContext
615 */
616static struct PeerContext *
617get_peer_ctx (const struct GNUNET_CONTAINER_MultiPeerMap *peer_map,
618 const struct GNUNET_PeerIdentity *peer)
619{
620 struct PeerContext *ctx;
621 int ret;
622
623 ret = GNUNET_CONTAINER_multipeermap_contains (peer_map, peer);
624 GNUNET_assert (GNUNET_YES == ret);
625 ctx = GNUNET_CONTAINER_multipeermap_get (peer_map, peer);
626 GNUNET_assert (NULL != ctx);
627 return ctx;
628}
629
630
631/**
632 * @brief Check whether we have information about the given peer.
633 *
634 * FIXME probably deprecated. Make this the new _online.
635 *
636 * @param peer_map The peer map to check for the existence of @a peer
637 * @param peer peer in question
638 *
639 * @return #GNUNET_YES if peer is known
640 * #GNUNET_NO if peer is not known
641 */
642static int
643check_peer_known (const struct GNUNET_CONTAINER_MultiPeerMap *peer_map,
644 const struct GNUNET_PeerIdentity *peer)
645{
646 if (NULL != peer_map)
647 {
648 return GNUNET_CONTAINER_multipeermap_contains (peer_map, peer);
649 }
650 else
651 {
652 return GNUNET_NO;
653 }
654}
655
656
657/**
658 * @brief Create a new #PeerContext and insert it into the peer map
659 *
660 * @param sub The Sub this context belongs to.
661 * @param peer the peer to create the #PeerContext for
662 *
663 * @return the #PeerContext
664 */
665static struct PeerContext *
666create_peer_ctx (struct Sub *sub,
667 const struct GNUNET_PeerIdentity *peer)
668{
669 struct PeerContext *ctx;
670 int ret;
671
672 GNUNET_assert (GNUNET_NO == check_peer_known (sub->peer_map, peer));
673
674 ctx = GNUNET_new (struct PeerContext);
675 ctx->peer_id = *peer;
676 ctx->sub = sub;
677 ret = GNUNET_CONTAINER_multipeermap_put (sub->peer_map, peer, ctx,
678 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
679 GNUNET_assert (GNUNET_OK == ret);
680 if (sub == msub)
681 {
682 GNUNET_STATISTICS_set (stats,
683 "# known peers",
684 GNUNET_CONTAINER_multipeermap_size (sub->peer_map),
685 GNUNET_NO);
686 }
687 return ctx;
688}
689
690
691/**
692 * @brief Create or get a #PeerContext
693 *
694 * @param sub The Sub to which the created context belongs to
695 * @param peer the peer to get the associated context to
696 *
697 * @return the context
698 */
699static struct PeerContext *
700create_or_get_peer_ctx (struct Sub *sub,
701 const struct GNUNET_PeerIdentity *peer)
702{
703 if (GNUNET_NO == check_peer_known (sub->peer_map, peer))
704 {
705 return create_peer_ctx (sub, peer);
706 }
707 return get_peer_ctx (sub->peer_map, peer);
708}
709
710
711/**
712 * @brief Check whether we have a connection to this @a peer
713 *
714 * Also sets the #Peers_ONLINE flag accordingly
715 *
716 * @param peer_ctx Context of the peer of which connectivity is to be checked
717 *
718 * @return #GNUNET_YES if we are connected
719 * #GNUNET_NO otherwise
720 */
721static int
722check_connected (struct PeerContext *peer_ctx)
723{
724 /* If we don't know about this peer we don't know whether it's online */
725 if (GNUNET_NO == check_peer_known (peer_ctx->sub->peer_map,
726 &peer_ctx->peer_id))
727 {
728 return GNUNET_NO;
729 }
730 /* Get the context */
731 peer_ctx = get_peer_ctx (peer_ctx->sub->peer_map, &peer_ctx->peer_id);
732 /* If we have no channel to this peer we don't know whether it's online */
733 if ((NULL == peer_ctx->send_channel_ctx) &&
734 (NULL == peer_ctx->recv_channel_ctx))
735 {
736 UNSET_PEER_FLAG (peer_ctx, Peers_ONLINE);
737 return GNUNET_NO;
738 }
739 /* Otherwise (if we have a channel, we know that it's online */
740 SET_PEER_FLAG (peer_ctx, Peers_ONLINE);
741 return GNUNET_YES;
742}
743
744
745/**
746 * @brief The closure to #get_rand_peer_iterator.
747 */
748struct GetRandPeerIteratorCls
749{
750 /**
751 * @brief The index of the peer to return.
752 * Will be decreased until 0.
753 * Then current peer is returned.
754 */
755 uint32_t index;
756
757 /**
758 * @brief Pointer to peer to return.
759 */
760 const struct GNUNET_PeerIdentity *peer;
761};
762
763
764/**
765 * @brief Iterator function for #get_random_peer_from_peermap.
766 *
767 * Implements #GNUNET_CONTAINER_PeerMapIterator.
768 * Decreases the index until the index is null.
769 * Then returns the current peer.
770 *
771 * @param cls the #GetRandPeerIteratorCls containing index and peer
772 * @param peer current peer
773 * @param value unused
774 *
775 * @return #GNUNET_YES if we should continue to
776 * iterate,
777 * #GNUNET_NO if not.
778 */
779static int
780get_rand_peer_iterator (void *cls,
781 const struct GNUNET_PeerIdentity *peer,
782 void *value)
783{
784 struct GetRandPeerIteratorCls *iterator_cls = cls;
785
786 (void) value;
787
788 if (0 >= iterator_cls->index)
789 {
790 iterator_cls->peer = peer;
791 return GNUNET_NO;
792 }
793 iterator_cls->index--;
794 return GNUNET_YES;
795}
796
797
798/**
799 * @brief Get a random peer from @a peer_map
800 *
801 * @param valid_peers Peer map containing valid peers from which to select a
802 * random one
803 *
804 * @return a random peer
805 */
806static const struct GNUNET_PeerIdentity *
807get_random_peer_from_peermap (struct GNUNET_CONTAINER_MultiPeerMap *valid_peers)
808{
809 struct GetRandPeerIteratorCls *iterator_cls;
810 const struct GNUNET_PeerIdentity *ret;
811
812 iterator_cls = GNUNET_new (struct GetRandPeerIteratorCls);
813 iterator_cls->index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
814 GNUNET_CONTAINER_multipeermap_size (
815 valid_peers));
816 (void) GNUNET_CONTAINER_multipeermap_iterate (valid_peers,
817 get_rand_peer_iterator,
818 iterator_cls);
819 ret = iterator_cls->peer;
820 GNUNET_free (iterator_cls);
821 return ret;
822}
823
824
825/**
826 * @brief Add a given @a peer to valid peers.
827 *
828 * If valid peers are already #num_valid_peers_max, delete a peer previously.
829 *
830 * @param peer The peer that is added to the valid peers.
831 * @param valid_peers Peer map of valid peers to which to add the @a peer
832 *
833 * @return #GNUNET_YES if no other peer had to be removed
834 * #GNUNET_NO otherwise
835 */
836static int
837add_valid_peer (const struct GNUNET_PeerIdentity *peer,
838 struct GNUNET_CONTAINER_MultiPeerMap *valid_peers)
839{
840 const struct GNUNET_PeerIdentity *rand_peer;
841 int ret;
842
843 ret = GNUNET_YES;
844 /* Remove random peers until there is space for a new one */
845 while (num_valid_peers_max <=
846 GNUNET_CONTAINER_multipeermap_size (valid_peers))
847 {
848 rand_peer = get_random_peer_from_peermap (valid_peers);
849 GNUNET_CONTAINER_multipeermap_remove_all (valid_peers, rand_peer);
850 ret = GNUNET_NO;
851 }
852 (void) GNUNET_CONTAINER_multipeermap_put (valid_peers, peer, NULL,
853 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
854 if (valid_peers == msub->valid_peers)
855 {
856 GNUNET_STATISTICS_set (stats,
857 "# valid peers",
858 GNUNET_CONTAINER_multipeermap_size (valid_peers),
859 GNUNET_NO);
860 }
861 return ret;
862}
863
864
865static void
866remove_pending_message (struct PendingMessage *pending_msg, int cancel);
867
868/**
869 * @brief Set the peer flag to living and
870 * call the pending operations on this peer.
871 *
872 * Also adds peer to #valid_peers.
873 *
874 * @param peer_ctx the #PeerContext of the peer to set online
875 */
876static void
877set_peer_online (struct PeerContext *peer_ctx)
878{
879 struct GNUNET_PeerIdentity *peer;
880 unsigned int i;
881
882 peer = &peer_ctx->peer_id;
883 LOG (GNUNET_ERROR_TYPE_DEBUG,
884 "Peer %s is online and valid, calling %i pending operations on it\n",
885 GNUNET_i2s (peer),
886 peer_ctx->num_pending_ops);
887
888 if (NULL != peer_ctx->online_check_pending)
889 {
890 LOG (GNUNET_ERROR_TYPE_DEBUG,
891 "Removing pending online check for peer %s\n",
892 GNUNET_i2s (&peer_ctx->peer_id));
893 // TODO wait until cadet sets mq->cancel_impl
894 // GNUNET_MQ_send_cancel (peer_ctx->online_check_pending->ev);
895 remove_pending_message (peer_ctx->online_check_pending, GNUNET_YES);
896 peer_ctx->online_check_pending = NULL;
897 }
898
899 SET_PEER_FLAG (peer_ctx, Peers_ONLINE);
900
901 /* Call pending operations */
902 for (i = 0; i < peer_ctx->num_pending_ops; i++)
903 {
904 peer_ctx->pending_ops[i].op (peer_ctx->pending_ops[i].op_cls, peer);
905 }
906 GNUNET_array_grow (peer_ctx->pending_ops, peer_ctx->num_pending_ops, 0);
907}
908
909
910static void
911cleanup_destroyed_channel (void *cls,
912 const struct GNUNET_CADET_Channel *channel);
913
914/* Declaration of handlers */
915static void
916handle_peer_check (void *cls,
917 const struct GNUNET_MessageHeader *msg);
918
919static void
920handle_peer_push (void *cls,
921 const struct GNUNET_MessageHeader *msg);
922
923static void
924handle_peer_pull_request (void *cls,
925 const struct GNUNET_MessageHeader *msg);
926
927static int
928check_peer_pull_reply (void *cls,
929 const struct GNUNET_RPS_P2P_PullReplyMessage *msg);
930
931static void
932handle_peer_pull_reply (void *cls,
933 const struct GNUNET_RPS_P2P_PullReplyMessage *msg);
934
935/* End declaration of handlers */
936
937/**
938 * @brief Allocate memory for a new channel context and insert it into DLL
939 *
940 * @param peer_ctx context of the according peer
941 *
942 * @return The channel context
943 */
944static struct ChannelCtx *
945add_channel_ctx (struct PeerContext *peer_ctx)
946{
947 struct ChannelCtx *channel_ctx;
948
949 channel_ctx = GNUNET_new (struct ChannelCtx);
950 channel_ctx->peer_ctx = peer_ctx;
951 return channel_ctx;
952}
953
954
955/**
956 * @brief Free memory and NULL pointers.
957 *
958 * @param channel_ctx The channel context.
959 */
960static void
961remove_channel_ctx (struct ChannelCtx *channel_ctx)
962{
963 struct PeerContext *peer_ctx = channel_ctx->peer_ctx;
964
965 if (NULL != channel_ctx->destruction_task)
966 {
967 GNUNET_SCHEDULER_cancel (channel_ctx->destruction_task);
968 channel_ctx->destruction_task = NULL;
969 }
970
971 if (NULL == peer_ctx)
972 return;
973 if (channel_ctx == peer_ctx->send_channel_ctx)
974 {
975 peer_ctx->send_channel_ctx = NULL;
976 peer_ctx->mq = NULL;
977 }
978 else if (channel_ctx == peer_ctx->recv_channel_ctx)
979 {
980 peer_ctx->recv_channel_ctx = NULL;
981 }
982 GNUNET_free (channel_ctx);
983}
984
985
986/**
987 * @brief Get the channel of a peer. If not existing, create.
988 *
989 * @param peer_ctx Context of the peer of which to get the channel
990 * @return the #GNUNET_CADET_Channel used to send data to @a peer_ctx
991 */
992struct GNUNET_CADET_Channel *
993get_channel (struct PeerContext *peer_ctx)
994{
995 /* There exists a copy-paste-clone in run() */
996 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
997 GNUNET_MQ_hd_fixed_size (peer_check,
998 GNUNET_MESSAGE_TYPE_RPS_PP_CHECK_LIVE,
999 struct GNUNET_MessageHeader,
1000 NULL),
1001 GNUNET_MQ_hd_fixed_size (peer_push,
1002 GNUNET_MESSAGE_TYPE_RPS_PP_PUSH,
1003 struct GNUNET_MessageHeader,
1004 NULL),
1005 GNUNET_MQ_hd_fixed_size (peer_pull_request,
1006 GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REQUEST,
1007 struct GNUNET_MessageHeader,
1008 NULL),
1009 GNUNET_MQ_hd_var_size (peer_pull_reply,
1010 GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REPLY,
1011 struct GNUNET_RPS_P2P_PullReplyMessage,
1012 NULL),
1013 GNUNET_MQ_handler_end ()
1014 };
1015
1016
1017 if (NULL == peer_ctx->send_channel_ctx)
1018 {
1019 LOG (GNUNET_ERROR_TYPE_DEBUG,
1020 "Trying to establish channel to peer %s\n",
1021 GNUNET_i2s (&peer_ctx->peer_id));
1022 peer_ctx->send_channel_ctx = add_channel_ctx (peer_ctx);
1023 peer_ctx->send_channel_ctx->channel =
1024 GNUNET_CADET_channel_create (cadet_handle,
1025 peer_ctx->send_channel_ctx, /* context */
1026 &peer_ctx->peer_id,
1027 &peer_ctx->sub->hash,
1028 NULL, /* WindowSize handler */
1029 &cleanup_destroyed_channel, /* Disconnect handler */
1030 cadet_handlers);
1031 }
1032 GNUNET_assert (NULL != peer_ctx->send_channel_ctx);
1033 GNUNET_assert (NULL != peer_ctx->send_channel_ctx->channel);
1034 return peer_ctx->send_channel_ctx->channel;
1035}
1036
1037
1038/**
1039 * Get the message queue (#GNUNET_MQ_Handle) of a specific peer.
1040 *
1041 * If we already have a message queue open to this client,
1042 * simply return it, otherwise create one.
1043 *
1044 * @param peer_ctx Context of the peer of which to get the mq
1045 * @return the #GNUNET_MQ_Handle
1046 */
1047static struct GNUNET_MQ_Handle *
1048get_mq (struct PeerContext *peer_ctx)
1049{
1050 if (NULL == peer_ctx->mq)
1051 {
1052 peer_ctx->mq = GNUNET_CADET_get_mq (get_channel (peer_ctx));
1053 }
1054 return peer_ctx->mq;
1055}
1056
1057
1058/**
1059 * @brief Add an envelope to a message passed to mq to list of pending messages
1060 *
1061 * @param peer_ctx Context of the peer for which to insert the envelope
1062 * @param ev envelope to the message
1063 * @param type type of the message to be sent
1064 * @return pointer to pending message
1065 */
1066static struct PendingMessage *
1067insert_pending_message (struct PeerContext *peer_ctx,
1068 struct GNUNET_MQ_Envelope *ev,
1069 const char *type)
1070{
1071 struct PendingMessage *pending_msg;
1072
1073 pending_msg = GNUNET_new (struct PendingMessage);
1074 pending_msg->ev = ev;
1075 pending_msg->peer_ctx = peer_ctx;
1076 pending_msg->type = type;
1077 GNUNET_CONTAINER_DLL_insert (peer_ctx->pending_messages_head,
1078 peer_ctx->pending_messages_tail,
1079 pending_msg);
1080 return pending_msg;
1081}
1082
1083
1084/**
1085 * @brief Remove a pending message from the respective DLL
1086 *
1087 * @param pending_msg the pending message to remove
1088 * @param cancel whether to cancel the pending message, too
1089 */
1090static void
1091remove_pending_message (struct PendingMessage *pending_msg, int cancel)
1092{
1093 struct PeerContext *peer_ctx;
1094
1095 (void) cancel;
1096
1097 peer_ctx = pending_msg->peer_ctx;
1098 GNUNET_assert (NULL != peer_ctx);
1099 GNUNET_CONTAINER_DLL_remove (peer_ctx->pending_messages_head,
1100 peer_ctx->pending_messages_tail,
1101 pending_msg);
1102 // TODO wait for the cadet implementation of message cancellation
1103 // if (GNUNET_YES == cancel)
1104 // {
1105 // GNUNET_MQ_send_cancel (pending_msg->ev);
1106 // }
1107 GNUNET_free (pending_msg);
1108}
1109
1110
1111/**
1112 * @brief This is called in response to the first message we sent as a
1113 * online check.
1114 *
1115 * @param cls #PeerContext of peer with pending online check
1116 */
1117static void
1118mq_online_check_successful (void *cls)
1119{
1120 struct PeerContext *peer_ctx = cls;
1121
1122 if (NULL != peer_ctx->online_check_pending)
1123 {
1124 LOG (GNUNET_ERROR_TYPE_DEBUG,
1125 "Online check for peer %s was successful\n",
1126 GNUNET_i2s (&peer_ctx->peer_id));
1127 remove_pending_message (peer_ctx->online_check_pending, GNUNET_YES);
1128 peer_ctx->online_check_pending = NULL;
1129 set_peer_online (peer_ctx);
1130 (void) add_valid_peer (&peer_ctx->peer_id, peer_ctx->sub->valid_peers);
1131 }
1132}
1133
1134
1135/**
1136 * Issue a check whether peer is online
1137 *
1138 * @param peer_ctx the context of the peer
1139 */
1140static void
1141check_peer_online (struct PeerContext *peer_ctx)
1142{
1143 LOG (GNUNET_ERROR_TYPE_DEBUG,
1144 "Get informed about peer %s getting online\n",
1145 GNUNET_i2s (&peer_ctx->peer_id));
1146
1147 struct GNUNET_MQ_Handle *mq;
1148 struct GNUNET_MQ_Envelope *ev;
1149
1150 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_PP_CHECK_LIVE);
1151 peer_ctx->online_check_pending =
1152 insert_pending_message (peer_ctx, ev, "Check online");
1153 mq = get_mq (peer_ctx);
1154 GNUNET_MQ_notify_sent (ev,
1155 mq_online_check_successful,
1156 peer_ctx);
1157 GNUNET_MQ_send (mq, ev);
1158 if (peer_ctx->sub == msub)
1159 {
1160 GNUNET_STATISTICS_update (stats,
1161 "# pending online checks",
1162 1,
1163 GNUNET_NO);
1164 }
1165}
1166
1167
1168/**
1169 * @brief Check whether function of type #PeerOp was already scheduled
1170 *
1171 * The array with pending operations will probably never grow really big, so
1172 * iterating over it should be ok.
1173 *
1174 * @param peer_ctx Context of the peer to check for the operation
1175 * @param peer_op the operation (#PeerOp) on the peer
1176 *
1177 * @return #GNUNET_YES if this operation is scheduled on that peer
1178 * #GNUNET_NO otherwise
1179 */
1180static int
1181check_operation_scheduled (const struct PeerContext *peer_ctx,
1182 const PeerOp peer_op)
1183{
1184 unsigned int i;
1185
1186 for (i = 0; i < peer_ctx->num_pending_ops; i++)
1187 if (peer_op == peer_ctx->pending_ops[i].op)
1188 return GNUNET_YES;
1189 return GNUNET_NO;
1190}
1191
1192
1193/**
1194 * @brief Callback for scheduler to destroy a channel
1195 *
1196 * @param channel_ctx Context of the channel
1197 */
1198static void
1199destroy_channel (struct ChannelCtx *channel_ctx)
1200{
1201 struct GNUNET_CADET_Channel *channel;
1202
1203 if (NULL != channel_ctx->destruction_task)
1204 {
1205 GNUNET_SCHEDULER_cancel (channel_ctx->destruction_task);
1206 channel_ctx->destruction_task = NULL;
1207 }
1208 GNUNET_assert (channel_ctx->channel != NULL);
1209 channel = channel_ctx->channel;
1210 channel_ctx->channel = NULL;
1211 GNUNET_CADET_channel_destroy (channel);
1212 remove_channel_ctx (channel_ctx);
1213}
1214
1215
1216/**
1217 * @brief Destroy a cadet channel.
1218 *
1219 * This satisfies the function signature of #GNUNET_SCHEDULER_TaskCallback.
1220 *
1221 * @param cls
1222 */
1223static void
1224destroy_channel_cb (void *cls)
1225{
1226 struct ChannelCtx *channel_ctx = cls;
1227
1228 channel_ctx->destruction_task = NULL;
1229 destroy_channel (channel_ctx);
1230}
1231
1232
1233/**
1234 * @brief Schedule the destruction of a channel for immediately afterwards.
1235 *
1236 * In case a channel is to be destroyed from within the callback to the
1237 * destruction of another channel (send channel), we cannot call
1238 * GNUNET_CADET_channel_destroy directly, but need to use this scheduling
1239 * construction.
1240 *
1241 * @param channel_ctx channel to be destroyed.
1242 */
1243static void
1244schedule_channel_destruction (struct ChannelCtx *channel_ctx)
1245{
1246 GNUNET_assert (NULL ==
1247 channel_ctx->destruction_task);
1248 GNUNET_assert (NULL !=
1249 channel_ctx->channel);
1250 channel_ctx->destruction_task =
1251 GNUNET_SCHEDULER_add_now (&destroy_channel_cb,
1252 channel_ctx);
1253}
1254
1255
1256/**
1257 * @brief Remove peer
1258 *
1259 * - Empties the list with pending operations
1260 * - Empties the list with pending messages
1261 * - Cancels potentially existing online check
1262 * - Schedules closing of send and recv channels
1263 * - Removes peer from peer map
1264 *
1265 * @param peer_ctx Context of the peer to be destroyed
1266 * @return #GNUNET_YES if peer was removed
1267 * #GNUNET_NO otherwise
1268 */
1269static int
1270destroy_peer (struct PeerContext *peer_ctx)
1271{
1272 GNUNET_assert (NULL != peer_ctx);
1273 GNUNET_assert (NULL != peer_ctx->sub->peer_map);
1274 if (GNUNET_NO ==
1275 GNUNET_CONTAINER_multipeermap_contains (peer_ctx->sub->peer_map,
1276 &peer_ctx->peer_id))
1277 {
1278 return GNUNET_NO;
1279 }
1280 SET_PEER_FLAG (peer_ctx, Peers_TO_DESTROY);
1281 LOG (GNUNET_ERROR_TYPE_DEBUG,
1282 "Going to remove peer %s\n",
1283 GNUNET_i2s (&peer_ctx->peer_id));
1284 UNSET_PEER_FLAG (peer_ctx, Peers_ONLINE);
1285
1286 /* Clear list of pending operations */
1287 // TODO this probably leaks memory
1288 // ('only' the cls to the function. Not sure what to do with it)
1289 GNUNET_array_grow (peer_ctx->pending_ops,
1290 peer_ctx->num_pending_ops,
1291 0);
1292 /* Remove all pending messages */
1293 while (NULL != peer_ctx->pending_messages_head)
1294 {
1295 LOG (GNUNET_ERROR_TYPE_DEBUG,
1296 "Removing unsent %s\n",
1297 peer_ctx->pending_messages_head->type);
1298 /* Cancel pending message, too */
1299 if ((NULL != peer_ctx->online_check_pending) &&
1300 (0 == memcmp (peer_ctx->pending_messages_head,
1301 peer_ctx->online_check_pending,
1302 sizeof(struct PendingMessage))))
1303 {
1304 peer_ctx->online_check_pending = NULL;
1305 if (peer_ctx->sub == msub)
1306 {
1307 GNUNET_STATISTICS_update (stats,
1308 "# pending online checks",
1309 -1,
1310 GNUNET_NO);
1311 }
1312 }
1313 remove_pending_message (peer_ctx->pending_messages_head,
1314 GNUNET_YES);
1315 }
1316
1317 /* If we are still waiting for notification whether this peer is online
1318 * cancel the according task */
1319 if (NULL != peer_ctx->online_check_pending)
1320 {
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1322 "Removing pending online check for peer %s\n",
1323 GNUNET_i2s (&peer_ctx->peer_id));
1324 // TODO wait until cadet sets mq->cancel_impl
1325 // GNUNET_MQ_send_cancel (peer_ctx->online_check_pending->ev);
1326 remove_pending_message (peer_ctx->online_check_pending,
1327 GNUNET_YES);
1328 peer_ctx->online_check_pending = NULL;
1329 }
1330
1331 if (NULL != peer_ctx->send_channel_ctx)
1332 {
1333 /* This is possibly called from within channel destruction */
1334 peer_ctx->send_channel_ctx->peer_ctx = NULL;
1335 schedule_channel_destruction (peer_ctx->send_channel_ctx);
1336 peer_ctx->send_channel_ctx = NULL;
1337 peer_ctx->mq = NULL;
1338 }
1339 if (NULL != peer_ctx->recv_channel_ctx)
1340 {
1341 /* This is possibly called from within channel destruction */
1342 peer_ctx->recv_channel_ctx->peer_ctx = NULL;
1343 schedule_channel_destruction (peer_ctx->recv_channel_ctx);
1344 peer_ctx->recv_channel_ctx = NULL;
1345 }
1346
1347 if (GNUNET_YES !=
1348 GNUNET_CONTAINER_multipeermap_remove_all (peer_ctx->sub->peer_map,
1349 &peer_ctx->peer_id))
1350 {
1351 LOG (GNUNET_ERROR_TYPE_WARNING,
1352 "removing peer from peer_ctx->sub->peer_map failed\n");
1353 }
1354 if (peer_ctx->sub == msub)
1355 {
1356 GNUNET_STATISTICS_set (stats,
1357 "# known peers",
1358 GNUNET_CONTAINER_multipeermap_size (
1359 peer_ctx->sub->peer_map),
1360 GNUNET_NO);
1361 }
1362 GNUNET_free (peer_ctx);
1363 return GNUNET_YES;
1364}
1365
1366
1367/**
1368 * Iterator over hash map entries. Deletes all contexts of peers.
1369 *
1370 * @param cls closure
1371 * @param key current public key
1372 * @param value value in the hash map
1373 * @return #GNUNET_YES if we should continue to iterate,
1374 * #GNUNET_NO if not.
1375 */
1376static int
1377peermap_clear_iterator (void *cls,
1378 const struct GNUNET_PeerIdentity *key,
1379 void *value)
1380{
1381 struct Sub *sub = cls;
1382
1383 (void) value;
1384
1385 destroy_peer (get_peer_ctx (sub->peer_map, key));
1386 return GNUNET_YES;
1387}
1388
1389
1390/**
1391 * @brief This is called once a message is sent.
1392 *
1393 * Removes the pending message
1394 *
1395 * @param cls type of the message that was sent
1396 */
1397static void
1398mq_notify_sent_cb (void *cls)
1399{
1400 struct PendingMessage *pending_msg = (struct PendingMessage *) cls;
1401
1402 LOG (GNUNET_ERROR_TYPE_DEBUG,
1403 "%s was sent.\n",
1404 pending_msg->type);
1405 if (pending_msg->peer_ctx->sub == msub)
1406 {
1407 if (0 == strncmp ("PULL REPLY", pending_msg->type, 10))
1408 GNUNET_STATISTICS_update (stats, "# pull replies sent", 1, GNUNET_NO);
1409 if (0 == strncmp ("PULL REQUEST", pending_msg->type, 12))
1410 GNUNET_STATISTICS_update (stats, "# pull requests sent", 1, GNUNET_NO);
1411 if (0 == strncmp ("PUSH", pending_msg->type, 4))
1412 GNUNET_STATISTICS_update (stats, "# pushes sent", 1, GNUNET_NO);
1413 if ((0 == strncmp ("PULL REQUEST", pending_msg->type, 12)) &&
1414 (NULL != map_single_hop) &&
1415 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
1416 &pending_msg->
1417 peer_ctx->peer_id)) )
1418 GNUNET_STATISTICS_update (stats,
1419 "# pull requests sent (multi-hop peer)",
1420 1,
1421 GNUNET_NO);
1422 }
1423 /* Do not cancel message */
1424 remove_pending_message (pending_msg, GNUNET_NO);
1425}
1426
1427
1428/**
1429 * @brief Iterator function for #store_valid_peers.
1430 *
1431 * Implements #GNUNET_CONTAINER_PeerMapIterator.
1432 * Writes single peer to disk.
1433 *
1434 * @param cls the file handle to write to.
1435 * @param peer current peer
1436 * @param value unused
1437 *
1438 * @return #GNUNET_YES if we should continue to
1439 * iterate,
1440 * #GNUNET_NO if not.
1441 */
1442static int
1443store_peer_presistently_iterator (void *cls,
1444 const struct GNUNET_PeerIdentity *peer,
1445 void *value)
1446{
1447 const struct GNUNET_DISK_FileHandle *fh = cls;
1448 char peer_string[128];
1449 int size;
1450 ssize_t ret;
1451
1452 (void) value;
1453
1454 if (NULL == peer)
1455 {
1456 return GNUNET_YES;
1457 }
1458 size = GNUNET_snprintf (peer_string,
1459 sizeof(peer_string),
1460 "%s\n",
1461 GNUNET_i2s_full (peer));
1462 GNUNET_assert (53 == size);
1463 ret = GNUNET_DISK_file_write (fh,
1464 peer_string,
1465 size);
1466 GNUNET_assert (size == ret);
1467 return GNUNET_YES;
1468}
1469
1470
1471/**
1472 * @brief Store the peers currently in #valid_peers to disk.
1473 *
1474 * @param sub Sub for which to store the valid peers
1475 */
1476static void
1477store_valid_peers (const struct Sub *sub)
1478{
1479 struct GNUNET_DISK_FileHandle *fh;
1480 uint32_t number_written_peers;
1481 int ret;
1482
1483 if (0 == strncmp ("DISABLE", sub->filename_valid_peers, 7))
1484 {
1485 return;
1486 }
1487
1488 ret = GNUNET_DISK_directory_create_for_file (sub->filename_valid_peers);
1489 if (GNUNET_SYSERR == ret)
1490 {
1491 LOG (GNUNET_ERROR_TYPE_WARNING,
1492 "Not able to create directory for file `%s'\n",
1493 sub->filename_valid_peers);
1494 GNUNET_break (0);
1495 }
1496 else if (GNUNET_NO == ret)
1497 {
1498 LOG (GNUNET_ERROR_TYPE_WARNING,
1499 "Directory for file `%s' exists but is not writable for us\n",
1500 sub->filename_valid_peers);
1501 GNUNET_break (0);
1502 }
1503 fh = GNUNET_DISK_file_open (sub->filename_valid_peers,
1504 GNUNET_DISK_OPEN_WRITE
1505 | GNUNET_DISK_OPEN_CREATE,
1506 GNUNET_DISK_PERM_USER_READ
1507 | GNUNET_DISK_PERM_USER_WRITE);
1508 if (NULL == fh)
1509 {
1510 LOG (GNUNET_ERROR_TYPE_WARNING,
1511 "Not able to write valid peers to file `%s'\n",
1512 sub->filename_valid_peers);
1513 return;
1514 }
1515 LOG (GNUNET_ERROR_TYPE_DEBUG,
1516 "Writing %u valid peers to disk\n",
1517 GNUNET_CONTAINER_multipeermap_size (sub->valid_peers));
1518 number_written_peers =
1519 GNUNET_CONTAINER_multipeermap_iterate (sub->valid_peers,
1520 store_peer_presistently_iterator,
1521 fh);
1522 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1523 GNUNET_assert (number_written_peers ==
1524 GNUNET_CONTAINER_multipeermap_size (sub->valid_peers));
1525}
1526
1527
1528/**
1529 * @brief Convert string representation of peer id to peer id.
1530 *
1531 * Counterpart to #GNUNET_i2s_full.
1532 *
1533 * @param string_repr The string representation of the peer id
1534 *
1535 * @return The peer id
1536 */
1537static const struct GNUNET_PeerIdentity *
1538s2i_full (const char *string_repr)
1539{
1540 struct GNUNET_PeerIdentity *peer;
1541 size_t len;
1542 int ret;
1543
1544 peer = GNUNET_new (struct GNUNET_PeerIdentity);
1545 len = strlen (string_repr);
1546 if (52 > len)
1547 {
1548 LOG (GNUNET_ERROR_TYPE_WARNING,
1549 "Not able to convert string representation of PeerID to PeerID\n"
1550 "String representation: %s (len %lu) - too short\n",
1551 string_repr,
1552 len);
1553 GNUNET_break (0);
1554 }
1555 else if (52 < len)
1556 {
1557 len = 52;
1558 }
1559 ret = GNUNET_CRYPTO_eddsa_public_key_from_string (string_repr,
1560 len,
1561 &peer->public_key);
1562 if (GNUNET_OK != ret)
1563 {
1564 LOG (GNUNET_ERROR_TYPE_WARNING,
1565 "Not able to convert string representation of PeerID to PeerID\n"
1566 "String representation: %s\n",
1567 string_repr);
1568 GNUNET_break (0);
1569 }
1570 return peer;
1571}
1572
1573
1574/**
1575 * @brief Restore the peers on disk to #valid_peers.
1576 *
1577 * @param sub Sub for which to restore the valid peers
1578 */
1579static void
1580restore_valid_peers (const struct Sub *sub)
1581{
1582 off_t file_size;
1583 uint32_t num_peers;
1584 struct GNUNET_DISK_FileHandle *fh;
1585 char *buf;
1586 ssize_t size_read;
1587 char *iter_buf;
1588 char *str_repr;
1589 const struct GNUNET_PeerIdentity *peer;
1590
1591 if (0 == strncmp ("DISABLE", sub->filename_valid_peers, 7))
1592 {
1593 return;
1594 }
1595
1596 if (GNUNET_OK != GNUNET_DISK_file_test (sub->filename_valid_peers))
1597 {
1598 return;
1599 }
1600 fh = GNUNET_DISK_file_open (sub->filename_valid_peers,
1601 GNUNET_DISK_OPEN_READ,
1602 GNUNET_DISK_PERM_NONE);
1603 GNUNET_assert (NULL != fh);
1604 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_handle_size (fh, &file_size));
1605 num_peers = file_size / 53;
1606 buf = GNUNET_malloc (file_size);
1607 size_read = GNUNET_DISK_file_read (fh, buf, file_size);
1608 GNUNET_assert (size_read == file_size);
1609 LOG (GNUNET_ERROR_TYPE_DEBUG,
1610 "Restoring %" PRIu32 " peers from file `%s'\n",
1611 num_peers,
1612 sub->filename_valid_peers);
1613 for (iter_buf = buf; iter_buf < buf + file_size - 1; iter_buf += 53)
1614 {
1615 str_repr = GNUNET_strndup (iter_buf, 53);
1616 peer = s2i_full (str_repr);
1617 GNUNET_free (str_repr);
1618 add_valid_peer (peer, sub->valid_peers);
1619 LOG (GNUNET_ERROR_TYPE_DEBUG,
1620 "Restored valid peer %s from disk\n",
1621 GNUNET_i2s_full (peer));
1622 }
1623 iter_buf = NULL;
1624 GNUNET_free (buf);
1625 LOG (GNUNET_ERROR_TYPE_DEBUG,
1626 "num_peers: %" PRIu32 ", _size (sub->valid_peers): %u\n",
1627 num_peers,
1628 GNUNET_CONTAINER_multipeermap_size (sub->valid_peers));
1629 if (num_peers != GNUNET_CONTAINER_multipeermap_size (sub->valid_peers))
1630 {
1631 LOG (GNUNET_ERROR_TYPE_WARNING,
1632 "Number of restored peers does not match file size. Have probably duplicates.\n");
1633 }
1634 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
1635 LOG (GNUNET_ERROR_TYPE_DEBUG,
1636 "Restored %u valid peers from disk\n",
1637 GNUNET_CONTAINER_multipeermap_size (sub->valid_peers));
1638}
1639
1640
1641/**
1642 * @brief Delete storage of peers that was created with #initialise_peers ()
1643 *
1644 * @param sub Sub for which the storage is deleted
1645 */
1646static void
1647peers_terminate (struct Sub *sub)
1648{
1649 if (GNUNET_SYSERR ==
1650 GNUNET_CONTAINER_multipeermap_iterate (sub->peer_map,
1651 &peermap_clear_iterator,
1652 sub))
1653 {
1654 LOG (GNUNET_ERROR_TYPE_WARNING,
1655 "Iteration destroying peers was aborted.\n");
1656 }
1657 GNUNET_CONTAINER_multipeermap_destroy (sub->peer_map);
1658 sub->peer_map = NULL;
1659 store_valid_peers (sub);
1660 GNUNET_free (sub->filename_valid_peers);
1661 sub->filename_valid_peers = NULL;
1662 GNUNET_CONTAINER_multipeermap_destroy (sub->valid_peers);
1663 sub->valid_peers = NULL;
1664}
1665
1666
1667/**
1668 * Iterator over #valid_peers hash map entries.
1669 *
1670 * @param cls Closure that contains iterator function and closure
1671 * @param peer current peer id
1672 * @param value value in the hash map - unused
1673 * @return #GNUNET_YES if we should continue to
1674 * iterate,
1675 * #GNUNET_NO if not.
1676 */
1677static int
1678valid_peer_iterator (void *cls,
1679 const struct GNUNET_PeerIdentity *peer,
1680 void *value)
1681{
1682 struct PeersIteratorCls *it_cls = cls;
1683
1684 (void) value;
1685
1686 return it_cls->iterator (it_cls->cls, peer);
1687}
1688
1689
1690/**
1691 * @brief Get all currently known, valid peer ids.
1692 *
1693 * @param valid_peers Peer map containing the valid peers in question
1694 * @param iterator function to call on each peer id
1695 * @param it_cls extra argument to @a iterator
1696 * @return the number of key value pairs processed,
1697 * #GNUNET_SYSERR if it aborted iteration
1698 */
1699static int
1700get_valid_peers (struct GNUNET_CONTAINER_MultiPeerMap *valid_peers,
1701 PeersIterator iterator,
1702 void *it_cls)
1703{
1704 struct PeersIteratorCls *cls;
1705 int ret;
1706
1707 cls = GNUNET_new (struct PeersIteratorCls);
1708 cls->iterator = iterator;
1709 cls->cls = it_cls;
1710 ret = GNUNET_CONTAINER_multipeermap_iterate (valid_peers,
1711 valid_peer_iterator,
1712 cls);
1713 GNUNET_free (cls);
1714 return ret;
1715}
1716
1717
1718/**
1719 * @brief Add peer to known peers.
1720 *
1721 * This function is called on new peer_ids from 'external' sources
1722 * (client seed, cadet get_peers(), ...)
1723 *
1724 * @param sub Sub with the peer map that the @a peer will be added to
1725 * @param peer the new #GNUNET_PeerIdentity
1726 *
1727 * @return #GNUNET_YES if peer was inserted
1728 * #GNUNET_NO otherwise
1729 */
1730static int
1731insert_peer (struct Sub *sub,
1732 const struct GNUNET_PeerIdentity *peer)
1733{
1734 if (GNUNET_YES == check_peer_known (sub->peer_map, peer))
1735 {
1736 return GNUNET_NO; /* We already know this peer - nothing to do */
1737 }
1738 (void) create_peer_ctx (sub, peer);
1739 return GNUNET_YES;
1740}
1741
1742
1743/**
1744 * @brief Check whether flags on a peer are set.
1745 *
1746 * @param peer_map Peer map that is expected to contain the @a peer
1747 * @param peer the peer to check the flag of
1748 * @param flags the flags to check
1749 *
1750 * @return #GNUNET_SYSERR if peer is not known
1751 * #GNUNET_YES if all given flags are set
1752 * #GNUNET_NO otherwise
1753 */
1754static int
1755check_peer_flag (const struct GNUNET_CONTAINER_MultiPeerMap *peer_map,
1756 const struct GNUNET_PeerIdentity *peer,
1757 enum Peers_PeerFlags flags)
1758{
1759 struct PeerContext *peer_ctx;
1760
1761 if (GNUNET_NO == check_peer_known (peer_map, peer))
1762 {
1763 return GNUNET_SYSERR;
1764 }
1765 peer_ctx = get_peer_ctx (peer_map, peer);
1766 return check_peer_flag_set (peer_ctx, flags);
1767}
1768
1769
1770/**
1771 * @brief Try connecting to a peer to see whether it is online
1772 *
1773 * If not known yet, insert into known peers
1774 *
1775 * @param sub Sub which would contain the @a peer
1776 * @param peer the peer whose online is to be checked
1777 * @return #GNUNET_YES if the check was issued
1778 * #GNUNET_NO otherwise
1779 */
1780static int
1781issue_peer_online_check (struct Sub *sub,
1782 const struct GNUNET_PeerIdentity *peer)
1783{
1784 struct PeerContext *peer_ctx;
1785
1786 (void) insert_peer (sub, peer); // TODO even needed?
1787 peer_ctx = get_peer_ctx (sub->peer_map, peer);
1788 if ((GNUNET_NO == check_peer_flag (sub->peer_map, peer, Peers_ONLINE)) &&
1789 (NULL == peer_ctx->online_check_pending))
1790 {
1791 check_peer_online (peer_ctx);
1792 return GNUNET_YES;
1793 }
1794 return GNUNET_NO;
1795}
1796
1797
1798/**
1799 * @brief Check if peer is removable.
1800 *
1801 * Check if
1802 * - a recv channel exists
1803 * - there are pending messages
1804 * - there is no pending pull reply
1805 *
1806 * @param peer_ctx Context of the peer in question
1807 * @return #GNUNET_YES if peer is removable
1808 * #GNUNET_NO if peer is NOT removable
1809 * #GNUNET_SYSERR if peer is not known
1810 */
1811static int
1812check_removable (const struct PeerContext *peer_ctx)
1813{
1814 if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (
1815 peer_ctx->sub->peer_map,
1816 &peer_ctx->peer_id))
1817 {
1818 return GNUNET_SYSERR;
1819 }
1820
1821 if ((NULL != peer_ctx->recv_channel_ctx) ||
1822 (NULL != peer_ctx->pending_messages_head) ||
1823 (GNUNET_YES == check_peer_flag_set (peer_ctx, Peers_PULL_REPLY_PENDING)))
1824 {
1825 return GNUNET_NO;
1826 }
1827 return GNUNET_YES;
1828}
1829
1830
1831/**
1832 * @brief Check whether @a peer is actually a peer.
1833 *
1834 * A valid peer is a peer that we know exists eg. we were connected to once.
1835 *
1836 * @param valid_peers Peer map that would contain the @a peer
1837 * @param peer peer in question
1838 *
1839 * @return #GNUNET_YES if peer is valid
1840 * #GNUNET_NO if peer is not valid
1841 */
1842static int
1843check_peer_valid (const struct GNUNET_CONTAINER_MultiPeerMap *valid_peers,
1844 const struct GNUNET_PeerIdentity *peer)
1845{
1846 return GNUNET_CONTAINER_multipeermap_contains (valid_peers, peer);
1847}
1848
1849
1850/**
1851 * @brief Indicate that we want to send to the other peer
1852 *
1853 * This establishes a sending channel
1854 *
1855 * @param peer_ctx Context of the target peer
1856 */
1857static void
1858indicate_sending_intention (struct PeerContext *peer_ctx)
1859{
1860 GNUNET_assert (GNUNET_YES == check_peer_known (peer_ctx->sub->peer_map,
1861 &peer_ctx->peer_id));
1862 (void) get_channel (peer_ctx);
1863}
1864
1865
1866/**
1867 * @brief Check whether other peer has the intention to send/opened channel
1868 * towars us
1869 *
1870 * @param peer_ctx Context of the peer in question
1871 *
1872 * @return #GNUNET_YES if peer has the intention to send
1873 * #GNUNET_NO otherwise
1874 */
1875static int
1876check_peer_send_intention (const struct PeerContext *peer_ctx)
1877{
1878 if (NULL != peer_ctx->recv_channel_ctx)
1879 {
1880 return GNUNET_YES;
1881 }
1882 return GNUNET_NO;
1883}
1884
1885
1886/**
1887 * Handle the channel a peer opens to us.
1888 *
1889 * @param cls The closure - Sub
1890 * @param channel The channel the peer wants to establish
1891 * @param initiator The peer's peer ID
1892 *
1893 * @return initial channel context for the channel
1894 * (can be NULL -- that's not an error)
1895 */
1896static void *
1897handle_inbound_channel (void *cls,
1898 struct GNUNET_CADET_Channel *channel,
1899 const struct GNUNET_PeerIdentity *initiator)
1900{
1901 struct PeerContext *peer_ctx;
1902 struct ChannelCtx *channel_ctx;
1903 struct Sub *sub = cls;
1904
1905 LOG (GNUNET_ERROR_TYPE_DEBUG,
1906 "New channel was established to us (Peer %s).\n",
1907 GNUNET_i2s (initiator));
1908 GNUNET_assert (NULL != channel); /* according to cadet API */
1909 /* Make sure we 'know' about this peer */
1910 peer_ctx = create_or_get_peer_ctx (sub, initiator);
1911 set_peer_online (peer_ctx);
1912 (void) add_valid_peer (&peer_ctx->peer_id, peer_ctx->sub->valid_peers);
1913 channel_ctx = add_channel_ctx (peer_ctx);
1914 channel_ctx->channel = channel;
1915 /* We only accept one incoming channel per peer */
1916 if (GNUNET_YES == check_peer_send_intention (get_peer_ctx (sub->peer_map,
1917 initiator)))
1918 {
1919 LOG (GNUNET_ERROR_TYPE_WARNING,
1920 "Already got one receive channel. Destroying old one.\n");
1921 GNUNET_break_op (0);
1922 destroy_channel (peer_ctx->recv_channel_ctx);
1923 peer_ctx->recv_channel_ctx = channel_ctx;
1924 /* return the channel context */
1925 return channel_ctx;
1926 }
1927 peer_ctx->recv_channel_ctx = channel_ctx;
1928 return channel_ctx;
1929}
1930
1931
1932/**
1933 * @brief Check whether a sending channel towards the given peer exists
1934 *
1935 * @param peer_ctx Context of the peer in question
1936 *
1937 * @return #GNUNET_YES if a sending channel towards that peer exists
1938 * #GNUNET_NO otherwise
1939 */
1940static int
1941check_sending_channel_exists (const struct PeerContext *peer_ctx)
1942{
1943 if (GNUNET_NO == check_peer_known (peer_ctx->sub->peer_map,
1944 &peer_ctx->peer_id))
1945 { /* If no such peer exists, there is no channel */
1946 return GNUNET_NO;
1947 }
1948 if (NULL == peer_ctx->send_channel_ctx)
1949 {
1950 return GNUNET_NO;
1951 }
1952 return GNUNET_YES;
1953}
1954
1955
1956/**
1957 * @brief Destroy the send channel of a peer e.g. stop indicating a sending
1958 * intention to another peer
1959 *
1960 * @param peer_ctx Context to the peer
1961 * @return #GNUNET_YES if channel was destroyed
1962 * #GNUNET_NO otherwise
1963 */
1964static int
1965destroy_sending_channel (struct PeerContext *peer_ctx)
1966{
1967 if (GNUNET_NO == check_peer_known (peer_ctx->sub->peer_map,
1968 &peer_ctx->peer_id))
1969 {
1970 return GNUNET_NO;
1971 }
1972 if (NULL != peer_ctx->send_channel_ctx)
1973 {
1974 destroy_channel (peer_ctx->send_channel_ctx);
1975 (void) check_connected (peer_ctx);
1976 return GNUNET_YES;
1977 }
1978 return GNUNET_NO;
1979}
1980
1981
1982/**
1983 * @brief Send a message to another peer.
1984 *
1985 * Keeps track about pending messages so they can be properly removed when the
1986 * peer is destroyed.
1987 *
1988 * @param peer_ctx Context of the peer to which the message is to be sent
1989 * @param ev envelope of the message
1990 * @param type type of the message
1991 */
1992static void
1993send_message (struct PeerContext *peer_ctx,
1994 struct GNUNET_MQ_Envelope *ev,
1995 const char *type)
1996{
1997 struct PendingMessage *pending_msg;
1998 struct GNUNET_MQ_Handle *mq;
1999
2000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2001 "Sending message to %s of type %s\n",
2002 GNUNET_i2s (&peer_ctx->peer_id),
2003 type);
2004 pending_msg = insert_pending_message (peer_ctx, ev, type);
2005 mq = get_mq (peer_ctx);
2006 GNUNET_MQ_notify_sent (ev,
2007 mq_notify_sent_cb,
2008 pending_msg);
2009 GNUNET_MQ_send (mq, ev);
2010}
2011
2012
2013/**
2014 * @brief Schedule a operation on given peer
2015 *
2016 * Avoids scheduling an operation twice.
2017 *
2018 * @param peer_ctx Context of the peer for which to schedule the operation
2019 * @param peer_op the operation to schedule
2020 * @param cls Closure to @a peer_op
2021 *
2022 * @return #GNUNET_YES if the operation was scheduled
2023 * #GNUNET_NO otherwise
2024 */
2025static int
2026schedule_operation (struct PeerContext *peer_ctx,
2027 const PeerOp peer_op,
2028 void *cls)
2029{
2030 struct PeerPendingOp pending_op;
2031
2032 GNUNET_assert (GNUNET_YES == check_peer_known (peer_ctx->sub->peer_map,
2033 &peer_ctx->peer_id));
2034
2035 // TODO if ONLINE execute immediately
2036
2037 if (GNUNET_NO == check_operation_scheduled (peer_ctx, peer_op))
2038 {
2039 pending_op.op = peer_op;
2040 pending_op.op_cls = cls;
2041 GNUNET_array_append (peer_ctx->pending_ops,
2042 peer_ctx->num_pending_ops,
2043 pending_op);
2044 return GNUNET_YES;
2045 }
2046 return GNUNET_NO;
2047}
2048
2049
2050/***********************************************************************
2051* /Old gnunet-service-rps_peers.c
2052***********************************************************************/
2053
2054
2055/***********************************************************************
2056* Housekeeping with clients
2057***********************************************************************/
2058
2059/**
2060 * Closure used to pass the client and the id to the callback
2061 * that replies to a client's request
2062 */
2063struct ReplyCls
2064{
2065 /**
2066 * DLL
2067 */
2068 struct ReplyCls *next;
2069 struct ReplyCls *prev;
2070
2071 /**
2072 * The identifier of the request
2073 */
2074 uint32_t id;
2075
2076 /**
2077 * The handle to the request
2078 */
2079 struct RPS_SamplerRequestHandle *req_handle;
2080
2081 /**
2082 * The client handle to send the reply to
2083 */
2084 struct ClientContext *cli_ctx;
2085};
2086
2087
2088/**
2089 * Struct used to store the context of a connected client.
2090 */
2091struct ClientContext
2092{
2093 /**
2094 * DLL
2095 */
2096 struct ClientContext *next;
2097 struct ClientContext *prev;
2098
2099 /**
2100 * The message queue to communicate with the client.
2101 */
2102 struct GNUNET_MQ_Handle *mq;
2103
2104 /**
2105 * @brief How many updates this client expects to receive.
2106 */
2107 int64_t view_updates_left;
2108
2109 /**
2110 * @brief Whether this client wants to receive stream updates.
2111 * Either #GNUNET_YES or #GNUNET_NO
2112 */
2113 int8_t stream_update;
2114
2115 /**
2116 * The client handle to send the reply to
2117 */
2118 struct GNUNET_SERVICE_Client *client;
2119
2120 /**
2121 * The #Sub this context belongs to
2122 */
2123 struct Sub *sub;
2124};
2125
2126/**
2127 * DLL with all clients currently connected to us
2128 */
2129struct ClientContext *cli_ctx_head;
2130struct ClientContext *cli_ctx_tail;
2131
2132/***********************************************************************
2133* /Housekeeping with clients
2134***********************************************************************/
2135
2136
2137/***********************************************************************
2138* Util functions
2139***********************************************************************/
2140
2141
2142/**
2143 * Print peerlist to log.
2144 */
2145static void
2146print_peer_list (struct GNUNET_PeerIdentity *list,
2147 unsigned int len)
2148{
2149 unsigned int i;
2150
2151 LOG (GNUNET_ERROR_TYPE_DEBUG,
2152 "Printing peer list of length %u at %p:\n",
2153 len,
2154 list);
2155 for (i = 0; i < len; i++)
2156 {
2157 LOG (GNUNET_ERROR_TYPE_DEBUG,
2158 "%u. peer: %s\n",
2159 i, GNUNET_i2s (&list[i]));
2160 }
2161}
2162
2163
2164/**
2165 * Remove peer from list.
2166 */
2167static void
2168rem_from_list (struct GNUNET_PeerIdentity **peer_list,
2169 unsigned int *list_size,
2170 const struct GNUNET_PeerIdentity *peer)
2171{
2172 unsigned int i;
2173 struct GNUNET_PeerIdentity *tmp;
2174
2175 tmp = *peer_list;
2176
2177 LOG (GNUNET_ERROR_TYPE_DEBUG,
2178 "Removing peer %s from list at %p\n",
2179 GNUNET_i2s (peer),
2180 tmp);
2181
2182 for (i = 0; i < *list_size; i++)
2183 {
2184 if (0 == GNUNET_memcmp (&tmp[i], peer))
2185 {
2186 if (i < *list_size - 1)
2187 { /* Not at the last entry -- shift peers left */
2188 memmove (&tmp[i], &tmp[i + 1],
2189 ((*list_size) - i - 1) * sizeof(struct GNUNET_PeerIdentity));
2190 }
2191 /* Remove last entry (should be now useless PeerID) */
2192 GNUNET_array_grow (tmp, *list_size, (*list_size) - 1);
2193 }
2194 }
2195 *peer_list = tmp;
2196}
2197
2198
2199/**
2200 * Insert PeerID in #view
2201 *
2202 * Called once we know a peer is online.
2203 * Implements #PeerOp
2204 *
2205 * @return GNUNET_OK if peer was actually inserted
2206 * GNUNET_NO if peer was not inserted
2207 */
2208static void
2209insert_in_view_op (void *cls,
2210 const struct GNUNET_PeerIdentity *peer);
2211
2212/**
2213 * Insert PeerID in #view
2214 *
2215 * Called once we know a peer is online.
2216 *
2217 * @param sub Sub in with the view to insert in
2218 * @param peer the peer to insert
2219 *
2220 * @return GNUNET_OK if peer was actually inserted
2221 * GNUNET_NO if peer was not inserted
2222 */
2223static int
2224insert_in_view (struct Sub *sub,
2225 const struct GNUNET_PeerIdentity *peer)
2226{
2227 struct PeerContext *peer_ctx;
2228 int online;
2229 int ret;
2230
2231 online = check_peer_flag (sub->peer_map, peer, Peers_ONLINE);
2232 peer_ctx = get_peer_ctx (sub->peer_map, peer); // TODO indirection needed?
2233 if ((GNUNET_NO == online) ||
2234 (GNUNET_SYSERR == online)) /* peer is not even known */
2235 {
2236 (void) issue_peer_online_check (sub, peer);
2237 (void) schedule_operation (peer_ctx, insert_in_view_op, sub);
2238 return GNUNET_NO;
2239 }
2240 /* Open channel towards peer to keep connection open */
2241 indicate_sending_intention (peer_ctx);
2242 ret = View_put (sub->view, peer);
2243 if (peer_ctx->sub == msub)
2244 {
2245 GNUNET_STATISTICS_set (stats,
2246 "view size",
2247 View_size (peer_ctx->sub->view),
2248 GNUNET_NO);
2249 }
2250 return ret;
2251}
2252
2253
2254/**
2255 * @brief Send view to client
2256 *
2257 * @param cli_ctx the context of the client
2258 * @param view_array the peerids of the view as array (can be empty)
2259 * @param view_size the size of the view array (can be 0)
2260 */
2261static void
2262send_view (const struct ClientContext *cli_ctx,
2263 const struct GNUNET_PeerIdentity *view_array,
2264 uint64_t view_size)
2265{
2266 struct GNUNET_MQ_Envelope *ev;
2267 struct GNUNET_RPS_CS_DEBUG_ViewReply *out_msg;
2268 struct Sub *sub;
2269
2270 if (NULL == view_array)
2271 {
2272 if (NULL == cli_ctx->sub)
2273 sub = msub;
2274 else
2275 sub = cli_ctx->sub;
2276 view_size = View_size (sub->view);
2277 view_array = View_get_as_array (sub->view);
2278 }
2279
2280 ev = GNUNET_MQ_msg_extra (out_msg,
2281 view_size * sizeof(struct GNUNET_PeerIdentity),
2282 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_REPLY);
2283 out_msg->num_peers = htonl (view_size);
2284
2285 GNUNET_memcpy (&out_msg[1],
2286 view_array,
2287 view_size * sizeof(struct GNUNET_PeerIdentity));
2288 GNUNET_MQ_send (cli_ctx->mq, ev);
2289}
2290
2291
2292/**
2293 * @brief Send peer from biased stream to client.
2294 *
2295 * TODO merge with send_view, parameterise
2296 *
2297 * @param cli_ctx the context of the client
2298 * @param view_array the peerids of the view as array (can be empty)
2299 * @param view_size the size of the view array (can be 0)
2300 */
2301static void
2302send_stream_peers (const struct ClientContext *cli_ctx,
2303 uint64_t num_peers,
2304 const struct GNUNET_PeerIdentity *peers)
2305{
2306 struct GNUNET_MQ_Envelope *ev;
2307 struct GNUNET_RPS_CS_DEBUG_StreamReply *out_msg;
2308
2309 GNUNET_assert (NULL != peers);
2310
2311 ev = GNUNET_MQ_msg_extra (out_msg,
2312 num_peers * sizeof(struct GNUNET_PeerIdentity),
2313 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_REPLY);
2314 out_msg->num_peers = htonl (num_peers);
2315
2316 GNUNET_memcpy (&out_msg[1],
2317 peers,
2318 num_peers * sizeof(struct GNUNET_PeerIdentity));
2319 GNUNET_MQ_send (cli_ctx->mq, ev);
2320}
2321
2322
2323/**
2324 * @brief sends updates to clients that are interested
2325 *
2326 * @param sub Sub for which to notify clients
2327 */
2328static void
2329clients_notify_view_update (const struct Sub *sub)
2330{
2331 struct ClientContext *cli_ctx_iter;
2332 uint64_t num_peers;
2333 const struct GNUNET_PeerIdentity *view_array;
2334
2335 num_peers = View_size (sub->view);
2336 view_array = View_get_as_array (sub->view);
2337 /* check size of view is small enough */
2338 if (GNUNET_MAX_MESSAGE_SIZE < num_peers)
2339 {
2340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2341 "View is too big to send\n");
2342 return;
2343 }
2344
2345 for (cli_ctx_iter = cli_ctx_head;
2346 NULL != cli_ctx_iter;
2347 cli_ctx_iter = cli_ctx_iter->next)
2348 {
2349 if (1 < cli_ctx_iter->view_updates_left)
2350 {
2351 /* Client wants to receive limited amount of updates */
2352 cli_ctx_iter->view_updates_left -= 1;
2353 }
2354 else if (1 == cli_ctx_iter->view_updates_left)
2355 {
2356 /* Last update of view for client */
2357 cli_ctx_iter->view_updates_left = -1;
2358 }
2359 else if (0 > cli_ctx_iter->view_updates_left)
2360 {
2361 /* Client is not interested in updates */
2362 continue;
2363 }
2364 /* else _updates_left == 0 - infinite amount of updates */
2365
2366 /* send view */
2367 send_view (cli_ctx_iter, view_array, num_peers);
2368 }
2369}
2370
2371
2372/**
2373 * @brief sends updates to clients that are interested
2374 *
2375 * @param num_peers Number of peers to send
2376 * @param peers the array of peers to send
2377 */
2378static void
2379clients_notify_stream_peer (const struct Sub *sub,
2380 uint64_t num_peers,
2381 const struct GNUNET_PeerIdentity *peers)
2382// TODO enum StreamPeerSource)
2383{
2384 struct ClientContext *cli_ctx_iter;
2385
2386 LOG (GNUNET_ERROR_TYPE_DEBUG,
2387 "Got peer (%s) from biased stream - update all clients\n",
2388 GNUNET_i2s (peers));
2389
2390 for (cli_ctx_iter = cli_ctx_head;
2391 NULL != cli_ctx_iter;
2392 cli_ctx_iter = cli_ctx_iter->next)
2393 {
2394 if ((GNUNET_YES == cli_ctx_iter->stream_update) &&
2395 ((sub == cli_ctx_iter->sub) || (sub == msub) ))
2396 {
2397 send_stream_peers (cli_ctx_iter, num_peers, peers);
2398 }
2399 }
2400}
2401
2402
2403/**
2404 * Put random peer from sampler into the view as history update.
2405 *
2406 * @param ids Array of Peers to insert into view
2407 * @param num_peers Number of peers to insert
2408 * @param cls Closure - The Sub for which this is to be done
2409 */
2410static void
2411hist_update (const struct GNUNET_PeerIdentity *ids,
2412 uint32_t num_peers,
2413 void *cls)
2414{
2415 unsigned int i;
2416 struct Sub *sub = cls;
2417
2418 for (i = 0; i < num_peers; i++)
2419 {
2420 int inserted;
2421 if (GNUNET_YES != check_peer_known (sub->peer_map, &ids[i]))
2422 {
2423 LOG (GNUNET_ERROR_TYPE_WARNING,
2424 "Peer in history update not known!\n");
2425 continue;
2426 }
2427 inserted = insert_in_view (sub, &ids[i]);
2428 if (GNUNET_OK == inserted)
2429 {
2430 clients_notify_stream_peer (sub, 1, &ids[i]);
2431 }
2432#ifdef TO_FILE_FULL
2433 to_file (sub->file_name_view_log,
2434 "+%s\t(history)",
2435 GNUNET_i2s_full (ids));
2436#endif /* TO_FILE_FULL */
2437 }
2438 clients_notify_view_update (sub);
2439}
2440
2441
2442/**
2443 * Wrapper around #RPS_sampler_resize()
2444 *
2445 * If we do not have enough sampler elements, double current sampler size
2446 * If we have more than enough sampler elements, halv current sampler size
2447 *
2448 * @param sampler The sampler to resize
2449 * @param new_size New size to which to resize
2450 */
2451static void
2452resize_wrapper (struct RPS_Sampler *sampler, uint32_t new_size)
2453{
2454 unsigned int sampler_size;
2455
2456 // TODO statistics
2457 // TODO respect the min, max
2458 sampler_size = RPS_sampler_get_size (sampler);
2459 if (sampler_size > new_size * 4)
2460 { /* Shrinking */
2461 RPS_sampler_resize (sampler, sampler_size / 2);
2462 }
2463 else if (sampler_size < new_size)
2464 { /* Growing */
2465 RPS_sampler_resize (sampler, sampler_size * 2);
2466 }
2467 LOG (GNUNET_ERROR_TYPE_DEBUG, "sampler_size is now %u\n", sampler_size);
2468}
2469
2470
2471#if ENABLE_MALICIOUS
2472/**
2473 * Add all peers in @a peer_array to @a peer_map used as set.
2474 *
2475 * @param peer_array array containing the peers
2476 * @param num_peers number of peers in @peer_array
2477 * @param peer_map the peermap to use as set
2478 */
2479static void
2480add_peer_array_to_set (const struct GNUNET_PeerIdentity *peer_array,
2481 unsigned int num_peers,
2482 struct GNUNET_CONTAINER_MultiPeerMap *peer_map)
2483{
2484 unsigned int i;
2485
2486 if (NULL == peer_map)
2487 {
2488 LOG (GNUNET_ERROR_TYPE_WARNING,
2489 "Trying to add peers to non-existing peermap.\n");
2490 return;
2491 }
2492
2493 for (i = 0; i < num_peers; i++)
2494 {
2495 GNUNET_CONTAINER_multipeermap_put (peer_map,
2496 &peer_array[i],
2497 NULL,
2498 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
2499 if (msub->peer_map == peer_map)
2500 {
2501 GNUNET_STATISTICS_set (stats,
2502 "# known peers",
2503 GNUNET_CONTAINER_multipeermap_size (peer_map),
2504 GNUNET_NO);
2505 }
2506 }
2507}
2508
2509
2510#endif /* ENABLE_MALICIOUS */
2511
2512
2513/**
2514 * Send a PULL REPLY to @a peer_id
2515 *
2516 * @param peer_ctx Context of the peer to send the reply to
2517 * @param peer_ids the peers to send to @a peer_id
2518 * @param num_peer_ids the number of peers to send to @a peer_id
2519 */
2520static void
2521send_pull_reply (struct PeerContext *peer_ctx,
2522 const struct GNUNET_PeerIdentity *peer_ids,
2523 unsigned int num_peer_ids)
2524{
2525 uint32_t send_size;
2526 struct GNUNET_MQ_Envelope *ev;
2527 struct GNUNET_RPS_P2P_PullReplyMessage *out_msg;
2528
2529 /* Compute actual size */
2530 send_size = sizeof(struct GNUNET_RPS_P2P_PullReplyMessage)
2531 + num_peer_ids * sizeof(struct GNUNET_PeerIdentity);
2532
2533 if (GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE < send_size)
2534 /* Compute number of peers to send
2535 * If too long, simply truncate */
2536 // TODO select random ones via permutation
2537 // or even better: do good protocol design
2538 send_size =
2539 (GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE
2540 - sizeof(struct GNUNET_RPS_P2P_PullReplyMessage))
2541 / sizeof(struct GNUNET_PeerIdentity);
2542 else
2543 send_size = num_peer_ids;
2544
2545 LOG (GNUNET_ERROR_TYPE_DEBUG,
2546 "Going to send PULL REPLY with %u peers to %s\n",
2547 send_size, GNUNET_i2s (&peer_ctx->peer_id));
2548
2549 ev = GNUNET_MQ_msg_extra (out_msg,
2550 send_size * sizeof(struct GNUNET_PeerIdentity),
2551 GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REPLY);
2552 out_msg->num_peers = htonl (send_size);
2553 GNUNET_memcpy (&out_msg[1], peer_ids,
2554 send_size * sizeof(struct GNUNET_PeerIdentity));
2555
2556 send_message (peer_ctx, ev, "PULL REPLY");
2557 if (peer_ctx->sub == msub)
2558 {
2559 GNUNET_STATISTICS_update (stats, "# pull reply send issued", 1, GNUNET_NO);
2560 }
2561 // TODO check with send intention: as send_channel is used/opened we indicate
2562 // a sending intention without intending it.
2563 // -> clean peer afterwards?
2564 // -> use recv_channel?
2565}
2566
2567
2568/**
2569 * Insert PeerID in #pull_map
2570 *
2571 * Called once we know a peer is online.
2572 *
2573 * @param cls Closure - Sub with the pull map to insert into
2574 * @param peer Peer to insert
2575 */
2576static void
2577insert_in_pull_map (void *cls,
2578 const struct GNUNET_PeerIdentity *peer)
2579{
2580 struct Sub *sub = cls;
2581
2582 CustomPeerMap_put (sub->pull_map, peer);
2583}
2584
2585
2586/**
2587 * Insert PeerID in #view
2588 *
2589 * Called once we know a peer is online.
2590 * Implements #PeerOp
2591 *
2592 * @param cls Closure - Sub with view to insert peer into
2593 * @param peer the peer to insert
2594 */
2595static void
2596insert_in_view_op (void *cls,
2597 const struct GNUNET_PeerIdentity *peer)
2598{
2599 struct Sub *sub = cls;
2600 int inserted;
2601
2602 inserted = insert_in_view (sub, peer);
2603 if (GNUNET_OK == inserted)
2604 {
2605 clients_notify_stream_peer (sub, 1, peer);
2606 }
2607}
2608
2609
2610/**
2611 * Update sampler with given PeerID.
2612 * Implements #PeerOp
2613 *
2614 * @param cls Closure - Sub containing the sampler to insert into
2615 * @param peer Peer to insert
2616 */
2617static void
2618insert_in_sampler (void *cls,
2619 const struct GNUNET_PeerIdentity *peer)
2620{
2621 struct Sub *sub = cls;
2622
2623 LOG (GNUNET_ERROR_TYPE_DEBUG,
2624 "Updating samplers with peer %s from insert_in_sampler()\n",
2625 GNUNET_i2s (peer));
2626 RPS_sampler_update (sub->sampler, peer);
2627 if (0 < RPS_sampler_count_id (sub->sampler, peer))
2628 {
2629 /* Make sure we 'know' about this peer */
2630 (void) issue_peer_online_check (sub, peer);
2631 /* Establish a channel towards that peer to indicate we are going to send
2632 * messages to it */
2633 // indicate_sending_intention (peer);
2634 }
2635 if (sub == msub)
2636 {
2637 GNUNET_STATISTICS_update (stats,
2638 "# observed peers in gossip",
2639 1,
2640 GNUNET_NO);
2641 }
2642#ifdef TO_FILE
2643 sub->num_observed_peers++;
2644 (void) GNUNET_CONTAINER_multipeermap_put
2645 (sub->observed_unique_peers,
2646 peer,
2647 NULL,
2648 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
2649 uint32_t num_observed_unique_peers =
2650 GNUNET_CONTAINER_multipeermap_size (sub->observed_unique_peers);
2651 GNUNET_STATISTICS_set (stats,
2652 "# unique peers in gossip",
2653 num_observed_unique_peers,
2654 GNUNET_NO);
2655#ifdef TO_FILE_FULL
2656 to_file (sub->file_name_observed_log,
2657 "%" PRIu32 " %" PRIu32 " %f\n",
2658 sub->num_observed_peers,
2659 num_observed_unique_peers,
2660 1.0 * num_observed_unique_peers / sub->num_observed_peers)
2661#endif /* TO_FILE_FULL */
2662#endif /* TO_FILE */
2663}
2664
2665
2666/**
2667 * @brief This is called on peers from external sources (cadet, peerinfo, ...)
2668 * If the peer is not known, online check is issued and it is
2669 * scheduled to be inserted in sampler and view.
2670 *
2671 * "External sources" refer to every source except the gossip.
2672 *
2673 * @param sub Sub for which @a peer was received
2674 * @param peer peer to insert/peer received
2675 */
2676static void
2677got_peer (struct Sub *sub,
2678 const struct GNUNET_PeerIdentity *peer)
2679{
2680 /* If we did not know this peer already, insert it into sampler and view */
2681 if (GNUNET_YES == issue_peer_online_check (sub, peer))
2682 {
2683 schedule_operation (get_peer_ctx (sub->peer_map, peer),
2684 &insert_in_sampler, sub);
2685 schedule_operation (get_peer_ctx (sub->peer_map, peer),
2686 &insert_in_view_op, sub);
2687 }
2688 if (sub == msub)
2689 {
2690 GNUNET_STATISTICS_update (stats,
2691 "# learnd peers",
2692 1,
2693 GNUNET_NO);
2694 }
2695}
2696
2697
2698/**
2699 * @brief Checks if there is a sending channel and if it is needed
2700 *
2701 * @param peer_ctx Context of the peer to check
2702 * @return GNUNET_YES if sending channel exists and is still needed
2703 * GNUNET_NO otherwise
2704 */
2705static int
2706check_sending_channel_needed (const struct PeerContext *peer_ctx)
2707{
2708 /* struct GNUNET_CADET_Channel *channel; */
2709 if (GNUNET_NO == check_peer_known (peer_ctx->sub->peer_map,
2710 &peer_ctx->peer_id))
2711 {
2712 return GNUNET_NO;
2713 }
2714 if (GNUNET_YES == check_sending_channel_exists (peer_ctx))
2715 {
2716 if ((0 < RPS_sampler_count_id (peer_ctx->sub->sampler,
2717 &peer_ctx->peer_id)) ||
2718 (GNUNET_YES == View_contains_peer (peer_ctx->sub->view,
2719 &peer_ctx->peer_id)) ||
2720 (GNUNET_YES == CustomPeerMap_contains_peer (peer_ctx->sub->push_map,
2721 &peer_ctx->peer_id)) ||
2722 (GNUNET_YES == CustomPeerMap_contains_peer (peer_ctx->sub->pull_map,
2723 &peer_ctx->peer_id)) ||
2724 (GNUNET_YES == check_peer_flag (peer_ctx->sub->peer_map,
2725 &peer_ctx->peer_id,
2726 Peers_PULL_REPLY_PENDING)))
2727 { /* If we want to keep the connection to peer open */
2728 return GNUNET_YES;
2729 }
2730 return GNUNET_NO;
2731 }
2732 return GNUNET_NO;
2733}
2734
2735
2736/**
2737 * @brief remove peer from our knowledge, the view, push and pull maps and
2738 * samplers.
2739 *
2740 * @param sub Sub with the data structures the peer is to be removed from
2741 * @param peer the peer to remove
2742 */
2743static void
2744remove_peer (struct Sub *sub,
2745 const struct GNUNET_PeerIdentity *peer)
2746{
2747 (void) View_remove_peer (sub->view,
2748 peer);
2749 CustomPeerMap_remove_peer (sub->pull_map,
2750 peer);
2751 CustomPeerMap_remove_peer (sub->push_map,
2752 peer);
2753 RPS_sampler_reinitialise_by_value (sub->sampler,
2754 peer);
2755 /* We want to destroy the peer now.
2756 * Sometimes, it just seems that it's already been removed from the peer_map,
2757 * so check the peer_map first. */
2758 if (GNUNET_YES == check_peer_known (sub->peer_map,
2759 peer))
2760 {
2761 destroy_peer (get_peer_ctx (sub->peer_map,
2762 peer));
2763 }
2764}
2765
2766
2767/**
2768 * @brief Remove data that is not needed anymore.
2769 *
2770 * If the sending channel is no longer needed it is destroyed.
2771 *
2772 * @param sub Sub in which the current peer is to be cleaned
2773 * @param peer the peer whose data is about to be cleaned
2774 */
2775static void
2776clean_peer (struct Sub *sub,
2777 const struct GNUNET_PeerIdentity *peer)
2778{
2779 if (GNUNET_NO == check_sending_channel_needed (get_peer_ctx (sub->peer_map,
2780 peer)))
2781 {
2782 LOG (GNUNET_ERROR_TYPE_DEBUG,
2783 "Going to remove send channel to peer %s\n",
2784 GNUNET_i2s (peer));
2785 #if ENABLE_MALICIOUS
2786 if (0 != GNUNET_memcmp (&attacked_peer,
2787 peer))
2788 (void) destroy_sending_channel (get_peer_ctx (sub->peer_map,
2789 peer));
2790 #else /* ENABLE_MALICIOUS */
2791 (void) destroy_sending_channel (get_peer_ctx (sub->peer_map,
2792 peer));
2793 #endif /* ENABLE_MALICIOUS */
2794 }
2795
2796 if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (sub->peer_map,
2797 peer))
2798 {
2799 /* Peer was already removed by callback on destroyed channel */
2800 LOG (GNUNET_ERROR_TYPE_WARNING,
2801 "Peer was removed from our knowledge during cleanup\n");
2802 return;
2803 }
2804
2805 if ((GNUNET_NO == check_peer_send_intention (get_peer_ctx (sub->peer_map,
2806 peer))) &&
2807 (GNUNET_NO == View_contains_peer (sub->view, peer)) &&
2808 (GNUNET_NO == CustomPeerMap_contains_peer (sub->push_map, peer)) &&
2809 (GNUNET_NO == CustomPeerMap_contains_peer (sub->pull_map, peer)) &&
2810 (0 == RPS_sampler_count_id (sub->sampler, peer)) &&
2811 (GNUNET_YES == check_removable (get_peer_ctx (sub->peer_map, peer))))
2812 { /* We can safely remove this peer */
2813 LOG (GNUNET_ERROR_TYPE_DEBUG,
2814 "Going to remove peer %s\n",
2815 GNUNET_i2s (peer));
2816 remove_peer (sub, peer);
2817 return;
2818 }
2819}
2820
2821
2822/**
2823 * @brief This is called when a channel is destroyed.
2824 *
2825 * Removes peer completely from our knowledge if the send_channel was destroyed
2826 * Otherwise simply delete the recv_channel
2827 * Also check if the knowledge about this peer is still needed.
2828 * If not, remove this peer from our knowledge.
2829 *
2830 * @param cls The closure - Context to the channel
2831 * @param channel The channel being closed
2832 */
2833static void
2834cleanup_destroyed_channel (void *cls,
2835 const struct GNUNET_CADET_Channel *channel)
2836{
2837 struct ChannelCtx *channel_ctx = cls;
2838 struct PeerContext *peer_ctx = channel_ctx->peer_ctx;
2839
2840 (void) channel;
2841
2842 channel_ctx->channel = NULL;
2843 if ((NULL != peer_ctx) &&
2844 (peer_ctx->send_channel_ctx == channel_ctx) &&
2845 (GNUNET_YES == check_sending_channel_needed (peer_ctx)) )
2846 {
2847 remove_channel_ctx (channel_ctx);
2848 remove_peer (peer_ctx->sub, &peer_ctx->peer_id);
2849 }
2850 else
2851 {
2852 /* We need this if-else construct because we need to make sure the channel
2853 * (context) is cleaned up before removing the peer, but still need to
2854 * compare it while checking the condition */
2855 remove_channel_ctx (channel_ctx);
2856 }
2857}
2858
2859
2860/***********************************************************************
2861* /Util functions
2862***********************************************************************/
2863
2864
2865/***********************************************************************
2866* Sub
2867***********************************************************************/
2868
2869/**
2870 * @brief Create a new Sub
2871 *
2872 * @param hash Hash of value shared among rps instances on other hosts that
2873 * defines a subgroup to sample from.
2874 * @param sampler_size Size of the sampler
2875 * @param round_interval Interval (in average) between two rounds
2876 *
2877 * @return Sub
2878 */
2879struct Sub *
2880new_sub (const struct GNUNET_HashCode *hash,
2881 uint32_t sampler_size,
2882 struct GNUNET_TIME_Relative round_interval)
2883{
2884 struct Sub *sub;
2885
2886 sub = GNUNET_new (struct Sub);
2887
2888 /* With the hash generated from the secret value this service only connects
2889 * to rps instances that share the value */
2890 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
2891 GNUNET_MQ_hd_fixed_size (peer_check,
2892 GNUNET_MESSAGE_TYPE_RPS_PP_CHECK_LIVE,
2893 struct GNUNET_MessageHeader,
2894 NULL),
2895 GNUNET_MQ_hd_fixed_size (peer_push,
2896 GNUNET_MESSAGE_TYPE_RPS_PP_PUSH,
2897 struct GNUNET_MessageHeader,
2898 NULL),
2899 GNUNET_MQ_hd_fixed_size (peer_pull_request,
2900 GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REQUEST,
2901 struct GNUNET_MessageHeader,
2902 NULL),
2903 GNUNET_MQ_hd_var_size (peer_pull_reply,
2904 GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REPLY,
2905 struct GNUNET_RPS_P2P_PullReplyMessage,
2906 NULL),
2907 GNUNET_MQ_handler_end ()
2908 };
2909 sub->hash = *hash;
2910 sub->cadet_port =
2911 GNUNET_CADET_open_port (cadet_handle,
2912 &sub->hash,
2913 &handle_inbound_channel, /* Connect handler */
2914 sub, /* cls */
2915 NULL, /* WindowSize handler */
2916 &cleanup_destroyed_channel, /* Disconnect handler */
2917 cadet_handlers);
2918 if (NULL == sub->cadet_port)
2919 {
2920 LOG (GNUNET_ERROR_TYPE_ERROR,
2921 "Cadet port `%s' is already in use.\n",
2922 GNUNET_APPLICATION_PORT_RPS);
2923 GNUNET_assert (0);
2924 }
2925
2926 /* Set up general data structure to keep track about peers */
2927 sub->valid_peers = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_NO);
2928 if (GNUNET_OK !=
2929 GNUNET_CONFIGURATION_get_value_filename (cfg,
2930 "rps",
2931 "FILENAME_VALID_PEERS",
2932 &sub->filename_valid_peers))
2933 {
2934 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2935 "rps",
2936 "FILENAME_VALID_PEERS");
2937 }
2938 if (0 != strncmp ("DISABLE", sub->filename_valid_peers, 7))
2939 {
2940 char *tmp_filename_valid_peers;
2941 char str_hash[105];
2942
2943 GNUNET_snprintf (str_hash,
2944 sizeof(str_hash), "%s",
2945 GNUNET_h2s_full (hash));
2946 tmp_filename_valid_peers = sub->filename_valid_peers;
2947 GNUNET_asprintf (&sub->filename_valid_peers,
2948 "%s%s",
2949 tmp_filename_valid_peers,
2950 str_hash);
2951 GNUNET_free (tmp_filename_valid_peers);
2952 }
2953 sub->peer_map = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_NO);
2954
2955 /* Set up the sampler */
2956 sub->sampler_size_est_min = sampler_size;
2957 sub->sampler_size_est_need = sampler_size;;
2958 LOG (GNUNET_ERROR_TYPE_DEBUG, "MINSIZE is %u\n", sub->sampler_size_est_min);
2959 GNUNET_assert (0 != round_interval.rel_value_us);
2960 sub->round_interval = round_interval;
2961 sub->sampler = RPS_sampler_init (sampler_size,
2962 round_interval);
2963
2964 /* Logging of internals */
2965#ifdef TO_FILE_FULL
2966 // FIXME: The service cannot know the index, which is required by this
2967 // function:
2968 // sub->file_name_view_log = store_prefix_file_name (&own_identity, "view");
2969#endif /* TO_FILE_FULL */
2970#ifdef TO_FILE
2971#ifdef TO_FILE_FULL
2972 // FIXME: The service cannot know the index, which is required by this
2973 // function:
2974 // sub->file_name_observed_log = store_prefix_file_name (&own_identity,
2975 // "observed");
2976#endif /* TO_FILE_FULL */
2977 sub->num_observed_peers = 0;
2978 sub->observed_unique_peers = GNUNET_CONTAINER_multipeermap_create (1,
2979 GNUNET_NO);
2980#endif /* TO_FILE */
2981
2982 /* Set up data structures for gossip */
2983 sub->push_map = CustomPeerMap_create (4);
2984 sub->pull_map = CustomPeerMap_create (4);
2985 sub->view_size_est_min = sampler_size;;
2986 sub->view = View_create (sub->view_size_est_min);
2987 if (sub == msub)
2988 {
2989 GNUNET_STATISTICS_set (stats,
2990 "view size aim",
2991 sub->view_size_est_min,
2992 GNUNET_NO);
2993 }
2994
2995 /* Start executing rounds */
2996 sub->do_round_task = GNUNET_SCHEDULER_add_now (&do_round, sub);
2997
2998 return sub;
2999}
3000
3001
3002#ifdef TO_FILE
3003// /**
3004// * @brief Write all numbers in the given array into the given file
3005// *
3006// * Single numbers divided by a newline
3007// *
3008// * FIXME: The call to store_prefix_file_name expects the index of the peer,
3009// * which cannot be known to the service.
3010// * Write a dedicated function that uses the peer id.
3011// *
3012// * @param hist_array[] the array to dump
3013// * @param file_name file to dump into
3014// */
3015// static void
3016// write_histogram_to_file (const uint32_t hist_array[],
3017// const char *file_name)
3018// {
3019// char collect_str[SIZE_DUMP_FILE + 1] = "";
3020// char *recv_str_iter;
3021// char *file_name_full;
3022//
3023// recv_str_iter = collect_str;
3024// file_name_full = store_prefix_file_name (&own_identity,
3025// file_name);
3026// for (uint32_t i = 0; i < HISTOGRAM_FILE_SLOTS; i++)
3027// {
3028// char collect_str_tmp[8];
3029//
3030// GNUNET_snprintf (collect_str_tmp,
3031// sizeof(collect_str_tmp),
3032// "%" PRIu32 "\n",
3033// hist_array[i]);
3034// recv_str_iter = stpncpy (recv_str_iter,
3035// collect_str_tmp,
3036// 6);
3037// }
3038// (void) stpcpy (recv_str_iter,
3039// "\n");
3040// LOG (GNUNET_ERROR_TYPE_DEBUG,
3041// "Writing push stats to disk\n");
3042// to_file_w_len (file_name_full,
3043// SIZE_DUMP_FILE, "%s",
3044// collect_str);
3045// GNUNET_free (file_name_full);
3046// }
3047
3048
3049#endif /* TO_FILE */
3050
3051
3052/**
3053 * @brief Destroy Sub.
3054 *
3055 * @param sub Sub to destroy
3056 */
3057static void
3058destroy_sub (struct Sub *sub)
3059{
3060 GNUNET_assert (NULL != sub);
3061 GNUNET_assert (NULL != sub->do_round_task);
3062 GNUNET_SCHEDULER_cancel (sub->do_round_task);
3063 sub->do_round_task = NULL;
3064
3065 /* Disconnect from cadet */
3066 GNUNET_CADET_close_port (sub->cadet_port);
3067 sub->cadet_port = NULL;
3068
3069 /* Clean up data structures for peers */
3070 RPS_sampler_destroy (sub->sampler);
3071 sub->sampler = NULL;
3072 View_destroy (sub->view);
3073 sub->view = NULL;
3074 CustomPeerMap_destroy (sub->push_map);
3075 sub->push_map = NULL;
3076 CustomPeerMap_destroy (sub->pull_map);
3077 sub->pull_map = NULL;
3078 peers_terminate (sub);
3079
3080 /* Free leftover data structures */
3081#ifdef TO_FILE_FULL
3082 GNUNET_free (sub->file_name_view_log);
3083 sub->file_name_view_log = NULL;
3084#endif /* TO_FILE_FULL */
3085#ifdef TO_FILE
3086#ifdef TO_FILE_FULL
3087 GNUNET_free (sub->file_name_observed_log);
3088 sub->file_name_observed_log = NULL;
3089#endif /* TO_FILE_FULL */
3090
3091 // FIXME: Currently this calls malfunctionning code
3092 // /* Write push frequencies to disk */
3093 // write_histogram_to_file (sub->push_recv,
3094 // "push_recv");
3095
3096 // /* Write push deltas to disk */
3097 // write_histogram_to_file (sub->push_delta,
3098 // "push_delta");
3099
3100 // /* Write pull delays to disk */
3101 // write_histogram_to_file (sub->pull_delays,
3102 // "pull_delays");
3103
3104 GNUNET_CONTAINER_multipeermap_destroy (sub->observed_unique_peers);
3105 sub->observed_unique_peers = NULL;
3106#endif /* TO_FILE */
3107
3108 GNUNET_free (sub);
3109}
3110
3111
3112/***********************************************************************
3113* /Sub
3114***********************************************************************/
3115
3116
3117/***********************************************************************
3118* Core handlers
3119***********************************************************************/
3120
3121/**
3122 * @brief Callback on initialisation of Core.
3123 *
3124 * @param cls - unused
3125 * @param my_identity - unused
3126 */
3127void
3128core_init (void *cls,
3129 const struct GNUNET_PeerIdentity *my_identity)
3130{
3131 (void) cls;
3132 (void) my_identity;
3133
3134 map_single_hop = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_NO);
3135}
3136
3137
3138/**
3139 * @brief Callback for core.
3140 * Method called whenever a given peer connects.
3141 *
3142 * @param cls closure - unused
3143 * @param peer peer identity this notification is about
3144 * @return closure given to #core_disconnects as peer_cls
3145 */
3146void *
3147core_connects (void *cls,
3148 const struct GNUNET_PeerIdentity *peer,
3149 struct GNUNET_MQ_Handle *mq)
3150{
3151 (void) cls;
3152 (void) mq;
3153
3154 GNUNET_assert (GNUNET_YES ==
3155 GNUNET_CONTAINER_multipeermap_put (map_single_hop,
3156 peer,
3157 NULL,
3158 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3159 return NULL;
3160}
3161
3162
3163/**
3164 * @brief Callback for core.
3165 * Method called whenever a peer disconnects.
3166 *
3167 * @param cls closure - unused
3168 * @param peer peer identity this notification is about
3169 * @param peer_cls closure given in #core_connects - unused
3170 */
3171void
3172core_disconnects (void *cls,
3173 const struct GNUNET_PeerIdentity *peer,
3174 void *peer_cls)
3175{
3176 (void) cls;
3177 (void) peer_cls;
3178
3179 GNUNET_CONTAINER_multipeermap_remove_all (map_single_hop, peer);
3180}
3181
3182
3183/***********************************************************************
3184* /Core handlers
3185***********************************************************************/
3186
3187
3188/**
3189 * @brief Destroy the context for a (connected) client
3190 *
3191 * @param cli_ctx Context to destroy
3192 */
3193static void
3194destroy_cli_ctx (struct ClientContext *cli_ctx)
3195{
3196 GNUNET_assert (NULL != cli_ctx);
3197 GNUNET_CONTAINER_DLL_remove (cli_ctx_head,
3198 cli_ctx_tail,
3199 cli_ctx);
3200 if (NULL != cli_ctx->sub)
3201 {
3202 destroy_sub (cli_ctx->sub);
3203 cli_ctx->sub = NULL;
3204 }
3205 GNUNET_free (cli_ctx);
3206}
3207
3208
3209/**
3210 * @brief Update sizes in sampler and view on estimate update from nse service
3211 *
3212 * @param sub Sub
3213 * @param logestimate the log(Base 2) value of the current network size estimate
3214 * @param std_dev standard deviation for the estimate
3215 */
3216static void
3217adapt_sizes (struct Sub *sub, double logestimate, double std_dev)
3218{
3219 double estimate;
3220
3221 // double scale; // TODO this might go global/config
3222
3223 LOG (GNUNET_ERROR_TYPE_DEBUG,
3224 "Received a ns estimate - logest: %f, std_dev: %f (old_size: %u)\n",
3225 logestimate, std_dev, RPS_sampler_get_size (sub->sampler));
3226 // scale = .01;
3227 estimate = GNUNET_NSE_log_estimate_to_n (logestimate);
3228 // GNUNET_NSE_log_estimate_to_n (logestimate);
3229 estimate = pow (estimate, 1.0 / 3);
3230 // TODO add if std_dev is a number
3231 // estimate += (std_dev * scale);
3232 if (sub->view_size_est_min < ceil (estimate))
3233 {
3234 LOG (GNUNET_ERROR_TYPE_DEBUG, "Changing estimate to %f\n", estimate);
3235 sub->sampler_size_est_need = estimate;
3236 sub->view_size_est_need = estimate;
3237 }
3238 else
3239 {
3240 LOG (GNUNET_ERROR_TYPE_DEBUG, "Not using estimate %f\n", estimate);
3241 // sub->sampler_size_est_need = sub->view_size_est_min;
3242 sub->view_size_est_need = sub->view_size_est_min;
3243 }
3244 if (sub == msub)
3245 {
3246 GNUNET_STATISTICS_set (stats,
3247 "view size aim",
3248 sub->view_size_est_need,
3249 GNUNET_NO);
3250 }
3251
3252 /* If the NSE has changed adapt the lists accordingly */
3253 resize_wrapper (sub->sampler, sub->sampler_size_est_need);
3254 View_change_len (sub->view, sub->view_size_est_need);
3255}
3256
3257
3258/**
3259 * Function called by NSE.
3260 *
3261 * Updates sizes of sampler list and view and adapt those lists
3262 * accordingly.
3263 *
3264 * implements #GNUNET_NSE_Callback
3265 *
3266 * @param cls Closure - unused
3267 * @param timestamp time when the estimate was received from the server (or created by the server)
3268 * @param logestimate the log(Base 2) value of the current network size estimate
3269 * @param std_dev standard deviation for the estimate
3270 */
3271static void
3272nse_callback (void *cls,
3273 struct GNUNET_TIME_Absolute timestamp,
3274 double logestimate, double std_dev)
3275{
3276 (void) cls;
3277 (void) timestamp;
3278 struct ClientContext *cli_ctx_iter;
3279
3280 adapt_sizes (msub, logestimate, std_dev);
3281 for (cli_ctx_iter = cli_ctx_head;
3282 NULL != cli_ctx_iter;
3283 cli_ctx_iter = cli_ctx_iter->next)
3284 {
3285 if (NULL != cli_ctx_iter->sub)
3286 {
3287 adapt_sizes (cli_ctx_iter->sub, logestimate, std_dev);
3288 }
3289 }
3290}
3291
3292
3293/**
3294 * @brief This function is called, when the client seeds peers.
3295 * It verifies that @a msg is well-formed.
3296 *
3297 * @param cls the closure (#ClientContext)
3298 * @param msg the message
3299 * @return #GNUNET_OK if @a msg is well-formed
3300 * #GNUNET_SYSERR otherwise
3301 */
3302static int
3303check_client_seed (void *cls, const struct GNUNET_RPS_CS_SeedMessage *msg)
3304{
3305 struct ClientContext *cli_ctx = cls;
3306 uint16_t msize = ntohs (msg->header.size);
3307 uint32_t num_peers = ntohl (msg->num_peers);
3308
3309 msize -= sizeof(struct GNUNET_RPS_CS_SeedMessage);
3310 if ((msize / sizeof(struct GNUNET_PeerIdentity) != num_peers) ||
3311 (msize % sizeof(struct GNUNET_PeerIdentity) != 0))
3312 {
3313 LOG (GNUNET_ERROR_TYPE_ERROR,
3314 "message says it sends %" PRIu32 " peers, have space for %lu peers\n",
3315 ntohl (msg->num_peers),
3316 (msize / sizeof(struct GNUNET_PeerIdentity)));
3317 GNUNET_break (0);
3318 GNUNET_SERVICE_client_drop (cli_ctx->client);
3319 return GNUNET_SYSERR;
3320 }
3321 return GNUNET_OK;
3322}
3323
3324
3325/**
3326 * Handle seed from the client.
3327 *
3328 * @param cls closure
3329 * @param msg the actual message
3330 */
3331static void
3332handle_client_seed (void *cls,
3333 const struct GNUNET_RPS_CS_SeedMessage *msg)
3334{
3335 struct ClientContext *cli_ctx = cls;
3336 struct GNUNET_PeerIdentity *peers;
3337 uint32_t num_peers;
3338 uint32_t i;
3339
3340 num_peers = ntohl (msg->num_peers);
3341 peers = (struct GNUNET_PeerIdentity *) &msg[1];
3342
3343 LOG (GNUNET_ERROR_TYPE_DEBUG,
3344 "Client seeded peers:\n");
3345 print_peer_list (peers, num_peers);
3346
3347 for (i = 0; i < num_peers; i++)
3348 {
3349 LOG (GNUNET_ERROR_TYPE_DEBUG,
3350 "Updating samplers with seed %" PRIu32 ": %s\n",
3351 i,
3352 GNUNET_i2s (&peers[i]));
3353
3354 if (NULL != msub)
3355 got_peer (msub, &peers[i]); /* Condition needed? */
3356 if (NULL != cli_ctx->sub)
3357 got_peer (cli_ctx->sub, &peers[i]);
3358 }
3359 GNUNET_SERVICE_client_continue (cli_ctx->client);
3360}
3361
3362
3363/**
3364 * Handle RPS request from the client.
3365 *
3366 * @param cls Client context
3367 * @param msg Message containing the number of updates the client wants to
3368 * receive
3369 */
3370static void
3371handle_client_view_request (void *cls,
3372 const struct GNUNET_RPS_CS_DEBUG_ViewRequest *msg)
3373{
3374 struct ClientContext *cli_ctx = cls;
3375 uint64_t num_updates;
3376
3377 num_updates = ntohl (msg->num_updates);
3378
3379 LOG (GNUNET_ERROR_TYPE_DEBUG,
3380 "Client requested %" PRIu64 " updates of view.\n",
3381 num_updates);
3382
3383 GNUNET_assert (NULL != cli_ctx);
3384 cli_ctx->view_updates_left = num_updates;
3385 send_view (cli_ctx, NULL, 0);
3386 GNUNET_SERVICE_client_continue (cli_ctx->client);
3387}
3388
3389
3390/**
3391 * @brief Handle the cancellation of the view updates.
3392 *
3393 * @param cls The client context
3394 * @param msg Unused
3395 */
3396static void
3397handle_client_view_cancel (void *cls,
3398 const struct GNUNET_MessageHeader *msg)
3399{
3400 struct ClientContext *cli_ctx = cls;
3401
3402 (void) msg;
3403
3404 LOG (GNUNET_ERROR_TYPE_DEBUG,
3405 "Client does not want to receive updates of view any more.\n");
3406
3407 GNUNET_assert (NULL != cli_ctx);
3408 cli_ctx->view_updates_left = 0;
3409 GNUNET_SERVICE_client_continue (cli_ctx->client);
3410 if (GNUNET_YES == cli_ctx->stream_update)
3411 {
3412 destroy_cli_ctx (cli_ctx);
3413 }
3414}
3415
3416
3417/**
3418 * Handle RPS request for biased stream from the client.
3419 *
3420 * @param cls Client context
3421 * @param msg unused
3422 */
3423static void
3424handle_client_stream_request (void *cls,
3425 const struct
3426 GNUNET_RPS_CS_DEBUG_StreamRequest *msg)
3427{
3428 struct ClientContext *cli_ctx = cls;
3429
3430 (void) msg;
3431
3432 LOG (GNUNET_ERROR_TYPE_DEBUG,
3433 "Client requested peers from biased stream.\n");
3434 cli_ctx->stream_update = GNUNET_YES;
3435
3436 GNUNET_assert (NULL != cli_ctx);
3437 GNUNET_SERVICE_client_continue (cli_ctx->client);
3438}
3439
3440
3441/**
3442 * @brief Handles the cancellation of the stream of biased peer ids
3443 *
3444 * @param cls The client context
3445 * @param msg unused
3446 */
3447static void
3448handle_client_stream_cancel (void *cls,
3449 const struct GNUNET_MessageHeader *msg)
3450{
3451 struct ClientContext *cli_ctx = cls;
3452
3453 (void) msg;
3454
3455 LOG (GNUNET_ERROR_TYPE_DEBUG,
3456 "Client canceled receiving peers from biased stream.\n");
3457 cli_ctx->stream_update = GNUNET_NO;
3458
3459 GNUNET_assert (NULL != cli_ctx);
3460 GNUNET_SERVICE_client_continue (cli_ctx->client);
3461}
3462
3463
3464/**
3465 * @brief Create and start a Sub.
3466 *
3467 * @param cls Closure - unused
3468 * @param msg Message containing the necessary information
3469 */
3470static void
3471handle_client_start_sub (void *cls,
3472 const struct GNUNET_RPS_CS_SubStartMessage *msg)
3473{
3474 struct ClientContext *cli_ctx = cls;
3475
3476 LOG (GNUNET_ERROR_TYPE_DEBUG, "Client requested start of a new sub.\n");
3477 if ((NULL != cli_ctx->sub) &&
3478 (0 != memcmp (&cli_ctx->sub->hash,
3479 &msg->hash,
3480 sizeof(struct GNUNET_HashCode))) )
3481 {
3482 LOG (GNUNET_ERROR_TYPE_WARNING,
3483 "Already have a Sub with different share for this client. Remove old one, add new.\n");
3484 destroy_sub (cli_ctx->sub);
3485 cli_ctx->sub = NULL;
3486 }
3487 cli_ctx->sub = new_sub (&msg->hash,
3488 msub->sampler_size_est_min, // TODO make api input?
3489 GNUNET_TIME_relative_ntoh (msg->round_interval));
3490 GNUNET_SERVICE_client_continue (cli_ctx->client);
3491}
3492
3493
3494/**
3495 * @brief Destroy the Sub
3496 *
3497 * @param cls Closure - unused
3498 * @param msg Message containing the hash that identifies the Sub
3499 */
3500static void
3501handle_client_stop_sub (void *cls,
3502 const struct GNUNET_RPS_CS_SubStopMessage *msg)
3503{
3504 struct ClientContext *cli_ctx = cls;
3505
3506 GNUNET_assert (NULL != cli_ctx->sub);
3507 if (0 != memcmp (&cli_ctx->sub->hash, &msg->hash, sizeof(struct
3508 GNUNET_HashCode)))
3509 {
3510 LOG (GNUNET_ERROR_TYPE_WARNING,
3511 "Share of current sub and request differ!\n");
3512 }
3513 destroy_sub (cli_ctx->sub);
3514 cli_ctx->sub = NULL;
3515 GNUNET_SERVICE_client_continue (cli_ctx->client);
3516}
3517
3518
3519/**
3520 * Handle a CHECK_LIVE message from another peer.
3521 *
3522 * This does nothing. But without calling #GNUNET_CADET_receive_done()
3523 * the channel is blocked for all other communication.
3524 *
3525 * @param cls Closure - Context of channel
3526 * @param msg Message - unused
3527 */
3528static void
3529handle_peer_check (void *cls,
3530 const struct GNUNET_MessageHeader *msg)
3531{
3532 const struct ChannelCtx *channel_ctx = cls;
3533 const struct GNUNET_PeerIdentity *peer = &channel_ctx->peer_ctx->peer_id;
3534
3535 (void) msg;
3536
3537 LOG (GNUNET_ERROR_TYPE_DEBUG,
3538 "Received CHECK_LIVE (%s)\n", GNUNET_i2s (peer));
3539 if (channel_ctx->peer_ctx->sub == msub)
3540 {
3541 GNUNET_STATISTICS_update (stats,
3542 "# pending online checks",
3543 -1,
3544 GNUNET_NO);
3545 }
3546
3547 GNUNET_CADET_receive_done (channel_ctx->channel);
3548}
3549
3550
3551/**
3552 * Handle a PUSH message from another peer.
3553 *
3554 * Check the proof of work and store the PeerID
3555 * in the temporary list for pushed PeerIDs.
3556 *
3557 * @param cls Closure - Context of channel
3558 * @param msg Message - unused
3559 */
3560static void
3561handle_peer_push (void *cls,
3562 const struct GNUNET_MessageHeader *msg)
3563{
3564 const struct ChannelCtx *channel_ctx = cls;
3565 const struct GNUNET_PeerIdentity *peer = &channel_ctx->peer_ctx->peer_id;
3566
3567 (void) msg;
3568
3569 // (check the proof of work (?))
3570
3571 LOG (GNUNET_ERROR_TYPE_DEBUG,
3572 "Received PUSH (%s)\n",
3573 GNUNET_i2s (peer));
3574 if (channel_ctx->peer_ctx->sub == msub)
3575 {
3576 GNUNET_STATISTICS_update (stats, "# push message received", 1, GNUNET_NO);
3577 if ((NULL != map_single_hop) &&
3578 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
3579 peer)))
3580 {
3581 GNUNET_STATISTICS_update (stats,
3582 "# push message received (multi-hop peer)",
3583 1,
3584 GNUNET_NO);
3585 }
3586 }
3587
3588 #if ENABLE_MALICIOUS
3589 struct AttackedPeer *tmp_att_peer;
3590
3591 if ((1 == mal_type) ||
3592 (3 == mal_type))
3593 { /* Try to maximise representation */
3594 tmp_att_peer = GNUNET_new (struct AttackedPeer);
3595 tmp_att_peer->peer_id = *peer;
3596 if (NULL == att_peer_set)
3597 att_peer_set = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO);
3598 if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (att_peer_set,
3599 peer))
3600 {
3601 GNUNET_CONTAINER_DLL_insert (att_peers_head,
3602 att_peers_tail,
3603 tmp_att_peer);
3604 add_peer_array_to_set (peer, 1, att_peer_set);
3605 }
3606 else
3607 {
3608 GNUNET_free (tmp_att_peer);
3609 }
3610 }
3611
3612
3613 else if (2 == mal_type)
3614 {
3615 /* We attack one single well-known peer - simply ignore */
3616 }
3617 #endif /* ENABLE_MALICIOUS */
3618
3619 /* Add the sending peer to the push_map */
3620 CustomPeerMap_put (channel_ctx->peer_ctx->sub->push_map, peer);
3621
3622 GNUNET_break_op (check_peer_known (channel_ctx->peer_ctx->sub->peer_map,
3623 &channel_ctx->peer_ctx->peer_id));
3624 GNUNET_CADET_receive_done (channel_ctx->channel);
3625}
3626
3627
3628/**
3629 * Handle PULL REQUEST request message from another peer.
3630 *
3631 * Reply with the view of PeerIDs.
3632 *
3633 * @param cls Closure - Context of channel
3634 * @param msg Message - unused
3635 */
3636static void
3637handle_peer_pull_request (void *cls,
3638 const struct GNUNET_MessageHeader *msg)
3639{
3640 const struct ChannelCtx *channel_ctx = cls;
3641 struct PeerContext *peer_ctx = channel_ctx->peer_ctx;
3642 const struct GNUNET_PeerIdentity *peer = &peer_ctx->peer_id;
3643 const struct GNUNET_PeerIdentity *view_array;
3644
3645 (void) msg;
3646
3647 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received PULL REQUEST (%s)\n", GNUNET_i2s (
3648 peer));
3649 if (peer_ctx->sub == msub)
3650 {
3651 GNUNET_STATISTICS_update (stats,
3652 "# pull request message received",
3653 1,
3654 GNUNET_NO);
3655 if ((NULL != map_single_hop) &&
3656 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
3657 &peer_ctx->peer_id)))
3658 {
3659 GNUNET_STATISTICS_update (stats,
3660 "# pull request message received (multi-hop peer)",
3661 1,
3662 GNUNET_NO);
3663 }
3664 }
3665
3666 #if ENABLE_MALICIOUS
3667 if ((1 == mal_type)
3668 || (3 == mal_type))
3669 { /* Try to maximise representation */
3670 send_pull_reply (peer_ctx, mal_peers, num_mal_peers);
3671 }
3672
3673 else if (2 == mal_type)
3674 { /* Try to partition network */
3675 if (0 == GNUNET_memcmp (&attacked_peer, peer))
3676 {
3677 send_pull_reply (peer_ctx, mal_peers, num_mal_peers);
3678 }
3679 }
3680 #endif /* ENABLE_MALICIOUS */
3681
3682 GNUNET_break_op (check_peer_known (channel_ctx->peer_ctx->sub->peer_map,
3683 &channel_ctx->peer_ctx->peer_id));
3684 GNUNET_CADET_receive_done (channel_ctx->channel);
3685 view_array = View_get_as_array (channel_ctx->peer_ctx->sub->view);
3686 send_pull_reply (peer_ctx,
3687 view_array,
3688 View_size (channel_ctx->peer_ctx->sub->view));
3689}
3690
3691
3692/**
3693 * Check whether we sent a corresponding request and
3694 * whether this reply is the first one.
3695 *
3696 * @param cls Closure - Context of channel
3697 * @param msg Message containing the replied peers
3698 */
3699static int
3700check_peer_pull_reply (void *cls,
3701 const struct GNUNET_RPS_P2P_PullReplyMessage *msg)
3702{
3703 struct ChannelCtx *channel_ctx = cls;
3704 struct PeerContext *sender_ctx = channel_ctx->peer_ctx;
3705
3706 if (sizeof(struct GNUNET_RPS_P2P_PullReplyMessage) > ntohs (msg->header.size))
3707 {
3708 GNUNET_break_op (0);
3709 return GNUNET_SYSERR;
3710 }
3711
3712 if ((ntohs (msg->header.size) - sizeof(struct
3713 GNUNET_RPS_P2P_PullReplyMessage))
3714 / sizeof(struct GNUNET_PeerIdentity) != ntohl (msg->num_peers))
3715 {
3716 LOG (GNUNET_ERROR_TYPE_ERROR,
3717 "message says it sends %" PRIu32 " peers, have space for %lu peers\n",
3718 ntohl (msg->num_peers),
3719 (ntohs (msg->header.size) - sizeof(struct
3720 GNUNET_RPS_P2P_PullReplyMessage))
3721 / sizeof(struct GNUNET_PeerIdentity));
3722 GNUNET_break_op (0);
3723 return GNUNET_SYSERR;
3724 }
3725
3726 if (GNUNET_YES != check_peer_flag (sender_ctx->sub->peer_map,
3727 &sender_ctx->peer_id,
3728 Peers_PULL_REPLY_PENDING))
3729 {
3730 LOG (GNUNET_ERROR_TYPE_WARNING,
3731 "Received a pull reply from a peer (%s) we didn't request one from!\n",
3732 GNUNET_i2s (&sender_ctx->peer_id));
3733 if (sender_ctx->sub == msub)
3734 {
3735 GNUNET_STATISTICS_update (stats,
3736 "# unrequested pull replies",
3737 1,
3738 GNUNET_NO);
3739 }
3740 }
3741 return GNUNET_OK;
3742}
3743
3744
3745/**
3746 * Handle PULL REPLY message from another peer.
3747 *
3748 * @param cls Closure
3749 * @param msg The message header
3750 */
3751static void
3752handle_peer_pull_reply (void *cls,
3753 const struct GNUNET_RPS_P2P_PullReplyMessage *msg)
3754{
3755 const struct ChannelCtx *channel_ctx = cls;
3756 const struct GNUNET_PeerIdentity *sender = &channel_ctx->peer_ctx->peer_id;
3757 const struct GNUNET_PeerIdentity *peers;
3758 struct Sub *sub = channel_ctx->peer_ctx->sub;
3759 uint32_t i;
3760
3761#if ENABLE_MALICIOUS
3762 struct AttackedPeer *tmp_att_peer;
3763#endif /* ENABLE_MALICIOUS */
3764
3765 sub->pull_delays[sub->num_rounds - channel_ctx->peer_ctx->round_pull_req]++;
3766 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received PULL REPLY (%s)\n", GNUNET_i2s (
3767 sender));
3768 if (channel_ctx->peer_ctx->sub == msub)
3769 {
3770 GNUNET_STATISTICS_update (stats,
3771 "# pull reply messages received",
3772 1,
3773 GNUNET_NO);
3774 if ((NULL != map_single_hop) &&
3775 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
3776 &channel_ctx->
3777 peer_ctx->peer_id)) )
3778 {
3779 GNUNET_STATISTICS_update (stats,
3780 "# pull reply messages received (multi-hop peer)",
3781 1,
3782 GNUNET_NO);
3783 }
3784 }
3785
3786 #if ENABLE_MALICIOUS
3787 // We shouldn't even receive pull replies as we're not sending
3788 if (2 == mal_type)
3789 {
3790 }
3791 #endif /* ENABLE_MALICIOUS */
3792
3793 /* Do actual logic */
3794 peers = (const struct GNUNET_PeerIdentity *) &msg[1];
3795
3796 LOG (GNUNET_ERROR_TYPE_DEBUG,
3797 "PULL REPLY received, got following %u peers:\n",
3798 ntohl (msg->num_peers));
3799
3800 for (i = 0; i < ntohl (msg->num_peers); i++)
3801 {
3802 LOG (GNUNET_ERROR_TYPE_DEBUG,
3803 "%u. %s\n",
3804 i,
3805 GNUNET_i2s (&peers[i]));
3806
3807 #if ENABLE_MALICIOUS
3808 if ((NULL != att_peer_set) &&
3809 ((1 == mal_type) || (3 == mal_type) ))
3810 { /* Add attacked peer to local list */
3811 // TODO check if we sent a request and this was the first reply
3812 if ((GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (att_peer_set,
3813 &peers[i]))
3814 && (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (mal_peer_set,
3815 &peers[i])) )
3816 {
3817 tmp_att_peer = GNUNET_new (struct AttackedPeer);
3818 tmp_att_peer->peer_id = peers[i];
3819 GNUNET_CONTAINER_DLL_insert (att_peers_head,
3820 att_peers_tail,
3821 tmp_att_peer);
3822 add_peer_array_to_set (&peers[i], 1, att_peer_set);
3823 }
3824 continue;
3825 }
3826 #endif /* ENABLE_MALICIOUS */
3827 /* Make sure we 'know' about this peer */
3828 (void) insert_peer (channel_ctx->peer_ctx->sub,
3829 &peers[i]);
3830
3831 if (GNUNET_YES == check_peer_valid (channel_ctx->peer_ctx->sub->valid_peers,
3832 &peers[i]))
3833 {
3834 CustomPeerMap_put (channel_ctx->peer_ctx->sub->pull_map,
3835 &peers[i]);
3836 }
3837 else
3838 {
3839 schedule_operation (channel_ctx->peer_ctx,
3840 insert_in_pull_map,
3841 channel_ctx->peer_ctx->sub); /* cls */
3842 (void) issue_peer_online_check (channel_ctx->peer_ctx->sub,
3843 &peers[i]);
3844 }
3845 }
3846
3847 UNSET_PEER_FLAG (get_peer_ctx (channel_ctx->peer_ctx->sub->peer_map,
3848 sender),
3849 Peers_PULL_REPLY_PENDING);
3850 clean_peer (channel_ctx->peer_ctx->sub,
3851 sender);
3852
3853 GNUNET_break_op (check_peer_known (channel_ctx->peer_ctx->sub->peer_map,
3854 sender));
3855 GNUNET_CADET_receive_done (channel_ctx->channel);
3856}
3857
3858
3859/**
3860 * Compute a random delay.
3861 * A uniformly distributed value between mean + spread and mean - spread.
3862 *
3863 * For example for mean 4 min and spread 2 the minimum is (4 min - (1/2 * 4 min))
3864 * It would return a random value between 2 and 6 min.
3865 *
3866 * @param mean the mean time until the next round
3867 * @param spread the inverse amount of deviation from the mean
3868 */
3869static struct GNUNET_TIME_Relative
3870compute_rand_delay (struct GNUNET_TIME_Relative mean,
3871 unsigned int spread)
3872{
3873 struct GNUNET_TIME_Relative half_interval;
3874 struct GNUNET_TIME_Relative ret;
3875 unsigned int rand_delay;
3876 unsigned int max_rand_delay;
3877
3878 if (0 == spread)
3879 {
3880 LOG (GNUNET_ERROR_TYPE_WARNING,
3881 "Not accepting spread of 0\n");
3882 GNUNET_break (0);
3883 GNUNET_assert (0);
3884 }
3885 GNUNET_assert (0 != mean.rel_value_us);
3886
3887 /* Compute random time value between spread * mean and spread * mean */
3888 half_interval = GNUNET_TIME_relative_divide (mean, spread);
3889
3890 max_rand_delay = GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us
3891 / mean.rel_value_us * (2 / spread);
3892 /**
3893 * Compute random value between (0 and 1) * round_interval
3894 * via multiplying round_interval with a 'fraction' (0 to value)/value
3895 */
3896 rand_delay = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
3897 max_rand_delay);
3898 ret = GNUNET_TIME_relative_saturating_multiply (mean, rand_delay);
3899 ret = GNUNET_TIME_relative_divide (ret, max_rand_delay);
3900 ret = GNUNET_TIME_relative_add (ret, half_interval);
3901
3902 if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == ret.rel_value_us)
3903 LOG (GNUNET_ERROR_TYPE_WARNING,
3904 "Returning FOREVER_REL\n");
3905
3906 return ret;
3907}
3908
3909
3910/**
3911 * Send single pull request
3912 *
3913 * @param peer_ctx Context to the peer to send request to
3914 */
3915static void
3916send_pull_request (struct PeerContext *peer_ctx)
3917{
3918 struct GNUNET_MQ_Envelope *ev;
3919
3920 GNUNET_assert (GNUNET_NO == check_peer_flag (peer_ctx->sub->peer_map,
3921 &peer_ctx->peer_id,
3922 Peers_PULL_REPLY_PENDING));
3923 SET_PEER_FLAG (peer_ctx,
3924 Peers_PULL_REPLY_PENDING);
3925 peer_ctx->round_pull_req = peer_ctx->sub->num_rounds;
3926
3927 LOG (GNUNET_ERROR_TYPE_DEBUG,
3928 "Going to send PULL REQUEST to peer %s.\n",
3929 GNUNET_i2s (&peer_ctx->peer_id));
3930
3931 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_PP_PULL_REQUEST);
3932 send_message (peer_ctx,
3933 ev,
3934 "PULL REQUEST");
3935 if (peer_ctx->sub)
3936 {
3937 GNUNET_STATISTICS_update (stats,
3938 "# pull request send issued",
3939 1,
3940 GNUNET_NO);
3941 if ((NULL != map_single_hop) &&
3942 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
3943 &peer_ctx->peer_id)))
3944 {
3945 GNUNET_STATISTICS_update (stats,
3946 "# pull request send issued (multi-hop peer)",
3947 1,
3948 GNUNET_NO);
3949 }
3950 }
3951}
3952
3953
3954/**
3955 * Send single push
3956 *
3957 * @param peer_ctx Context of peer to send push to
3958 */
3959static void
3960send_push (struct PeerContext *peer_ctx)
3961{
3962 struct GNUNET_MQ_Envelope *ev;
3963
3964 LOG (GNUNET_ERROR_TYPE_DEBUG,
3965 "Going to send PUSH to peer %s.\n",
3966 GNUNET_i2s (&peer_ctx->peer_id));
3967
3968 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_PP_PUSH);
3969 send_message (peer_ctx, ev, "PUSH");
3970 if (peer_ctx->sub)
3971 {
3972 GNUNET_STATISTICS_update (stats,
3973 "# push send issued",
3974 1,
3975 GNUNET_NO);
3976 if ((NULL != map_single_hop) &&
3977 (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (map_single_hop,
3978 &peer_ctx->peer_id)))
3979 {
3980 GNUNET_STATISTICS_update (stats,
3981 "# push send issued (multi-hop peer)",
3982 1,
3983 GNUNET_NO);
3984 }
3985 }
3986}
3987
3988
3989#if ENABLE_MALICIOUS
3990
3991
3992/**
3993 * @brief This function is called, when the client tells us to act malicious.
3994 * It verifies that @a msg is well-formed.
3995 *
3996 * @param cls the closure (#ClientContext)
3997 * @param msg the message
3998 * @return #GNUNET_OK if @a msg is well-formed
3999 */
4000static int
4001check_client_act_malicious (void *cls,
4002 const struct GNUNET_RPS_CS_ActMaliciousMessage *msg)
4003{
4004 struct ClientContext *cli_ctx = cls;
4005 uint16_t msize = ntohs (msg->header.size);
4006 uint32_t num_peers = ntohl (msg->num_peers);
4007
4008 msize -= sizeof(struct GNUNET_RPS_CS_ActMaliciousMessage);
4009 if ((msize / sizeof(struct GNUNET_PeerIdentity) != num_peers) ||
4010 (msize % sizeof(struct GNUNET_PeerIdentity) != 0))
4011 {
4012 LOG (GNUNET_ERROR_TYPE_ERROR,
4013 "message says it sends %" PRIu32 " peers, have space for %lu peers\n",
4014 ntohl (msg->num_peers),
4015 (msize / sizeof(struct GNUNET_PeerIdentity)));
4016 GNUNET_break (0);
4017 GNUNET_SERVICE_client_drop (cli_ctx->client);
4018 return GNUNET_SYSERR;
4019 }
4020 return GNUNET_OK;
4021}
4022
4023
4024/**
4025 * Turn RPS service to act malicious.
4026 *
4027 * @param cls Closure
4028 * @param client The client that sent the message
4029 * @param msg The message header
4030 */
4031static void
4032handle_client_act_malicious (void *cls,
4033 const struct
4034 GNUNET_RPS_CS_ActMaliciousMessage *msg)
4035{
4036 struct ClientContext *cli_ctx = cls;
4037 struct GNUNET_PeerIdentity *peers;
4038 uint32_t num_mal_peers_sent;
4039 uint32_t num_mal_peers_old;
4040 struct Sub *sub = cli_ctx->sub;
4041
4042 if (NULL == sub)
4043 sub = msub;
4044 /* Do actual logic */
4045 peers = (struct GNUNET_PeerIdentity *) &msg[1];
4046 mal_type = ntohl (msg->type);
4047 if (NULL == mal_peer_set)
4048 mal_peer_set = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO);
4049
4050 LOG (GNUNET_ERROR_TYPE_DEBUG,
4051 "Now acting malicious type %" PRIu32 ", got %" PRIu32 " peers.\n",
4052 mal_type,
4053 ntohl (msg->num_peers));
4054
4055 if (1 == mal_type)
4056 { /* Try to maximise representation */
4057 /* Add other malicious peers to those we already know */
4058
4059 num_mal_peers_sent = ntohl (msg->num_peers);
4060 num_mal_peers_old = num_mal_peers;
4061 GNUNET_array_grow (mal_peers,
4062 num_mal_peers,
4063 num_mal_peers + num_mal_peers_sent);
4064 GNUNET_memcpy (&mal_peers[num_mal_peers_old],
4065 peers,
4066 num_mal_peers_sent * sizeof(struct GNUNET_PeerIdentity));
4067
4068 /* Add all mal peers to mal_peer_set */
4069 add_peer_array_to_set (&mal_peers[num_mal_peers_old],
4070 num_mal_peers_sent,
4071 mal_peer_set);
4072
4073 /* Substitute do_round () with do_mal_round () */
4074 GNUNET_assert (NULL != sub->do_round_task);
4075 GNUNET_SCHEDULER_cancel (sub->do_round_task);
4076 sub->do_round_task = GNUNET_SCHEDULER_add_now (&do_mal_round, sub);
4077 }
4078
4079 else if ((2 == mal_type) ||
4080 (3 == mal_type))
4081 { /* Try to partition the network */
4082 /* Add other malicious peers to those we already know */
4083
4084 num_mal_peers_sent = ntohl (msg->num_peers) - 1;
4085 num_mal_peers_old = num_mal_peers;
4086 GNUNET_assert (GNUNET_MAX_MALLOC_CHECKED > num_mal_peers_sent);
4087 GNUNET_array_grow (mal_peers,
4088 num_mal_peers,
4089 num_mal_peers + num_mal_peers_sent);
4090 if ((NULL != mal_peers) &&
4091 (0 != num_mal_peers) )
4092 {
4093 GNUNET_memcpy (&mal_peers[num_mal_peers_old],
4094 peers,
4095 num_mal_peers_sent * sizeof(struct GNUNET_PeerIdentity));
4096
4097 /* Add all mal peers to mal_peer_set */
4098 add_peer_array_to_set (&mal_peers[num_mal_peers_old],
4099 num_mal_peers_sent,
4100 mal_peer_set);
4101 }
4102
4103 /* Store the one attacked peer */
4104 GNUNET_memcpy (&attacked_peer,
4105 &msg->attacked_peer,
4106 sizeof(struct GNUNET_PeerIdentity));
4107 /* Set the flag of the attacked peer to valid to avoid problems */
4108 if (GNUNET_NO == check_peer_known (sub->peer_map, &attacked_peer))
4109 {
4110 (void) issue_peer_online_check (sub, &attacked_peer);
4111 }
4112
4113 LOG (GNUNET_ERROR_TYPE_DEBUG,
4114 "Attacked peer is %s\n",
4115 GNUNET_i2s (&attacked_peer));
4116
4117 /* Substitute do_round () with do_mal_round () */
4118 if (NULL != sub->do_round_task)
4119 {
4120 /* Probably in shutdown */
4121 GNUNET_SCHEDULER_cancel (sub->do_round_task);
4122 sub->do_round_task = GNUNET_SCHEDULER_add_now (&do_mal_round, sub);
4123 }
4124 }
4125 else if (0 == mal_type)
4126 { /* Stop acting malicious */
4127 GNUNET_array_grow (mal_peers, num_mal_peers, 0);
4128
4129 /* Substitute do_mal_round () with do_round () */
4130 GNUNET_SCHEDULER_cancel (sub->do_round_task);
4131 sub->do_round_task = GNUNET_SCHEDULER_add_now (&do_round, sub);
4132 }
4133 else
4134 {
4135 GNUNET_break (0);
4136 GNUNET_SERVICE_client_continue (cli_ctx->client);
4137 }
4138 GNUNET_SERVICE_client_continue (cli_ctx->client);
4139}
4140
4141
4142/**
4143 * Send out PUSHes and PULLs maliciously.
4144 *
4145 * This is executed regylary.
4146 *
4147 * @param cls Closure - Sub
4148 */
4149static void
4150do_mal_round (void *cls)
4151{
4152 uint32_t num_pushes;
4153 uint32_t i;
4154 struct GNUNET_TIME_Relative time_next_round;
4155 struct AttackedPeer *tmp_att_peer;
4156 struct Sub *sub = cls;
4157
4158 LOG (GNUNET_ERROR_TYPE_DEBUG,
4159 "Going to execute next round maliciously type %" PRIu32 ".\n",
4160 mal_type);
4161 sub->do_round_task = NULL;
4162 GNUNET_assert (mal_type <= 3);
4163 /* Do malicious actions */
4164 if (1 == mal_type)
4165 { /* Try to maximise representation */
4166 /* The maximum of pushes we're going to send this round */
4167 num_pushes = GNUNET_MIN (GNUNET_MIN (push_limit,
4168 num_attacked_peers),
4169 GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE);
4170
4171 LOG (GNUNET_ERROR_TYPE_DEBUG,
4172 "Going to send %" PRIu32 " pushes\n",
4173 num_pushes);
4174
4175 /* Send PUSHes to attacked peers */
4176 for (i = 0; i < num_pushes; i++)
4177 {
4178 if (att_peers_tail == att_peer_index)
4179 att_peer_index = att_peers_head;
4180 else
4181 att_peer_index = att_peer_index->next;
4182
4183 send_push (get_peer_ctx (sub->peer_map, &att_peer_index->peer_id));
4184 }
4185
4186 /* Send PULLs to some peers to learn about additional peers to attack */
4187 tmp_att_peer = att_peer_index;
4188 for (i = 0; i < num_pushes * alpha; i++)
4189 {
4190 if (att_peers_tail == tmp_att_peer)
4191 tmp_att_peer = att_peers_head;
4192 else
4193 att_peer_index = tmp_att_peer->next;
4194
4195 send_pull_request (get_peer_ctx (sub->peer_map, &tmp_att_peer->peer_id));
4196 }
4197 }
4198
4199
4200 else if (2 == mal_type)
4201 { /**
4202 * Try to partition the network
4203 * Send as many pushes to the attacked peer as possible
4204 * That is one push per round as it will ignore more.
4205 */
4206 (void) issue_peer_online_check (sub, &attacked_peer);
4207 if (GNUNET_YES == check_peer_flag (sub->peer_map,
4208 &attacked_peer,
4209 Peers_ONLINE))
4210 send_push (get_peer_ctx (sub->peer_map, &attacked_peer));
4211 }
4212
4213
4214 if (3 == mal_type)
4215 { /* Combined attack */
4216 /* Send PUSH to attacked peers */
4217 if (GNUNET_YES == check_peer_known (sub->peer_map, &attacked_peer))
4218 {
4219 (void) issue_peer_online_check (sub, &attacked_peer);
4220 if (GNUNET_YES == check_peer_flag (sub->peer_map,
4221 &attacked_peer,
4222 Peers_ONLINE))
4223 {
4224 LOG (GNUNET_ERROR_TYPE_DEBUG,
4225 "Goding to send push to attacked peer (%s)\n",
4226 GNUNET_i2s (&attacked_peer));
4227 send_push (get_peer_ctx (sub->peer_map, &attacked_peer));
4228 }
4229 }
4230 (void) issue_peer_online_check (sub, &attacked_peer);
4231
4232 /* The maximum of pushes we're going to send this round */
4233 num_pushes = GNUNET_MIN (GNUNET_MIN (push_limit - 1,
4234 num_attacked_peers),
4235 GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE);
4236
4237 LOG (GNUNET_ERROR_TYPE_DEBUG,
4238 "Going to send %" PRIu32 " pushes\n",
4239 num_pushes);
4240
4241 for (i = 0; i < num_pushes; i++)
4242 {
4243 if (att_peers_tail == att_peer_index)
4244 att_peer_index = att_peers_head;
4245 else
4246 att_peer_index = att_peer_index->next;
4247
4248 send_push (get_peer_ctx (sub->peer_map, &att_peer_index->peer_id));
4249 }
4250
4251 /* Send PULLs to some peers to learn about additional peers to attack */
4252 tmp_att_peer = att_peer_index;
4253 for (i = 0; i < num_pushes * alpha; i++)
4254 {
4255 if (att_peers_tail == tmp_att_peer)
4256 tmp_att_peer = att_peers_head;
4257 else
4258 att_peer_index = tmp_att_peer->next;
4259
4260 send_pull_request (get_peer_ctx (sub->peer_map, &tmp_att_peer->peer_id));
4261 }
4262 }
4263
4264 /* Schedule next round */
4265 time_next_round = compute_rand_delay (sub->round_interval, 2);
4266
4267 GNUNET_assert (NULL == sub->do_round_task);
4268 sub->do_round_task = GNUNET_SCHEDULER_add_delayed (time_next_round,
4269 &do_mal_round, sub);
4270 LOG (GNUNET_ERROR_TYPE_DEBUG, "Finished round\n");
4271}
4272
4273
4274#endif /* ENABLE_MALICIOUS */
4275
4276
4277/**
4278 * Send out PUSHes and PULLs, possibly update #view, samplers.
4279 *
4280 * This is executed regylary.
4281 *
4282 * @param cls Closure - Sub
4283 */
4284static void
4285do_round (void *cls)
4286{
4287 unsigned int i;
4288 const struct GNUNET_PeerIdentity *view_array;
4289 unsigned int *permut;
4290 unsigned int a_peers; /* Number of peers we send pushes to */
4291 unsigned int b_peers; /* Number of peers we send pull requests to */
4292 uint32_t first_border;
4293 uint32_t second_border;
4294 struct GNUNET_PeerIdentity peer;
4295 struct GNUNET_PeerIdentity *update_peer;
4296 struct Sub *sub = cls;
4297
4298 sub->num_rounds++;
4299 LOG (GNUNET_ERROR_TYPE_DEBUG,
4300 "Going to execute next round.\n");
4301 if (sub == msub)
4302 {
4303 GNUNET_STATISTICS_update (stats, "# rounds", 1, GNUNET_NO);
4304 }
4305 sub->do_round_task = NULL;
4306#ifdef TO_FILE_FULL
4307 to_file (sub->file_name_view_log,
4308 "___ new round ___");
4309#endif /* TO_FILE_FULL */
4310 view_array = View_get_as_array (sub->view);
4311 for (i = 0; i < View_size (sub->view); i++)
4312 {
4313 LOG (GNUNET_ERROR_TYPE_DEBUG,
4314 "\t%s\n", GNUNET_i2s (&view_array[i]));
4315#ifdef TO_FILE_FULL
4316 to_file (sub->file_name_view_log,
4317 "=%s\t(do round)",
4318 GNUNET_i2s_full (&view_array[i]));
4319#endif /* TO_FILE_FULL */
4320 }
4321
4322
4323 /* Send pushes and pull requests */
4324 if (0 < View_size (sub->view))
4325 {
4326 permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_STRONG,
4327 View_size (sub->view));
4328
4329 /* Send PUSHes */
4330 a_peers = ceil (alpha * View_size (sub->view));
4331
4332 LOG (GNUNET_ERROR_TYPE_DEBUG,
4333 "Going to send pushes to %u (ceil (%f * %u)) peers.\n",
4334 a_peers, alpha, View_size (sub->view));
4335 for (i = 0; i < a_peers; i++)
4336 {
4337 peer = view_array[permut[i]];
4338 // FIXME if this fails schedule/loop this for later
4339 send_push (get_peer_ctx (sub->peer_map, &peer));
4340 }
4341
4342 /* Send PULL requests */
4343 b_peers = ceil (beta * View_size (sub->view));
4344 first_border = a_peers;
4345 second_border = a_peers + b_peers;
4346 if (second_border > View_size (sub->view))
4347 {
4348 first_border = View_size (sub->view) - b_peers;
4349 second_border = View_size (sub->view);
4350 }
4351 LOG (GNUNET_ERROR_TYPE_DEBUG,
4352 "Going to send pulls to %u (ceil (%f * %u)) peers.\n",
4353 b_peers, beta, View_size (sub->view));
4354 for (i = first_border; i < second_border; i++)
4355 {
4356 peer = view_array[permut[i]];
4357 if (GNUNET_NO == check_peer_flag (sub->peer_map,
4358 &peer,
4359 Peers_PULL_REPLY_PENDING))
4360 { // FIXME if this fails schedule/loop this for later
4361 send_pull_request (get_peer_ctx (sub->peer_map, &peer));
4362 }
4363 }
4364
4365 GNUNET_free (permut);
4366 permut = NULL;
4367 }
4368
4369
4370 /* Update view */
4371 /* TODO see how many peers are in push-/pull- list! */
4372
4373 if ((CustomPeerMap_size (sub->push_map) <= alpha * sub->view_size_est_need) &&
4374 (0 < CustomPeerMap_size (sub->push_map)) &&
4375 (0 < CustomPeerMap_size (sub->pull_map)))
4376 { /* If conditions for update are fulfilled, update */
4377 LOG (GNUNET_ERROR_TYPE_DEBUG, "Update of the view.\n");
4378
4379 uint32_t final_size;
4380 uint32_t peers_to_clean_size;
4381 struct GNUNET_PeerIdentity *peers_to_clean;
4382
4383 peers_to_clean = NULL;
4384 peers_to_clean_size = 0;
4385 GNUNET_array_grow (peers_to_clean,
4386 peers_to_clean_size,
4387 View_size (sub->view));
4388 GNUNET_memcpy (peers_to_clean,
4389 view_array,
4390 View_size (sub->view) * sizeof(struct GNUNET_PeerIdentity));
4391
4392 /* Seems like recreating is the easiest way of emptying the peermap */
4393 View_clear (sub->view);
4394#ifdef TO_FILE_FULL
4395 to_file (sub->file_name_view_log,
4396 "--- emptied ---");
4397#endif /* TO_FILE_FULL */
4398
4399 first_border = GNUNET_MIN (ceil (alpha * sub->view_size_est_need),
4400 CustomPeerMap_size (sub->push_map));
4401 second_border = first_border
4402 + GNUNET_MIN (floor (beta * sub->view_size_est_need),
4403 CustomPeerMap_size (sub->pull_map));
4404 final_size = second_border
4405 + ceil ((1 - (alpha + beta)) * sub->view_size_est_need);
4406 LOG (GNUNET_ERROR_TYPE_DEBUG,
4407 "first border: %" PRIu32 ", second border: %" PRIu32 ", final size: %"
4408 PRIu32 "\n",
4409 first_border,
4410 second_border,
4411 final_size);
4412
4413 /* Update view with peers received through PUSHes */
4414 permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_STRONG,
4415 CustomPeerMap_size (sub->push_map));
4416 for (i = 0; i < first_border; i++)
4417 {
4418 int inserted;
4419 inserted = insert_in_view (sub,
4420 CustomPeerMap_get_peer_by_index (sub->push_map,
4421 permut[i]));
4422 if (GNUNET_OK == inserted)
4423 {
4424 clients_notify_stream_peer (sub,
4425 1,
4426 CustomPeerMap_get_peer_by_index (
4427 sub->push_map, permut[i]));
4428 }
4429#ifdef TO_FILE_FULL
4430 to_file (sub->file_name_view_log,
4431 "+%s\t(push list)",
4432 GNUNET_i2s_full (&view_array[i]));
4433#endif /* TO_FILE_FULL */
4434 // TODO change the peer_flags accordingly
4435 }
4436 GNUNET_free (permut);
4437 permut = NULL;
4438
4439 /* Update view with peers received through PULLs */
4440 permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_STRONG,
4441 CustomPeerMap_size (sub->pull_map));
4442 for (i = first_border; i < second_border; i++)
4443 {
4444 int inserted;
4445 inserted = insert_in_view (sub,
4446 CustomPeerMap_get_peer_by_index (sub->pull_map,
4447 permut[i
4448 -
4449 first_border
4450 ]));
4451 if (GNUNET_OK == inserted)
4452 {
4453 clients_notify_stream_peer (sub,
4454 1,
4455 CustomPeerMap_get_peer_by_index (
4456 sub->pull_map,
4457 permut[i
4458 - first_border]));
4459 }
4460#ifdef TO_FILE_FULL
4461 to_file (sub->file_name_view_log,
4462 "+%s\t(pull list)",
4463 GNUNET_i2s_full (&view_array[i]));
4464#endif /* TO_FILE_FULL */
4465 // TODO change the peer_flags accordingly
4466 }
4467 GNUNET_free (permut);
4468 permut = NULL;
4469
4470 /* Update view with peers from history */
4471 RPS_sampler_get_n_rand_peers (sub->sampler,
4472 final_size - second_border,
4473 hist_update,
4474 sub);
4475 // TODO change the peer_flags accordingly
4476
4477 for (i = 0; i < View_size (sub->view); i++)
4478 rem_from_list (&peers_to_clean, &peers_to_clean_size, &view_array[i]);
4479
4480 /* Clean peers that were removed from the view */
4481 for (i = 0; i < peers_to_clean_size; i++)
4482 {
4483#ifdef TO_FILE_FULL
4484 to_file (sub->file_name_view_log,
4485 "-%s",
4486 GNUNET_i2s_full (&peers_to_clean[i]));
4487#endif /* TO_FILE_FULL */
4488 clean_peer (sub, &peers_to_clean[i]);
4489 }
4490
4491 GNUNET_array_grow (peers_to_clean, peers_to_clean_size, 0);
4492 clients_notify_view_update (sub);
4493 }
4494 else
4495 {
4496 LOG (GNUNET_ERROR_TYPE_DEBUG, "No update of the view.\n");
4497 if (sub == msub)
4498 {
4499 GNUNET_STATISTICS_update (stats, "# rounds blocked", 1, GNUNET_NO);
4500 if ((CustomPeerMap_size (sub->push_map) > alpha
4501 * sub->view_size_est_need) &&
4502 ! (0 >= CustomPeerMap_size (sub->pull_map)))
4503 GNUNET_STATISTICS_update (stats, "# rounds blocked - too many pushes",
4504 1, GNUNET_NO);
4505 if ((CustomPeerMap_size (sub->push_map) > alpha
4506 * sub->view_size_est_need) &&
4507 (0 >= CustomPeerMap_size (sub->pull_map)))
4508 GNUNET_STATISTICS_update (stats,
4509 "# rounds blocked - too many pushes, no pull replies",
4510 1, GNUNET_NO);
4511 if ((0 >= CustomPeerMap_size (sub->push_map)) &&
4512 ! (0 >= CustomPeerMap_size (sub->pull_map)))
4513 GNUNET_STATISTICS_update (stats, "# rounds blocked - no pushes", 1,
4514 GNUNET_NO);
4515 if ((0 >= CustomPeerMap_size (sub->push_map)) &&
4516 (0 >= CustomPeerMap_size (sub->pull_map)))
4517 GNUNET_STATISTICS_update (stats,
4518 "# rounds blocked - no pushes, no pull replies",
4519 1, GNUNET_NO);
4520 if ((0 >= CustomPeerMap_size (sub->pull_map)) &&
4521 (CustomPeerMap_size (sub->push_map) > alpha
4522 * sub->view_size_est_need) &&
4523 (0 >= CustomPeerMap_size (sub->push_map)) )
4524 GNUNET_STATISTICS_update (stats, "# rounds blocked - no pull replies",
4525 1, GNUNET_NO);
4526 }
4527 }
4528 // TODO independent of that also get some peers from CADET_get_peers()?
4529 if (CustomPeerMap_size (sub->push_map) < HISTOGRAM_FILE_SLOTS)
4530 {
4531 sub->push_recv[CustomPeerMap_size (sub->push_map)]++;
4532 }
4533 else
4534 {
4535 LOG (GNUNET_ERROR_TYPE_WARNING,
4536 "Push map size too big for histogram (%u, %u)\n",
4537 CustomPeerMap_size (sub->push_map),
4538 HISTOGRAM_FILE_SLOTS);
4539 }
4540 // FIXME check bounds of histogram
4541 sub->push_delta[(int32_t) (CustomPeerMap_size (sub->push_map)
4542 - (alpha * sub->view_size_est_need))
4543 + (HISTOGRAM_FILE_SLOTS / 2)]++;
4544 if (sub == msub)
4545 {
4546 GNUNET_STATISTICS_set (stats,
4547 "# peers in push map at end of round",
4548 CustomPeerMap_size (sub->push_map),
4549 GNUNET_NO);
4550 GNUNET_STATISTICS_set (stats,
4551 "# peers in pull map at end of round",
4552 CustomPeerMap_size (sub->pull_map),
4553 GNUNET_NO);
4554 GNUNET_STATISTICS_set (stats,
4555 "# peers in view at end of round",
4556 View_size (sub->view),
4557 GNUNET_NO);
4558 GNUNET_STATISTICS_set (stats,
4559 "# expected pushes",
4560 alpha * sub->view_size_est_need,
4561 GNUNET_NO);
4562 GNUNET_STATISTICS_set (stats,
4563 "delta expected - received pushes",
4564 CustomPeerMap_size (sub->push_map) - (alpha
4565 * sub->
4566 view_size_est_need),
4567 GNUNET_NO);
4568 }
4569
4570 LOG (GNUNET_ERROR_TYPE_DEBUG,
4571 "Received %u pushes and %u pulls last round (alpha (%.2f) * view_size (sub->view%u) = %.2f)\n",
4572 CustomPeerMap_size (sub->push_map),
4573 CustomPeerMap_size (sub->pull_map),
4574 alpha,
4575 View_size (sub->view),
4576 alpha * View_size (sub->view));
4577
4578 /* Update samplers */
4579 for (i = 0; i < CustomPeerMap_size (sub->push_map); i++)
4580 {
4581 update_peer = CustomPeerMap_get_peer_by_index (sub->push_map, i);
4582 LOG (GNUNET_ERROR_TYPE_DEBUG,
4583 "Updating with peer %s from push list\n",
4584 GNUNET_i2s (update_peer));
4585 insert_in_sampler (sub, update_peer);
4586 clean_peer (sub, update_peer); /* This cleans only if it is not in the view */
4587 }
4588
4589 for (i = 0; i < CustomPeerMap_size (sub->pull_map); i++)
4590 {
4591 LOG (GNUNET_ERROR_TYPE_DEBUG,
4592 "Updating with peer %s from pull list\n",
4593 GNUNET_i2s (CustomPeerMap_get_peer_by_index (sub->pull_map, i)));
4594 insert_in_sampler (sub, CustomPeerMap_get_peer_by_index (sub->pull_map, i));
4595 /* This cleans only if it is not in the view */
4596 clean_peer (sub, CustomPeerMap_get_peer_by_index (sub->pull_map, i));
4597 }
4598
4599
4600 /* Empty push/pull lists */
4601 CustomPeerMap_clear (sub->push_map);
4602 CustomPeerMap_clear (sub->pull_map);
4603
4604 if (sub == msub)
4605 {
4606 GNUNET_STATISTICS_set (stats,
4607 "view size",
4608 View_size (sub->view),
4609 GNUNET_NO);
4610 }
4611
4612 struct GNUNET_TIME_Relative time_next_round;
4613
4614 time_next_round = compute_rand_delay (sub->round_interval, 2);
4615
4616 /* Schedule next round */
4617 sub->do_round_task = GNUNET_SCHEDULER_add_delayed (time_next_round,
4618 &do_round, sub);
4619 LOG (GNUNET_ERROR_TYPE_DEBUG, "Finished round\n");
4620}
4621
4622
4623/**
4624 * This is called from GNUNET_CADET_get_peers().
4625 *
4626 * It is called on every peer(ID) that cadet somehow has contact with.
4627 * We use those to initialise the sampler.
4628 *
4629 * implements #GNUNET_CADET_PeersCB
4630 *
4631 * @param cls Closure - Sub
4632 * @param peer Peer, or NULL on "EOF".
4633 * @param tunnel Do we have a tunnel towards this peer?
4634 * @param n_paths Number of known paths towards this peer.
4635 * @param best_path How long is the best path?
4636 * (0 = unknown, 1 = ourselves, 2 = neighbor)
4637 */
4638void
4639init_peer_cb (void *cls,
4640 const struct GNUNET_PeerIdentity *peer,
4641 int tunnel, /* "Do we have a tunnel towards this peer?" */
4642 unsigned int n_paths, /* "Number of known paths towards this peer" */
4643 unsigned int best_path) /* "How long is the best path?
4644 * (0 = unknown, 1 = ourselves, 2 = neighbor)" */
4645{
4646 struct Sub *sub = cls;
4647
4648 (void) tunnel;
4649 (void) n_paths;
4650 (void) best_path;
4651
4652 if (NULL != peer)
4653 {
4654 LOG (GNUNET_ERROR_TYPE_DEBUG,
4655 "Got peer_id %s from cadet\n",
4656 GNUNET_i2s (peer));
4657 got_peer (sub, peer);
4658 }
4659}
4660
4661
4662/**
4663 * @brief Iterator function over stored, valid peers.
4664 *
4665 * We initialise the sampler with those.
4666 *
4667 * @param cls Closure - Sub
4668 * @param peer the peer id
4669 * @return #GNUNET_YES if we should continue to
4670 * iterate,
4671 * #GNUNET_NO if not.
4672 */
4673static int
4674valid_peers_iterator (void *cls,
4675 const struct GNUNET_PeerIdentity *peer)
4676{
4677 struct Sub *sub = cls;
4678
4679 if (NULL != peer)
4680 {
4681 LOG (GNUNET_ERROR_TYPE_DEBUG,
4682 "Got stored, valid peer %s\n",
4683 GNUNET_i2s (peer));
4684 got_peer (sub, peer);
4685 }
4686 return GNUNET_YES;
4687}
4688
4689
4690/**
4691 * Iterator over peers from peerinfo.
4692 *
4693 * @param cls Closure - Sub
4694 * @param peer id of the peer, NULL for last call
4695 * @param hello hello message for the peer (can be NULL)
4696 * @param err_msg error message
4697 */
4698void
4699process_peerinfo_peers (void *cls,
4700 const struct GNUNET_PEERSTORE_Record *record,
4701 const char *emsg)
4702{
4703 struct Sub *sub = cls;
4704 (void) emsg;
4705
4706 LOG (GNUNET_ERROR_TYPE_DEBUG,
4707 "Got peer_id %s from peerinfo\n",
4708 GNUNET_i2s (&record->peer));
4709 got_peer (sub, &record->peer);
4710 GNUNET_PEERSTORE_monitor_next(peerstore_notify, 1);
4711}
4712
4713
4714/**
4715 * Task run during shutdown.
4716 *
4717 * @param cls Closure - unused
4718 */
4719static void
4720shutdown_task (void *cls)
4721{
4722 (void) cls;
4723 struct ClientContext *client_ctx;
4724
4725 LOG (GNUNET_ERROR_TYPE_DEBUG,
4726 "RPS service is going down\n");
4727
4728 /* Clean all clients */
4729 for (client_ctx = cli_ctx_head;
4730 NULL != cli_ctx_head;
4731 client_ctx = cli_ctx_head)
4732 {
4733 destroy_cli_ctx (client_ctx);
4734 }
4735 if (NULL != msub)
4736 {
4737 destroy_sub (msub);
4738 msub = NULL;
4739 }
4740
4741 /* Disconnect from other services */
4742 GNUNET_PEERSTORE_monitor_stop (peerstore_notify);
4743 GNUNET_PEERSTORE_disconnect (peerstore);
4744 peerstore = NULL;
4745 GNUNET_NSE_disconnect (nse);
4746 if (NULL != map_single_hop)
4747 {
4748 /* core_init was called - core was initialised */
4749 /* disconnect first, so no callback tries to access missing peermap */
4750 GNUNET_CORE_disconnect (core_handle);
4751 core_handle = NULL;
4752 GNUNET_CONTAINER_multipeermap_destroy (map_single_hop);
4753 map_single_hop = NULL;
4754 }
4755
4756 if (NULL != stats)
4757 {
4758 GNUNET_STATISTICS_destroy (stats,
4759 GNUNET_NO);
4760 stats = NULL;
4761 }
4762 GNUNET_CADET_disconnect (cadet_handle);
4763 cadet_handle = NULL;
4764#if ENABLE_MALICIOUS
4765 struct AttackedPeer *tmp_att_peer;
4766 GNUNET_array_grow (mal_peers,
4767 num_mal_peers,
4768 0);
4769 if (NULL != mal_peer_set)
4770 GNUNET_CONTAINER_multipeermap_destroy (mal_peer_set);
4771 if (NULL != att_peer_set)
4772 GNUNET_CONTAINER_multipeermap_destroy (att_peer_set);
4773 while (NULL != att_peers_head)
4774 {
4775 tmp_att_peer = att_peers_head;
4776 GNUNET_CONTAINER_DLL_remove (att_peers_head,
4777 att_peers_tail,
4778 tmp_att_peer);
4779 GNUNET_free (tmp_att_peer);
4780 }
4781#endif /* ENABLE_MALICIOUS */
4782 close_all_files ();
4783}
4784
4785
4786/**
4787 * Handle client connecting to the service.
4788 *
4789 * @param cls unused
4790 * @param client the new client
4791 * @param mq the message queue of @a client
4792 * @return @a client
4793 */
4794static void *
4795client_connect_cb (void *cls,
4796 struct GNUNET_SERVICE_Client *client,
4797 struct GNUNET_MQ_Handle *mq)
4798{
4799 struct ClientContext *cli_ctx;
4800
4801 (void) cls;
4802
4803 LOG (GNUNET_ERROR_TYPE_DEBUG,
4804 "Client connected\n");
4805 if (NULL == client)
4806 return client; /* Server was destroyed before a client connected. Shutting down */
4807 cli_ctx = GNUNET_new (struct ClientContext);
4808 cli_ctx->mq = mq;
4809 cli_ctx->view_updates_left = -1;
4810 cli_ctx->stream_update = GNUNET_NO;
4811 cli_ctx->client = client;
4812 GNUNET_CONTAINER_DLL_insert (cli_ctx_head,
4813 cli_ctx_tail,
4814 cli_ctx);
4815 return cli_ctx;
4816}
4817
4818
4819/**
4820 * Callback called when a client disconnected from the service
4821 *
4822 * @param cls closure for the service
4823 * @param client the client that disconnected
4824 * @param internal_cls should be equal to @a c
4825 */
4826static void
4827client_disconnect_cb (void *cls,
4828 struct GNUNET_SERVICE_Client *client,
4829 void *internal_cls)
4830{
4831 struct ClientContext *cli_ctx = internal_cls;
4832
4833 (void) cls;
4834 GNUNET_assert (client == cli_ctx->client);
4835 if (NULL == client)
4836 { /* shutdown task - destroy all clients */
4837 while (NULL != cli_ctx_head)
4838 destroy_cli_ctx (cli_ctx_head);
4839 }
4840 else
4841 { /* destroy this client */
4842 LOG (GNUNET_ERROR_TYPE_DEBUG,
4843 "Client disconnected. Destroy its context.\n");
4844 destroy_cli_ctx (cli_ctx);
4845 }
4846}
4847
4848
4849static void
4850error_cb (void *cls)
4851{
4852 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4853 "Error in PEERSTORE monitoring\n");
4854}
4855
4856
4857static void
4858sync_cb (void *cls)
4859{
4860 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4861 "Done with initial PEERSTORE iteration during monitoring\n");
4862}
4863
4864
4865/**
4866 * Handle random peer sampling clients.
4867 *
4868 * @param cls closure
4869 * @param c configuration to use
4870 * @param service the initialized service
4871 */
4872static void
4873run (void *cls,
4874 const struct GNUNET_CONFIGURATION_Handle *c,
4875 struct GNUNET_SERVICE_Handle *service)
4876{
4877 struct GNUNET_TIME_Relative round_interval;
4878 long long unsigned int sampler_size;
4879 char hash_port_string[] = GNUNET_APPLICATION_PORT_RPS;
4880 struct GNUNET_HashCode hash;
4881
4882 (void) cls;
4883 (void) service;
4884
4885 GNUNET_log_setup ("rps",
4886 GNUNET_error_type_to_string (GNUNET_ERROR_TYPE_DEBUG),
4887 NULL);
4888 cfg = c;
4889 /* Get own ID */
4890 GNUNET_CRYPTO_get_peer_identity (cfg,
4891 &own_identity); // TODO check return value
4892 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4893 "STARTING SERVICE (rps) for peer [%s]\n",
4894 GNUNET_i2s (&own_identity));
4895#if ENABLE_MALICIOUS
4896 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4897 "Malicious execution compiled in.\n");
4898#endif /* ENABLE_MALICIOUS */
4899
4900 /* Get time interval from the configuration */
4901 if (GNUNET_OK !=
4902 GNUNET_CONFIGURATION_get_value_time (cfg,
4903 "RPS",
4904 "ROUNDINTERVAL",
4905 &round_interval))
4906 {
4907 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
4908 "RPS", "ROUNDINTERVAL");
4909 GNUNET_SCHEDULER_shutdown ();
4910 return;
4911 }
4912
4913 /* Get initial size of sampler/view from the configuration */
4914 if (GNUNET_OK !=
4915 GNUNET_CONFIGURATION_get_value_number (cfg,
4916 "RPS",
4917 "MINSIZE",
4918 &sampler_size))
4919 {
4920 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
4921 "RPS", "MINSIZE");
4922 GNUNET_SCHEDULER_shutdown ();
4923 return;
4924 }
4925
4926 cadet_handle = GNUNET_CADET_connect (cfg);
4927 GNUNET_assert (NULL != cadet_handle);
4928 core_handle = GNUNET_CORE_connect (cfg,
4929 NULL, /* cls */
4930 core_init, /* init */
4931 core_connects, /* connects */
4932 core_disconnects, /* disconnects */
4933 NULL); /* handlers */
4934 GNUNET_assert (NULL != core_handle);
4935
4936
4937 alpha = 0.45;
4938 beta = 0.45;
4939
4940
4941 /* Set up main Sub */
4942 GNUNET_CRYPTO_hash (hash_port_string,
4943 strlen (hash_port_string),
4944 &hash);
4945 msub = new_sub (&hash,
4946 sampler_size, /* Will be overwritten by config */
4947 round_interval);
4948
4949
4950 peerstore = GNUNET_PEERSTORE_connect (cfg);
4951
4952 /* connect to NSE */
4953 nse = GNUNET_NSE_connect (cfg, nse_callback, NULL);
4954
4955 // LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting peers from CADET\n");
4956 // GNUNET_CADET_get_peers (cadet_handle, &init_peer_cb, msub);
4957 // TODO send push/pull to each of those peers?
4958 LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting stored valid peers\n");
4959 restore_valid_peers (msub);
4960 get_valid_peers (msub->valid_peers, valid_peers_iterator, msub);
4961
4962 peerstore_notify =
4963 GNUNET_PEERSTORE_monitor_start (c,
4964 GNUNET_YES,
4965 "peerstore",
4966 NULL,
4967 GNUNET_PEERSTORE_HELLO_KEY,
4968 &error_cb,
4969 NULL,
4970 &sync_cb,
4971 NULL,
4972 &process_peerinfo_peers,
4973 msub);
4974
4975 LOG (GNUNET_ERROR_TYPE_INFO, "Ready to receive requests from clients\n");
4976
4977 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
4978 stats = GNUNET_STATISTICS_create ("rps", cfg);
4979}
4980
4981
4982/**
4983 * Define "main" method using service macro.
4984 */
4985GNUNET_SERVICE_MAIN
4986 ("rps",
4987 GNUNET_SERVICE_OPTION_NONE,
4988 &run,
4989 &client_connect_cb,
4990 &client_disconnect_cb,
4991 NULL,
4992 GNUNET_MQ_hd_var_size (client_seed,
4993 GNUNET_MESSAGE_TYPE_RPS_CS_SEED,
4994 struct GNUNET_RPS_CS_SeedMessage,
4995 NULL),
4996#if ENABLE_MALICIOUS
4997 GNUNET_MQ_hd_var_size (client_act_malicious,
4998 GNUNET_MESSAGE_TYPE_RPS_ACT_MALICIOUS,
4999 struct GNUNET_RPS_CS_ActMaliciousMessage,
5000 NULL),
5001#endif /* ENABLE_MALICIOUS */
5002 GNUNET_MQ_hd_fixed_size (client_view_request,
5003 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_REQUEST,
5004 struct GNUNET_RPS_CS_DEBUG_ViewRequest,
5005 NULL),
5006 GNUNET_MQ_hd_fixed_size (client_view_cancel,
5007 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_CANCEL,
5008 struct GNUNET_MessageHeader,
5009 NULL),
5010 GNUNET_MQ_hd_fixed_size (client_stream_request,
5011 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_REQUEST,
5012 struct GNUNET_RPS_CS_DEBUG_StreamRequest,
5013 NULL),
5014 GNUNET_MQ_hd_fixed_size (client_stream_cancel,
5015 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_CANCEL,
5016 struct GNUNET_MessageHeader,
5017 NULL),
5018 GNUNET_MQ_hd_fixed_size (client_start_sub,
5019 GNUNET_MESSAGE_TYPE_RPS_CS_SUB_START,
5020 struct GNUNET_RPS_CS_SubStartMessage,
5021 NULL),
5022 GNUNET_MQ_hd_fixed_size (client_stop_sub,
5023 GNUNET_MESSAGE_TYPE_RPS_CS_SUB_STOP,
5024 struct GNUNET_RPS_CS_SubStopMessage,
5025 NULL),
5026 GNUNET_MQ_handler_end ());
5027
5028/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_custommap.c b/src/contrib/service/rps/gnunet-service-rps_custommap.c
new file mode 100644
index 000000000..b1e73be17
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_custommap.c
@@ -0,0 +1,356 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_custommap.c
23 * @brief utilities for managing (information about) peers
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet-service-rps_custommap.h"
29#include <inttypes.h>
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "rps-peers", __VA_ARGS__)
32
33
34/**
35 * Peer map to store peers with specialised use-cases (push_list, pull_list,
36 * view, ...)
37 *
38 * It is aimed for use as unordered list-like structures that can be indexed.
39 * Main use-case:
40 *
41 * permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_STRONG,
42 * CustomPeerMap_size (peer_map));
43 * for (i = 0; i < some_border; i++)
44 * some_array[i] = *CustomPeerMap_get_peer_by_index (peer_map, permut[i]);
45 * for (i = some_border; i < CustomPeerMap_size (peer_map); i++)
46 * other_array[i-some_border] =
47 * *CustomPeerMap_get_peer_by_index (peer_map, permut[i]);
48 *
49 * This list is expected to
50 * - be altered in small steps frequently
51 * - be cleared regularly
52 * - often being queried whether a peer is contained
53 * - alter indices of peers
54 * - contain continuous indices 0 <= i < len
55 * - not contain duplicate peers
56 */
57struct CustomPeerMap
58{
59 /**
60 * Multihashmap to be able to access a random index
61 */
62 struct GNUNET_CONTAINER_MultiHashMap32 *hash_map;
63
64 /**
65 * Peermap to quickly check whether a peer is contained
66 */
67 struct GNUNET_CONTAINER_MultiPeerMap *peer_map;
68};
69
70
71/**
72 * Create an empty peermap.
73 *
74 * @param len the initial length for the internal maps
75 *
76 * @return the newly created custom peer map
77 */
78struct CustomPeerMap *
79CustomPeerMap_create (unsigned int len)
80{
81 struct CustomPeerMap *c_peer_map;
82
83 c_peer_map = GNUNET_new (struct CustomPeerMap);
84 c_peer_map->hash_map = GNUNET_CONTAINER_multihashmap32_create (len);
85 c_peer_map->peer_map = GNUNET_CONTAINER_multipeermap_create (len,
86 GNUNET_NO);
87 return c_peer_map;
88}
89
90
91/**
92 * Get the size of the custom peer map
93 *
94 * @param c_peer_map the custom peer map to look in
95 *
96 * @return size of the map
97 */
98unsigned int
99CustomPeerMap_size (const struct CustomPeerMap *c_peer_map)
100{
101 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (c_peer_map->hash_map) ==
102 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
103 return GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map);
104}
105
106
107/**
108 * Insert peer into the custom peer map
109 *
110 * @param c_peer_map the custom peer map to insert peer
111 * @param peer the peer to insert
112 *
113 * @return GNUNET_OK if map did not contain peer previously
114 * GNUNET_NO if map did contain peer previously
115 */
116int
117CustomPeerMap_put (const struct CustomPeerMap *c_peer_map,
118 const struct GNUNET_PeerIdentity *peer)
119{
120 uint32_t *index;
121 struct GNUNET_PeerIdentity *p;
122
123 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (c_peer_map->hash_map) ==
124 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
125 if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (c_peer_map->peer_map,
126 peer))
127 {
128 /* Need to store the index of the peer in the peermap to be able to remove
129 * it properly */
130 index = GNUNET_new (uint32_t);
131 *index = CustomPeerMap_size (c_peer_map);
132 p = GNUNET_new (struct GNUNET_PeerIdentity);
133 *p = *peer;
134 GNUNET_assert (p != peer);
135 GNUNET_assert (0 == memcmp (p,
136 peer,
137 sizeof(struct GNUNET_PeerIdentity)));
138 GNUNET_CONTAINER_multipeermap_put (c_peer_map->peer_map,
139 p,
140 index,
141 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
142 GNUNET_CONTAINER_multihashmap32_put (c_peer_map->hash_map,
143 *index,
144 p,
145 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
146 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (
147 c_peer_map->hash_map) ==
148 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
149 return GNUNET_OK;
150 }
151 return GNUNET_NO;
152}
153
154
155/**
156 * Check whether custom peer map contains a peer
157 *
158 * @param c_peer_map the custom peer map to look in
159 * @param peer the peer to check for
160 *
161 * @return GNUNET_OK if map contains peer
162 * GNUNET_NO otherwise
163 */
164int
165CustomPeerMap_contains_peer (const struct CustomPeerMap *c_peer_map,
166 const struct GNUNET_PeerIdentity *peer)
167{
168 return GNUNET_CONTAINER_multipeermap_contains (c_peer_map->peer_map, peer);
169}
170
171
172/**
173 * Get index of peer in custom peer map
174 *
175 * @param c_peer_map the custom peer map to look in
176 * @param peer the peer to get the index from
177 *
178 * @return the index
179 */
180static uint32_t *
181CustomPeerMap_get_index_pointer (const struct CustomPeerMap *c_peer_map,
182 const struct GNUNET_PeerIdentity *peer)
183{
184 uint32_t *index;
185
186 GNUNET_assert (GNUNET_YES == CustomPeerMap_contains_peer (c_peer_map, peer));
187 index = GNUNET_CONTAINER_multipeermap_get (c_peer_map->peer_map, peer);
188 return index;
189}
190
191
192/**
193 * Remove peer from custom peer map
194 *
195 * @param c_peer_map the custom peer map to remove the peer from
196 * @param peer the peer to remove
197 *
198 * @return GNUNET_OK if map contained peer and removed it successfully
199 * GNUNET_NO if map does not contain peer
200 */
201int
202CustomPeerMap_remove_peer (const struct CustomPeerMap *c_peer_map,
203 const struct GNUNET_PeerIdentity *peer)
204{
205 uint32_t *index;
206 struct GNUNET_PeerIdentity *p;
207 uint32_t *last_index;
208 struct GNUNET_PeerIdentity *last_p;
209 int ret;
210
211 if (GNUNET_NO == CustomPeerMap_contains_peer (c_peer_map,
212 peer))
213 {
214 return GNUNET_NO;
215 }
216 index = CustomPeerMap_get_index_pointer (c_peer_map,
217 peer);
218 GNUNET_assert (*index < CustomPeerMap_size (c_peer_map));
219 /* Need to get the pointer stored in the hashmap to free it */
220 p = GNUNET_CONTAINER_multihashmap32_get (c_peer_map->hash_map,
221 *index);
222 GNUNET_assert (NULL != p);
223 GNUNET_CONTAINER_multihashmap32_remove_all (c_peer_map->hash_map,
224 *index);
225 // TODO wrong peerid?
226 GNUNET_CONTAINER_multipeermap_remove_all (c_peer_map->peer_map,
227 peer);
228 if (*index != CustomPeerMap_size (c_peer_map))
229 { /* fill 'gap' with peer at last index */
230 last_p =
231 GNUNET_CONTAINER_multihashmap32_get (c_peer_map->hash_map,
232 CustomPeerMap_size (c_peer_map));
233 GNUNET_assert (NULL != last_p);
234 last_index = GNUNET_CONTAINER_multipeermap_get (c_peer_map->peer_map,
235 last_p);
236 GNUNET_assert (NULL != last_index);
237 GNUNET_assert (CustomPeerMap_size (c_peer_map) == *last_index);
238 ret = GNUNET_CONTAINER_multihashmap32_put (c_peer_map->hash_map,
239 *index,
240 last_p,
241 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
242 GNUNET_assert (GNUNET_OK == ret);
243 GNUNET_CONTAINER_multihashmap32_remove_all (c_peer_map->hash_map,
244 *last_index);
245 *last_index = *index;
246 }
247 GNUNET_free (index);
248 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (c_peer_map->hash_map) ==
249 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
250 GNUNET_free (p);
251 return GNUNET_OK;
252}
253
254
255/**
256 * Get a peer by index
257 *
258 * @param c_peer_map the custom peer map to look in
259 * @param index the index of the peer to get
260 *
261 * @return peer to the corresponding index.
262 * if this index is not known, return NULL
263 */
264struct GNUNET_PeerIdentity *
265CustomPeerMap_get_peer_by_index (const struct CustomPeerMap *c_peer_map,
266 uint32_t index)
267{
268 if (GNUNET_YES ==
269 GNUNET_CONTAINER_multihashmap32_contains (c_peer_map->hash_map, index))
270 {
271 return GNUNET_CONTAINER_multihashmap32_get (c_peer_map->hash_map, index);
272 }
273 return NULL;
274}
275
276
277/**
278 * Remove peer from custom peer map by index
279 *
280 * @param c_peer_map the custom peer map to remove the peer from
281 * @param index the index of the peer to remove
282 *
283 * @return GNUNET_OK if map contained peer and removed it successfully
284 * GNUNET_NO if map does not contain (index of) peer
285 */
286int
287CustomPeerMap_remove_peer_by_index (const struct CustomPeerMap *c_peer_map,
288 uint32_t index)
289{
290 uint32_t *index_p;
291 struct GNUNET_PeerIdentity *peer;
292
293 if (index >= CustomPeerMap_size (c_peer_map))
294 {
295 return GNUNET_NO;
296 }
297 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (c_peer_map->hash_map) ==
298 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
299 if (GNUNET_NO ==
300 GNUNET_CONTAINER_multihashmap32_contains (c_peer_map->hash_map, index))
301 {
302 return GNUNET_NO;
303 }
304 peer = CustomPeerMap_get_peer_by_index (c_peer_map, index);
305 GNUNET_assert (NULL != peer);
306 index_p = CustomPeerMap_get_index_pointer (c_peer_map, peer);
307 GNUNET_assert (index == *index_p);
308 CustomPeerMap_remove_peer (c_peer_map, peer);
309 GNUNET_assert (GNUNET_CONTAINER_multihashmap32_size (c_peer_map->hash_map) ==
310 GNUNET_CONTAINER_multipeermap_size (c_peer_map->peer_map));
311 return GNUNET_OK;
312}
313
314
315/**
316 * Clear the custom peer map
317 *
318 * @param c_peer_map the custom peer map to look in
319 *
320 * @return size of the map
321 */
322void
323CustomPeerMap_clear (const struct CustomPeerMap *c_peer_map)
324{
325 while (0 < CustomPeerMap_size (c_peer_map))
326 {
327 GNUNET_assert (GNUNET_YES ==
328 GNUNET_CONTAINER_multihashmap32_contains (
329 c_peer_map->hash_map,
330 CustomPeerMap_size (
331 c_peer_map) - 1));
332 GNUNET_assert (GNUNET_OK ==
333 CustomPeerMap_remove_peer_by_index (c_peer_map,
334 CustomPeerMap_size (
335 c_peer_map) - 1));
336 }
337 GNUNET_assert (0 == CustomPeerMap_size (c_peer_map));
338}
339
340
341/**
342 * Destroy peermap.
343 *
344 * @param c_peer_map the map to destroy
345 */
346void
347CustomPeerMap_destroy (struct CustomPeerMap *c_peer_map)
348{
349 CustomPeerMap_clear (c_peer_map);
350 GNUNET_CONTAINER_multihashmap32_destroy (c_peer_map->hash_map);
351 GNUNET_CONTAINER_multipeermap_destroy (c_peer_map->peer_map);
352 GNUNET_free (c_peer_map);
353}
354
355
356/* end of gnunet-service-rps_custommap.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_custommap.h b/src/contrib/service/rps/gnunet-service-rps_custommap.h
new file mode 100644
index 000000000..e7f669c63
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_custommap.h
@@ -0,0 +1,159 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_custommap.h
23 * @brief utilities for managing (information about) peers
24 * @author Julius Bünger
25 */
26#include "gnunet_util_lib.h"
27#include <inttypes.h>
28
29
30/**
31 * Peer map to store peers with specialised use-cases (push_list, pull_list,
32 * view, ...)
33 *
34 * It is aimed for use as unordered list-like structures that can be indexed.
35 * Main use-case:
36 *
37 * permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_STRONG,
38 * CustomPeerMap_size (peer_map));
39 * for (i = 0; i < some_border; i++)
40 * some_array[i] = *CustomPeerMap_get_peer_by_index (peer_map, permut[i]);
41 * for (i = some_border; i < CustomPeerMap_size (peer_map); i++)
42 * other_array[i-some_border] =
43 * *CustomPeerMap_get_peer_by_index (peer_map, permut[i]);
44 *
45 * This list is expected to
46 * - be altered in small steps frequently
47 * - be cleared regularly
48 * - often being queried whether a peer is contained
49 * - alter indices of peers
50 * - contain continuous indices 0 <= i < len
51 * - not contain duplicate peers
52 */
53struct CustomPeerMap;
54
55
56/**
57 * Create an empty peermap.
58 *
59 * @param len the initial length for the internal maps
60 *
61 * @return the newly created custom peer map
62 */
63struct CustomPeerMap *
64CustomPeerMap_create (unsigned int len);
65
66/**
67 * Get the size of the custom peer map
68 *
69 * @param c_peer_map the custom peer map to look in
70 *
71 * @return size of the map
72 */
73unsigned int
74CustomPeerMap_size (const struct CustomPeerMap *c_peer_map);
75
76/**
77 * Insert peer into the custom peer map
78 *
79 * @param c_peer_map the custom peer map to insert peer
80 * @param peer the peer to insert
81 *
82 * @return GNUNET_OK if map did not contain peer previously
83 * GNUNET_NO if map did contain peer previously
84 */
85int
86CustomPeerMap_put (const struct CustomPeerMap *c_peer_map,
87 const struct GNUNET_PeerIdentity *peer);
88
89/**
90 * Check whether custom peer map contains a peer
91 *
92 * @param c_peer_map the custom peer map to look in
93 * @param peer the peer to check for
94 *
95 * @return GNUNET_OK if map contains peer
96 * GNUNET_NO otherwise
97 */
98int
99CustomPeerMap_contains_peer (const struct CustomPeerMap *c_peer_map,
100 const struct GNUNET_PeerIdentity *peer);
101
102/**
103 * Remove peer from custom peer map
104 *
105 * @param c_peer_map the custom peer map to remove the peer from
106 * @param peer the peer to remove
107 *
108 * @return GNUNET_OK if map contained peer and removed it successfully
109 * GNUNET_NO if map does not contain peer
110 */
111int
112CustomPeerMap_remove_peer (const struct CustomPeerMap *c_peer_map,
113 const struct GNUNET_PeerIdentity *peer);
114
115/**
116 * Get a peer by index
117 *
118 * @param c_peer_map the custom peer map to look in
119 * @param index the index of the peer to get
120 *
121 * @return peer to the corresponding index.
122 * if this index is not known, return NULL
123 */
124struct GNUNET_PeerIdentity *
125CustomPeerMap_get_peer_by_index (const struct CustomPeerMap *c_peer_map,
126 uint32_t index);
127
128/**
129 * Remove peer from custom peer map by index
130 *
131 * @param c_peer_map the custom peer map to remove the peer from
132 * @param index the index of the peer to remove
133 *
134 * @return GNUNET_OK if map contained peer and removed it successfully
135 * GNUNET_NO if map does not contain (index of) peer
136 */
137int
138CustomPeerMap_remove_peer_by_index (const struct CustomPeerMap *c_peer_map,
139 uint32_t index);
140
141/**
142 * Clear the custom peer map
143 *
144 * @param c_peer_map the custom peer map to look in
145 *
146 * @return size of the map
147 */
148void
149CustomPeerMap_clear (const struct CustomPeerMap *c_peer_map);
150
151/**
152 * Destroy peermap.
153 *
154 * @param c_peer_map the map to destroy
155 */
156void
157CustomPeerMap_destroy (struct CustomPeerMap *c_peer_map);
158
159/* end of gnunet-service-rps_custommap.h */
diff --git a/src/contrib/service/rps/gnunet-service-rps_sampler.c b/src/contrib/service/rps/gnunet-service-rps_sampler.c
new file mode 100644
index 000000000..598cc887e
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_sampler.c
@@ -0,0 +1,268 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_sampler.c
23 * @brief sampler implementation
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "rps.h"
30
31#include "rps-sampler_common.h"
32#include "gnunet-service-rps_sampler.h"
33#include "gnunet-service-rps_sampler_elem.h"
34
35#include <math.h>
36#include <inttypes.h>
37
38#include "rps-test_util.h"
39
40#define LOG(kind, ...) GNUNET_log_from (kind, "rps-sampler", __VA_ARGS__)
41
42
43// multiple 'clients'?
44
45// TODO check for overflows
46
47// TODO align message structs
48
49// hist_size_init, hist_size_max
50
51/***********************************************************************
52* WARNING: This section needs to be reviewed regarding the use of
53* functions providing (pseudo)randomness!
54***********************************************************************/
55
56// TODO care about invalid input of the caller (size 0 or less...)
57
58/**
59 * @brief Callback called each time a new peer was put into the sampler
60 *
61 * @param cls A possibly given closure
62 */
63typedef void
64(*SamplerNotifyUpdateCB) (void *cls);
65
66/**
67 * @brief Context for a callback. Contains callback and closure.
68 *
69 * Meant to be an entry in an DLL.
70 */
71struct SamplerNotifyUpdateCTX
72{
73 /**
74 * @brief The Callback to call on updates
75 */
76 SamplerNotifyUpdateCB notify_cb;
77
78 /**
79 * @brief The according closure.
80 */
81 void *cls;
82
83 /**
84 * @brief Next element in DLL.
85 */
86 struct SamplerNotifyUpdateCTX *next;
87
88 /**
89 * @brief Previous element in DLL.
90 */
91 struct SamplerNotifyUpdateCTX *prev;
92};
93
94
95/**
96 * Type of function used to differentiate between modified and not modified
97 * Sampler.
98 */
99typedef void
100(*RPS_get_peers_type) (void *cls);
101
102/**
103 * Get one random peer out of the sampled peers.
104 *
105 * We might want to reinitialise this sampler after giving the
106 * corrsponding peer to the client.
107 * Only used internally
108 */
109static void
110sampler_get_rand_peer (void *cls);
111
112
113/**
114 * Closure to _get_n_rand_peers_ready_cb()
115 */
116struct RPS_SamplerRequestHandle
117{
118 /**
119 * DLL
120 */
121 struct RPS_SamplerRequestHandle *next;
122 struct RPS_SamplerRequestHandle *prev;
123
124 /**
125 * Number of peers we are waiting for.
126 */
127 uint32_t num_peers;
128
129 /**
130 * Number of peers we currently have.
131 */
132 uint32_t cur_num_peers;
133
134 /**
135 * Pointer to the array holding the ids.
136 */
137 struct GNUNET_PeerIdentity *ids;
138
139 /**
140 * Head and tail for the DLL to store the tasks for single requests
141 */
142 struct GetPeerCls *gpc_head;
143 struct GetPeerCls *gpc_tail;
144
145 /**
146 * Sampler.
147 */
148 struct RPS_Sampler *sampler;
149
150 /**
151 * Callback to be called when all ids are available.
152 */
153 RPS_sampler_n_rand_peers_ready_cb callback;
154
155 /**
156 * Closure given to the callback
157 */
158 void *cls;
159};
160
161///**
162// * Global sampler variable.
163// */
164// struct RPS_Sampler *sampler;
165
166
167/**
168 * The minimal size for the extended sampler elements.
169 */
170static size_t min_size;
171
172/**
173 * The maximal size the extended sampler elements should grow to.
174 */
175static size_t max_size;
176
177/**
178 * The size the extended sampler elements currently have.
179 */
180// static size_t extra_size;
181
182/**
183 * Inedex to the sampler element that is the next to be returned
184 */
185static uint32_t client_get_index;
186
187
188/**
189 * Initialise a tuple of sampler elements.
190 *
191 * @param init_size the size the sampler is initialised with
192 * @param max_round_interval maximum time a round takes
193 * @return a handle to a sampler that consists of sampler elements.
194 */
195struct RPS_Sampler *
196RPS_sampler_init (size_t init_size,
197 struct GNUNET_TIME_Relative max_round_interval)
198{
199 struct RPS_Sampler *sampler;
200
201 /* Initialise context around extended sampler */
202 min_size = 10; // TODO make input to _samplers_init()
203 max_size = 1000; // TODO make input to _samplers_init()
204
205 sampler = GNUNET_new (struct RPS_Sampler);
206
207 sampler->max_round_interval = max_round_interval;
208 sampler->get_peers = sampler_get_rand_peer;
209 // sampler->sampler_elements = GNUNET_new_array(init_size, struct GNUNET_PeerIdentity);
210 // GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, min_size);
211 RPS_sampler_resize (sampler, init_size);
212
213 client_get_index = 0;
214
215 // GNUNET_assert (init_size == sampler->sampler_size);
216 return sampler;
217}
218
219
220/**
221 * Get one random peer out of the sampled peers.
222 *
223 * We might want to reinitialise this sampler after giving the
224 * corrsponding peer to the client.
225 * Only used internally
226 */
227static void
228sampler_get_rand_peer (void *cls)
229{
230 struct GetPeerCls *gpc = cls;
231 uint32_t r_index;
232 struct RPS_Sampler *sampler;
233
234 gpc->get_peer_task = NULL;
235 gpc->notify_ctx = NULL;
236 sampler = gpc->req_handle->sampler;
237
238 /**;
239 * Choose the r_index of the peer we want to return
240 * at random from the interval of the gossip list
241 */
242 r_index = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
243 sampler->sampler_size);
244
245 if (EMPTY == sampler->sampler_elements[r_index]->is_empty)
246 {
247 // LOG (GNUNET_ERROR_TYPE_DEBUG,
248 // "Not returning randomly selected, empty PeerID. - Rescheduling.\n");
249
250 gpc->notify_ctx =
251 sampler_notify_on_update (sampler,
252 &sampler_get_rand_peer,
253 gpc);
254 return;
255 }
256
257 GNUNET_CONTAINER_DLL_remove (gpc->req_handle->gpc_head,
258 gpc->req_handle->gpc_tail,
259 gpc);
260 *gpc->id = sampler->sampler_elements[r_index]->peer_id;
261 gpc->cont (gpc->cont_cls, gpc->id, 0,
262 sampler->sampler_elements[r_index]->num_peers);
263
264 GNUNET_free (gpc);
265}
266
267
268/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_sampler.h b/src/contrib/service/rps/gnunet-service-rps_sampler.h
new file mode 100644
index 000000000..410823bea
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_sampler.h
@@ -0,0 +1,157 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_sampler.h
23 * @brief sampler implementation
24 * @author Julius Bünger
25 */
26
27#ifndef RPS_SAMPLER_H
28#define RPS_SAMPLER_H
29#include <inttypes.h>
30#include "rps-sampler_common.h"
31
32
33/**
34 * A sampler sampling a stream of PeerIDs.
35 */
36struct RPS_Sampler;
37
38/**
39 * A handle to cancel a request.
40 */
41struct RPS_SamplerRequestHandle;
42
43
44/**
45 * Get the size of the sampler.
46 *
47 * @param sampler the sampler to return the size of.
48 * @return the size of the sampler
49 */
50unsigned int
51RPS_sampler_get_size (struct RPS_Sampler *sampler);
52
53
54/**
55 * Grow or shrink the size of the sampler.
56 *
57 * @param sampler the sampler to resize.
58 * @param new_size the new size of the sampler (not 0)
59 */
60void
61RPS_sampler_resize (struct RPS_Sampler *sampler, unsigned int new_size);
62
63
64/**
65 * Initialise a tuple of samplers.
66 *
67 * @param init_size the size the sampler is initialised with
68 * @param max_round_interval maximum time a round takes
69 * @return a handle to a sampler that consists of sampler elements.
70 */
71struct RPS_Sampler *
72RPS_sampler_init (size_t init_size,
73 struct GNUNET_TIME_Relative max_round_interval);
74
75
76/**
77 * Update every sampler element of this sampler with given peer
78 *
79 * @param sampler the sampler to update.
80 * @param id the PeerID that is put in the sampler
81 */
82void
83RPS_sampler_update (struct RPS_Sampler *sampler,
84 const struct GNUNET_PeerIdentity *id);
85
86
87/**
88 * Reinitialise all previously initialised sampler elements with the given
89 * value.
90 *
91 * Used to get rid of a PeerID.
92 *
93 * FIXME: This should also consider currently pending requests
94 * (Pending requests already collect peerids. As long as not all
95 * requested IDs have been collected, they are kept.
96 * Ideally, the @p id should be removed from all pending requests. This
97 * seems quite complicated.)
98 *
99 * @param sampler the sampler to reinitialise a sampler in.
100 * @param id the id of the samplers to update.
101 */
102void
103RPS_sampler_reinitialise_by_value (struct RPS_Sampler *sampler,
104 const struct GNUNET_PeerIdentity *id);
105
106
107/**
108 * Get n random peers out of the sampled peers.
109 *
110 * We might want to reinitialise this sampler after giving the
111 * corrsponding peer to the client.
112 * Random with or without consumption?
113 *
114 * @param sampler the sampler to get peers from.
115 * @param cb callback that will be called once the ids are ready.
116 * @param cls closure given to @a cb
117 * @param for_client #GNUNET_YES if result is used for client,
118 * #GNUNET_NO if used internally
119 * @param num_peers the number of peers requested
120 */
121struct RPS_SamplerRequestHandle *
122RPS_sampler_get_n_rand_peers (struct RPS_Sampler *sampler,
123 uint32_t num_peers,
124 RPS_sampler_n_rand_peers_ready_cb cb,
125 void *cls);
126
127/**
128 * Cancel a request issued through #RPS_sampler_n_rand_peers_ready_cb.
129 *
130 * @param req_handle the handle to the request
131 */
132void
133RPS_sampler_request_cancel (struct RPS_SamplerRequestHandle *req_handle);
134
135
136/**
137 * Counts how many Samplers currently hold a given PeerID.
138 *
139 * @param sampler the sampler to count ids in.
140 * @param id the PeerID to count.
141 * @return the number of occurrences of id.
142 */
143uint32_t
144RPS_sampler_count_id (struct RPS_Sampler *sampler,
145 const struct GNUNET_PeerIdentity *id);
146
147
148/**
149 * Cleans the samplers.
150 *
151 * @param sampler the sampler to destroy.
152 */
153void
154RPS_sampler_destroy (struct RPS_Sampler *sampler);
155
156#endif
157/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_sampler_elem.c b/src/contrib/service/rps/gnunet-service-rps_sampler_elem.c
new file mode 100644
index 000000000..a6dc755a3
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_sampler_elem.c
@@ -0,0 +1,170 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_sampler.c
23 * @brief sampler implementation
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29#include "gnunet-service-rps_sampler_elem.h"
30
31#include <inttypes.h>
32
33#include "rps-test_util.h"
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "rps-sampler_elem", __VA_ARGS__)
36
37
38/***********************************************************************
39* WARNING: This section needs to be reviewed regarding the use of
40* functions providing (pseudo)randomness!
41***********************************************************************/
42
43
44/**
45 * Reinitialise a previously initialised sampler element.
46 *
47 * @param sampler_elem The sampler element to (re-) initialise
48 */
49void
50RPS_sampler_elem_reinit (struct RPS_SamplerElement *sampler_elem)
51{
52 sampler_elem->is_empty = EMPTY;
53
54 // I guess I don't need to call GNUNET_CRYPTO_hmac_derive_key()...
55 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
56 &(sampler_elem->auth_key.key),
57 GNUNET_CRYPTO_HASH_LENGTH);
58
59 sampler_elem->last_client_request = GNUNET_TIME_UNIT_FOREVER_ABS;
60
61 sampler_elem->birth = GNUNET_TIME_absolute_get ();
62 sampler_elem->num_peers = 0;
63 sampler_elem->num_change = 0;
64}
65
66
67/**
68 * Create a sampler element and initialise it.
69 *
70 * In this implementation this means choosing an auth_key for later use in
71 * a hmac at random.
72 *
73 * @return a newly created RPS_SamplerElement which currently holds no id.
74 */
75struct RPS_SamplerElement *
76RPS_sampler_elem_create (void)
77{
78 struct RPS_SamplerElement *s;
79
80 s = GNUNET_new (struct RPS_SamplerElement);
81
82 RPS_sampler_elem_reinit (s);
83
84 return s;
85}
86
87
88/**
89 * Destroy a sampler element.
90 *
91 * @param sampler_elem the element to destroy
92 */
93void
94RPS_sampler_elem_destroy (struct RPS_SamplerElement *sampler_elem)
95{
96 GNUNET_free (sampler_elem);
97}
98
99
100/**
101 * Update a sampler element with a PeerID
102 *
103 * @param sampler_elem The sampler element to update
104 * @param new_ID The PeerID to update with
105 */
106void
107RPS_sampler_elem_next (struct RPS_SamplerElement *sampler_elem,
108 const struct GNUNET_PeerIdentity *new_ID)
109{
110 struct GNUNET_HashCode other_hash;
111
112 sampler_elem->num_peers++;
113
114 if (0 == GNUNET_memcmp (new_ID, &(sampler_elem->peer_id)))
115 {
116 LOG (GNUNET_ERROR_TYPE_DEBUG, "Have already PeerID %s\n",
117 GNUNET_i2s (&(sampler_elem->peer_id)));
118 }
119 else
120 {
121 GNUNET_CRYPTO_hmac (&sampler_elem->auth_key,
122 new_ID,
123 sizeof(struct GNUNET_PeerIdentity),
124 &other_hash);
125
126 if (EMPTY == sampler_elem->is_empty)
127 {
128 LOG (GNUNET_ERROR_TYPE_DEBUG,
129 "Got PeerID %s; Simply accepting (was empty previously).\n",
130 GNUNET_i2s (new_ID));
131 sampler_elem->peer_id = *new_ID;
132 sampler_elem->peer_id_hash = other_hash;
133
134 sampler_elem->num_change++;
135 }
136 else if (0 > GNUNET_CRYPTO_hash_cmp (&other_hash,
137 &sampler_elem->peer_id_hash))
138 {
139 LOG (GNUNET_ERROR_TYPE_DEBUG, "Discarding old PeerID %s\n",
140 GNUNET_i2s (&sampler_elem->peer_id));
141 sampler_elem->peer_id = *new_ID;
142 sampler_elem->peer_id_hash = other_hash;
143
144 sampler_elem->num_change++;
145 }
146 else
147 {
148 LOG (GNUNET_ERROR_TYPE_DEBUG, "Keeping old PeerID %s\n",
149 GNUNET_i2s (&sampler_elem->peer_id));
150 }
151 }
152 sampler_elem->is_empty = NOT_EMPTY;
153}
154
155
156/**
157 * Set the min-wise independent function of the given sampler element.
158 *
159 * @param sampler_elem the sampler element
160 * @param auth_key the key to use
161 */
162void
163RPS_sampler_elem_set (struct RPS_SamplerElement *sampler_elem,
164 struct GNUNET_CRYPTO_AuthKey auth_key)
165{
166 sampler_elem->auth_key = auth_key;
167}
168
169
170/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_sampler_elem.h b/src/contrib/service/rps/gnunet-service-rps_sampler_elem.h
new file mode 100644
index 000000000..98959a88f
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_sampler_elem.h
@@ -0,0 +1,153 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_sampler_elem.h
23 * @brief sampler element implementation
24 * @author Julius Bünger
25 */
26
27#ifndef RPS_SAMPLER_ELEM_H
28#define RPS_SAMPLER_ELEM_H
29#include <inttypes.h>
30
31
32/***********************************************************************
33* WARNING: This section needs to be reviewed regarding the use of
34* functions providing (pseudo)randomness!
35***********************************************************************/
36
37/**
38 * Used to indicate whether a sampler element is empty.
39 */
40enum RPS_SamplerEmpty
41{
42 NOT_EMPTY = 0x0,
43 EMPTY = 0x1
44};
45
46/**
47 * A sampler element sampling one PeerID at a time.
48 */
49struct RPS_SamplerElement
50{
51 /**
52 * Min-wise linear permutation used by this sampler.
53 *
54 * This is an key later used by a hmac.
55 */
56 struct GNUNET_CRYPTO_AuthKey auth_key;
57
58 /**
59 * The PeerID this sampler currently samples.
60 */
61 struct GNUNET_PeerIdentity peer_id;
62
63 /**
64 * The according hash value of this PeerID.
65 */
66 struct GNUNET_HashCode peer_id_hash;
67
68
69 /**
70 * Time of last request.
71 */
72 struct GNUNET_TIME_Absolute last_client_request;
73
74 /**
75 * Flag that indicates that we are not holding a valid PeerID right now.
76 */
77 enum RPS_SamplerEmpty is_empty;
78
79 /**
80 * 'Birth'
81 */
82 struct GNUNET_TIME_Absolute birth;
83
84 /**
85 * How many times a PeerID was put in this sampler.
86 */
87 uint32_t num_peers;
88
89 /**
90 * How many times this sampler changed the peer_id.
91 */
92 uint32_t num_change;
93
94 /**
95 * The file name this sampler element should log to
96 */
97 char *file_name;
98};
99
100
101/**
102 * Reinitialise a previously initialised sampler element.
103 *
104 * @param sampler_el The sampler element to (re-) initialise
105 */
106void
107RPS_sampler_elem_reinit (struct RPS_SamplerElement *sampler_elem);
108
109
110/**
111 * Create a sampler element and initialise it.
112 *
113 * In this implementation this means choosing an auth_key for later use in
114 * a hmac at random.
115 *
116 * @return a newly created RPS_SamplerElement which currently holds no id.
117 */
118struct RPS_SamplerElement *
119RPS_sampler_elem_create (void);
120
121
122/**
123 * Destroy a sampler element.
124 *
125 * @param sampler_elem the element to destroy
126 */
127void
128RPS_sampler_elem_destroy (struct RPS_SamplerElement *sampler_elem);
129
130
131/**
132 * Update a sampler element with a PeerID
133 *
134 * @param sampler_elem The sampler element to update
135 * @param new_ID The PeerID to update with
136 */
137void
138RPS_sampler_elem_next (struct RPS_SamplerElement *sampler_elem,
139 const struct GNUNET_PeerIdentity *new_ID);
140
141/**
142 * Set the min-wise independent function of the given sampler element.
143 *
144 * @param sampler_elem the sampler element
145 * @param auth_key the key to use
146 */
147void
148RPS_sampler_elem_set (struct RPS_SamplerElement *sampler_elem,
149 struct GNUNET_CRYPTO_AuthKey auth_key);
150
151
152#endif /* RPS_SAMPLER_ELEM_H */
153/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_view.c b/src/contrib/service/rps/gnunet-service-rps_view.c
new file mode 100644
index 000000000..d58b995a3
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_view.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_view.c
23 * @brief wrapper around the "local view"
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet-service-rps_view.h"
29#include <inttypes.h>
30
31struct View
32{
33 /**
34 * Array containing the peers
35 */
36 struct GNUNET_PeerIdentity *array;
37
38 /**
39 * (Maximum) length of the view
40 */
41 uint32_t length;
42
43 /**
44 * Multipeermap containing the peers
45 */
46 struct GNUNET_CONTAINER_MultiPeerMap *mpm;
47};
48
49
50/**
51 * Create an empty view.
52 *
53 * @param len the maximum length for the view
54 * @return The newly created view
55 */
56struct View *
57View_create (uint32_t len)
58{
59 struct View *view;
60
61 view = GNUNET_new (struct View);
62 view->length = len;
63 view->array = GNUNET_new_array (len, struct GNUNET_PeerIdentity);
64 view->mpm =
65 GNUNET_CONTAINER_multipeermap_create (len, GNUNET_NO); /* might even be
66 * set to _YES */
67 return view;
68}
69
70
71/**
72 * Change length of view
73 *
74 * If size is decreased, peers with higher indices are removed.
75 *
76 * @param view The view that is changed
77 * @param len the (maximum) length for the view
78 */
79void
80View_change_len (struct View *view,
81 uint32_t len)
82{
83 uint32_t i;
84 uint32_t *index;
85
86 if (GNUNET_CONTAINER_multipeermap_size (view->mpm) < len)
87 { /* Simply shrink */
88 /* We might simply clear and free the left over space */
89 GNUNET_array_grow (view->array, view->length, len);
90 }
91 else /* We have to remove elements */
92 {
93 /* TODO find a way to preserve indices */
94 for (i = 0; i < len; i++)
95 {
96 index = GNUNET_CONTAINER_multipeermap_get (view->mpm, &view->array[i]);
97 GNUNET_assert (NULL != index);
98 GNUNET_free (index);
99 }
100 GNUNET_array_grow (view->array, view->length, len);
101 GNUNET_CONTAINER_multipeermap_destroy (view->mpm);
102 view->mpm = GNUNET_CONTAINER_multipeermap_create (len, GNUNET_NO);
103 for (i = 0; i < len; i++)
104 {
105 index = GNUNET_new (uint32_t);
106 *index = i;
107 GNUNET_CONTAINER_multipeermap_put (view->mpm, &view->array[i], index,
108 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
109 }
110 }
111 GNUNET_assert (view->length == len);
112}
113
114
115/**
116 * Get the view as an array
117 *
118 * @param view The view of which the array representation is of interest
119 * @return the view in array representation
120 */
121const struct GNUNET_PeerIdentity *
122View_get_as_array (const struct View *view)
123{
124 return view->array;
125}
126
127
128/**
129 * Get the size of the view
130 *
131 * @param view The view of which the size should be returned
132 * @return current number of actually contained peers
133 */
134unsigned int
135View_size (const struct View *view)
136{
137 return GNUNET_CONTAINER_multipeermap_size (view->mpm);
138}
139
140
141/**
142 * Insert peer into the view
143 *
144 * @param view The view to put the peer into
145 * @param peer the peer to insert
146 *
147 * @return GNUNET_OK if peer was actually inserted
148 * GNUNET_NO if peer was not inserted
149 */
150int
151View_put (struct View *view,
152 const struct GNUNET_PeerIdentity *peer)
153{
154 uint32_t *index;
155
156 if ((view->length <= View_size (view)) || /* If array is 'full' */
157 (GNUNET_YES == View_contains_peer (view, peer)))
158 {
159 return GNUNET_NO;
160 }
161 else
162 {
163 index = GNUNET_new (uint32_t);
164 *index = (uint32_t) View_size (view);
165 view->array[*index] = *peer;
166 GNUNET_CONTAINER_multipeermap_put (view->mpm, peer, index,
167 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
168 return GNUNET_OK;
169 }
170}
171
172
173/**
174 * Check whether view contains a peer
175 *
176 * @param view The which is checked for a peer
177 * @param peer the peer to check for
178 *
179 * @return GNUNET_OK if view contains peer
180 * GNUNET_NO otherwise
181 */
182int
183View_contains_peer (const struct View *view,
184 const struct GNUNET_PeerIdentity *peer)
185{
186 return GNUNET_CONTAINER_multipeermap_contains (view->mpm, peer);
187}
188
189
190/**
191 * Remove peer from view
192 *
193 * @param view The view of which to remove the peer
194 * @param peer the peer to remove
195 *
196 * @return GNUNET_OK if view contained peer and removed it successfully
197 * GNUNET_NO if view does not contain peer
198 */
199int
200View_remove_peer (struct View *view,
201 const struct GNUNET_PeerIdentity *peer)
202{
203 uint32_t *index;
204 uint32_t *swap_index;
205 uint32_t last_index;
206
207 if (GNUNET_NO == View_contains_peer (view, peer))
208 {
209 return GNUNET_NO;
210 }
211 index = GNUNET_CONTAINER_multipeermap_get (view->mpm, peer);
212 GNUNET_assert (NULL != index);
213 last_index = View_size (view) - 1;
214 if (*index < last_index)
215 { /* Fill the 'gap' in the array with the last peer */
216 view->array[*index] = view->array[last_index];
217 GNUNET_assert (GNUNET_YES == View_contains_peer (view,
218 &view->array[last_index]));
219 swap_index = GNUNET_CONTAINER_multipeermap_get (view->mpm,
220 &view->array[last_index]);
221 GNUNET_assert (NULL != swap_index);
222 *swap_index = *index;
223 GNUNET_free (index);
224 }
225 GNUNET_CONTAINER_multipeermap_remove_all (view->mpm, peer);
226 return GNUNET_OK;
227}
228
229
230/**
231 * Get a peer by index
232 *
233 * @param view the view of which to get the peer
234 * @param index the index of the peer to get
235 *
236 * @return peer to the corresponding index.
237 * NULL if this index is not known
238 */
239const struct GNUNET_PeerIdentity *
240View_get_peer_by_index (const struct View *view,
241 uint32_t index)
242{
243 if (index < GNUNET_CONTAINER_multipeermap_size (view->mpm))
244 {
245 return &view->array[index];
246 }
247 else
248 {
249 return NULL;
250 }
251}
252
253
254/**
255 * Clear the view
256 *
257 * @param view The view to clear
258 */
259void
260View_clear (struct View *view)
261{
262 for (uint32_t i = 0; 0 < View_size (view); i++)
263 { /* Need to free indices stored at peers */
264 uint32_t *index;
265
266 GNUNET_assert (GNUNET_YES ==
267 GNUNET_CONTAINER_multipeermap_contains (view->mpm,
268 &view->array[i]));
269 index = GNUNET_CONTAINER_multipeermap_get (view->mpm, &view->array[i]);
270 GNUNET_assert (NULL != index);
271 GNUNET_free (index);
272 GNUNET_CONTAINER_multipeermap_remove_all (view->mpm, &view->array[i]);
273 }
274 GNUNET_assert (0 == View_size (view));
275}
276
277
278/**
279 * Destroy view.
280 *
281 * @param view the view to destroy
282 */
283void
284View_destroy (struct View *view)
285{
286 View_clear (view);
287 GNUNET_free (view->array);
288 view->array = NULL;
289 GNUNET_CONTAINER_multipeermap_destroy (view->mpm);
290 GNUNET_free (view);
291}
292
293
294/* end of gnunet-service-rps_view.c */
diff --git a/src/contrib/service/rps/gnunet-service-rps_view.h b/src/contrib/service/rps/gnunet-service-rps_view.h
new file mode 100644
index 000000000..4d42272c1
--- /dev/null
+++ b/src/contrib/service/rps/gnunet-service-rps_view.h
@@ -0,0 +1,145 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_view.h
23 * @brief wrapper around the "local view"
24 * @author Julius Bünger
25 */
26#include "gnunet_util_lib.h"
27#include <inttypes.h>
28
29struct View;
30
31/**
32 * Create an empty view.
33 *
34 * @param len the maximum length for the view
35 * @return The newly created view
36 */
37struct View *
38View_create (unsigned int len);
39
40
41/**
42 * Change length of view
43 *
44 * If size is decreased, peers with higher indices are removed.
45 *
46 * @param view The view that is changed
47 * @param len the (maximum) length for the view
48 */
49void
50View_change_len (struct View *view,
51 unsigned int len);
52
53/**
54 * Get the view as an array
55 *
56 * @return the view in array representation
57 */
58const struct GNUNET_PeerIdentity *
59View_get_as_array (const struct View *view);
60
61
62/**
63 * Get the size of the view
64 *
65 * @param view The view of which the size should be returned
66 * @return current number of actually contained peers
67 */
68unsigned int
69View_size (const struct View *view);
70
71
72/**
73 * Insert peer into the view
74 *
75 * @param view The view to put the peer into
76 * @param peer the peer to insert
77 *
78 * @return GNUNET_OK if peer was actually inserted
79 * GNUNET_NO if peer was not inserted
80 */
81int
82View_put (struct View *view,
83 const struct GNUNET_PeerIdentity *peer);
84
85
86/**
87 * Check whether view contains a peer
88 *
89 * @param view The which is checked for a peer
90 * @param peer the peer to check for
91 *
92 * @return GNUNET_OK if view contains peer
93 * GNUNET_NO otherwise
94 */
95int
96View_contains_peer (const struct View *view,
97 const struct GNUNET_PeerIdentity *peer);
98
99
100/**
101 * Remove peer from view
102 *
103 * @param view The view of which to remove the peer
104 * @param peer the peer to remove
105 *
106 * @return GNUNET_OK if view contained peer and removed it successfully
107 * GNUNET_NO if view does not contain peer
108 */
109int
110View_remove_peer (struct View *view,
111 const struct GNUNET_PeerIdentity *peer);
112
113
114/**
115 * Get a peer by index
116 *
117 * @param view the view of which to get the peer
118 * @param index the index of the peer to get
119 *
120 * @return peer to the corresponding index.
121 * NULL if this index is not known
122 */
123const struct GNUNET_PeerIdentity *
124View_get_peer_by_index (const struct View *view,
125 uint32_t index);
126
127
128/**
129 * Clear the view
130 *
131 * @param view The view to clear
132 */
133void
134View_clear (struct View *view);
135
136
137/**
138 * Destroy view.
139 *
140 * @param view the view to destroy
141 */
142void
143View_destroy (struct View *view);
144
145/* end of gnunet-service-rps_view.h */
diff --git a/src/contrib/service/rps/profiler_rps.conf b/src/contrib/service/rps/profiler_rps.conf
new file mode 100644
index 000000000..d23a6383d
--- /dev/null
+++ b/src/contrib/service/rps/profiler_rps.conf
@@ -0,0 +1,135 @@
1[rps]
2#PREFIX = valgrind --leak-check=full --show-leak-kinds=all --log-file=/tmp/rps/valgrind!gnunet-service-rps!%p
3#PREFIX = valgrind --log-file=/tmp/rps/valgrind!gnunet-service-rps!%p
4#PREFIX = valgrind
5UNIXPATH = $GNUNET_TMP/gnunet-service-rps.sock
6HOME = $SERVICEHOME
7# PORT = 2106
8#@UNIXONLY@ PORT = 2087
9IMMEDIATE_START = YES
10START_ON_DEMAND = NO
11NOARMBIND = YES
12#OPTIONS=-l /tmp/rps_profiler_logs/rps-[]-%Y-%m-%d.log
13
14# This is the timeinterval between the rounds
15ROUNDINTERVAL = 2 s
16FILENAME_VALID_PEERS = $GNUNET_DATA_HOME/rps/valid_peers.txt
17
18# This is the 'estimate' in the beginning.
19# This determines the size of the peers we keep in memory
20# until we receive the first estimate from NSE.
21# Keep in mind, that (networksize)^(1/3) should be enough.
22# So, 50 is enough for a network of size 50^3 = 125000
23MINSIZE = 4
24
25DESIRED_PROBABILITY = 0.75
26
27DEFICIENCY_FACTOR = 0.4
28
29
30[testbed]
31HOSTNAME = localhost
32
33# MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 100
34
35#OVERLAY_TOPOLOGY = CLIQUE
36
37#OVERLAY_TOPOLOGY = SCALE_FREE
38#SCALE_FREE_TOPOLOGY_CAP = 100
39#SCALE_FREE_TOPOLOGY_M = 2
40
41OVERLAY_TOPOLOGY = RANDOM
42## We take half of the links a complete graph would have, so (n * n-1)/4
43## for n = 128, this would be
44OVERLAY_RANDOM_LINKS = 4064
45
46#OVERLAY_TOPOLOGY = SMALL_WORLD
47#OVERLAY_RANDOM_LINKS = 25
48
49SETUP_TIMEOUT = 1 h
50
51[nse]
52WORKBITS = 0
53
54[nat]
55# Use addresses from the local network interfaces (including loopback, but also others)
56USE_LOCALADDR = YES
57ENABLE_UPNP = NO
58
59# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
60RETURN_LOCAL_ADDRESSES = YES
61
62[transport]
63PLUGINS = unix
64
65[ats]
66# Network specific inbound/outbound quotas
67UNSPECIFIED_QUOTA_IN = unlimited
68UNSPECIFIED_QUOTA_OUT = unlimited
69# LOOPBACK
70LOOPBACK_QUOTA_IN = unlimited
71LOOPBACK_QUOTA_OUT = unlimited
72# LAN
73LAN_QUOTA_IN = unlimited
74LAN_QUOTA_OUT = unlimited
75#WAN
76WAN_QUOTA_OUT = unlimited
77WAN_QUOTA_IN = unlimited
78# WLAN
79WLAN_QUOTA_IN = unlimited
80WLAN_QUOTA_OUT = unlimited
81# BLUETOOTH
82BLUETOOTH_QUOTA_IN = unlimited
83BLUETOOTH_QUOTA_OUT = unlimited
84
85[dht]
86DISABLE_TRY_CONNECT = YES
87
88[cadet]
89OPTIONS=-l /tmp/rps_profiler_logs/cadet-[]-%Y-%m-%d.log
90
91#[arm]
92#GLOBAL_POSTFIX=-l /tmp/rps_profiler_logs/other-[]-%Y-%m-%d.log
93
94#[statistics]
95#IMMEDIATE_START = NO
96#START_ON_DEMAND = NO
97
98[peerinfo]
99NO_IO = YES
100
101[hostlist]
102IMMEDIATE_START = NO
103START_ON_DEMAND = NO
104
105[zonemaster]
106IMMEDIATE_START = NO
107START_ON_DEMAND = NO
108
109[namecache]
110IMMEDIATE_START = NO
111START_ON_DEMAND = NO
112
113[namestore]
114IMMEDIATE_START = NO
115START_ON_DEMAND = NO
116
117[topology]
118IMMEDIATE_START = NO
119START_ON_DEMAND = NO
120
121[vpn]
122IMMEDIATE_START = NO
123START_ON_DEMAND = NO
124
125[revocation]
126IMMEDIATE_START = NO
127START_ON_DEMAND = NO
128
129[gns]
130IMMEDIATE_START = NO
131START_ON_DEMAND = NO
132
133[fs]
134IMMEDIATE_START = NO
135START_ON_DEMAND = NO
diff --git a/src/contrib/service/rps/rps-sampler_client.c b/src/contrib/service/rps/rps-sampler_client.c
new file mode 100644
index 000000000..f6e98ce29
--- /dev/null
+++ b/src/contrib/service/rps/rps-sampler_client.c
@@ -0,0 +1,439 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/gnunet-service-rps_sampler.c
23 * @brief sampler implementation
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "rps.h"
30
31#include "rps-sampler_common.h"
32#include "gnunet-service-rps_sampler.h"
33#include "gnunet-service-rps_sampler_elem.h"
34
35#include <math.h>
36#include <inttypes.h>
37
38#include "rps-test_util.h"
39
40#define LOG(kind, ...) GNUNET_log_from (kind, "rps-sampler", __VA_ARGS__)
41
42
43// multiple 'clients'?
44
45// TODO check for overflows
46
47// TODO align message structs
48
49// hist_size_init, hist_size_max
50
51/***********************************************************************
52* WARNING: This section needs to be reviewed regarding the use of
53* functions providing (pseudo)randomness!
54***********************************************************************/
55
56// TODO care about invalid input of the caller (size 0 or less...)
57
58/**
59 * @brief Callback called each time a new peer was put into the sampler
60 *
61 * @param cls A possibly given closure
62 */
63typedef void
64(*SamplerNotifyUpdateCB) (void *cls);
65
66/**
67 * @brief Context for a callback. Contains callback and closure.
68 *
69 * Meant to be an entry in an DLL.
70 */
71struct SamplerNotifyUpdateCTX
72{
73 /**
74 * @brief The Callback to call on updates
75 */
76 SamplerNotifyUpdateCB notify_cb;
77
78 /**
79 * @brief The according closure.
80 */
81 void *cls;
82
83 /**
84 * @brief Next element in DLL.
85 */
86 struct SamplerNotifyUpdateCTX *next;
87
88 /**
89 * @brief Previous element in DLL.
90 */
91 struct SamplerNotifyUpdateCTX *prev;
92};
93
94
95/**
96 * Type of function used to differentiate between modified and not modified
97 * Sampler.
98 */
99typedef void
100(*RPS_get_peers_type) (void *cls);
101
102
103/**
104 * Get one random peer out of the sampled peers.
105 *
106 * We might want to reinitialise this sampler after giving the
107 * corrsponding peer to the client.
108 */
109static void
110sampler_mod_get_rand_peer (void *cls);
111
112
113/**
114 * Closure to _get_n_rand_peers_ready_cb()
115 */
116struct RPS_SamplerRequestHandle
117{
118 /**
119 * DLL
120 */
121 struct RPS_SamplerRequestHandle *next;
122 struct RPS_SamplerRequestHandle *prev;
123
124 /**
125 * Number of peers we are waiting for.
126 */
127 uint32_t num_peers;
128
129 /**
130 * Number of peers we currently have.
131 */
132 uint32_t cur_num_peers;
133
134 /**
135 * Pointer to the array holding the ids.
136 */
137 struct GNUNET_PeerIdentity *ids;
138
139 /**
140 * Head and tail for the DLL to store the tasks for single requests
141 */
142 struct GetPeerCls *gpc_head;
143 struct GetPeerCls *gpc_tail;
144
145 /**
146 * Sampler.
147 */
148 struct RPS_Sampler *sampler;
149
150 /**
151 * Callback to be called when all ids are available.
152 */
153 RPS_sampler_n_rand_peers_ready_cb callback;
154
155 /**
156 * Closure given to the callback
157 */
158 void *cls;
159};
160
161
162/**
163 * Closure to _get_rand_peer_info()
164 */
165struct RPS_SamplerRequestHandleSingleInfo
166{
167 /**
168 * DLL
169 */
170 struct RPS_SamplerRequestHandleSingleInfo *next;
171 struct RPS_SamplerRequestHandleSingleInfo *prev;
172
173 /**
174 * Pointer to the id
175 */
176 struct GNUNET_PeerIdentity *id;
177
178 /**
179 * Head and tail for the DLL to store the tasks for single requests
180 */
181 struct GetPeerCls *gpc_head;
182 struct GetPeerCls *gpc_tail;
183
184 /**
185 * Sampler.
186 */
187 struct RPS_Sampler *sampler;
188
189 /**
190 * Callback to be called when all ids are available.
191 */
192 RPS_sampler_sinlge_info_ready_cb callback;
193
194 /**
195 * Closure given to the callback
196 */
197 void *cls;
198};
199
200
201///**
202// * Global sampler variable.
203// */
204// struct RPS_Sampler *sampler;
205
206
207/**
208 * The minimal size for the extended sampler elements.
209 */
210static size_t min_size;
211
212/**
213 * The maximal size the extended sampler elements should grow to.
214 */
215static size_t max_size;
216
217/**
218 * The size the extended sampler elements currently have.
219 */
220// static size_t extra_size;
221
222/**
223 * Inedex to the sampler element that is the next to be returned
224 */
225static uint32_t client_get_index;
226
227
228/**
229 * Initialise a modified tuple of sampler elements.
230 *
231 * @param init_size the size the sampler is initialised with
232 * @param max_round_interval maximum time a round takes
233 * @return a handle to a sampler that consists of sampler elements.
234 */
235struct RPS_Sampler *
236RPS_sampler_mod_init (size_t init_size,
237 struct GNUNET_TIME_Relative max_round_interval)
238{
239 struct RPS_Sampler *sampler;
240
241 /* Initialise context around extended sampler */
242 min_size = 10; // TODO make input to _samplers_init()
243 max_size = 1000; // TODO make input to _samplers_init()
244
245 sampler = GNUNET_new (struct RPS_Sampler);
246 sampler->max_round_interval = max_round_interval;
247 sampler->get_peers = sampler_mod_get_rand_peer;
248 // sampler->sampler_elements = GNUNET_new_array(init_size, struct GNUNET_PeerIdentity);
249 // GNUNET_array_grow (sampler->sampler_elements, sampler->sampler_size, min_size);
250
251 client_get_index = 0;
252
253 // GNUNET_assert (init_size == sampler->sampler_size);
254
255 RPS_sampler_resize (sampler, init_size);
256
257 return sampler;
258}
259
260
261// /**
262// * @brief Compute the probability that we already observed all peers from a
263// * biased stream of peer ids.
264// *
265// * Deficiency factor:
266// * As introduced by Brahms: Factor between the number of unique ids in a
267// * truly random stream and number of unique ids in the gossip stream.
268// *
269// * @param num_peers_estim The estimated number of peers in the network
270// * @param num_peers_observed The number of peers the given element has observed
271// * @param deficiency_factor A factor that catches the 'bias' of a random stream
272// * of peer ids
273// *
274// * @return The estimated probability
275// */
276// static double
277// prob_observed_n_peers (uint32_t num_peers_estim,
278// uint32_t num_peers_observed,
279// double deficiency_factor)
280// {
281// uint32_t num_peers = num_peers_estim * (1 / deficiency_factor);
282// uint64_t sum = 0;
283//
284// for (uint32_t i = 0; i < num_peers; i++)
285// {
286// uint64_t a = pow (-1, num_peers - i);
287// uint64_t b = binom (num_peers, i);
288// uint64_t c = pow (i, num_peers_observed);
289// sum += a * b * c;
290// }
291//
292// return sum / (double) pow (num_peers, num_peers_observed);
293// }
294
295
296/**
297 * Get one random peer out of the sampled peers.
298 *
299 * This reinitialises the queried sampler element.
300 */
301static void
302sampler_mod_get_rand_peer (void *cls)
303{
304 struct GetPeerCls *gpc = cls;
305 struct RPS_SamplerElement *s_elem;
306 struct GNUNET_TIME_Relative last_request_diff;
307 struct RPS_Sampler *sampler;
308 double prob_observed_n;
309 uint32_t num_observed;
310
311 gpc->get_peer_task = NULL;
312 gpc->notify_ctx = NULL;
313 GNUNET_assert ((NULL != gpc->req_handle) ||
314 (NULL != gpc->req_single_info_handle));
315 if (NULL != gpc->req_handle)
316 sampler = gpc->req_handle->sampler;
317 else
318 sampler = gpc->req_single_info_handle->sampler;
319
320 LOG (GNUNET_ERROR_TYPE_DEBUG, "Single peer was requested\n");
321
322 /* Cycle the #client_get_index one step further */
323 client_get_index = (client_get_index + 1) % sampler->sampler_size;
324
325 s_elem = sampler->sampler_elements[client_get_index];
326 *gpc->id = s_elem->peer_id;
327 GNUNET_assert (NULL != s_elem);
328
329 if (EMPTY == s_elem->is_empty)
330 {
331 LOG (GNUNET_ERROR_TYPE_DEBUG,
332 "Sampler_mod element empty, rescheduling.\n");
333 GNUNET_assert (NULL == gpc->notify_ctx);
334 gpc->notify_ctx =
335 sampler_notify_on_update (sampler,
336 &sampler_mod_get_rand_peer,
337 gpc);
338 return;
339 }
340
341 /* Check whether we may use this sampler to give it back to the client */
342 if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
343 s_elem->last_client_request.abs_value_us)
344 {
345 // TODO remove this condition at least for the client sampler
346 last_request_diff =
347 GNUNET_TIME_absolute_get_difference (s_elem->last_client_request,
348 GNUNET_TIME_absolute_get ());
349 /* We're not going to give it back now if it was
350 * already requested by a client this round */
351 if (last_request_diff.rel_value_us <
352 sampler->max_round_interval.rel_value_us)
353 {
354 LOG (GNUNET_ERROR_TYPE_DEBUG,
355 "Last client request on this sampler was less than max round interval ago -- scheduling for later\n");
356 ///* How many time remains untile the next round has started? */
357 // inv_last_request_diff =
358 // GNUNET_TIME_absolute_get_difference (last_request_diff,
359 // sampler->max_round_interval);
360 // add a little delay
361 /* Schedule it one round later */
362 GNUNET_assert (NULL == gpc->notify_ctx);
363 gpc->notify_ctx =
364 sampler_notify_on_update (sampler,
365 &sampler_mod_get_rand_peer,
366 gpc);
367 return;
368 }
369 }
370 if (2 > s_elem->num_peers)
371 {
372 LOG (GNUNET_ERROR_TYPE_DEBUG,
373 "This s_elem saw less than two peers -- scheduling for later\n");
374 GNUNET_assert (NULL == gpc->notify_ctx);
375 gpc->notify_ctx =
376 sampler_notify_on_update (sampler,
377 &sampler_mod_get_rand_peer,
378 gpc);
379 return;
380 }
381 /* compute probability */
382 /* FIXME: Currently disabled due to numerical limitations */
383 prob_observed_n = 0; // Inititialise to some value
384 // prob_observed_n = prob_observed_n_peers (sampler->num_peers_estim,
385 // s_elem->num_peers,
386 // sampler->deficiency_factor);
387 // LOG (GNUNET_ERROR_TYPE_DEBUG,
388 // "Computed sample - prob %f, %" PRIu32 " peers, n: %" PRIu32 ", roh: %f\n",
389 // prob_observed_n,
390 // s_elem->num_peers,
391 // sampler->num_peers_estim,
392 // sampler->deficiency_factor);
393 ///* check if probability is above desired */
394 // if (prob_observed_n < sampler->desired_probability)
395 // {
396 // LOG (GNUNET_ERROR_TYPE_DEBUG,
397 // "Probability of having observed all peers (%f) too small ( < %f).\n",
398 // prob_observed_n,
399 // sampler->desired_probability);
400 // GNUNET_assert (NULL == gpc->notify_ctx);
401 // gpc->notify_ctx =
402 // sampler_notify_on_update (sampler,
403 // &sampler_mod_get_rand_peer,
404 // gpc);
405 // return;
406 // }
407 /* More reasons to wait could be added here */
408
409// GNUNET_STATISTICS_set (stats,
410// "# client sampler element input",
411// s_elem->num_peers,
412// GNUNET_NO);
413// GNUNET_STATISTICS_set (stats,
414// "# client sampler element change",
415// s_elem->num_change,
416// GNUNET_NO);
417
418 num_observed = s_elem->num_peers;
419 RPS_sampler_elem_reinit (s_elem);
420 s_elem->last_client_request = GNUNET_TIME_absolute_get ();
421
422 if (NULL != gpc->req_handle)
423 {
424 GNUNET_CONTAINER_DLL_remove (gpc->req_handle->gpc_head,
425 gpc->req_handle->gpc_tail,
426 gpc);
427 }
428 else
429 {
430 GNUNET_CONTAINER_DLL_remove (gpc->req_single_info_handle->gpc_head,
431 gpc->req_single_info_handle->gpc_tail,
432 gpc);
433 }
434 gpc->cont (gpc->cont_cls, gpc->id, prob_observed_n, num_observed);
435 GNUNET_free (gpc);
436}
437
438
439/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/rps-sampler_client.h b/src/contrib/service/rps/rps-sampler_client.h
new file mode 100644
index 000000000..7a674371d
--- /dev/null
+++ b/src/contrib/service/rps/rps-sampler_client.h
@@ -0,0 +1,156 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps-sampler_client.h
23 * @brief client sampler implementation
24 * @author Julius Bünger
25 */
26
27#ifndef RPS_SAMPLER_CLIENT_H
28#define RPS_SAMPLER_CLIENT_H
29#include <inttypes.h>
30#include "rps-sampler_common.h"
31
32
33/**
34 * A sampler sampling a stream of PeerIDs.
35 */
36struct RPS_Sampler;
37
38/**
39 * A handle to cancel a request.
40 */
41struct RPS_SamplerRequestHandle;
42
43/**
44 * Closure to _get_rand_peer_info()
45 */
46struct RPS_SamplerRequestHandleSingleInfo;
47
48
49/**
50 * Get the size of the sampler.
51 *
52 * @param sampler the sampler to return the size of.
53 * @return the size of the sampler
54 */
55unsigned int
56RPS_sampler_get_size (struct RPS_Sampler *sampler);
57
58
59/**
60 * Grow or shrink the size of the sampler.
61 *
62 * @param sampler the sampler to resize.
63 * @param new_size the new size of the sampler (not 0)
64 */
65void
66RPS_sampler_resize (struct RPS_Sampler *sampler, unsigned int new_size);
67
68
69/**
70 * Initialise a modified tuple of sampler elements.
71 *
72 * @param init_size the size the sampler is initialised with
73 * @param max_round_interval maximum time a round takes
74 * @return a handle to a sampler that consists of sampler elements.
75 */
76struct RPS_Sampler *
77RPS_sampler_mod_init (size_t init_size,
78 struct GNUNET_TIME_Relative max_round_interval);
79
80
81/**
82 * Update every sampler element of this sampler with given peer
83 *
84 * @param sampler the sampler to update.
85 * @param id the PeerID that is put in the sampler
86 */
87void
88RPS_sampler_update (struct RPS_Sampler *sampler,
89 const struct GNUNET_PeerIdentity *id);
90
91
92/**
93 * Reinitialise all previously initialised sampler elements with the given
94 * value.
95 *
96 * Used to get rid of a PeerID.
97 *
98 * @param sampler the sampler to reinitialise a sampler in.
99 * @param id the id of the samplers to update.
100 */
101void
102RPS_sampler_reinitialise_by_value (struct RPS_Sampler *sampler,
103 const struct GNUNET_PeerIdentity *id);
104
105
106/**
107 * Get n random peers out of the sampled peers.
108 *
109 * We might want to reinitialise this sampler after giving the
110 * corrsponding peer to the client.
111 * Random with or without consumption?
112 *
113 * @param sampler the sampler to get peers from.
114 * @param cb callback that will be called once the ids are ready.
115 * @param cls closure given to @a cb
116 * @param num_peers the number of peers requested
117 */
118struct RPS_SamplerRequestHandle *
119RPS_sampler_get_n_rand_peers (struct RPS_Sampler *sampler,
120 uint32_t num_peers,
121 RPS_sampler_n_rand_peers_ready_cb cb,
122 void *cls);
123
124
125/**
126 * Cancel a request issued through #RPS_sampler_n_rand_peers_ready_cb.
127 *
128 * @param req_handle the handle to the request
129 */
130void
131RPS_sampler_request_cancel (struct RPS_SamplerRequestHandle *req_handle);
132
133
134/**
135 * Counts how many Samplers currently hold a given PeerID.
136 *
137 * @param sampler the sampler to count ids in.
138 * @param id the PeerID to count.
139 *
140 * @return the number of occurrences of id.
141 */
142uint32_t
143RPS_sampler_count_id (struct RPS_Sampler *sampler,
144 const struct GNUNET_PeerIdentity *id);
145
146
147/**
148 * Cleans the samplers.
149 *
150 * @param sampler the sampler to destroy.
151 */
152void
153RPS_sampler_destroy (struct RPS_Sampler *sampler);
154
155#endif /* RPS_SAMPLER_CLIENT_H */
156/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/rps-sampler_common.c b/src/contrib/service/rps/rps-sampler_common.c
new file mode 100644
index 000000000..e3fb79501
--- /dev/null
+++ b/src/contrib/service/rps/rps-sampler_common.c
@@ -0,0 +1,742 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps-sampler_common.c
23 * @brief Code common to client and service sampler
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29
30#include "rps-sampler_common.h"
31#include "gnunet-service-rps_sampler_elem.h"
32
33#include <math.h>
34#include <inttypes.h>
35
36#include "rps-test_util.h"
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "rps-sampler_common", __VA_ARGS__)
39
40/**
41 * @brief Context for a callback. Contains callback and closure.
42 *
43 * Meant to be an entry in an DLL.
44 */
45struct SamplerNotifyUpdateCTX
46{
47 /**
48 * @brief The Callback to call on updates
49 */
50 SamplerNotifyUpdateCB notify_cb;
51
52 /**
53 * @brief The according closure.
54 */
55 void *cls;
56
57 /**
58 * @brief Next element in DLL.
59 */
60 struct SamplerNotifyUpdateCTX *next;
61
62 /**
63 * @brief Previous element in DLL.
64 */
65 struct SamplerNotifyUpdateCTX *prev;
66};
67
68
69/**
70 * Closure to _get_n_rand_peers_ready_cb()
71 */
72struct RPS_SamplerRequestHandle
73{
74 /**
75 * DLL
76 */
77 struct RPS_SamplerRequestHandle *next;
78 struct RPS_SamplerRequestHandle *prev;
79
80 /**
81 * Number of peers we are waiting for.
82 */
83 uint32_t num_peers;
84
85 /**
86 * Number of peers we currently have.
87 */
88 uint32_t cur_num_peers;
89
90 /**
91 * Pointer to the array holding the ids.
92 */
93 struct GNUNET_PeerIdentity *ids;
94
95 /**
96 * Head and tail for the DLL to store the tasks for single requests
97 */
98 struct GetPeerCls *gpc_head;
99 struct GetPeerCls *gpc_tail;
100
101 /**
102 * Sampler.
103 */
104 struct RPS_Sampler *sampler;
105
106 /**
107 * Callback to be called when all ids are available.
108 */
109 RPS_sampler_n_rand_peers_ready_cb callback;
110
111 /**
112 * Closure given to the callback
113 */
114 void *cls;
115};
116
117
118/**
119 * Closure to _get_rand_peer_info()
120 */
121struct RPS_SamplerRequestHandleSingleInfo
122{
123 /**
124 * DLL
125 */
126 struct RPS_SamplerRequestHandleSingleInfo *next;
127 struct RPS_SamplerRequestHandleSingleInfo *prev;
128
129 /**
130 * Pointer to the id
131 */
132 struct GNUNET_PeerIdentity *id;
133
134 /**
135 * Head and tail for the DLL to store the tasks for single requests
136 */
137 struct GetPeerCls *gpc_head;
138 struct GetPeerCls *gpc_tail;
139
140 /**
141 * Sampler.
142 */
143 struct RPS_Sampler *sampler;
144
145 /**
146 * Callback to be called when all ids are available.
147 */
148 RPS_sampler_sinlge_info_ready_cb callback;
149
150 /**
151 * Closure given to the callback
152 */
153 void *cls;
154};
155
156
157/**
158 * @brief Update the current estimate of the network size stored at the sampler
159 *
160 * Used for computing the condition when to return elements to the client
161 *
162 * Only used/useful with the client sampler
163 * (Maybe move to rps-sampler_client.{h|c} ?)
164 *
165 * @param sampler The sampler to update
166 * @param num_peers The estimated value
167 */
168void
169RPS_sampler_update_with_nw_size (struct RPS_Sampler *sampler,
170 uint32_t num_peers)
171{
172 sampler->num_peers_estim = num_peers;
173}
174
175
176/**
177 * @brief Set the probability that is needed at least with what a sampler
178 * element has to have observed all elements from the network.
179 *
180 * Only used/useful with the client sampler
181 * (Maybe move to rps-sampler_client.{h|c} ?)
182 *
183 * @param sampler
184 * @param desired_probability
185 */
186void
187RPS_sampler_set_desired_probability (struct RPS_Sampler *sampler,
188 double desired_probability)
189{
190 sampler->desired_probability = desired_probability;
191}
192
193
194void
195RPS_sampler_set_deficiency_factor (struct RPS_Sampler *sampler,
196 double deficiency_factor)
197{
198 sampler->deficiency_factor = deficiency_factor;
199}
200
201
202/**
203 * @brief Add a callback that will be called when the next peer is inserted
204 * into the sampler
205 *
206 * @param sampler The sampler on which update it will be called
207 * @param notify_cb The callback
208 * @param cls Closure given to the callback
209 *
210 * @return The context containing callback and closure
211 */
212struct SamplerNotifyUpdateCTX *
213sampler_notify_on_update (struct RPS_Sampler *sampler,
214 SamplerNotifyUpdateCB notify_cb,
215 void *cls)
216{
217 struct SamplerNotifyUpdateCTX *notify_ctx;
218
219 LOG (GNUNET_ERROR_TYPE_DEBUG,
220 "Inserting new context for notification\n");
221 notify_ctx = GNUNET_new (struct SamplerNotifyUpdateCTX);
222 notify_ctx->notify_cb = notify_cb;
223 notify_ctx->cls = cls;
224 GNUNET_CONTAINER_DLL_insert (sampler->notify_ctx_head,
225 sampler->notify_ctx_tail,
226 notify_ctx);
227 return notify_ctx;
228}
229
230
231/**
232 * Get the size of the sampler.
233 *
234 * @param sampler the sampler to return the size of.
235 * @return the size of the sampler
236 */
237unsigned int
238RPS_sampler_get_size (struct RPS_Sampler *sampler)
239{
240 return sampler->sampler_size;
241}
242
243
244/**
245 * @brief Notify about update of the sampler.
246 *
247 * Call the callbacks that are waiting for notification on updates to the
248 * sampler.
249 *
250 * @param sampler The sampler the updates are waiting for
251 */
252static void
253notify_update (struct RPS_Sampler *sampler)
254{
255 struct SamplerNotifyUpdateCTX *tmp_notify_head;
256 struct SamplerNotifyUpdateCTX *tmp_notify_tail;
257
258 LOG (GNUNET_ERROR_TYPE_DEBUG,
259 "Calling callbacks waiting for update notification.\n");
260 tmp_notify_head = sampler->notify_ctx_head;
261 tmp_notify_tail = sampler->notify_ctx_tail;
262 sampler->notify_ctx_head = NULL;
263 sampler->notify_ctx_tail = NULL;
264 for (struct SamplerNotifyUpdateCTX *notify_iter = tmp_notify_head;
265 NULL != tmp_notify_head;
266 notify_iter = tmp_notify_head)
267 {
268 GNUNET_assert (NULL != notify_iter->notify_cb);
269 GNUNET_CONTAINER_DLL_remove (tmp_notify_head,
270 tmp_notify_tail,
271 notify_iter);
272 notify_iter->notify_cb (notify_iter->cls);
273 GNUNET_free (notify_iter);
274 }
275}
276
277
278/**
279 * Update every sampler element of this sampler with given peer
280 *
281 * @param sampler the sampler to update.
282 * @param id the PeerID that is put in the sampler
283 */
284void
285RPS_sampler_update (struct RPS_Sampler *sampler,
286 const struct GNUNET_PeerIdentity *id)
287{
288 for (uint32_t i = 0; i < sampler->sampler_size; i++)
289 {
290 RPS_sampler_elem_next (sampler->sampler_elements[i],
291 id);
292 }
293 notify_update (sampler);
294}
295
296
297/**
298 * Reinitialise all previously initialised sampler elements with the given value.
299 *
300 * Used to get rid of a PeerID.
301 *
302 * FIXME: This should also consider currently pending requests
303 * (Pending requests already collect peerids. As long as not all
304 * requested IDs have been collected, they are kept.
305 * Ideally, the @p id should be removed from all pending requests. This
306 * seems quite complicated.)
307 *
308 * @param sampler the sampler to reinitialise a sampler element in.
309 * @param id the id of the sampler elements to update.
310 */
311void
312RPS_sampler_reinitialise_by_value (struct RPS_Sampler *sampler,
313 const struct GNUNET_PeerIdentity *id)
314{
315 uint32_t i;
316
317 for (i = 0; i < sampler->sampler_size; i++)
318 {
319 if (0 == GNUNET_memcmp (id,
320 &(sampler->sampler_elements[i]->peer_id)))
321 {
322 LOG (GNUNET_ERROR_TYPE_DEBUG, "Reinitialising sampler\n");
323 RPS_sampler_elem_reinit (sampler->sampler_elements[i]);
324 }
325 }
326}
327
328
329/**
330 * Counts how many Samplers currently hold a given PeerID.
331 *
332 * @param sampler the sampler to count ids in.
333 * @param id the PeerID to count.
334 *
335 * @return the number of occurrences of id.
336 */
337uint32_t
338RPS_sampler_count_id (struct RPS_Sampler *sampler,
339 const struct GNUNET_PeerIdentity *id)
340{
341 uint32_t count;
342 uint32_t i;
343
344 count = 0;
345 for (i = 0; i < sampler->sampler_size; i++)
346 {
347 if ((0 == GNUNET_memcmp (&sampler->sampler_elements[i]->peer_id, id))
348 && (EMPTY != sampler->sampler_elements[i]->is_empty) )
349 count++;
350 }
351 return count;
352}
353
354
355/**
356 * Grow or shrink the size of the sampler.
357 *
358 * @param sampler the sampler to resize.
359 * @param new_size the new size of the sampler
360 */
361static void
362sampler_resize (struct RPS_Sampler *sampler, unsigned int new_size)
363{
364 unsigned int old_size;
365 uint32_t i;
366
367 // TODO check min and max size
368
369 old_size = sampler->sampler_size;
370
371 if (old_size > new_size)
372 { /* Shrinking */
373 LOG (GNUNET_ERROR_TYPE_DEBUG,
374 "Shrinking sampler %d -> %d\n",
375 old_size,
376 new_size);
377
378 for (i = new_size; i < old_size; i++)
379 {
380 RPS_sampler_elem_destroy (sampler->sampler_elements[i]);
381 }
382
383 GNUNET_array_grow (sampler->sampler_elements,
384 sampler->sampler_size,
385 new_size);
386 LOG (GNUNET_ERROR_TYPE_DEBUG,
387 "sampler->sampler_elements now points to %p\n",
388 sampler->sampler_elements);
389 }
390 else if (old_size < new_size)
391 { /* Growing */
392 LOG (GNUNET_ERROR_TYPE_DEBUG,
393 "Growing sampler %d -> %d\n",
394 old_size,
395 new_size);
396
397 GNUNET_array_grow (sampler->sampler_elements,
398 sampler->sampler_size,
399 new_size);
400
401 for (i = old_size; i < new_size; i++)
402 { /* Add new sampler elements */
403 sampler->sampler_elements[i] = RPS_sampler_elem_create ();
404 }
405 }
406 else
407 {
408 LOG (GNUNET_ERROR_TYPE_DEBUG, "Size remains the same -- nothing to do\n");
409 return;
410 }
411
412 GNUNET_assert (sampler->sampler_size == new_size);
413}
414
415
416/**
417 * Grow or shrink the size of the sampler.
418 *
419 * @param sampler the sampler to resize.
420 * @param new_size the new size of the sampler
421 */
422void
423RPS_sampler_resize (struct RPS_Sampler *sampler, unsigned int new_size)
424{
425 GNUNET_assert (0 < new_size);
426 sampler_resize (sampler, new_size);
427}
428
429
430/**
431 * Empty the sampler.
432 *
433 * @param sampler the sampler to empty.
434 */
435static void
436sampler_empty (struct RPS_Sampler *sampler)
437{
438 sampler_resize (sampler, 0);
439}
440
441
442/**
443 * Callback to _get_rand_peer() used by _get_n_rand_peers().
444 *
445 * Implements #RPS_sampler_rand_peer_ready_cont
446 *
447 * Checks whether all n peers are available. If they are,
448 * give those back.
449 * @param cls Closure
450 * @param id Peer ID
451 * @param probability The probability with which this sampler has seen all ids
452 * @param num_observed How many ids this sampler has observed
453 */
454static void
455check_n_peers_ready (void *cls,
456 const struct GNUNET_PeerIdentity *id,
457 double probability,
458 uint32_t num_observed)
459{
460 struct RPS_SamplerRequestHandle *req_handle = cls;
461
462 (void) id;
463 RPS_sampler_n_rand_peers_ready_cb tmp_cb;
464 struct GNUNET_PeerIdentity *peers;
465 uint32_t num_peers;
466 void *cb_cls;
467 (void) probability;
468 (void) num_observed;
469
470 req_handle->cur_num_peers++;
471 LOG (GNUNET_ERROR_TYPE_DEBUG,
472 "Got %" PRIX32 ". of %" PRIX32 " peers\n",
473 req_handle->cur_num_peers, req_handle->num_peers);
474
475 if (req_handle->num_peers == req_handle->cur_num_peers)
476 { /* All peers are ready -- return those to the client */
477 GNUNET_assert (NULL != req_handle->callback);
478
479 LOG (GNUNET_ERROR_TYPE_DEBUG,
480 "returning %" PRIX32 " peers to the client\n",
481 req_handle->num_peers);
482
483 /* Copy pointers and peers temporarily as they
484 * might be deleted from within the callback */
485 tmp_cb = req_handle->callback;
486 num_peers = req_handle->num_peers;
487 peers = GNUNET_new_array (num_peers, struct GNUNET_PeerIdentity);
488 GNUNET_memcpy (peers,
489 req_handle->ids,
490 num_peers * sizeof(struct GNUNET_PeerIdentity));
491 cb_cls = req_handle->cls;
492 RPS_sampler_request_cancel (req_handle);
493 req_handle = NULL;
494 tmp_cb (peers, num_peers, cb_cls);
495 GNUNET_free (peers);
496 }
497}
498
499
500/**
501 * Callback to _get_rand_peer() used by _get_rand_peer_info().
502 *
503 * Implements #RPS_sampler_rand_peer_ready_cont
504 *
505 * @param cls Closure
506 * @param id Peer ID
507 * @param probability The probability with which this sampler has seen all ids
508 * @param num_observed How many ids this sampler has observed
509 */
510static void
511check_peer_info_ready (void *cls,
512 const struct GNUNET_PeerIdentity *id,
513 double probability,
514 uint32_t num_observed)
515{
516 struct RPS_SamplerRequestHandleSingleInfo *req_handle = cls;
517
518 (void) id;
519 RPS_sampler_sinlge_info_ready_cb tmp_cb;
520 struct GNUNET_PeerIdentity *peer;
521 void *cb_cls;
522 (void) probability;
523 (void) num_observed;
524
525 LOG (GNUNET_ERROR_TYPE_DEBUG,
526 "Got single peer with additional info\n");
527
528 GNUNET_assert (NULL != req_handle->callback);
529
530 LOG (GNUNET_ERROR_TYPE_DEBUG,
531 "returning single peer with info to the client\n");
532
533 /* Copy pointers and peers temporarily as they
534 * might be deleted from within the callback */
535 tmp_cb = req_handle->callback;
536 peer = GNUNET_new (struct GNUNET_PeerIdentity);
537 GNUNET_memcpy (peer,
538 req_handle->id,
539 sizeof(struct GNUNET_PeerIdentity));
540 cb_cls = req_handle->cls;
541 RPS_sampler_request_single_info_cancel (req_handle);
542 req_handle = NULL;
543 tmp_cb (peer, cb_cls, probability, num_observed);
544 GNUNET_free (peer);
545}
546
547
548struct RPS_SamplerRequestHandle *
549RPS_sampler_get_n_rand_peers (struct RPS_Sampler *sampler,
550 uint32_t num_peers,
551 RPS_sampler_n_rand_peers_ready_cb cb,
552 void *cls)
553{
554 uint32_t i;
555 struct RPS_SamplerRequestHandle *req_handle;
556 struct GetPeerCls *gpc;
557
558 GNUNET_assert (0 != sampler->sampler_size);
559 if (0 == num_peers)
560 return NULL;
561
562 // TODO check if we have too much (distinct) sampled peers
563 req_handle = GNUNET_new (struct RPS_SamplerRequestHandle);
564 req_handle->num_peers = num_peers;
565 req_handle->cur_num_peers = 0;
566 req_handle->ids = GNUNET_new_array (num_peers, struct GNUNET_PeerIdentity);
567 req_handle->sampler = sampler;
568 req_handle->callback = cb;
569 req_handle->cls = cls;
570 GNUNET_CONTAINER_DLL_insert (sampler->req_handle_head,
571 sampler->req_handle_tail,
572 req_handle);
573
574 LOG (GNUNET_ERROR_TYPE_DEBUG,
575 "Scheduling requests for %" PRIu32 " peers\n", num_peers);
576
577 for (i = 0; i < num_peers; i++)
578 {
579 gpc = GNUNET_new (struct GetPeerCls);
580 gpc->req_handle = req_handle;
581 gpc->req_single_info_handle = NULL;
582 gpc->cont = check_n_peers_ready;
583 gpc->cont_cls = req_handle;
584 gpc->id = &req_handle->ids[i];
585
586 GNUNET_CONTAINER_DLL_insert (req_handle->gpc_head,
587 req_handle->gpc_tail,
588 gpc);
589 // maybe add a little delay
590 gpc->get_peer_task = GNUNET_SCHEDULER_add_now (sampler->get_peers,
591 gpc);
592 }
593 return req_handle;
594}
595
596
597/**
598 * Get one random peer with additional information.
599 *
600 * @param sampler the sampler to get peers from.
601 * @param cb callback that will be called once the ids are ready.
602 * @param cls closure given to @a cb
603 */
604struct RPS_SamplerRequestHandleSingleInfo *
605RPS_sampler_get_rand_peer_info (struct RPS_Sampler *sampler,
606 RPS_sampler_sinlge_info_ready_cb cb,
607 void *cls)
608{
609 struct RPS_SamplerRequestHandleSingleInfo *req_handle;
610 struct GetPeerCls *gpc;
611
612 GNUNET_assert (0 != sampler->sampler_size);
613
614 // TODO check if we have too much (distinct) sampled peers
615 req_handle = GNUNET_new (struct RPS_SamplerRequestHandleSingleInfo);
616 req_handle->id = GNUNET_malloc (sizeof(struct GNUNET_PeerIdentity));
617 req_handle->sampler = sampler;
618 req_handle->callback = cb;
619 req_handle->cls = cls;
620 GNUNET_CONTAINER_DLL_insert (sampler->req_handle_single_head,
621 sampler->req_handle_single_tail,
622 req_handle);
623
624 gpc = GNUNET_new (struct GetPeerCls);
625 gpc->req_handle = NULL;
626 gpc->req_single_info_handle = req_handle;
627 gpc->cont = check_peer_info_ready;
628 gpc->cont_cls = req_handle;
629 gpc->id = req_handle->id;
630
631 GNUNET_CONTAINER_DLL_insert (req_handle->gpc_head,
632 req_handle->gpc_tail,
633 gpc);
634 // maybe add a little delay
635 gpc->get_peer_task = GNUNET_SCHEDULER_add_now (sampler->get_peers,
636 gpc);
637 return req_handle;
638}
639
640
641/**
642 * Cancel a request issued through #RPS_sampler_n_rand_peers_ready_cb.
643 *
644 * @param req_handle the handle to the request
645 */
646void
647RPS_sampler_request_cancel (struct RPS_SamplerRequestHandle *req_handle)
648{
649 struct GetPeerCls *i;
650
651 while (NULL != (i = req_handle->gpc_head))
652 {
653 GNUNET_CONTAINER_DLL_remove (req_handle->gpc_head,
654 req_handle->gpc_tail,
655 i);
656 if (NULL != i->get_peer_task)
657 {
658 GNUNET_SCHEDULER_cancel (i->get_peer_task);
659 }
660 if (NULL != i->notify_ctx)
661 {
662 GNUNET_CONTAINER_DLL_remove (req_handle->sampler->notify_ctx_head,
663 req_handle->sampler->notify_ctx_tail,
664 i->notify_ctx);
665 GNUNET_free (i->notify_ctx);
666 i->notify_ctx = NULL;
667 }
668 GNUNET_free (i);
669 }
670 GNUNET_free (req_handle->ids);
671 req_handle->ids = NULL;
672 GNUNET_CONTAINER_DLL_remove (req_handle->sampler->req_handle_head,
673 req_handle->sampler->req_handle_tail,
674 req_handle);
675 GNUNET_free (req_handle);
676}
677
678
679/**
680 * Cancel a request issued through #RPS_sampler_sinlge_info_ready_cb.
681 *
682 * @param req_handle the handle to the request
683 */
684void
685RPS_sampler_request_single_info_cancel (
686 struct RPS_SamplerRequestHandleSingleInfo *req_single_info_handle)
687{
688 struct GetPeerCls *i;
689
690 while (NULL != (i = req_single_info_handle->gpc_head))
691 {
692 GNUNET_CONTAINER_DLL_remove (req_single_info_handle->gpc_head,
693 req_single_info_handle->gpc_tail,
694 i);
695 if (NULL != i->get_peer_task)
696 {
697 GNUNET_SCHEDULER_cancel (i->get_peer_task);
698 }
699 if (NULL != i->notify_ctx)
700 {
701 GNUNET_CONTAINER_DLL_remove (
702 req_single_info_handle->sampler->notify_ctx_head,
703 req_single_info_handle->sampler->
704 notify_ctx_tail,
705 i->notify_ctx);
706 GNUNET_free (i->notify_ctx);
707 i->notify_ctx = NULL;
708 }
709 GNUNET_free (i);
710 }
711 GNUNET_free (req_single_info_handle->id);
712 req_single_info_handle->id = NULL;
713 GNUNET_CONTAINER_DLL_remove (
714 req_single_info_handle->sampler->req_handle_single_head,
715 req_single_info_handle->sampler->
716 req_handle_single_tail,
717 req_single_info_handle);
718 GNUNET_free (req_single_info_handle);
719}
720
721
722/**
723 * Cleans the sampler.
724 */
725void
726RPS_sampler_destroy (struct RPS_Sampler *sampler)
727{
728 if (NULL != sampler->req_handle_head)
729 {
730 LOG (GNUNET_ERROR_TYPE_WARNING,
731 "There are still pending requests. Going to remove them.\n");
732 while (NULL != sampler->req_handle_head)
733 {
734 RPS_sampler_request_cancel (sampler->req_handle_head);
735 }
736 }
737 sampler_empty (sampler);
738 GNUNET_free (sampler);
739}
740
741
742/* end of rps-sampler_common.c */
diff --git a/src/contrib/service/rps/rps-sampler_common.h b/src/contrib/service/rps/rps-sampler_common.h
new file mode 100644
index 000000000..94727028a
--- /dev/null
+++ b/src/contrib/service/rps/rps-sampler_common.h
@@ -0,0 +1,389 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps-sampler_common.h
23 * @brief Code common to client and service sampler
24 * @author Julius Bünger
25 */
26
27#ifndef RPS_SAMPLER_COMMON_H
28#define RPS_SAMPLER_COMMON_H
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_statistics_service.h"
33
34#include "gnunet-service-rps_sampler_elem.h"
35
36#include <math.h>
37#include <inttypes.h>
38
39#include "rps-test_util.h"
40
41
42/**
43 * Callback that is called from _get_rand_peer() when the PeerID is ready.
44 *
45 * @param cls the closure given alongside this function.
46 * @param id the PeerID that was returned
47 * @param probability The probability with which this sampler has seen all ids
48 * @param num_observed How many ids this sampler has observed
49 */
50typedef void
51(*RPS_sampler_rand_peer_ready_cont) (void *cls,
52 const struct GNUNET_PeerIdentity *id,
53 double probability,
54 uint32_t num_observed);
55
56
57/**
58 * Type of function used to differentiate between modified and not modified
59 * Sampler.
60 */
61typedef void
62(*RPS_get_peers_type) (void *cls);
63
64
65/**
66 * Callback that is called from _get_n_rand_peers() when the PeerIDs are ready.
67 *
68 * @param cls the closure given alongside this function.
69 * @param ids the PeerIDs that were returned
70 * to be freed
71 */
72typedef void
73(*RPS_sampler_n_rand_peers_ready_cb) (const struct GNUNET_PeerIdentity *ids,
74 uint32_t num_peers,
75 void *cls);
76
77
78/**
79 * Callback that is called from _get_n_rand_peers() when the PeerIDs are ready.
80 *
81 * @param cls the closure given alongside this function.
82 * @param probability Probability with which all IDs have been observed
83 * @param num_observed Number of observed IDs
84 * @param ids the PeerIDs that were returned
85 * to be freed
86 */
87typedef void
88(*RPS_sampler_sinlge_info_ready_cb) (const struct GNUNET_PeerIdentity *ids,
89 void *cls,
90 double probability,
91 uint32_t num_observed);
92
93
94/**
95 * @brief Callback called each time a new peer was put into the sampler
96 *
97 * @param cls A possibly given closure
98 */
99typedef void
100(*SamplerNotifyUpdateCB) (void *cls);
101
102
103/**
104 * Closure for #sampler_mod_get_rand_peer() and #sampler_get_rand_peer
105 */
106struct GetPeerCls
107{
108 /**
109 * DLL
110 */
111 struct GetPeerCls *next;
112 struct GetPeerCls *prev;
113
114 /**
115 * The #RPS_SamplerRequestHandle this single request belongs to.
116 */
117 struct RPS_SamplerRequestHandle *req_handle;
118
119 /**
120 * The #RPS_SamplerRequestHandleSingleInfo this single request belongs to.
121 */
122 struct RPS_SamplerRequestHandleSingleInfo *req_single_info_handle;
123
124 /**
125 * The task for this function.
126 */
127 struct GNUNET_SCHEDULER_Task *get_peer_task;
128
129 /**
130 * @brief Context to the given callback.
131 */
132 struct SamplerNotifyUpdateCTX *notify_ctx;
133
134 /**
135 * The callback
136 */
137 RPS_sampler_rand_peer_ready_cont cont;
138
139 /**
140 * The closure to the callback @e cont
141 */
142 void *cont_cls;
143
144 /**
145 * The address of the id to be stored at
146 */
147 struct GNUNET_PeerIdentity *id;
148};
149
150
151/**
152 * Sampler with its own array of SamplerElements
153 */
154struct RPS_Sampler
155{
156 /**
157 * Number of sampler elements we hold.
158 */
159 unsigned int sampler_size;
160 // size_t size;
161
162 /**
163 * All sampler elements in one array.
164 */
165 struct RPS_SamplerElement **sampler_elements;
166
167 /**
168 * Maximum time a round takes
169 *
170 * Used in the context of RPS
171 */
172 struct GNUNET_TIME_Relative max_round_interval;
173
174 /**
175 * @brief The estimated total number of peers in the network
176 */
177 uint32_t num_peers_estim;
178
179 /**
180 * @brief The desired probability with which we want to have observed all
181 * peers.
182 */
183 double desired_probability;
184
185 /**
186 * @brief A factor that catches the 'bias' of a random stream of peer ids.
187 *
188 * As introduced by Brahms: Factor between the number of unique ids in a
189 * truly random stream and number of unique ids in the gossip stream.
190 */
191 double deficiency_factor;
192
193 /**
194 * Stores the function to return peers. Which one it is depends on whether
195 * the Sampler is the modified one or not.
196 */
197 RPS_get_peers_type get_peers;
198
199 /**
200 * Head and tail for the DLL to store the #RPS_SamplerRequestHandle
201 */
202 struct RPS_SamplerRequestHandle *req_handle_head;
203 struct RPS_SamplerRequestHandle *req_handle_tail;
204
205 /**
206 * Head and tail for the DLL to store the #RPS_SamplerRequestHandleSingleInfo
207 */
208 struct RPS_SamplerRequestHandleSingleInfo *req_handle_single_head;
209 struct RPS_SamplerRequestHandleSingleInfo *req_handle_single_tail;
210
211 struct SamplerNotifyUpdateCTX *notify_ctx_head;
212 struct SamplerNotifyUpdateCTX *notify_ctx_tail;
213};
214
215
216/**
217 * @brief Update the current estimate of the network size stored at the sampler
218 *
219 * Used for computing the condition when to return elements to the client
220 *
221 * @param sampler The sampler to update
222 * @param num_peers The estimated value
223 */
224void
225RPS_sampler_update_with_nw_size (struct RPS_Sampler *sampler,
226 uint32_t num_peers);
227
228
229/**
230 * @brief Set the probability that is needed at least with what a sampler
231 * element has to have observed all elements from the network.
232 *
233 * Only used/useful with the client sampler
234 * (Maybe move to rps-sampler_client.{h|c} ?)
235 *
236 * @param sampler
237 * @param desired_probability
238 */
239void
240RPS_sampler_set_desired_probability (struct RPS_Sampler *sampler,
241 double desired_probability);
242
243
244/**
245 * @brief Set the deficiency factor.
246 *
247 * Only used/useful with the client sampler
248 * (Maybe move to rps-sampler_client.{h|c} ?)
249 *
250 * @param sampler
251 * @param desired_probability
252 */
253void
254RPS_sampler_set_deficiency_factor (struct RPS_Sampler *sampler,
255 double deficiency_factor);
256
257
258/**
259 * @brief Add a callback that will be called when the next peer is inserted
260 * into the sampler
261 *
262 * @param sampler The sampler on which update it will be called
263 * @param notify_cb The callback
264 * @param cls Closure given to the callback
265 *
266 * @return The context containing callback and closure
267 */
268struct SamplerNotifyUpdateCTX *
269sampler_notify_on_update (struct RPS_Sampler *sampler,
270 SamplerNotifyUpdateCB notify_cb,
271 void *cls);
272
273
274/**
275 * Update every sampler element of this sampler with given peer
276 *
277 * @param sampler the sampler to update.
278 * @param id the PeerID that is put in the sampler
279 */
280void
281RPS_sampler_update (struct RPS_Sampler *sampler,
282 const struct GNUNET_PeerIdentity *id);
283
284
285/**
286 * Reinitialise all previously initialised sampler elements with the given value.
287 *
288 * Used to get rid of a PeerID.
289 *
290 * @param sampler the sampler to reinitialise a sampler element in.
291 * @param id the id of the sampler elements to update.
292 */
293void
294RPS_sampler_reinitialise_by_value (struct RPS_Sampler *sampler,
295 const struct GNUNET_PeerIdentity *id);
296
297
298/**
299 * Get the size of the sampler.
300 *
301 * @param sampler the sampler to return the size of.
302 * @return the size of the sampler
303 */
304unsigned int
305RPS_sampler_get_size (struct RPS_Sampler *sampler);
306
307
308/**
309 * Grow or shrink the size of the sampler.
310 *
311 * @param sampler the sampler to resize.
312 * @param new_size the new size of the sampler
313 */
314void
315RPS_sampler_resize (struct RPS_Sampler *sampler, unsigned int new_size);
316
317
318/**
319 * Get n random peers out of the sampled peers.
320 *
321 * We might want to reinitialise this sampler after giving the
322 * corrsponding peer to the client.
323 * Random with or without consumption?
324 *
325 * @param sampler the sampler to get peers from.
326 * @param cb callback that will be called once the ids are ready.
327 * @param cls closure given to @a cb
328 * @param num_peers the number of peers requested
329 */
330struct RPS_SamplerRequestHandle *
331RPS_sampler_get_n_rand_peers (struct RPS_Sampler *sampler,
332 uint32_t num_peers,
333 RPS_sampler_n_rand_peers_ready_cb cb,
334 void *cls);
335
336
337/**
338 * Get one random peer with additional information.
339 *
340 * @param sampler the sampler to get peers from.
341 * @param cb callback that will be called once the ids are ready.
342 * @param cls closure given to @a cb
343 */
344struct RPS_SamplerRequestHandleSingleInfo *
345RPS_sampler_get_rand_peer_info (struct RPS_Sampler *sampler,
346 RPS_sampler_sinlge_info_ready_cb cb,
347 void *cls);
348
349
350/**
351 * Counts how many Samplers currently hold a given PeerID.
352 *
353 * @param sampler the sampler to count ids in.
354 * @param id the PeerID to count.
355 *
356 * @return the number of occurrences of id.
357 */
358uint32_t
359RPS_sampler_count_id (struct RPS_Sampler *sampler,
360 const struct GNUNET_PeerIdentity *id);
361
362
363/**
364 * Cancel a request issued through #RPS_sampler_n_rand_peers_ready_cb.
365 *
366 * @param req_handle the handle to the request
367 */
368void
369RPS_sampler_request_cancel (struct RPS_SamplerRequestHandle *req_handle);
370
371
372/**
373 * Cancel a request issued through #RPS_sampler_n_rand_peers_ready_cb.
374 *
375 * @param req_handle the handle to the request
376 */
377void
378RPS_sampler_request_single_info_cancel (
379 struct RPS_SamplerRequestHandleSingleInfo *req_single_info_handle);
380
381
382/**
383 * Cleans the sampler.
384 */
385void
386RPS_sampler_destroy (struct RPS_Sampler *sampler);
387
388#endif /* RPS_SAMPLER_COMMON_H */
389/* end of rps-sampler_common.h */
diff --git a/src/contrib/service/rps/rps-test_util.c b/src/contrib/service/rps/rps-test_util.c
new file mode 100644
index 000000000..8fd82f7b3
--- /dev/null
+++ b/src/contrib/service/rps/rps-test_util.c
@@ -0,0 +1,550 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps-test_util.c
23 * @brief Some utils facilitating the view into the internals for the sampler
24 * needed for evaluation
25 *
26 * @author Julius Bünger
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "rps-test_util.h"
32
33#include <inttypes.h>
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "rps-test_util", __VA_ARGS__)
36
37#define B2B_PAT "%c%c%c%c%c%c%c%c"
38#define B2B(byte) \
39 (byte & 0x80 ? '1' : '0'), \
40 (byte & 0x40 ? '1' : '0'), \
41 (byte & 0x20 ? '1' : '0'), \
42 (byte & 0x10 ? '1' : '0'), \
43 (byte & 0x08 ? '1' : '0'), \
44 (byte & 0x04 ? '1' : '0'), \
45 (byte & 0x02 ? '1' : '0'), \
46 (byte & 0x01 ? '1' : '0')
47
48#ifdef TO_FILE
49
50
51/**
52 * @brief buffer for storing the unaligned bits for the next write
53 */
54static char buf_unaligned;
55
56/**
57 * @brief number of bits in unaligned buffer
58 */
59static unsigned num_bits_buf_unaligned;
60
61static struct GNUNET_CONTAINER_MultiHashMap *open_files;
62
63
64/**
65 * @brief Get file handle
66 *
67 * If necessary, create file handle and store it with the other file handles.
68 *
69 * @param name Name of the file
70 *
71 * @return File handle
72 */
73struct GNUNET_DISK_FileHandle *
74get_file_handle (const char *name)
75{
76 struct GNUNET_HashCode hash;
77 struct GNUNET_DISK_FileHandle *fh;
78
79 if (NULL == open_files)
80 {
81 open_files = GNUNET_CONTAINER_multihashmap_create (16,
82 GNUNET_NO);
83 LOG (GNUNET_ERROR_TYPE_DEBUG,
84 "Created map of open files.\n");
85 }
86 GNUNET_CRYPTO_hash (name,
87 strlen (name),
88 &hash);
89 if (NULL != (fh = GNUNET_CONTAINER_multihashmap_get (open_files,
90 &hash)))
91 return fh;
92 fh = GNUNET_DISK_file_open (name,
93 GNUNET_DISK_OPEN_WRITE
94 | GNUNET_DISK_OPEN_CREATE
95 | GNUNET_DISK_OPEN_APPEND,
96 GNUNET_DISK_PERM_USER_READ
97 | GNUNET_DISK_PERM_USER_WRITE
98 | GNUNET_DISK_PERM_GROUP_READ);
99 if (NULL == fh)
100 {
101 LOG (GNUNET_ERROR_TYPE_ERROR,
102 "Opening file `%s' failed.\n",
103 name);
104 GNUNET_assert (0);
105 }
106 GNUNET_assert (GNUNET_YES ==
107 GNUNET_CONTAINER_multihashmap_put (open_files,
108 &hash,
109 fh,
110 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
111 return fh;
112}
113
114
115/**
116 * @brief Closes the file of the current entry
117 *
118 * Implements #GNUNET_CONTAINER_HashMapIterator
119 *
120 * @param cls unused
121 * @param key unused
122 * @param value the file handle
123 *
124 * @return #GNUNET_YES if we should continue to
125 * iterate,
126 * #GNUNET_NO if not.
127 */
128int
129close_files_iter (void *cls,
130 const struct GNUNET_HashCode *key,
131 void *value)
132{
133 (void) cls;
134 (void) key;
135 struct GNUNET_DISK_FileHandle *fh = value;
136
137 if (NULL != fh)
138 {
139 GNUNET_DISK_file_close (fh);
140 }
141 return GNUNET_YES;
142}
143
144
145/**
146 * @brief Close all files that were opened with #get_file_handle
147 *
148 * @return Success of iterating over files
149 */
150int
151close_all_files ()
152{
153 int ret;
154
155 if (NULL != open_files)
156 {
157 ret = GNUNET_CONTAINER_multihashmap_iterate (open_files,
158 close_files_iter,
159 NULL);
160 GNUNET_CONTAINER_multihashmap_destroy (open_files);
161 open_files = NULL;
162 return ret;
163 }
164 return GNUNET_YES;
165}
166
167
168void
169to_file_raw (const char *file_name, const char *buf, size_t size_buf)
170{
171 struct GNUNET_DISK_FileHandle *f;
172 size_t size_written;
173
174 if (NULL == (f = GNUNET_DISK_file_open (file_name,
175 GNUNET_DISK_OPEN_APPEND
176 | GNUNET_DISK_OPEN_WRITE
177 | GNUNET_DISK_OPEN_CREATE,
178 GNUNET_DISK_PERM_USER_READ
179 | GNUNET_DISK_PERM_USER_WRITE
180 | GNUNET_DISK_PERM_GROUP_READ
181 | GNUNET_DISK_PERM_OTHER_READ)))
182 {
183 LOG (GNUNET_ERROR_TYPE_WARNING,
184 "Not able to open file %s\n",
185 file_name);
186 return;
187 }
188
189 size_written = GNUNET_DISK_file_write (f, buf, size_buf);
190 if (size_buf != size_written)
191 {
192 LOG (GNUNET_ERROR_TYPE_WARNING,
193 "Unable to write to file! (Size: %llu, size_written: %llu)\n",
194 (unsigned long long) size_buf,
195 (unsigned long long) size_written);
196
197 if (GNUNET_YES != GNUNET_DISK_file_close (f))
198 LOG (GNUNET_ERROR_TYPE_WARNING,
199 "Unable to close file\n");
200
201 return;
202 }
203 LOG (GNUNET_ERROR_TYPE_WARNING,
204 "Wrote %llu bytes raw.\n",
205 (unsigned long long) size_written);
206 if (GNUNET_YES != GNUNET_DISK_file_close (f))
207 LOG (GNUNET_ERROR_TYPE_WARNING,
208 "Unable to close file\n");
209}
210
211
212void
213to_file_raw_unaligned (const char *file_name,
214 const char *buf,
215 size_t size_buf,
216 unsigned bits_needed)
217{
218 // TODO endianness!
219 GNUNET_assert (size_buf >= (bits_needed / 8));
220 // if (0 == num_bits_buf_unaligned)
221 // {
222 // if (0 == (bits_needed % 8))
223 // {
224 // to_file_raw (file_name, buf, size_buf);
225 // return;
226 // }
227 // to_file_raw (file_name, buf, size_buf - 1);
228 // buf_unaligned = buf[size_buf - 1];
229 // num_bits_buf_unaligned = bits_needed % 8;
230 // return;
231 // }
232 LOG (GNUNET_ERROR_TYPE_DEBUG,
233 "Was asked to write %u bits\n", bits_needed);
234
235 char buf_write[size_buf + 1];
236 const unsigned bytes_iter = (0 != bits_needed % 8 ?
237 (bits_needed / 8) + 1 :
238 bits_needed / 8);
239 // TODO what if no iteration happens?
240 unsigned size_buf_write = 0;
241 LOG (GNUNET_ERROR_TYPE_DEBUG,
242 "num_bits_buf_unaligned: %u\n",
243 num_bits_buf_unaligned);
244 LOG (GNUNET_ERROR_TYPE_DEBUG,
245 "ua args: size_buf: %llu, bits_needed: %u -> iter: %u\n",
246 (unsigned long long) size_buf,
247 bits_needed,
248 bytes_iter);
249 buf_write[0] = buf_unaligned;
250 /* Iterate over input bytes */
251 for (unsigned i = 0; i < bytes_iter; i++)
252 {
253 /* Number of bits needed in this iteration - 8 for all except last iter */
254 unsigned num_bits_needed_iter;
255 /* Mask for bits to actually use */
256 unsigned mask_bits_needed_iter;
257 char byte_input;
258 /* Number of bits needed to align unaligned byte */
259 unsigned num_bits_to_align;
260 /* Number of bits that are to be moved */
261 unsigned num_bits_to_move;
262 /* Mask for bytes to be moved */
263 char mask_input_to_move;
264 /* Masked bits to be moved */
265 char bits_to_move;
266 /* The amount of bits needed to fit the bits to shift to the nearest spot */
267 unsigned distance_shift_bits;
268 /* Shifted bits on the move */
269 char bits_moving;
270 /* (unaligned) byte being filled with bits */
271 char byte_to_fill;
272 /* mask for needed bits of the input byte that have not been moved */
273 char mask_input_leftover;
274 /* needed bits of the input byte that have not been moved */
275 char byte_input_leftover;
276 unsigned num_bits_leftover;
277 // unsigned num_bits_discard;
278 char byte_unaligned_new;
279
280 if ((bits_needed - (i * 8)) <= 8)
281 {
282 /* last iteration */
283 num_bits_needed_iter = bits_needed - (i * 8);
284 }
285 else
286 {
287 num_bits_needed_iter = 8;
288 }
289 LOG (GNUNET_ERROR_TYPE_DEBUG,
290 "number of bits needed in this iteration: %u\n",
291 num_bits_needed_iter);
292 mask_bits_needed_iter = ((char) 1 << num_bits_needed_iter) - 1;
293 LOG (GNUNET_ERROR_TYPE_DEBUG,
294 "mask needed bits (current iter): "B2B_PAT "\n",
295 B2B (mask_bits_needed_iter));
296 LOG (GNUNET_ERROR_TYPE_DEBUG,
297 "Unaligned byte: "B2B_PAT " (%u bits)\n",
298 B2B (buf_unaligned),
299 num_bits_buf_unaligned);
300 byte_input = buf[i];
301 LOG (GNUNET_ERROR_TYPE_DEBUG,
302 "next whole input byte: "B2B_PAT "\n",
303 B2B (byte_input));
304 byte_input &= mask_bits_needed_iter;
305 num_bits_to_align = 8 - num_bits_buf_unaligned;
306 LOG (GNUNET_ERROR_TYPE_DEBUG,
307 "input byte, needed bits: "B2B_PAT "\n",
308 B2B (byte_input));
309 LOG (GNUNET_ERROR_TYPE_DEBUG,
310 "number of bits needed to align unaligned bit: %u\n",
311 num_bits_to_align);
312 num_bits_to_move = GNUNET_MIN (num_bits_to_align, num_bits_needed_iter);
313 LOG (GNUNET_ERROR_TYPE_DEBUG,
314 "number of bits of new byte to move: %u\n",
315 num_bits_to_move);
316 mask_input_to_move = ((char) 1 << num_bits_to_move) - 1;
317 LOG (GNUNET_ERROR_TYPE_DEBUG,
318 "mask of bits of new byte to take for moving: "B2B_PAT "\n",
319 B2B (mask_input_to_move));
320 bits_to_move = byte_input & mask_input_to_move;
321 LOG (GNUNET_ERROR_TYPE_DEBUG,
322 "masked bits of new byte to take for moving: "B2B_PAT "\n",
323 B2B (bits_to_move));
324 distance_shift_bits = num_bits_buf_unaligned;
325 LOG (GNUNET_ERROR_TYPE_DEBUG,
326 "distance needed to shift bits to their correct spot: %u\n",
327 distance_shift_bits);
328 bits_moving = bits_to_move << distance_shift_bits;
329 LOG (GNUNET_ERROR_TYPE_DEBUG,
330 "shifted, masked bits of new byte being moved: "B2B_PAT "\n",
331 B2B (bits_moving));
332 byte_to_fill = buf_unaligned | bits_moving;
333 LOG (GNUNET_ERROR_TYPE_DEBUG,
334 "byte being filled: "B2B_PAT "\n",
335 B2B (byte_to_fill));
336 LOG (GNUNET_ERROR_TYPE_DEBUG,
337 "pending bytes: %u\n",
338 num_bits_buf_unaligned + num_bits_needed_iter);
339 if (num_bits_buf_unaligned + num_bits_needed_iter >= 8)
340 {
341 /* buf_unaligned was aligned by filling
342 * -> can be written to storage */
343 buf_write[i] = byte_to_fill;
344 size_buf_write++;
345
346 /* store the leftover, unaligned bits in buffer */
347 mask_input_leftover = mask_bits_needed_iter & (~mask_input_to_move);
348 LOG (GNUNET_ERROR_TYPE_DEBUG,
349 "mask of leftover bits of new byte: "B2B_PAT "\n",
350 B2B (mask_input_leftover));
351 byte_input_leftover = byte_input & mask_input_leftover;
352 LOG (GNUNET_ERROR_TYPE_DEBUG,
353 "masked, leftover bits of new byte: "B2B_PAT "\n",
354 B2B (byte_input_leftover));
355 num_bits_leftover = num_bits_needed_iter - num_bits_to_move;
356 LOG (GNUNET_ERROR_TYPE_DEBUG,
357 "number of unaligned bits left: %u\n",
358 num_bits_leftover);
359 // num_bits_discard = 8 - num_bits_needed_iter;
360 byte_unaligned_new = byte_input_leftover >> num_bits_to_move;
361 LOG (GNUNET_ERROR_TYPE_DEBUG,
362 "new unaligned byte: "B2B_PAT "\n",
363 B2B (byte_unaligned_new));
364 buf_unaligned = byte_unaligned_new;
365 num_bits_buf_unaligned = num_bits_leftover % 8;
366 }
367 else
368 {
369 /* unaligned buffer still unaligned but 'fuller' */
370 buf_unaligned = byte_to_fill;
371 num_bits_buf_unaligned = (num_bits_buf_unaligned + bits_needed) % 8;
372 }
373 }
374 to_file_raw (file_name, buf_write, size_buf_write);
375 LOG (GNUNET_ERROR_TYPE_DEBUG, "\n");
376}
377
378
379char *
380auth_key_to_string (struct GNUNET_CRYPTO_AuthKey auth_key)
381{
382 int size;
383 size_t name_buf_size;
384 char *end;
385 char *buf;
386 char *name_buf;
387 size_t keylen = (sizeof(struct GNUNET_CRYPTO_AuthKey)) * 8;
388
389 name_buf_size = 512 * sizeof(char);
390 name_buf = GNUNET_malloc (name_buf_size);
391
392 if (keylen % 5 > 0)
393 keylen += 5 - keylen % 5;
394 keylen /= 5;
395 buf = GNUNET_malloc (keylen + 1);
396
397 end = GNUNET_STRINGS_data_to_string (&(auth_key.key),
398 sizeof(struct GNUNET_CRYPTO_AuthKey),
399 buf,
400 keylen);
401
402 if (NULL == end)
403 {
404 GNUNET_free (buf);
405 GNUNET_break (0);
406 }
407 else
408 {
409 *end = '\0';
410 }
411
412 size = GNUNET_snprintf (name_buf, name_buf_size, "sampler_el-%s", buf);
413 if (0 > size)
414 LOG (GNUNET_ERROR_TYPE_WARNING, "Failed to create name_buf\n");
415
416 GNUNET_free (buf);
417
418 return name_buf;
419}
420
421
422#endif /* TO_FILE */
423
424
425struct GNUNET_CRYPTO_AuthKey
426string_to_auth_key (const char *str)
427{
428 struct GNUNET_CRYPTO_AuthKey auth_key;
429
430 if (GNUNET_OK !=
431 GNUNET_STRINGS_string_to_data (str,
432 strlen (str),
433 &auth_key.key,
434 sizeof(struct GNUNET_CRYPTO_AuthKey)))
435 {
436 LOG (GNUNET_ERROR_TYPE_WARNING, "Failed to convert string to data\n");
437 }
438
439 return auth_key;
440}
441
442
443/**
444 * @brief Try to ensure that `/tmp/rps` exists.
445 *
446 * @return #GNUNET_YES on success
447 * #GNUNET_SYSERR on failure
448 */
449static int
450ensure_folder_exist (void)
451{
452 if (GNUNET_OK !=
453 GNUNET_DISK_directory_create ("/tmp/rps"))
454 {
455 LOG (GNUNET_ERROR_TYPE_ERROR,
456 "Could not create directory `/tmp/rps'\n");
457 return GNUNET_SYSERR;
458 }
459 return GNUNET_YES;
460}
461
462
463char *
464store_prefix_file_name (const unsigned int index,
465 const char *prefix)
466{
467 int len_file_name;
468 int out_size;
469 char *file_name;
470 char index_str[64];
471
472 if (GNUNET_SYSERR == ensure_folder_exist ())
473 return NULL;
474 out_size = GNUNET_snprintf (index_str,
475 64,
476 "%u",
477 index);
478 if ((64 < out_size) ||
479 (0 > out_size) )
480 {
481 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
482 "Failed to write string to buffer (size: %i, out_size: %i)\n",
483 64,
484 out_size);
485 }
486 len_file_name = (strlen (prefix)
487 + strlen (index_str)
488 + 11)
489 * sizeof(char);
490 file_name = GNUNET_malloc (len_file_name);
491 out_size = GNUNET_snprintf (file_name,
492 len_file_name,
493 "/tmp/rps/%s-%s",
494 prefix,
495 index_str);
496 if ((len_file_name < out_size) ||
497 (0 > out_size) )
498 {
499 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
500 "Failed to write string to buffer (size: %i, out_size: %i)\n",
501 len_file_name,
502 out_size);
503 }
504 return file_name;
505}
506
507
508/**
509 * @brief Factorial
510 *
511 * @param x Number of which to compute the factorial
512 *
513 * @return Factorial of @a x
514 */
515uint32_t
516fac (uint32_t x)
517{
518 if (1 >= x)
519 {
520 return x;
521 }
522 return x * fac (x - 1);
523}
524
525
526/**
527 * @brief Binomial coefficient (n choose k)
528 *
529 * @param n
530 * @param k
531 *
532 * @return Binomial coefficient of @a n and @a k
533 */
534uint32_t
535binom (uint32_t n, uint32_t k)
536{
537 // GNUNET_assert (n >= k);
538 if (k > n)
539 return 0;
540 /* if (0 > n) return 0; - always false */
541 /* if (0 > k) return 0; - always false */
542 if (0 == k)
543 return 1;
544 return fac (n)
545 /
546 fac (k) * fac (n - k);
547}
548
549
550/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/rps-test_util.h b/src/contrib/service/rps/rps-test_util.h
new file mode 100644
index 000000000..9ff13bdcf
--- /dev/null
+++ b/src/contrib/service/rps/rps-test_util.h
@@ -0,0 +1,144 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps-test_util.h
23 * @brief Some utils facilitating the view into the internals for the sampler
24 * needed for evaluation
25 * @author Julius Bünger
26 */
27
28#ifndef RPS_TEST_UTIL_H
29#define RPS_TEST_UTIL_H
30
31#define TO_FILE 0
32
33
34char *
35auth_key_to_string (struct GNUNET_CRYPTO_AuthKey auth_key);
36
37struct GNUNET_CRYPTO_AuthKey
38string_to_auth_key (const char *str);
39
40
41/**
42 * @brief Get file handle
43 *
44 * If necessary, create file handle and store it with the other file handles.
45 *
46 * @param name Name of the file
47 *
48 * @return File handle
49 */
50struct GNUNET_DISK_FileHandle *
51get_file_handle (const char *name);
52
53/**
54 * @brief Close all files that were opened with #get_file_handle
55 *
56 * @return Success of iterating over files
57 */
58int
59close_all_files ();
60
61/**
62 * This function is used to facilitate writing important information to disk
63 */
64#ifdef TO_FILE
65#define to_file(file_name, ...) do { \
66 char tmp_buf[512] = ""; \
67 int size; \
68 if (NULL == file_name) break; \
69 size = GNUNET_snprintf (tmp_buf, sizeof(tmp_buf), __VA_ARGS__); \
70 if (0 > size) \
71 { \
72 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
73 "Failed to create tmp_buf\n"); \
74 break; \
75 } \
76 (void) strncat (tmp_buf, "\n", 511); \
77 GNUNET_DISK_file_write (get_file_handle (file_name), \
78 tmp_buf, \
79 strnlen (tmp_buf, 512)); \
80} while (0);
81
82
83#define to_file_w_len(file_name, len, ...) do { char tmp_buf [len]; \
84 int size; \
85 memset (tmp_buf, 0, len); \
86 size = GNUNET_snprintf (tmp_buf, \
87 sizeof( \
88 tmp_buf), \
89 __VA_ARGS__); \
90 if (0 > size) \
91 { \
92 GNUNET_log ( \
93 GNUNET_ERROR_TYPE_WARNING, \
94 "Failed to create tmp_buf\n"); \
95 break; \
96 } \
97 (void) strncat (tmp_buf, "\n", 2); \
98 GNUNET_DISK_file_write ( \
99 get_file_handle (file_name), \
100 tmp_buf, \
101 strnlen ( \
102 tmp_buf, len)); \
103} while (0);
104#else /* TO_FILE */
105# define to_file(file_name, ...)
106# define to_file_w_len(file_name, len, ...)
107#endif /* TO_FILE */
108
109char *
110store_prefix_file_name (const unsigned int index,
111 const char *prefix);
112
113void
114to_file_raw (const char *file_name, const char *buf, size_t size_buf);
115
116void
117to_file_raw_unaligned (const char *file_name,
118 const char *buf,
119 size_t size_buf,
120 unsigned bits_needed);
121
122
123/**
124 * @brief Factorial
125 *
126 * @param x Number of which to compute the factorial
127 *
128 * @return Factorial of @a x
129 */
130uint32_t fac (uint32_t x);
131
132
133/**
134 * @brief Binomial coefficient (n choose k)
135 *
136 * @param n
137 * @param k
138 *
139 * @return Binomial coefficient of @a n and @a k
140 */
141uint32_t binom (uint32_t n, uint32_t k);
142
143#endif /* RPS_TEST_UTIL_H */
144/* end of gnunet-service-rps.c */
diff --git a/src/contrib/service/rps/rps.conf.in b/src/contrib/service/rps/rps.conf.in
new file mode 100644
index 000000000..8c7098747
--- /dev/null
+++ b/src/contrib/service/rps/rps.conf.in
@@ -0,0 +1,38 @@
1[rps]
2START_ON_DEMAND = @START_ON_DEMAND@
3IMMEDIATE_START = YES
4BINARY = gnunet-service-rps
5UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-rps.sock
6HOME = $SERVICEHOME
7UNIX_MATCH_UID = NO
8UNIX_MATCH_GID = YES
9HOSTNAME = localhost
10ACCEPT_FROM = 127.0.0.1;
11ACCEPT_FROM6 = ::1;
12# PORT = 2119
13@UNIXONLY@ PORT = 2119
14
15# This is the timeinterval between the rounds
16ROUNDINTERVAL = 30 s
17
18# This is the file in which valid peers are stored
19FILENAME_VALID_PEERS = $GNUNET_DATA_HOME/rps/valid_peers.txt
20# Disable storage of valid peers
21#FILENAME_VALID_PEERS = DISABLE
22
23# This is the 'estimate' in the beginning.
24# This determines the size of the peers we keep in memory
25# until we receive the first estimate from NSE.
26# Keep in mind, that (networksize)^(1/3) should be enough.
27# So, 50 is enough for a network of size 50^3 = 125000
28MINSIZE = 10
29
30# The probability with which we want a sampler element to have observed all
31# peer ids in the network at least
32DESIRED_PROBABILITY = 0.9
33
34# A factor that catches the 'bias' of a random stream of peer ids.
35#
36# As introduced by Brahms: Factor between the number of unique ids in a
37# truly random stream and number of unique ids in the gossip stream.
38DEFICIENCY_FACTOR = 0.4
diff --git a/src/contrib/service/rps/rps.h b/src/contrib/service/rps/rps.h
new file mode 100644
index 000000000..202f881c9
--- /dev/null
+++ b/src/contrib/service/rps/rps.h
@@ -0,0 +1,336 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/rps.h
22 * @brief example IPC messages between RPS API and GNS service
23 * @author Julius Bünger
24 */
25
26#include "gnunet_rps_service.h"
27
28/**
29 * Mesh port used by RPS.
30 */
31#define GNUNET_RPS_CADET_PORT 31337
32
33
34GNUNET_NETWORK_STRUCT_BEGIN
35
36/***********************************************************************
37* P2P Messages
38***********************************************************************/
39
40/**
41 * P2P Message to send PeerIDs to other peer.
42 */
43struct GNUNET_RPS_P2P_PullReplyMessage
44{
45 /**
46 * Header including size and type in NBO
47 */
48 struct GNUNET_MessageHeader header;
49
50 /**
51 * Number of PeerIDs sent
52 */
53 uint32_t num_peers GNUNET_PACKED;
54
55 /* Followed by num_peers * GNUNET_PeerIdentity */
56};
57
58
59/***********************************************************************
60* Client-Service Messages
61***********************************************************************/
62
63/**
64 * Message from client to service with seed of peers.
65 */
66struct GNUNET_RPS_CS_SeedMessage
67{
68 /**
69 * Header including size and type in NBO
70 */
71 struct GNUNET_MessageHeader header;
72
73 /**
74 * Number of peers
75 */
76 uint32_t num_peers GNUNET_PACKED;
77
78 /* Followed by num_peers * GNUNET_PeerIdentity */
79};
80
81#if ENABLE_MALICIOUS
82/**
83 * Message from client to service to turn service malicious.
84 */
85struct GNUNET_RPS_CS_ActMaliciousMessage
86{
87 /**
88 * Header including size and type in NBO
89 */
90 struct GNUNET_MessageHeader header;
91
92 /**
93 * If the type is 2 this is the attacked peer,
94 * empty otherwise.
95 */
96 struct GNUNET_PeerIdentity attacked_peer;
97
98 /**
99 * Type of malicious behaviour.
100 *
101 * 0 No malicious behaviour at all
102 * 1 Try to maximise representation
103 * 2 Try to partition the network
104 */
105 uint32_t type GNUNET_PACKED;
106
107 /**
108 * Number of peers
109 */
110 uint32_t num_peers GNUNET_PACKED;
111
112 /* Followed by num_peers * GNUNET_PeerIdentity when the type of malicious
113 behaviour is 1 */
114};
115#endif /* ENABLE_MALICIOUS */
116
117
118/**
119 * Message from client to service telling it to start a new sub
120 */
121struct GNUNET_RPS_CS_SubStartMessage
122{
123 /**
124 * Header including size and type in NBO
125 */
126 struct GNUNET_MessageHeader header;
127
128 /**
129 * For alignment.
130 */
131 uint32_t reserved GNUNET_PACKED;
132
133 /**
134 * Mean interval between two rounds
135 */
136 struct GNUNET_TIME_RelativeNBO round_interval;
137
138 /**
139 * Length of the shared value represented as string.
140 */
141 struct GNUNET_HashCode hash GNUNET_PACKED;
142};
143
144
145/**
146 * Message from client to service telling it to stop a new sub
147 */
148struct GNUNET_RPS_CS_SubStopMessage
149{
150 /**
151 * Header including size and type in NBO
152 */
153 struct GNUNET_MessageHeader header;
154
155 /**
156 * Length of the shared value represented as string.
157 */
158 struct GNUNET_HashCode hash GNUNET_PACKED;
159};
160
161
162/* Debug messages */
163
164/**
165 * Message from client to service indicating that
166 * clients wants to get updates of the view
167 */
168struct GNUNET_RPS_CS_DEBUG_ViewRequest
169{
170 /**
171 * Header including size and type in NBO
172 */
173 struct GNUNET_MessageHeader header;
174
175 /**
176 * Number of updates
177 * 0 for sending updates until cancellation
178 */
179 uint32_t num_updates GNUNET_PACKED;
180};
181
182/**
183 * Message from service to client containing current update of view
184 */
185struct GNUNET_RPS_CS_DEBUG_ViewReply
186{
187 /**
188 * Header including size and type in NBO
189 */
190 struct GNUNET_MessageHeader header;
191
192 /**
193 * Identifyer of the message.
194 */
195 uint32_t id GNUNET_PACKED;
196
197 /**
198 * Number of peers in the view
199 */
200 uint64_t num_peers GNUNET_PACKED;
201};
202/* Followed by num_peers * GNUNET_PeerIdentity */
203
204/**
205 * Message from client to service indicating that
206 * clients wants to get stream of biased peers
207 */
208struct GNUNET_RPS_CS_DEBUG_StreamRequest
209{
210 /**
211 * Header including size and type in NBO
212 */
213 struct GNUNET_MessageHeader header;
214};
215
216/**
217 * Message from service to client containing peer from biased stream
218 */
219struct GNUNET_RPS_CS_DEBUG_StreamReply
220{
221 /**
222 * Header including size and type in NBO
223 */
224 struct GNUNET_MessageHeader header;
225
226 /**
227 * Number of peers
228 */
229 uint64_t num_peers GNUNET_PACKED;
230
231 // TODO maybe source of peer (pull/push list, peerinfo, ...)
232
233 /* Followed by num_peers * GNUNET_PeerIdentity */
234};
235
236GNUNET_NETWORK_STRUCT_END
237
238/***********************************************************************
239* Defines from old gnunet-service-rps_peers.h
240***********************************************************************/
241
242/**
243 * Different flags indicating the status of another peer.
244 */
245enum Peers_PeerFlags
246{
247 /**
248 * If we are waiting for a reply from that peer (sent a pull request).
249 */
250 Peers_PULL_REPLY_PENDING = 0x01,
251
252 /* IN_OTHER_GOSSIP_LIST = 0x02, unneeded? */
253 /* IN_OWN_SAMPLER_LIST = 0x04, unneeded? */
254 /* IN_OWN_GOSSIP_LIST = 0x08, unneeded? */
255
256 /**
257 * We set this bit when we know the peer is online.
258 */
259 Peers_ONLINE = 0x20,
260
261 /**
262 * We set this bit when we are going to destroy the channel to this peer.
263 * When cleanup_channel is called, we know that we wanted to destroy it.
264 * Otherwise the channel to the other peer was destroyed.
265 */
266 Peers_TO_DESTROY = 0x40,
267};
268
269/**
270 * Keep track of the status of a channel.
271 *
272 * This is needed in order to know what to do with a channel when it's
273 * destroyed.
274 */
275enum Peers_ChannelFlags
276{
277 /**
278 * We destroyed the channel because the other peer established a second one.
279 */
280 Peers_CHANNEL_ESTABLISHED_TWICE = 0x1,
281
282 /**
283 * The channel was removed because it was not needed any more. This should be
284 * the sending channel.
285 */
286 Peers_CHANNEL_CLEAN = 0x2,
287
288 /**
289 * We destroyed the channel because the other peer established a second one.
290 */
291 Peers_CHANNEL_DESTROING = 0x4,
292};
293
294
295/**
296 * @brief The role of a channel. Sending or receiving.
297 */
298enum Peers_ChannelRole
299{
300 /**
301 * Channel is used for sending
302 */
303 Peers_CHANNEL_ROLE_SENDING = 0x01,
304
305 /**
306 * Channel is used for receiving
307 */
308 Peers_CHANNEL_ROLE_RECEIVING = 0x02,
309};
310
311/**
312 * @brief Functions of this type can be used to be stored at a peer for later execution.
313 *
314 * @param cls closure
315 * @param peer peer to execute function on
316 */
317typedef void (*PeerOp) (void *cls, const struct GNUNET_PeerIdentity *peer);
318
319/**
320 * @brief Iterator over valid peers.
321 *
322 * @param cls closure
323 * @param peer current public peer id
324 * @return #GNUNET_YES if we should continue to
325 * iterate,
326 * #GNUNET_NO if not.
327 */
328typedef int
329(*PeersIterator) (void *cls,
330 const struct GNUNET_PeerIdentity *peer);
331
332
333/**
334 * Handle to the statistics service.
335 */
336extern struct GNUNET_STATISTICS_Handle *stats;
diff --git a/src/contrib/service/rps/rps_api.c b/src/contrib/service/rps/rps_api.c
new file mode 100644
index 000000000..c4166745a
--- /dev/null
+++ b/src/contrib/service/rps/rps_api.c
@@ -0,0 +1,1320 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file rps/rps_api.c
23 * @brief API for rps
24 * @author Julius Bünger
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "rps.h"
29#include "gnunet_rps_service.h"
30#include "rps-sampler_client.h"
31
32#include "gnunet_nse_service.h"
33
34#include <inttypes.h>
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "rps-api", __VA_ARGS__)
37
38/**
39 * Handle for a request to get peers from biased stream of ids
40 */
41struct GNUNET_RPS_StreamRequestHandle
42{
43 /**
44 * The client issuing the request.
45 */
46 struct GNUNET_RPS_Handle *rps_handle;
47
48 /**
49 * The callback to be called when we receive an answer.
50 */
51 GNUNET_RPS_NotifyReadyCB ready_cb;
52
53 /**
54 * The closure for the callback.
55 */
56 void *ready_cb_cls;
57
58 /**
59 * @brief Scheduler task for scheduled callback
60 */
61 struct GNUNET_SCHEDULER_Task *callback_task;
62
63 /**
64 * @brief Next element of the DLL
65 */
66 struct GNUNET_RPS_StreamRequestHandle *next;
67
68 /**
69 * @brief Previous element of the DLL
70 */
71 struct GNUNET_RPS_StreamRequestHandle *prev;
72};
73
74
75/**
76 * Handler to handle requests from a client.
77 */
78struct GNUNET_RPS_Handle
79{
80 /**
81 * The handle to the client configuration.
82 */
83 const struct GNUNET_CONFIGURATION_Handle *cfg;
84
85 /**
86 * The message queue to the client.
87 */
88 struct GNUNET_MQ_Handle *mq;
89
90 /**
91 * @brief Callback called on each update of the view
92 */
93 GNUNET_RPS_NotifyReadyCB view_update_cb;
94
95 /**
96 * @brief Closure to each requested update of the view
97 */
98 void *view_update_cls;
99
100 /**
101 * @brief Closure to each requested peer from the biased stream
102 */
103 void *stream_input_cls;
104
105 /**
106 * @brief Head of the DLL of stream requests
107 */
108 struct GNUNET_RPS_StreamRequestHandle *stream_requests_head;
109
110 /**
111 * @brief Tail of the DLL of stream requests
112 */
113 struct GNUNET_RPS_StreamRequestHandle *stream_requests_tail;
114
115 /**
116 * @brief Handle to nse service
117 */
118 struct GNUNET_NSE_Handle *nse;
119
120 /**
121 * @brief Pointer to the head element in DLL of request handles
122 */
123 struct GNUNET_RPS_Request_Handle *rh_head;
124
125 /**
126 * @brief Pointer to the tail element in DLL of request handles
127 */
128 struct GNUNET_RPS_Request_Handle *rh_tail;
129
130 /**
131 * @brief Pointer to the head element in DLL of single request handles
132 */
133 struct GNUNET_RPS_Request_Handle_Single_Info *rhs_head;
134
135 /**
136 * @brief Pointer to the tail element in DLL of single request handles
137 */
138 struct GNUNET_RPS_Request_Handle_Single_Info *rhs_tail;
139
140 /**
141 * @brief The desired probability with which we want to have observed all
142 * peers.
143 */
144 float desired_probability;
145
146 /**
147 * @brief A factor that catches the 'bias' of a random stream of peer ids.
148 *
149 * As introduced by Brahms: Factor between the number of unique ids in a
150 * truly random stream and number of unique ids in the gossip stream.
151 */
152 float deficiency_factor;
153};
154
155
156/**
157 * Handler for a single request from a client.
158 */
159struct GNUNET_RPS_Request_Handle
160{
161 /**
162 * The client issuing the request.
163 */
164 struct GNUNET_RPS_Handle *rps_handle;
165
166 /**
167 * The number of requested peers.
168 */
169 uint32_t num_requests;
170
171 /**
172 * @brief The Sampler for the client request
173 */
174 struct RPS_Sampler *sampler;
175
176 /**
177 * @brief Request handle of the request to the sampler - needed to cancel the request
178 */
179 struct RPS_SamplerRequestHandle *sampler_rh;
180
181 /**
182 * @brief Request handle of the request of the biased stream of peers -
183 * needed to cancel the request
184 */
185 struct GNUNET_RPS_StreamRequestHandle *srh;
186
187 /**
188 * The callback to be called when we receive an answer.
189 */
190 GNUNET_RPS_NotifyReadyCB ready_cb;
191
192 /**
193 * The closure for the callback.
194 */
195 void *ready_cb_cls;
196
197 /**
198 * @brief Pointer to next element in DLL
199 */
200 struct GNUNET_RPS_Request_Handle *next;
201
202 /**
203 * @brief Pointer to previous element in DLL
204 */
205 struct GNUNET_RPS_Request_Handle *prev;
206};
207
208
209/**
210 * Handler for a single request from a client.
211 */
212struct GNUNET_RPS_Request_Handle_Single_Info
213{
214 /**
215 * The client issuing the request.
216 */
217 struct GNUNET_RPS_Handle *rps_handle;
218
219 /**
220 * @brief The Sampler for the client request
221 */
222 struct RPS_Sampler *sampler;
223
224 /**
225 * @brief Request handle of the request to the sampler - needed to cancel the request
226 */
227 struct RPS_SamplerRequestHandleSingleInfo *sampler_rh;
228
229 /**
230 * @brief Request handle of the request of the biased stream of peers -
231 * needed to cancel the request
232 */
233 struct GNUNET_RPS_StreamRequestHandle *srh;
234
235 /**
236 * The callback to be called when we receive an answer.
237 */
238 GNUNET_RPS_NotifyReadySingleInfoCB ready_cb;
239
240 /**
241 * The closure for the callback.
242 */
243 void *ready_cb_cls;
244
245 /**
246 * @brief Pointer to next element in DLL
247 */
248 struct GNUNET_RPS_Request_Handle_Single_Info *next;
249
250 /**
251 * @brief Pointer to previous element in DLL
252 */
253 struct GNUNET_RPS_Request_Handle_Single_Info *prev;
254};
255
256
257/**
258 * Struct used to pack the callback, its closure (provided by the caller)
259 * and the connection handler to the service to pass it to a callback function.
260 */
261struct cb_cls_pack
262{
263 /**
264 * Callback provided by the client
265 */
266 GNUNET_RPS_NotifyReadyCB cb;
267
268 /**
269 * Closure provided by the client
270 */
271 void *cls;
272
273 /**
274 * Handle to the service connection
275 */
276 struct GNUNET_CLIENT_Connection *service_conn;
277};
278
279
280/**
281 * @brief Peers received from the biased stream to be passed to all
282 * srh_handlers
283 */
284static struct GNUNET_PeerIdentity *srh_callback_peers;
285
286/**
287 * @brief Number of peers in the biased stream that are to be passed to all
288 * srh_handlers
289 */
290static uint64_t srh_callback_num_peers;
291
292
293/**
294 * @brief Create a new handle for a stream request
295 *
296 * @param rps_handle The rps handle
297 * @param num_peers The number of desired peers
298 * @param ready_cb The callback to be called, once all peers are ready
299 * @param cls The colsure to provide to the callback
300 *
301 * @return The handle to the stream request
302 */
303static struct GNUNET_RPS_StreamRequestHandle *
304new_stream_request (struct GNUNET_RPS_Handle *rps_handle,
305 GNUNET_RPS_NotifyReadyCB ready_cb,
306 void *cls)
307{
308 struct GNUNET_RPS_StreamRequestHandle *srh;
309
310 srh = GNUNET_new (struct GNUNET_RPS_StreamRequestHandle);
311 srh->rps_handle = rps_handle;
312 srh->ready_cb = ready_cb;
313 srh->ready_cb_cls = cls;
314 GNUNET_CONTAINER_DLL_insert (rps_handle->stream_requests_head,
315 rps_handle->stream_requests_tail,
316 srh);
317
318 return srh;
319}
320
321
322/**
323 * @brief Remove the given stream request from the list of requests and memory
324 *
325 * @param srh The request to be removed
326 */
327static void
328remove_stream_request (struct GNUNET_RPS_StreamRequestHandle *srh)
329{
330 struct GNUNET_RPS_Handle *rps_handle = srh->rps_handle;
331
332 GNUNET_assert (NULL != srh);
333 if (NULL != srh->callback_task)
334 {
335 GNUNET_SCHEDULER_cancel (srh->callback_task);
336 srh->callback_task = NULL;
337 }
338 GNUNET_CONTAINER_DLL_remove (rps_handle->stream_requests_head,
339 rps_handle->stream_requests_tail,
340 srh);
341 GNUNET_free (srh);
342}
343
344
345/**
346 * @brief Called once the sampler has collected all requested peers.
347 *
348 * Calls the callback provided by the client with the corresponding cls.
349 *
350 * @param peers The array of @a num_peers that has been returned.
351 * @param num_peers The number of peers that have been returned
352 * @param cls The #GNUNET_RPS_Request_Handle
353 */
354static void
355peers_ready_cb (const struct GNUNET_PeerIdentity *peers,
356 uint32_t num_peers,
357 void *cls)
358{
359 struct GNUNET_RPS_Request_Handle *rh = cls;
360
361 rh->sampler_rh = NULL;
362 rh->ready_cb (rh->ready_cb_cls,
363 num_peers,
364 peers);
365 GNUNET_RPS_request_cancel (rh);
366}
367
368
369/**
370 * @brief Called once the sampler has collected the requested peer.
371 *
372 * Calls the callback provided by the client with the corresponding cls.
373 *
374 * @param peers The array of @a num_peers that has been returned.
375 * @param num_peers The number of peers that have been returned
376 * @param cls The #GNUNET_RPS_Request_Handle
377 * @param probability Probability with which all IDs have been observed
378 * @param num_observed Number of observed IDs
379 */
380static void
381peer_info_ready_cb (const struct GNUNET_PeerIdentity *peers,
382 void *cls,
383 double probability,
384 uint32_t num_observed)
385{
386 struct GNUNET_RPS_Request_Handle_Single_Info *rh = cls;
387
388 rh->sampler_rh = NULL;
389 rh->ready_cb (rh->ready_cb_cls,
390 peers,
391 probability,
392 num_observed);
393 GNUNET_RPS_request_single_info_cancel (rh);
394}
395
396
397/**
398 * @brief Callback to collect the peers from the biased stream and put those
399 * into the sampler.
400 *
401 * @param cls The #GNUNET_RPS_Request_Handle
402 * @param num_peers The number of peer that have been returned
403 * @param peers The array of @a num_peers that have been returned
404 */
405static void
406collect_peers_cb (void *cls,
407 uint64_t num_peers,
408 const struct GNUNET_PeerIdentity *peers)
409{
410 struct GNUNET_RPS_Request_Handle *rh = cls;
411
412 LOG (GNUNET_ERROR_TYPE_DEBUG,
413 "Service sent %" PRIu64 " peers from stream\n",
414 num_peers);
415 for (uint64_t i = 0; i < num_peers; i++)
416 {
417 RPS_sampler_update (rh->sampler, &peers[i]);
418 }
419}
420
421
422/**
423 * @brief Callback to collect the peers from the biased stream and put those
424 * into the sampler.
425 *
426 * This version is for the modified #GNUNET_RPS_Request_Handle_Single_Info
427 *
428 * @param cls The #GNUNET_RPS_Request_Handle
429 * @param num_peers The number of peer that have been returned
430 * @param peers The array of @a num_peers that have been returned
431 */
432static void
433collect_peers_info_cb (void *cls,
434 uint64_t num_peers,
435 const struct GNUNET_PeerIdentity *peers)
436{
437 struct GNUNET_RPS_Request_Handle_Single_Info *rhs = cls;
438
439 LOG (GNUNET_ERROR_TYPE_DEBUG,
440 "Service sent %" PRIu64 " peers from stream\n",
441 num_peers);
442 for (uint64_t i = 0; i < num_peers; i++)
443 {
444 RPS_sampler_update (rhs->sampler, &peers[i]);
445 }
446}
447
448
449/* Get internals for debugging/profiling purposes */
450
451/**
452 * Request updates of view
453 *
454 * @param rps_handle handle to the rps service
455 * @param num_req_peers number of peers we want to receive
456 * (0 for infinite updates)
457 * @param cls a closure that will be given to the callback
458 * @param ready_cb the callback called when the peers are available
459 */
460void
461GNUNET_RPS_view_request (struct GNUNET_RPS_Handle *rps_handle,
462 uint32_t num_updates,
463 GNUNET_RPS_NotifyReadyCB view_update_cb,
464 void *cls)
465{
466 struct GNUNET_MQ_Envelope *ev;
467 struct GNUNET_RPS_CS_DEBUG_ViewRequest *msg;
468
469 LOG (GNUNET_ERROR_TYPE_DEBUG,
470 "Client requests %" PRIu32 " view updates\n",
471 num_updates);
472 rps_handle->view_update_cb = view_update_cb;
473 rps_handle->view_update_cls = cls;
474
475 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_REQUEST);
476 msg->num_updates = htonl (num_updates);
477 GNUNET_MQ_send (rps_handle->mq, ev);
478}
479
480
481void
482GNUNET_RPS_view_request_cancel (struct GNUNET_RPS_Handle *rps_handle)
483{
484 struct GNUNET_MQ_Envelope *ev;
485
486 GNUNET_assert (NULL != rps_handle->view_update_cb);
487
488 rps_handle->view_update_cb = NULL;
489
490 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_CANCEL);
491 GNUNET_MQ_send (rps_handle->mq, ev);
492}
493
494
495/**
496 * Request biased stream of peers that are being put into the sampler
497 *
498 * @param rps_handle handle to the rps service
499 * @param cls a closure that will be given to the callback
500 * @param ready_cb the callback called when the peers are available
501 */
502struct GNUNET_RPS_StreamRequestHandle *
503GNUNET_RPS_stream_request (struct GNUNET_RPS_Handle *rps_handle,
504 GNUNET_RPS_NotifyReadyCB stream_input_cb,
505 void *cls)
506{
507 struct GNUNET_RPS_StreamRequestHandle *srh;
508 struct GNUNET_MQ_Envelope *ev;
509 struct GNUNET_RPS_CS_DEBUG_StreamRequest *msg;
510
511 srh = new_stream_request (rps_handle,
512 stream_input_cb,
513 cls);
514 LOG (GNUNET_ERROR_TYPE_DEBUG, "Client requests biased stream updates\n");
515
516 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_REQUEST);
517 GNUNET_MQ_send (rps_handle->mq, ev);
518 return srh;
519}
520
521
522/**
523 * This function is called, when the service updates the view.
524 * It verifies that @a msg is well-formed.
525 *
526 * @param cls the closure
527 * @param msg the message
528 * @return #GNUNET_OK if @a msg is well-formed
529 */
530static int
531check_view_update (void *cls,
532 const struct GNUNET_RPS_CS_DEBUG_ViewReply *msg)
533{
534 uint16_t msize = ntohs (msg->header.size);
535 uint32_t num_peers = ntohl (msg->num_peers);
536
537 (void) cls;
538
539 msize -= sizeof(struct GNUNET_RPS_CS_DEBUG_ViewReply);
540 if ((msize / sizeof(struct GNUNET_PeerIdentity) != num_peers) ||
541 (msize % sizeof(struct GNUNET_PeerIdentity) != 0))
542 {
543 GNUNET_break (0);
544 return GNUNET_SYSERR;
545 }
546 return GNUNET_OK;
547}
548
549
550/**
551 * This function is called, when the service updated its view.
552 * It calls the callback the caller provided
553 * and disconnects afterwards.
554 *
555 * @param msg the message
556 */
557static void
558handle_view_update (void *cls,
559 const struct GNUNET_RPS_CS_DEBUG_ViewReply *msg)
560{
561 struct GNUNET_RPS_Handle *h = cls;
562 struct GNUNET_PeerIdentity *peers;
563
564 /* Give the peers back */
565 LOG (GNUNET_ERROR_TYPE_DEBUG,
566 "New view of %" PRIu32 " peers:\n",
567 ntohl (msg->num_peers));
568
569 peers = (struct GNUNET_PeerIdentity *) &msg[1];
570 GNUNET_assert (NULL != h);
571 GNUNET_assert (NULL != h->view_update_cb);
572 h->view_update_cb (h->view_update_cls, ntohl (msg->num_peers), peers);
573}
574
575
576/**
577 * @brief Send message to service that this client does not want to receive
578 * further updates from the biased peer stream
579 *
580 * @param rps_handle The handle representing the service to the client
581 */
582static void
583cancel_stream (struct GNUNET_RPS_Handle *rps_handle)
584{
585 struct GNUNET_MQ_Envelope *ev;
586
587 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_CANCEL);
588 GNUNET_MQ_send (rps_handle->mq, ev);
589}
590
591
592/**
593 * @brief Cancel a specific request for updates from the biased peer stream
594 *
595 * @param srh The request handle to cancel
596 */
597void
598GNUNET_RPS_stream_cancel (struct GNUNET_RPS_StreamRequestHandle *srh)
599{
600 struct GNUNET_RPS_Handle *rps_handle;
601
602 rps_handle = srh->rps_handle;
603 remove_stream_request (srh);
604 if (NULL == rps_handle->stream_requests_head)
605 cancel_stream (rps_handle);
606}
607
608
609/**
610 * This function is called, when the service sends another peer from the biased
611 * stream.
612 * It calls the callback the caller provided
613 * and disconnects afterwards.
614 *
615 * TODO merge with check_view_update
616 *
617 * @param msg the message
618 */
619static int
620check_stream_input (void *cls,
621 const struct GNUNET_RPS_CS_DEBUG_StreamReply *msg)
622{
623 uint16_t msize = ntohs (msg->header.size);
624 uint32_t num_peers = ntohl (msg->num_peers);
625
626 (void) cls;
627
628 msize -= sizeof(struct GNUNET_RPS_CS_DEBUG_StreamReply);
629 if ((msize / sizeof(struct GNUNET_PeerIdentity) != num_peers) ||
630 (msize % sizeof(struct GNUNET_PeerIdentity) != 0))
631 {
632 GNUNET_break (0);
633 return GNUNET_SYSERR;
634 }
635 return GNUNET_OK;
636}
637
638
639/**
640 * @brief Called by the scheduler to call the callbacks of the srh handlers
641 *
642 * @param cls Stream request handle
643 */
644static void
645srh_callback_scheduled (void *cls)
646{
647 struct GNUNET_RPS_StreamRequestHandle *srh = cls;
648
649 srh->callback_task = NULL;
650 srh->ready_cb (srh->ready_cb_cls,
651 srh_callback_num_peers,
652 srh_callback_peers);
653}
654
655
656/**
657 * This function is called, when the service sends another peer from the biased
658 * stream.
659 * It calls the callback the caller provided
660 * and disconnects afterwards.
661 *
662 * @param msg the message
663 */
664static void
665handle_stream_input (void *cls,
666 const struct GNUNET_RPS_CS_DEBUG_StreamReply *msg)
667{
668 struct GNUNET_RPS_Handle *h = cls;
669 // const struct GNUNET_PeerIdentity *peers;
670 uint64_t num_peers;
671 struct GNUNET_RPS_StreamRequestHandle *srh_iter;
672 struct GNUNET_RPS_StreamRequestHandle *srh_next;
673
674 // peers = (struct GNUNET_PeerIdentity *) &msg[1];
675 num_peers = ntohl (msg->num_peers);
676 srh_callback_num_peers = num_peers;
677 GNUNET_free (srh_callback_peers);
678 srh_callback_peers = GNUNET_new_array (num_peers,
679 struct GNUNET_PeerIdentity);
680 GNUNET_memcpy (srh_callback_peers,
681 &msg[1],
682 num_peers * sizeof(struct GNUNET_PeerIdentity));
683 LOG (GNUNET_ERROR_TYPE_DEBUG,
684 "Received %" PRIu64 " peer(s) from stream input.\n",
685 num_peers);
686 for (srh_iter = h->stream_requests_head;
687 NULL != srh_iter;
688 srh_iter = srh_next)
689 {
690 LOG (GNUNET_ERROR_TYPE_DEBUG, "Calling srh \n");
691 /* Store next pointer - srh might be removed/freed in callback */
692 srh_next = srh_iter->next;
693 if (NULL != srh_iter->callback_task)
694 GNUNET_SCHEDULER_cancel (srh_iter->callback_task);
695 srh_iter->callback_task =
696 GNUNET_SCHEDULER_add_now (&srh_callback_scheduled,
697 srh_iter);
698 }
699
700 if (NULL == h->stream_requests_head)
701 {
702 cancel_stream (h);
703 }
704}
705
706
707/**
708 * Reconnect to the service
709 */
710static void
711reconnect (struct GNUNET_RPS_Handle *h);
712
713
714/**
715 * Error handler for mq.
716 *
717 * This function is called when mq encounters an error.
718 * Until now mq doesn't provide useful error messages.
719 *
720 * @param cls the closure
721 * @param error error code without specyfied meaning
722 */
723static void
724mq_error_handler (void *cls,
725 enum GNUNET_MQ_Error error)
726{
727 struct GNUNET_RPS_Handle *h = cls;
728
729 // TODO LOG
730 LOG (GNUNET_ERROR_TYPE_WARNING,
731 "Problem with message queue. error: %i\n\
732 1: READ,\n\
733 2: WRITE,\n\
734 4: TIMEOUT\n",
735 // TODO: write GNUNET_MQ_strerror (error)
736 error);
737 reconnect (h);
738 /* Resend all pending request as the service destroyed its knowledge
739 * about them */
740}
741
742
743/**
744 * @brief Create the hash value from the share value that defines the sub
745 * (-group)
746 *
747 * @param share_val Share value
748 * @param[out] hash Pointer to the location in which the hash will be stored.
749 */
750static void
751hash_from_share_val (const char *share_val,
752 struct GNUNET_HashCode *hash)
753{
754 GNUNET_CRYPTO_kdf (hash,
755 sizeof(struct GNUNET_HashCode),
756 "rps",
757 strlen ("rps"),
758 share_val,
759 strlen (share_val),
760 NULL, 0);
761}
762
763
764/**
765 * @brief Callback for network size estimate - called with new estimates about
766 * the network size, updates all samplers with the new estimate
767 *
768 * Implements #GNUNET_NSE_Callback
769 *
770 * @param cls the rps handle
771 * @param timestamp unused
772 * @param logestimate the estimate
773 * @param std_dev the standard distribution
774 */
775static void
776nse_cb (void *cls,
777 struct GNUNET_TIME_Absolute timestamp,
778 double logestimate,
779 double std_dev)
780{
781 struct GNUNET_RPS_Handle *h = cls;
782
783 (void) timestamp;
784 (void) std_dev;
785
786 for (struct GNUNET_RPS_Request_Handle *rh_iter = h->rh_head;
787 NULL != rh_iter && NULL != rh_iter->next;
788 rh_iter = rh_iter->next)
789 {
790 RPS_sampler_update_with_nw_size (rh_iter->sampler,
791 GNUNET_NSE_log_estimate_to_n (
792 logestimate));
793 }
794 for (struct GNUNET_RPS_Request_Handle_Single_Info *rhs_iter = h->rhs_head;
795 NULL != rhs_iter && NULL != rhs_iter->next;
796 rhs_iter = rhs_iter->next)
797 {
798 RPS_sampler_update_with_nw_size (rhs_iter->sampler,
799 GNUNET_NSE_log_estimate_to_n (
800 logestimate));
801 }
802}
803
804
805/**
806 * Reconnect to the service
807 */
808static void
809reconnect (struct GNUNET_RPS_Handle *h)
810{
811 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
812 GNUNET_MQ_hd_var_size (view_update,
813 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_VIEW_REPLY,
814 struct GNUNET_RPS_CS_DEBUG_ViewReply,
815 h),
816 GNUNET_MQ_hd_var_size (stream_input,
817 GNUNET_MESSAGE_TYPE_RPS_CS_DEBUG_STREAM_REPLY,
818 struct GNUNET_RPS_CS_DEBUG_StreamReply,
819 h),
820 GNUNET_MQ_handler_end ()
821 };
822
823 if (NULL != h->mq)
824 GNUNET_MQ_destroy (h->mq);
825 h->mq = GNUNET_CLIENT_connect (h->cfg,
826 "rps",
827 mq_handlers,
828 &mq_error_handler,
829 h);
830 if (NULL != h->nse)
831 GNUNET_NSE_disconnect (h->nse);
832 h->nse = GNUNET_NSE_connect (h->cfg, &nse_cb, h);
833}
834
835
836/**
837 * Connect to the rps service
838 *
839 * @param cfg configuration to use
840 * @return a handle to the service, NULL on error
841 */
842struct GNUNET_RPS_Handle *
843GNUNET_RPS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
844{
845 struct GNUNET_RPS_Handle *h;
846
847 h = GNUNET_new (struct GNUNET_RPS_Handle);
848 h->cfg = cfg;
849 if (GNUNET_OK !=
850 GNUNET_CONFIGURATION_get_value_float (cfg,
851 "RPS",
852 "DESIRED_PROBABILITY",
853 &h->desired_probability))
854 {
855 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
856 "RPS", "DESIRED_PROBABILITY");
857 GNUNET_free (h);
858 return NULL;
859 }
860 if ((0 > h->desired_probability) ||
861 (1 < h->desired_probability) )
862 {
863 LOG (GNUNET_ERROR_TYPE_ERROR,
864 "The desired probability must be in the interval [0;1]\n");
865 GNUNET_free (h);
866 return NULL;
867 }
868 if (GNUNET_OK !=
869 GNUNET_CONFIGURATION_get_value_float (cfg,
870 "RPS",
871 "DEFICIENCY_FACTOR",
872 &h->deficiency_factor))
873 {
874 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
875 "RPS", "DEFICIENCY_FACTOR");
876 GNUNET_free (h);
877 return NULL;
878 }
879 if ((0 > h->desired_probability) ||
880 (1 < h->desired_probability) )
881 {
882 LOG (GNUNET_ERROR_TYPE_ERROR,
883 "The deficiency factor must be in the interval [0;1]\n");
884 GNUNET_free (h);
885 return NULL;
886 }
887 reconnect (h);
888 if (NULL == h->mq)
889 {
890 GNUNET_free (h);
891 return NULL;
892 }
893 return h;
894}
895
896
897/**
898 * @brief Start a sub with the given shared value
899 *
900 * @param h Handle to rps
901 * @param shared_value The shared value that defines the members of the sub (-gorup)
902 */
903void
904GNUNET_RPS_sub_start (struct GNUNET_RPS_Handle *h,
905 const char *shared_value)
906{
907 struct GNUNET_RPS_CS_SubStartMessage *msg;
908 struct GNUNET_MQ_Envelope *ev;
909
910 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RPS_CS_SUB_START);
911 hash_from_share_val (shared_value, &msg->hash);
912 msg->round_interval = GNUNET_TIME_relative_hton ( // TODO read from config!
913 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30));
914 GNUNET_assert (0 != msg->round_interval.rel_value_us__);
915
916 GNUNET_MQ_send (h->mq, ev);
917}
918
919
920/**
921 * @brief Stop a sub with the given shared value
922 *
923 * @param h Handle to rps
924 * @param shared_value The shared value that defines the members of the sub (-gorup)
925 */
926void
927GNUNET_RPS_sub_stop (struct GNUNET_RPS_Handle *h,
928 const char *shared_value)
929{
930 struct GNUNET_RPS_CS_SubStopMessage *msg;
931 struct GNUNET_MQ_Envelope *ev;
932
933 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RPS_CS_SUB_STOP);
934 hash_from_share_val (shared_value, &msg->hash);
935
936 GNUNET_MQ_send (h->mq, ev);
937}
938
939
940/**
941 * Request n random peers.
942 *
943 * @param rps_handle handle to the rps service
944 * @param num_req_peers number of peers we want to receive
945 * @param ready_cb the callback called when the peers are available
946 * @param cls closure given to the callback
947 * @return a handle to cancel this request
948 */
949struct GNUNET_RPS_Request_Handle *
950GNUNET_RPS_request_peers (struct GNUNET_RPS_Handle *rps_handle,
951 uint32_t num_req_peers,
952 GNUNET_RPS_NotifyReadyCB ready_cb,
953 void *cls)
954{
955 struct GNUNET_RPS_Request_Handle *rh;
956
957 LOG (GNUNET_ERROR_TYPE_INFO,
958 "Client requested %" PRIu32 " peers\n",
959 num_req_peers);
960 rh = GNUNET_new (struct GNUNET_RPS_Request_Handle);
961 rh->rps_handle = rps_handle;
962 rh->num_requests = num_req_peers;
963 rh->sampler = RPS_sampler_mod_init (num_req_peers,
964 GNUNET_TIME_UNIT_SECONDS); // TODO remove this time-stuff
965 RPS_sampler_set_desired_probability (rh->sampler,
966 rps_handle->desired_probability);
967 RPS_sampler_set_deficiency_factor (rh->sampler,
968 rps_handle->deficiency_factor);
969 rh->sampler_rh = RPS_sampler_get_n_rand_peers (rh->sampler,
970 num_req_peers,
971 peers_ready_cb,
972 rh);
973 rh->srh = GNUNET_RPS_stream_request (rps_handle,
974 collect_peers_cb,
975 rh); /* cls */
976 rh->ready_cb = ready_cb;
977 rh->ready_cb_cls = cls;
978 GNUNET_CONTAINER_DLL_insert (rps_handle->rh_head,
979 rps_handle->rh_tail,
980 rh);
981
982 return rh;
983}
984
985
986/**
987 * Request one random peer, getting additional information.
988 *
989 * @param rps_handle handle to the rps service
990 * @param ready_cb the callback called when the peers are available
991 * @param cls closure given to the callback
992 * @return a handle to cancel this request
993 */
994struct GNUNET_RPS_Request_Handle_Single_Info *
995GNUNET_RPS_request_peer_info (struct GNUNET_RPS_Handle *rps_handle,
996 GNUNET_RPS_NotifyReadySingleInfoCB ready_cb,
997 void *cls)
998{
999 struct GNUNET_RPS_Request_Handle_Single_Info *rhs;
1000 uint32_t num_req_peers = 1;
1001
1002 LOG (GNUNET_ERROR_TYPE_INFO,
1003 "Client requested peer with additional info\n");
1004 rhs = GNUNET_new (struct GNUNET_RPS_Request_Handle_Single_Info);
1005 rhs->rps_handle = rps_handle;
1006 rhs->sampler = RPS_sampler_mod_init (num_req_peers,
1007 GNUNET_TIME_UNIT_SECONDS); // TODO remove this time-stuff
1008 RPS_sampler_set_desired_probability (rhs->sampler,
1009 rps_handle->desired_probability);
1010 RPS_sampler_set_deficiency_factor (rhs->sampler,
1011 rps_handle->deficiency_factor);
1012 rhs->sampler_rh = RPS_sampler_get_rand_peer_info (rhs->sampler,
1013 peer_info_ready_cb,
1014 rhs);
1015 rhs->srh = GNUNET_RPS_stream_request (rps_handle,
1016 collect_peers_info_cb,
1017 rhs); /* cls */
1018 rhs->ready_cb = ready_cb;
1019 rhs->ready_cb_cls = cls;
1020 GNUNET_CONTAINER_DLL_insert (rps_handle->rhs_head,
1021 rps_handle->rhs_tail,
1022 rhs);
1023
1024 return rhs;
1025}
1026
1027
1028/**
1029 * Seed rps service with peerIDs.
1030 *
1031 * @param h handle to the rps service
1032 * @param n number of peers to seed
1033 * @param ids the ids of the peers seeded
1034 */
1035void
1036GNUNET_RPS_seed_ids (struct GNUNET_RPS_Handle *h,
1037 uint32_t n,
1038 const struct GNUNET_PeerIdentity *ids)
1039{
1040 size_t size_needed;
1041 uint32_t num_peers_max;
1042 const struct GNUNET_PeerIdentity *tmp_peer_pointer;
1043 struct GNUNET_MQ_Envelope *ev;
1044 struct GNUNET_RPS_CS_SeedMessage *msg;
1045
1046 LOG (GNUNET_ERROR_TYPE_DEBUG,
1047 "Client wants to seed %" PRIu32 " peers:\n",
1048 n);
1049 for (unsigned int i = 0; i < n; i++)
1050 LOG (GNUNET_ERROR_TYPE_DEBUG,
1051 "%u. peer: %s\n",
1052 i,
1053 GNUNET_i2s (&ids[i]));
1054
1055 /* The actual size the message occupies */
1056 size_needed = sizeof(struct GNUNET_RPS_CS_SeedMessage)
1057 + n * sizeof(struct GNUNET_PeerIdentity);
1058 /* The number of peers that fits in one message together with
1059 * the respective header */
1060 num_peers_max = (GNUNET_MAX_MESSAGE_SIZE
1061 - sizeof(struct GNUNET_RPS_CS_SeedMessage))
1062 / sizeof(struct GNUNET_PeerIdentity);
1063 tmp_peer_pointer = ids;
1064
1065 while (GNUNET_MAX_MESSAGE_SIZE < size_needed)
1066 {
1067 ev = GNUNET_MQ_msg_extra (msg,
1068 num_peers_max * sizeof(struct
1069 GNUNET_PeerIdentity),
1070 GNUNET_MESSAGE_TYPE_RPS_CS_SEED);
1071 msg->num_peers = htonl (num_peers_max);
1072 GNUNET_memcpy (&msg[1],
1073 tmp_peer_pointer,
1074 num_peers_max * sizeof(struct GNUNET_PeerIdentity));
1075 GNUNET_MQ_send (h->mq,
1076 ev);
1077 n -= num_peers_max;
1078 size_needed = sizeof(struct GNUNET_RPS_CS_SeedMessage)
1079 + n * sizeof(struct GNUNET_PeerIdentity);
1080 /* Set pointer to beginning of next block of num_peers_max peers */
1081 tmp_peer_pointer = &ids[num_peers_max];
1082 }
1083
1084 ev = GNUNET_MQ_msg_extra (msg,
1085 n * sizeof(struct GNUNET_PeerIdentity),
1086 GNUNET_MESSAGE_TYPE_RPS_CS_SEED);
1087 msg->num_peers = htonl (n);
1088 GNUNET_memcpy (&msg[1],
1089 tmp_peer_pointer,
1090 n * sizeof(struct GNUNET_PeerIdentity));
1091 GNUNET_MQ_send (h->mq,
1092 ev);
1093}
1094
1095
1096#if ENABLE_MALICIOUS
1097/**
1098 * Turn RPS service to act malicious.
1099 *
1100 * @param h handle to the rps service
1101 * @param type which type of malicious peer to turn to.
1102 * 0 Don't act malicious at all
1103 * 1 Try to maximise representation
1104 * 2 Try to partition the network
1105 * (isolate one peer from the rest)
1106 * @param n number of @a ids
1107 * @param ids the ids of the malicious peers
1108 * if @type is 2 the last id is the id of the
1109 * peer to be isolated from the rest
1110 */
1111void
1112GNUNET_RPS_act_malicious (struct GNUNET_RPS_Handle *h,
1113 uint32_t type,
1114 uint32_t num_peers,
1115 const struct GNUNET_PeerIdentity *peer_ids,
1116 const struct GNUNET_PeerIdentity *target_peer)
1117{
1118 size_t size_needed;
1119 uint32_t num_peers_max;
1120 const struct GNUNET_PeerIdentity *tmp_peer_pointer;
1121 struct GNUNET_MQ_Envelope *ev;
1122 struct GNUNET_RPS_CS_ActMaliciousMessage *msg;
1123
1124 unsigned int i;
1125
1126 LOG (GNUNET_ERROR_TYPE_DEBUG,
1127 "Client turns malicious (type %" PRIu32 ") with %" PRIu32
1128 " other peers:\n",
1129 type,
1130 num_peers);
1131 for (i = 0; i < num_peers; i++)
1132 LOG (GNUNET_ERROR_TYPE_DEBUG,
1133 "%u. peer: %s\n",
1134 i,
1135 GNUNET_i2s (&peer_ids[i]));
1136
1137 /* The actual size the message would occupy */
1138 size_needed = sizeof(struct GNUNET_RPS_CS_SeedMessage)
1139 + num_peers * sizeof(struct GNUNET_PeerIdentity);
1140 /* The number of peers that fit in one message together with
1141 * the respective header */
1142 num_peers_max = (GNUNET_MAX_MESSAGE_SIZE
1143 - sizeof(struct GNUNET_RPS_CS_SeedMessage))
1144 / sizeof(struct GNUNET_PeerIdentity);
1145 tmp_peer_pointer = peer_ids;
1146
1147 while (GNUNET_MAX_MESSAGE_SIZE < size_needed)
1148 {
1149 LOG (GNUNET_ERROR_TYPE_DEBUG,
1150 "Too many peers to send at once, sending %" PRIu32
1151 " (all we can so far)\n",
1152 num_peers_max);
1153 ev = GNUNET_MQ_msg_extra (msg,
1154 num_peers_max * sizeof(struct
1155 GNUNET_PeerIdentity),
1156 GNUNET_MESSAGE_TYPE_RPS_ACT_MALICIOUS);
1157 msg->type = htonl (type);
1158 msg->num_peers = htonl (num_peers_max);
1159 if ((2 == type) ||
1160 (3 == type))
1161 msg->attacked_peer = peer_ids[num_peers];
1162 GNUNET_memcpy (&msg[1],
1163 tmp_peer_pointer,
1164 num_peers_max * sizeof(struct GNUNET_PeerIdentity));
1165
1166 GNUNET_MQ_send (h->mq, ev);
1167
1168 num_peers -= num_peers_max;
1169 size_needed = sizeof(struct GNUNET_RPS_CS_SeedMessage)
1170 + num_peers * sizeof(struct GNUNET_PeerIdentity);
1171 /* Set pointer to beginning of next block of num_peers_max peers */
1172 tmp_peer_pointer = &peer_ids[num_peers_max];
1173 }
1174
1175 ev = GNUNET_MQ_msg_extra (msg,
1176 num_peers * sizeof(struct GNUNET_PeerIdentity),
1177 GNUNET_MESSAGE_TYPE_RPS_ACT_MALICIOUS);
1178 msg->type = htonl (type);
1179 msg->num_peers = htonl (num_peers);
1180 if ((2 == type) ||
1181 (3 == type))
1182 msg->attacked_peer = *target_peer;
1183 GNUNET_memcpy (&msg[1],
1184 tmp_peer_pointer,
1185 num_peers * sizeof(struct GNUNET_PeerIdentity));
1186
1187 GNUNET_MQ_send (h->mq, ev);
1188}
1189
1190
1191#endif /* ENABLE_MALICIOUS */
1192
1193
1194/**
1195 * Cancel an issued request.
1196 *
1197 * @param rh request handle of request to cancel
1198 */
1199void
1200GNUNET_RPS_request_cancel (struct GNUNET_RPS_Request_Handle *rh)
1201{
1202 struct GNUNET_RPS_Handle *h;
1203
1204 h = rh->rps_handle;
1205 GNUNET_assert (NULL != rh);
1206 GNUNET_assert (NULL != rh->srh);
1207 GNUNET_assert (h == rh->srh->rps_handle);
1208 GNUNET_RPS_stream_cancel (rh->srh);
1209 rh->srh = NULL;
1210 if (NULL == h->stream_requests_head)
1211 cancel_stream (h);
1212 if (NULL != rh->sampler_rh)
1213 {
1214 RPS_sampler_request_cancel (rh->sampler_rh);
1215 }
1216 RPS_sampler_destroy (rh->sampler);
1217 rh->sampler = NULL;
1218 GNUNET_CONTAINER_DLL_remove (h->rh_head,
1219 h->rh_tail,
1220 rh);
1221 GNUNET_free (rh);
1222}
1223
1224
1225/**
1226 * Cancel an issued single info request.
1227 *
1228 * @param rhs request handle of request to cancel
1229 */
1230void
1231GNUNET_RPS_request_single_info_cancel (
1232 struct GNUNET_RPS_Request_Handle_Single_Info *rhs)
1233{
1234 struct GNUNET_RPS_Handle *h;
1235
1236 h = rhs->rps_handle;
1237 GNUNET_assert (NULL != rhs);
1238 GNUNET_assert (NULL != rhs->srh);
1239 GNUNET_assert (h == rhs->srh->rps_handle);
1240 GNUNET_RPS_stream_cancel (rhs->srh);
1241 rhs->srh = NULL;
1242 if (NULL == h->stream_requests_head)
1243 cancel_stream (h);
1244 if (NULL != rhs->sampler_rh)
1245 {
1246 RPS_sampler_request_single_info_cancel (rhs->sampler_rh);
1247 }
1248 RPS_sampler_destroy (rhs->sampler);
1249 rhs->sampler = NULL;
1250 GNUNET_CONTAINER_DLL_remove (h->rhs_head,
1251 h->rhs_tail,
1252 rhs);
1253 GNUNET_free (rhs);
1254}
1255
1256
1257/**
1258 * Disconnect from the rps service
1259 *
1260 * @param h the handle to the rps service
1261 */
1262void
1263GNUNET_RPS_disconnect (struct GNUNET_RPS_Handle *h)
1264{
1265 if (NULL != h->stream_requests_head)
1266 {
1267 struct GNUNET_RPS_StreamRequestHandle *srh_next;
1268
1269 LOG (GNUNET_ERROR_TYPE_WARNING,
1270 "Still waiting for replies\n");
1271 for (struct GNUNET_RPS_StreamRequestHandle *srh_iter =
1272 h->stream_requests_head;
1273 NULL != srh_iter;
1274 srh_iter = srh_next)
1275 {
1276 srh_next = srh_iter->next;
1277 GNUNET_RPS_stream_cancel (srh_iter);
1278 }
1279 }
1280 if (NULL != h->rh_head)
1281 {
1282 LOG (GNUNET_ERROR_TYPE_WARNING,
1283 "Not all requests were cancelled!\n");
1284 for (struct GNUNET_RPS_Request_Handle *rh_iter = h->rh_head;
1285 h->rh_head != NULL;
1286 rh_iter = h->rh_head)
1287 {
1288 GNUNET_RPS_request_cancel (rh_iter);
1289 }
1290 }
1291 if (NULL != h->rhs_head)
1292 {
1293 LOG (GNUNET_ERROR_TYPE_WARNING,
1294 "Not all requests were cancelled!\n");
1295 for (struct GNUNET_RPS_Request_Handle_Single_Info *rhs_iter = h->rhs_head;
1296 h->rhs_head != NULL;
1297 rhs_iter = h->rhs_head)
1298 {
1299 GNUNET_RPS_request_single_info_cancel (rhs_iter);
1300 }
1301 }
1302 if (NULL != srh_callback_peers)
1303 {
1304 GNUNET_free (srh_callback_peers);
1305 srh_callback_peers = NULL;
1306 }
1307 if (NULL != h->view_update_cb)
1308 {
1309 LOG (GNUNET_ERROR_TYPE_WARNING,
1310 "Still waiting for view updates\n");
1311 GNUNET_RPS_view_request_cancel (h);
1312 }
1313 if (NULL != h->nse)
1314 GNUNET_NSE_disconnect (h->nse);
1315 GNUNET_MQ_destroy (h->mq);
1316 GNUNET_free (h);
1317}
1318
1319
1320/* end of rps_api.c */
diff --git a/src/contrib/service/rps/test_rps.c b/src/contrib/service/rps/test_rps.c
new file mode 100644
index 000000000..ab132ec8a
--- /dev/null
+++ b/src/contrib/service/rps/test_rps.c
@@ -0,0 +1,3183 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/test_rps.c
22 * @brief Testcase for the random peer sampling service. Starts
23 * a peergroup with a given number of peers, then waits to
24 * receive size pushes/pulls from each peer. Expects to wait
25 * for one message from each peer.
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testbed_service.h"
30
31#include "gnunet_rps_service.h"
32#include "rps-test_util.h"
33#include "gnunet-service-rps_sampler_elem.h"
34
35#include <inttypes.h>
36
37
38/**
39 * How many peers do we start?
40 */
41static uint32_t num_peers;
42
43/**
44 * How long do we run the test?
45 * In seconds.
46 */
47static uint32_t timeout_s;
48
49/**
50 * How long do we run the test?
51 */
52// #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
53static struct GNUNET_TIME_Relative timeout;
54
55
56/**
57 * Portion of malicious peers
58 */
59static double portion = .1;
60
61/**
62 * Type of malicious peer to test
63 */
64static unsigned int mal_type = 0;
65
66/**
67 * Handles to all of the running peers
68 */
69static struct GNUNET_TESTBED_Peer **testbed_peers;
70
71/**
72 * @brief Indicates whether peer should go off- or online
73 */
74enum PEER_ONLINE_DELTA
75{
76 /**
77 * @brief Indicates peer going online
78 */
79 PEER_GO_ONLINE = 1,
80 /**
81 * @brief Indicates peer going offline
82 */
83 PEER_GO_OFFLINE = -1,
84};
85
86/**
87 * Operation map entry
88 */
89struct OpListEntry
90{
91 /**
92 * DLL next ptr
93 */
94 struct OpListEntry *next;
95
96 /**
97 * DLL prev ptr
98 */
99 struct OpListEntry *prev;
100
101 /**
102 * The testbed operation
103 */
104 struct GNUNET_TESTBED_Operation *op;
105
106 /**
107 * Depending on whether we start or stop RPS service at the peer, set this to
108 * #PEER_GO_ONLINE (1) or #PEER_GO_OFFLINE (-1)
109 */
110 enum PEER_ONLINE_DELTA delta;
111
112 /**
113 * Index of the regarding peer
114 */
115 unsigned int index;
116};
117
118/**
119 * OpList DLL head
120 */
121static struct OpListEntry *oplist_head;
122
123/**
124 * OpList DLL tail
125 */
126static struct OpListEntry *oplist_tail;
127
128
129/**
130 * A pending reply: A request was sent and the reply is pending.
131 */
132struct PendingReply
133{
134 /**
135 * DLL next,prev ptr
136 */
137 struct PendingReply *next;
138 struct PendingReply *prev;
139
140 /**
141 * Handle to the request we are waiting for
142 */
143 struct GNUNET_RPS_Request_Handle *req_handle;
144
145 /**
146 * The peer that requested
147 */
148 struct RPSPeer *rps_peer;
149};
150
151
152/**
153 * A pending request: A request was not made yet but is scheduled for later.
154 */
155struct PendingRequest
156{
157 /**
158 * DLL next,prev ptr
159 */
160 struct PendingRequest *next;
161 struct PendingRequest *prev;
162
163 /**
164 * Handle to the request we are waiting for
165 */
166 struct GNUNET_SCHEDULER_Task *request_task;
167
168 /**
169 * The peer that requested
170 */
171 struct RPSPeer *rps_peer;
172};
173
174
175/**
176 * Information we track for each peer.
177 */
178struct RPSPeer
179{
180 /**
181 * Index of the peer.
182 */
183 uint32_t index;
184
185 /**
186 * Handle for RPS connect operation.
187 */
188 struct GNUNET_TESTBED_Operation *op;
189
190 /**
191 * Handle to RPS service.
192 */
193 struct GNUNET_RPS_Handle *rps_handle;
194
195 /**
196 * Handle to stream requests
197 */
198 struct GNUNET_RPS_StreamRequestHandle *rps_srh;
199
200 /**
201 * ID of the peer.
202 */
203 struct GNUNET_PeerIdentity *peer_id;
204
205 /**
206 * A request handle to check for an request
207 */
208 // struct GNUNET_RPS_Request_Handle *req_handle;
209
210 /**
211 * Peer on- or offline?
212 */
213 int online;
214
215 /**
216 * Number of Peer IDs to request during the whole test
217 */
218 unsigned int num_ids_to_request;
219
220 /**
221 * Pending requests DLL
222 */
223 struct PendingRequest *pending_req_head;
224 struct PendingRequest *pending_req_tail;
225
226 /**
227 * Number of pending requests
228 */
229 unsigned int num_pending_reqs;
230
231 /**
232 * Pending replies DLL
233 */
234 struct PendingReply *pending_rep_head;
235 struct PendingReply *pending_rep_tail;
236
237 /**
238 * Number of pending replies
239 */
240 unsigned int num_pending_reps;
241
242 /**
243 * Number of received PeerIDs
244 */
245 unsigned int num_recv_ids;
246
247 /**
248 * Pending operation on that peer
249 */
250 const struct OpListEntry *entry_op_manage;
251
252 /**
253 * Testbed operation to connect to statistics service
254 */
255 struct GNUNET_TESTBED_Operation *stat_op;
256
257 /**
258 * Handle to the statistics service
259 */
260 struct GNUNET_STATISTICS_Handle *stats_h;
261
262 /**
263 * @brief flags to indicate which statistics values have been already
264 * collected from the statistics service.
265 * Used to check whether we are able to shutdown.
266 */
267 uint32_t stat_collected_flags;
268
269 /**
270 * @brief File name of the file the stats are finally written to
271 */
272 const char *file_name_stats;
273
274 /**
275 * @brief File name of the file the stats are finally written to
276 */
277 const char *file_name_probs;
278
279 /**
280 * @brief The current view
281 */
282 struct GNUNET_PeerIdentity *cur_view;
283
284 /**
285 * @brief Number of peers in the #cur_view.
286 */
287 uint32_t cur_view_count;
288
289 /**
290 * @brief Number of occurrences in other peer's view
291 */
292 uint32_t count_in_views;
293
294 /**
295 * @brief statistics values
296 */
297 uint64_t num_rounds;
298 uint64_t num_blocks;
299 uint64_t num_blocks_many_push;
300 uint64_t num_blocks_no_push;
301 uint64_t num_blocks_no_pull;
302 uint64_t num_blocks_many_push_no_pull;
303 uint64_t num_blocks_no_push_no_pull;
304 uint64_t num_issued_push;
305 uint64_t num_issued_pull_req;
306 uint64_t num_issued_pull_rep;
307 uint64_t num_sent_push;
308 uint64_t num_sent_pull_req;
309 uint64_t num_sent_pull_rep;
310 uint64_t num_recv_push;
311 uint64_t num_recv_pull_req;
312 uint64_t num_recv_pull_rep;
313};
314
315enum STAT_TYPE
316{
317 STAT_TYPE_ROUNDS = 0x1, /* 1 */
318 STAT_TYPE_BLOCKS = 0x2, /* 2 */
319 STAT_TYPE_BLOCKS_MANY_PUSH = 0x4, /* 3 */
320 STAT_TYPE_BLOCKS_NO_PUSH = 0x8, /* 4 */
321 STAT_TYPE_BLOCKS_NO_PULL = 0x10, /* 5 */
322 STAT_TYPE_BLOCKS_MANY_PUSH_NO_PULL = 0x20, /* 6 */
323 STAT_TYPE_BLOCKS_NO_PUSH_NO_PULL = 0x40, /* 7 */
324 STAT_TYPE_ISSUED_PUSH_SEND = 0x80, /* 8 */
325 STAT_TYPE_ISSUED_PULL_REQ = 0x100, /* 9 */
326 STAT_TYPE_ISSUED_PULL_REP = 0x200, /* 10 */
327 STAT_TYPE_SENT_PUSH_SEND = 0x400, /* 11 */
328 STAT_TYPE_SENT_PULL_REQ = 0x800, /* 12 */
329 STAT_TYPE_SENT_PULL_REP = 0x1000, /* 13 */
330 STAT_TYPE_RECV_PUSH_SEND = 0x2000, /* 14 */
331 STAT_TYPE_RECV_PULL_REQ = 0x4000, /* 15 */
332 STAT_TYPE_RECV_PULL_REP = 0x8000, /* 16 */
333 STAT_TYPE_MAX = 0x80000000, /* 32 */
334};
335
336struct STATcls
337{
338 struct RPSPeer *rps_peer;
339 enum STAT_TYPE stat_type;
340};
341
342
343/**
344 * Information for all the peers.
345 */
346static struct RPSPeer *rps_peers;
347
348/**
349 * Peermap to get the index of a given peer ID quick.
350 */
351static struct GNUNET_CONTAINER_MultiPeerMap *peer_map;
352
353/**
354 * IDs of the peers.
355 */
356static struct GNUNET_PeerIdentity *rps_peer_ids;
357
358/**
359 * ID of the targeted peer.
360 */
361static struct GNUNET_PeerIdentity *target_peer;
362
363/**
364 * ID of the peer that requests for the evaluation.
365 */
366static struct RPSPeer *eval_peer;
367
368/**
369 * Number of online peers.
370 */
371static unsigned int num_peers_online;
372
373/**
374 * @brief The added sizes of the peer's views
375 */
376static unsigned int view_sizes;
377
378/**
379 * Return value from 'main'.
380 */
381static int ok;
382
383/**
384 * Identifier for the churn task that runs periodically
385 */
386static struct GNUNET_SCHEDULER_Task *post_test_task;
387
388/**
389 * Identifier for the churn task that runs periodically
390 */
391static struct GNUNET_SCHEDULER_Task *shutdown_task;
392
393/**
394 * Identifier for the churn task that runs periodically
395 */
396static struct GNUNET_SCHEDULER_Task *churn_task;
397
398/**
399 * Called to initialise the given RPSPeer
400 */
401typedef void (*InitPeer) (struct RPSPeer *rps_peer);
402
403/**
404 * @brief Called directly after connecting to the service
405 *
406 * @param rps_peer Specific peer the function is called on
407 * @param h the handle to the rps service
408 */
409typedef void (*PreTest) (struct RPSPeer *rps_peer, struct GNUNET_RPS_Handle *h);
410
411/**
412 * @brief Executes functions to test the api/service for a given peer
413 *
414 * Called from within #rps_connect_complete_cb ()
415 * Implemented by #churn_test_cb, #profiler_cb, #mal_cb, #single_req_cb,
416 * #delay_req_cb, #seed_big_cb, #single_peer_seed_cb, #seed_cb, #req_cancel_cb
417 *
418 * @param rps_peer the peer the task runs on
419 */
420typedef void (*MainTest) (struct RPSPeer *rps_peer);
421
422/**
423 * Callback called once the requested random peers are available
424 */
425typedef void (*ReplyHandle) (void *cls,
426 uint64_t n,
427 const struct GNUNET_PeerIdentity *recv_peers);
428
429/**
430 * Called directly before disconnecting from the service
431 */
432typedef void (*PostTest) (struct RPSPeer *peer);
433
434/**
435 * Function called after disconnect to evaluate test success
436 */
437typedef int (*EvaluationCallback) (void);
438
439/**
440 * @brief Do we have Churn?
441 */
442enum OPTION_CHURN
443{
444 /**
445 * @brief If we have churn this is set
446 */
447 HAVE_CHURN,
448 /**
449 * @brief If we have no churn this is set
450 */
451 HAVE_NO_CHURN,
452};
453
454/**
455 * @brief Is it ok to quit the test before the timeout?
456 */
457enum OPTION_QUICK_QUIT
458{
459 /**
460 * @brief It is ok for the test to quit before the timeout triggers
461 */
462 HAVE_QUICK_QUIT,
463
464 /**
465 * @brief It is NOT ok for the test to quit before the timeout triggers
466 */
467 HAVE_NO_QUICK_QUIT,
468};
469
470/**
471 * @brief Do we collect statistics at the end?
472 */
473enum OPTION_COLLECT_STATISTICS
474{
475 /**
476 * @brief We collect statistics at the end
477 */
478 COLLECT_STATISTICS,
479
480 /**
481 * @brief We do not collect statistics at the end
482 */
483 NO_COLLECT_STATISTICS,
484};
485
486/**
487 * @brief Do we collect views during run?
488 */
489enum OPTION_COLLECT_VIEW
490{
491 /**
492 * @brief We collect view during run
493 */
494 COLLECT_VIEW,
495
496 /**
497 * @brief We do not collect the view during run
498 */
499 NO_COLLECT_VIEW,
500};
501
502/**
503 * Structure to define a single test
504 */
505struct SingleTestRun
506{
507 /**
508 * Name of the test
509 */
510 char *name;
511
512 /**
513 * Called with a single peer in order to initialise that peer
514 */
515 InitPeer init_peer;
516
517 /**
518 * Called directly after connecting to the service
519 */
520 PreTest pre_test;
521
522 /**
523 * Main function for each peer
524 */
525 MainTest main_test;
526
527 /**
528 * Callback called once the requested peers are available
529 */
530 ReplyHandle reply_handle;
531
532 /**
533 * Called directly before disconnecting from the service
534 */
535 PostTest post_test;
536
537 /**
538 * Function to evaluate the test results
539 */
540 EvaluationCallback eval_cb;
541
542 /**
543 * Request interval
544 */
545 uint32_t request_interval;
546
547 /**
548 * Number of Requests to make.
549 */
550 uint32_t num_requests;
551
552 /**
553 * Run with (-out) churn
554 */
555 enum OPTION_CHURN have_churn;
556
557 /**
558 * Quit test before timeout?
559 */
560 enum OPTION_QUICK_QUIT have_quick_quit;
561
562 /**
563 * Collect statistics at the end?
564 */
565 enum OPTION_COLLECT_STATISTICS have_collect_statistics;
566
567 /**
568 * Collect view during run?
569 */
570 enum OPTION_COLLECT_VIEW have_collect_view;
571
572 /**
573 * @brief Mark which values from the statistics service to collect at the end
574 * of the run
575 */
576 uint32_t stat_collect_flags;
577} cur_test_run;
578
579/**
580 * Did we finish the test?
581 */
582static int post_test;
583
584/**
585 * Are we shutting down?
586 */
587static int in_shutdown;
588
589/**
590 * Append arguments to file
591 */
592static void
593tofile_ (const char *file_name, const char *line)
594{
595 struct GNUNET_DISK_FileHandle *f;
596 /* char output_buffer[512]; */
597 size_t size;
598 /* int size; */
599 size_t size2;
600
601 if (NULL == (f = GNUNET_DISK_file_open (file_name,
602 GNUNET_DISK_OPEN_APPEND
603 | GNUNET_DISK_OPEN_WRITE
604 | GNUNET_DISK_OPEN_CREATE,
605 GNUNET_DISK_PERM_USER_READ
606 | GNUNET_DISK_PERM_USER_WRITE
607 | GNUNET_DISK_PERM_GROUP_READ
608 | GNUNET_DISK_PERM_OTHER_READ)))
609 {
610 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
611 "Not able to open file %s\n",
612 file_name);
613 return;
614 }
615 /* size = GNUNET_snprintf (output_buffer,
616 sizeof (output_buffer),
617 "%llu %s\n",
618 GNUNET_TIME_absolute_get ().abs_value_us,
619 line);
620 if (0 > size)
621 {
622 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
623 "Failed to write string to buffer (size: %i)\n",
624 size);
625 return;
626 } */size = strlen (line) * sizeof(char);
627
628 size2 = GNUNET_DISK_file_write (f, line, size);
629 if (size != size2)
630 {
631 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
632 "Unable to write to file! (Size: %lu, size2: %lu)\n",
633 size,
634 size2);
635 if (GNUNET_YES != GNUNET_DISK_file_close (f))
636 {
637 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
638 "Unable to close file\n");
639 }
640 return;
641 }
642
643 if (GNUNET_YES != GNUNET_DISK_file_close (f))
644 {
645 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
646 "Unable to close file\n");
647 }
648}
649
650
651/**
652 * This function is used to facilitate writing important information to disk
653 */
654#define tofile(file_name, ...) do { \
655 char tmp_buf[512]; \
656 int size; \
657 size = GNUNET_snprintf (tmp_buf, sizeof(tmp_buf), __VA_ARGS__); \
658 if (0 > size) \
659 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
660 "Failed to create tmp_buf\n"); \
661 else \
662 tofile_ (file_name, tmp_buf); \
663} while (0);
664
665
666/**
667 * Write the ids and their according index in the given array to a file
668 * Unused
669 */
670/* static void
671 ids_to_file (char *file_name,
672 struct GNUNET_PeerIdentity *peer_ids,
673 unsigned int num_peer_ids)
674 {
675 unsigned int i;
676
677 for (i=0 ; i < num_peer_ids ; i++)
678 {
679 to_file (file_name,
680 "%u\t%s",
681 i,
682 GNUNET_i2s_full (&peer_ids[i]));
683 }
684 } */
685
686/**
687 * Task run on timeout to collect statistics and potentially shut down.
688 */
689static void
690post_test_op (void *cls);
691
692
693/**
694 * Test the success of a single test
695 */
696static int
697evaluate (void)
698{
699 unsigned int i;
700 int tmp_ok;
701
702 tmp_ok = 1;
703
704 for (i = 0; i < num_peers; i++)
705 {
706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707 "%u. peer [%s] received %u of %u expected peer_ids: %i\n",
708 i,
709 GNUNET_i2s (rps_peers[i].peer_id),
710 rps_peers[i].num_recv_ids,
711 rps_peers[i].num_ids_to_request,
712 (rps_peers[i].num_ids_to_request == rps_peers[i].num_recv_ids));
713 tmp_ok &= (rps_peers[i].num_ids_to_request == rps_peers[i].num_recv_ids);
714 }
715 return tmp_ok ? 0 : 1;
716}
717
718
719/**
720 * Creates an oplist entry and adds it to the oplist DLL
721 */
722static struct OpListEntry *
723make_oplist_entry ()
724{
725 struct OpListEntry *entry;
726
727 entry = GNUNET_new (struct OpListEntry);
728 GNUNET_CONTAINER_DLL_insert_tail (oplist_head, oplist_tail, entry);
729 return entry;
730}
731
732
733/**
734 * @brief Checks if given peer already received its statistics value from the
735 * statistics service.
736 *
737 * @param rps_peer the peer to check for
738 *
739 * @return #GNUNET_YES if so
740 * #GNUNET_NO otherwise
741 */
742static int
743check_statistics_collect_completed_single_peer (
744 const struct RPSPeer *rps_peer)
745{
746 if (cur_test_run.stat_collect_flags !=
747 (cur_test_run.stat_collect_flags
748 & rps_peer->stat_collected_flags))
749 {
750 return GNUNET_NO;
751 }
752 return GNUNET_YES;
753}
754
755
756/**
757 * @brief Checks if all peers already received their statistics value from the
758 * statistics service.
759 *
760 * @return #GNUNET_YES if so
761 * #GNUNET_NO otherwise
762 */
763static int
764check_statistics_collect_completed ()
765{
766 uint32_t i;
767
768 for (i = 0; i < num_peers; i++)
769 {
770 if (GNUNET_NO == check_statistics_collect_completed_single_peer (
771 &rps_peers[i]))
772 {
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774 "At least Peer %" PRIu32
775 " did not yet receive all statistics values\n",
776 i);
777 return GNUNET_NO;
778 }
779 }
780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781 "All peers received their statistics values\n");
782 return GNUNET_YES;
783}
784
785
786/**
787 * Task run on timeout to shut everything down.
788 */
789static void
790shutdown_op (void *cls)
791{
792 unsigned int i;
793
794 (void) cls;
795
796 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
797 "Shutdown task scheduled, going down.\n");
798 in_shutdown = GNUNET_YES;
799 if (NULL != post_test_task)
800 {
801 GNUNET_SCHEDULER_cancel (post_test_task);
802 post_test_op (NULL);
803 }
804 if (NULL != churn_task)
805 {
806 GNUNET_SCHEDULER_cancel (churn_task);
807 churn_task = NULL;
808 }
809 for (i = 0; i < num_peers; i++)
810 {
811 if (NULL != rps_peers[i].rps_handle)
812 {
813 GNUNET_RPS_disconnect (rps_peers[i].rps_handle);
814 }
815 if (NULL != rps_peers[i].op)
816 {
817 GNUNET_TESTBED_operation_done (rps_peers[i].op);
818 }
819 }
820}
821
822
823/**
824 * Task run on timeout to collect statistics and potentially shut down.
825 */
826static void
827post_test_op (void *cls)
828{
829 unsigned int i;
830
831 (void) cls;
832
833 post_test_task = NULL;
834 post_test = GNUNET_YES;
835 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
836 "Post test task scheduled, going down.\n");
837 if (NULL != churn_task)
838 {
839 GNUNET_SCHEDULER_cancel (churn_task);
840 churn_task = NULL;
841 }
842 for (i = 0; i < num_peers; i++)
843 {
844 if (NULL != cur_test_run.post_test)
845 {
846 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing post_test for peer %u\n",
847 i);
848 cur_test_run.post_test (&rps_peers[i]);
849 }
850 if (NULL != rps_peers[i].op)
851 {
852 GNUNET_TESTBED_operation_done (rps_peers[i].op);
853 rps_peers[i].op = NULL;
854 }
855 }
856 /* If we do not collect statistics, shut down directly */
857 if ((NO_COLLECT_STATISTICS == cur_test_run.have_collect_statistics) ||
858 (GNUNET_YES == check_statistics_collect_completed ()) )
859 {
860 GNUNET_SCHEDULER_shutdown ();
861 }
862}
863
864
865/**
866 * Seed peers.
867 */
868static void
869seed_peers (void *cls)
870{
871 struct RPSPeer *peer = cls;
872 unsigned int amount;
873 unsigned int i;
874
875 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
876 {
877 return;
878 }
879
880 GNUNET_assert (NULL != peer->rps_handle);
881
882 // TODO if malicious don't seed mal peers
883 amount = round (.5 * num_peers);
884
885 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Seeding peers:\n");
886 for (i = 0; i < amount; i++)
887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Seeding %u. peer: %s\n",
888 i,
889 GNUNET_i2s (&rps_peer_ids[i]));
890
891 GNUNET_RPS_seed_ids (peer->rps_handle, amount, rps_peer_ids);
892}
893
894
895/**
896 * Seed peers.
897 */
898static void
899seed_peers_big (void *cls)
900{
901 struct RPSPeer *peer = cls;
902 unsigned int seed_msg_size;
903 uint32_t num_peers_max;
904 unsigned int amount;
905 unsigned int i;
906
907 seed_msg_size = 8; /* sizeof (struct GNUNET_RPS_CS_SeedMessage) */
908 num_peers_max = (GNUNET_MAX_MESSAGE_SIZE - seed_msg_size)
909 / sizeof(struct GNUNET_PeerIdentity);
910 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
911 "Peers that fit in one seed msg; %u\n",
912 num_peers_max);
913 amount = num_peers_max + (0.5 * num_peers_max);
914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915 "Seeding many (%u) peers:\n",
916 amount);
917 struct GNUNET_PeerIdentity ids_to_seed[amount];
918 for (i = 0; i < amount; i++)
919 {
920 ids_to_seed[i] = *peer->peer_id;
921 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Seeding %u. peer: %s\n",
922 i,
923 GNUNET_i2s (&ids_to_seed[i]));
924 }
925
926 GNUNET_RPS_seed_ids (peer->rps_handle, amount, ids_to_seed);
927}
928
929
930/**
931 * Get the id of peer i.
932 */
933void
934info_cb (void *cb_cls,
935 struct GNUNET_TESTBED_Operation *op,
936 const struct GNUNET_TESTBED_PeerInformation *pinfo,
937 const char *emsg)
938{
939 struct OpListEntry *entry = (struct OpListEntry *) cb_cls;
940
941 (void) op;
942
943 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
944 {
945 return;
946 }
947
948 if ((NULL == pinfo) || (NULL != emsg))
949 {
950 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Got Error: %s\n", emsg);
951 GNUNET_TESTBED_operation_done (entry->op);
952 return;
953 }
954
955 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
956 "Peer %u is %s\n",
957 entry->index,
958 GNUNET_i2s (pinfo->result.id));
959
960 rps_peer_ids[entry->index] = *(pinfo->result.id);
961 rps_peers[entry->index].peer_id = &rps_peer_ids[entry->index];
962
963 GNUNET_assert (GNUNET_OK ==
964 GNUNET_CONTAINER_multipeermap_put (peer_map,
965 &rps_peer_ids[entry->index],
966 &rps_peers[entry->index],
967 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
968 tofile ("/tmp/rps/peer_ids",
969 "%u\t%s\n",
970 entry->index,
971 GNUNET_i2s_full (&rps_peer_ids[entry->index]));
972
973 GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry);
974 GNUNET_TESTBED_operation_done (entry->op);
975 GNUNET_free (entry);
976}
977
978
979/**
980 * Callback to be called when RPS service connect operation is completed
981 *
982 * @param cls the callback closure from functions generating an operation
983 * @param op the operation that has been finished
984 * @param ca_result the RPS service handle returned from rps_connect_adapter
985 * @param emsg error message in case the operation has failed; will be NULL if
986 * operation has executed successfully.
987 */
988static void
989rps_connect_complete_cb (void *cls,
990 struct GNUNET_TESTBED_Operation *op,
991 void *ca_result,
992 const char *emsg)
993{
994 struct RPSPeer *rps_peer = cls;
995 struct GNUNET_RPS_Handle *rps = ca_result;
996
997 GNUNET_assert (NULL != ca_result);
998
999 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1000 {
1001 return;
1002 }
1003
1004 rps_peer->rps_handle = rps;
1005 rps_peer->online = GNUNET_YES;
1006 num_peers_online++;
1007
1008 GNUNET_assert (op == rps_peer->op);
1009 if (NULL != emsg)
1010 {
1011 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1012 "Failed to connect to RPS service: %s\n",
1013 emsg);
1014 ok = 1;
1015 GNUNET_SCHEDULER_shutdown ();
1016 return;
1017 }
1018
1019 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started client successfully\n");
1020
1021 cur_test_run.main_test (rps_peer);
1022}
1023
1024
1025/**
1026 * Adapter function called to establish a connection to
1027 * the RPS service.
1028 *
1029 * @param cls closure
1030 * @param cfg configuration of the peer to connect to; will be available until
1031 * GNUNET_TESTBED_operation_done() is called on the operation returned
1032 * from GNUNET_TESTBED_service_connect()
1033 * @return service handle to return in 'op_result', NULL on error
1034 */
1035static void *
1036rps_connect_adapter (void *cls,
1037 const struct GNUNET_CONFIGURATION_Handle *cfg)
1038{
1039 struct GNUNET_RPS_Handle *h;
1040
1041 h = GNUNET_RPS_connect (cfg);
1042 GNUNET_assert (NULL != h);
1043
1044 if (NULL != cur_test_run.pre_test)
1045 cur_test_run.pre_test (cls, h);
1046 GNUNET_assert (NULL != h);
1047
1048 return h;
1049}
1050
1051
1052/**
1053 * Called to open a connection to the peer's statistics
1054 *
1055 * @param cls peer context
1056 * @param cfg configuration of the peer to connect to; will be available until
1057 * GNUNET_TESTBED_operation_done() is called on the operation returned
1058 * from GNUNET_TESTBED_service_connect()
1059 * @return service handle to return in 'op_result', NULL on error
1060 */
1061static void *
1062stat_connect_adapter (void *cls,
1063 const struct GNUNET_CONFIGURATION_Handle *cfg)
1064{
1065 struct RPSPeer *peer = cls;
1066
1067 peer->stats_h = GNUNET_STATISTICS_create ("rps-profiler", cfg);
1068 return peer->stats_h;
1069}
1070
1071
1072/**
1073 * Called to disconnect from peer's statistics service
1074 *
1075 * @param cls peer context
1076 * @param op_result service handle returned from the connect adapter
1077 */
1078static void
1079stat_disconnect_adapter (void *cls, void *op_result)
1080{
1081 struct RPSPeer *peer = cls;
1082
1083 // GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
1084 // (peer->stats_h, "core", "# peers connected",
1085 // stat_iterator, peer));
1086 // GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
1087 // (peer->stats_h, "nse", "# peers connected",
1088 // stat_iterator, peer));
1089 GNUNET_STATISTICS_destroy (op_result, GNUNET_NO);
1090 peer->stats_h = NULL;
1091}
1092
1093
1094/**
1095 * Called after successfully opening a connection to a peer's statistics
1096 * service; we register statistics monitoring for CORE and NSE here.
1097 *
1098 * @param cls the callback closure from functions generating an operation
1099 * @param op the operation that has been finished
1100 * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter()
1101 * @param emsg error message in case the operation has failed; will be NULL if
1102 * operation has executed successfully.
1103 */
1104static void
1105stat_complete_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
1106 void *ca_result, const char *emsg)
1107{
1108 // struct GNUNET_STATISTICS_Handle *sh = ca_result;
1109 // struct RPSPeer *peer = (struct RPSPeer *) cls;
1110 (void) cls;
1111 (void) op;
1112 (void) ca_result;
1113
1114 if (NULL != emsg)
1115 {
1116 GNUNET_break (0);
1117 return;
1118 }
1119 // GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
1120 // (sh, "core", "# peers connected",
1121 // stat_iterator, peer));
1122 // GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch
1123 // (sh, "nse", "# peers connected",
1124 // stat_iterator, peer));
1125}
1126
1127
1128/**
1129 * Adapter function called to destroy connection to
1130 * RPS service.
1131 *
1132 * @param cls closure
1133 * @param op_result service handle returned from the connect adapter
1134 */
1135static void
1136rps_disconnect_adapter (void *cls,
1137 void *op_result)
1138{
1139 struct RPSPeer *peer = cls;
1140 struct GNUNET_RPS_Handle *h = op_result;
1141
1142 if (NULL != peer->rps_srh)
1143 {
1144 GNUNET_RPS_stream_cancel (peer->rps_srh);
1145 peer->rps_srh = NULL;
1146 }
1147 GNUNET_assert (NULL != peer);
1148 GNUNET_RPS_disconnect (h);
1149 peer->rps_handle = NULL;
1150}
1151
1152
1153/***********************************************************************
1154* Definition of tests
1155***********************************************************************/
1156
1157// TODO check whether tests can be stopped earlier
1158static int
1159default_eval_cb (void)
1160{
1161 return evaluate ();
1162}
1163
1164
1165static int
1166no_eval (void)
1167{
1168 return 0;
1169}
1170
1171
1172/**
1173 * Initialise given RPSPeer
1174 */
1175static void
1176default_init_peer (struct RPSPeer *rps_peer)
1177{
1178 rps_peer->num_ids_to_request = 1;
1179}
1180
1181
1182/**
1183 * Callback to call on receipt of a reply
1184 *
1185 * @param cls closure
1186 * @param n number of peers
1187 * @param recv_peers the received peers
1188 */
1189static void
1190default_reply_handle (void *cls,
1191 uint64_t n,
1192 const struct GNUNET_PeerIdentity *recv_peers)
1193{
1194 struct RPSPeer *rps_peer;
1195 struct PendingReply *pending_rep = (struct PendingReply *) cls;
1196 unsigned int i;
1197
1198 rps_peer = pending_rep->rps_peer;
1199 GNUNET_CONTAINER_DLL_remove (rps_peer->pending_rep_head,
1200 rps_peer->pending_rep_tail,
1201 pending_rep);
1202 rps_peer->num_pending_reps--;
1203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1204 "[%s] got %" PRIu64 " peers:\n",
1205 GNUNET_i2s (rps_peer->peer_id),
1206 n);
1207
1208 for (i = 0; i < n; i++)
1209 {
1210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1211 "%u: %s\n",
1212 i,
1213 GNUNET_i2s (&recv_peers[i]));
1214
1215 rps_peer->num_recv_ids++;
1216 }
1217
1218 if ((0 == evaluate ()) && (HAVE_QUICK_QUIT == cur_test_run.have_quick_quit))
1219 {
1220 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test succeeded before timeout\n");
1221 GNUNET_assert (NULL != post_test_task);
1222 GNUNET_SCHEDULER_cancel (post_test_task);
1223 post_test_task = GNUNET_SCHEDULER_add_now (&post_test_op, NULL);
1224 GNUNET_assert (NULL != post_test_task);
1225 }
1226}
1227
1228
1229/**
1230 * Request random peers.
1231 */
1232static void
1233request_peers (void *cls)
1234{
1235 struct PendingRequest *pending_req = cls;
1236 struct RPSPeer *rps_peer;
1237 struct PendingReply *pending_rep;
1238
1239 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1240 return;
1241 rps_peer = pending_req->rps_peer;
1242 GNUNET_assert (1 <= rps_peer->num_pending_reqs);
1243 GNUNET_CONTAINER_DLL_remove (rps_peer->pending_req_head,
1244 rps_peer->pending_req_tail,
1245 pending_req);
1246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247 "Requesting one peer\n");
1248 pending_rep = GNUNET_new (struct PendingReply);
1249 pending_rep->rps_peer = rps_peer;
1250 pending_rep->req_handle = GNUNET_RPS_request_peers (rps_peer->rps_handle,
1251 1,
1252 cur_test_run.reply_handle,
1253 pending_rep);
1254 GNUNET_CONTAINER_DLL_insert_tail (rps_peer->pending_rep_head,
1255 rps_peer->pending_rep_tail,
1256 pending_rep);
1257 rps_peer->num_pending_reps++;
1258 rps_peer->num_pending_reqs--;
1259}
1260
1261
1262static void
1263cancel_pending_req (struct PendingRequest *pending_req)
1264{
1265 struct RPSPeer *rps_peer;
1266
1267 rps_peer = pending_req->rps_peer;
1268 GNUNET_CONTAINER_DLL_remove (rps_peer->pending_req_head,
1269 rps_peer->pending_req_tail,
1270 pending_req);
1271 rps_peer->num_pending_reqs--;
1272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1273 "Cancelling pending request\n");
1274 GNUNET_SCHEDULER_cancel (pending_req->request_task);
1275 GNUNET_free (pending_req);
1276}
1277
1278
1279static void
1280cancel_request (struct PendingReply *pending_rep)
1281{
1282 struct RPSPeer *rps_peer;
1283
1284 rps_peer = pending_rep->rps_peer;
1285 GNUNET_CONTAINER_DLL_remove (rps_peer->pending_rep_head,
1286 rps_peer->pending_rep_tail,
1287 pending_rep);
1288 rps_peer->num_pending_reps--;
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "Cancelling request\n");
1291 GNUNET_RPS_request_cancel (pending_rep->req_handle);
1292 GNUNET_free (pending_rep);
1293}
1294
1295
1296/**
1297 * Cancel a request.
1298 */
1299static void
1300cancel_request_cb (void *cls)
1301{
1302 struct RPSPeer *rps_peer = cls;
1303 struct PendingReply *pending_rep;
1304
1305 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1306 return;
1307 pending_rep = rps_peer->pending_rep_head;
1308 GNUNET_assert (1 <= rps_peer->num_pending_reps);
1309 cancel_request (pending_rep);
1310}
1311
1312
1313/**
1314 * Schedule requests for peer @a rps_peer that have neither been scheduled, nor
1315 * issued, nor replied
1316 */
1317void
1318schedule_missing_requests (struct RPSPeer *rps_peer)
1319{
1320 unsigned int i;
1321 struct PendingRequest *pending_req;
1322
1323 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1324 "Scheduling %u - %u missing requests\n",
1325 rps_peer->num_ids_to_request,
1326 rps_peer->num_pending_reqs + rps_peer->num_pending_reps);
1327 GNUNET_assert (rps_peer->num_pending_reqs + rps_peer->num_pending_reps <=
1328 rps_peer->num_ids_to_request);
1329 for (i = rps_peer->num_pending_reqs + rps_peer->num_pending_reps;
1330 i < rps_peer->num_ids_to_request; i++)
1331 {
1332 pending_req = GNUNET_new (struct PendingRequest);
1333 pending_req->rps_peer = rps_peer;
1334 pending_req->request_task = GNUNET_SCHEDULER_add_delayed (
1335 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1336 cur_test_run.request_interval * i),
1337 request_peers,
1338 pending_req);
1339 GNUNET_CONTAINER_DLL_insert_tail (rps_peer->pending_req_head,
1340 rps_peer->pending_req_tail,
1341 pending_req);
1342 rps_peer->num_pending_reqs++;
1343 }
1344}
1345
1346
1347void
1348cancel_pending_req_rep (struct RPSPeer *rps_peer)
1349{
1350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1351 "Cancelling all (pending) requests.\n");
1352 while (NULL != rps_peer->pending_req_head)
1353 cancel_pending_req (rps_peer->pending_req_head);
1354 GNUNET_assert (0 == rps_peer->num_pending_reqs);
1355 while (NULL != rps_peer->pending_rep_head)
1356 cancel_request (rps_peer->pending_rep_head);
1357 GNUNET_assert (0 == rps_peer->num_pending_reps);
1358}
1359
1360
1361/***********************************
1362* MALICIOUS
1363***********************************/
1364
1365/**
1366 * Initialise only non-mal RPSPeers
1367 */
1368static void
1369mal_init_peer (struct RPSPeer *rps_peer)
1370{
1371 if (rps_peer->index >= round (portion * num_peers))
1372 rps_peer->num_ids_to_request = 1;
1373}
1374
1375
1376/**
1377 * @brief Set peers to (non-)malicious before execution
1378 *
1379 * Of signature #PreTest
1380 *
1381 * @param rps_peer the peer to set (non-) malicious
1382 * @param h the handle to the service
1383 */
1384static void
1385mal_pre (struct RPSPeer *rps_peer, struct GNUNET_RPS_Handle *h)
1386{
1387 #if ENABLE_MALICIOUS
1388 uint32_t num_mal_peers;
1389
1390 GNUNET_assert ((1 >= portion) &&
1391 (0 < portion));
1392 num_mal_peers = round (portion * num_peers);
1393
1394 if (rps_peer->index < num_mal_peers)
1395 {
1396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1397 "%u. peer [%s] of %" PRIu32
1398 " malicious peers turning malicious\n",
1399 rps_peer->index,
1400 GNUNET_i2s (rps_peer->peer_id),
1401 num_mal_peers);
1402
1403 GNUNET_RPS_act_malicious (h, mal_type, num_mal_peers,
1404 rps_peer_ids, target_peer);
1405 }
1406 #endif /* ENABLE_MALICIOUS */
1407}
1408
1409
1410static void
1411mal_cb (struct RPSPeer *rps_peer)
1412{
1413 uint32_t num_mal_peers;
1414
1415 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1416 {
1417 return;
1418 }
1419
1420 #if ENABLE_MALICIOUS
1421 GNUNET_assert ((1 >= portion) &&
1422 (0 < portion));
1423 num_mal_peers = round (portion * num_peers);
1424
1425 if (rps_peer->index >= num_mal_peers)
1426 { /* It's useless to ask a malicious peer about a random sample -
1427 it's not sampling */
1428 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
1429 GNUNET_TIME_UNIT_SECONDS, 2),
1430 seed_peers, rps_peer);
1431 schedule_missing_requests (rps_peer);
1432 }
1433 #endif /* ENABLE_MALICIOUS */
1434}
1435
1436
1437/***********************************
1438* SINGLE_REQUEST
1439***********************************/
1440static void
1441single_req_cb (struct RPSPeer *rps_peer)
1442{
1443 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1444 {
1445 return;
1446 }
1447
1448 schedule_missing_requests (rps_peer);
1449}
1450
1451
1452/***********************************
1453* DELAYED_REQUESTS
1454***********************************/
1455static void
1456delay_req_cb (struct RPSPeer *rps_peer)
1457{
1458 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1459 {
1460 return;
1461 }
1462
1463 schedule_missing_requests (rps_peer);
1464}
1465
1466
1467/***********************************
1468* SEED
1469***********************************/
1470static void
1471seed_cb (struct RPSPeer *rps_peer)
1472{
1473 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1474 {
1475 return;
1476 }
1477
1478 GNUNET_SCHEDULER_add_delayed (
1479 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
1480 seed_peers, rps_peer);
1481}
1482
1483
1484/***********************************
1485* SEED_BIG
1486***********************************/
1487static void
1488seed_big_cb (struct RPSPeer *rps_peer)
1489{
1490 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1491 {
1492 return;
1493 }
1494
1495 // TODO test seeding > GNUNET_MAX_MESSAGE_SIZE peers
1496 GNUNET_SCHEDULER_add_delayed (
1497 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2),
1498 seed_peers_big, rps_peer);
1499}
1500
1501
1502/***********************************
1503* SINGLE_PEER_SEED
1504***********************************/
1505static void
1506single_peer_seed_cb (struct RPSPeer *rps_peer)
1507{
1508 (void) rps_peer;
1509 // TODO
1510}
1511
1512
1513/***********************************
1514* SEED_REQUEST
1515***********************************/
1516static void
1517seed_req_cb (struct RPSPeer *rps_peer)
1518{
1519 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1520 {
1521 return;
1522 }
1523
1524 GNUNET_SCHEDULER_add_delayed (
1525 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2),
1526 seed_peers, rps_peer);
1527 schedule_missing_requests (rps_peer);
1528}
1529
1530
1531// TODO start big mal
1532
1533/***********************************
1534* REQUEST_CANCEL
1535***********************************/
1536static void
1537req_cancel_cb (struct RPSPeer *rps_peer)
1538{
1539 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1540 {
1541 return;
1542 }
1543
1544 schedule_missing_requests (rps_peer);
1545 GNUNET_SCHEDULER_add_delayed (
1546 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1547 (cur_test_run.request_interval + 1)),
1548 cancel_request_cb, rps_peer);
1549}
1550
1551
1552/***********************************
1553* CHURN
1554***********************************/
1555
1556static void
1557churn (void *cls);
1558
1559/**
1560 * @brief Starts churn
1561 *
1562 * Has signature of #MainTest
1563 *
1564 * This is not implemented too nicely as this is called for each peer, but we
1565 * only need to call it once. (Yes we check that we only schedule the task
1566 * once.)
1567 *
1568 * @param rps_peer The peer it's called for
1569 */
1570static void
1571churn_test_cb (struct RPSPeer *rps_peer)
1572{
1573 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1574 {
1575 return;
1576 }
1577
1578 /* Start churn */
1579 if ((HAVE_CHURN == cur_test_run.have_churn) && (NULL == churn_task))
1580 {
1581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1582 "Starting churn task\n");
1583 churn_task = GNUNET_SCHEDULER_add_delayed (
1584 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
1585 churn,
1586 NULL);
1587 }
1588 else
1589 {
1590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1591 "Not starting churn task\n");
1592 }
1593
1594 schedule_missing_requests (rps_peer);
1595}
1596
1597
1598/***********************************
1599* SUB
1600***********************************/
1601
1602static void
1603got_stream_peer_cb (void *cls,
1604 uint64_t num_peers,
1605 const struct GNUNET_PeerIdentity *peers)
1606{
1607 const struct RPSPeer *rps_peer = cls;
1608
1609 for (uint64_t i = 0; i < num_peers; i++)
1610 {
1611 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1612 "Peer %" PRIu32 " received [%s] from stream.\n",
1613 rps_peer->index,
1614 GNUNET_i2s (&peers[i]));
1615 if ((0 != rps_peer->index) &&
1616 (0 == memcmp (&peers[i],
1617 &rps_peers[0].peer_id,
1618 sizeof(struct GNUNET_PeerIdentity))))
1619 {
1620 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1621 "Received a peer id outside sub\n");
1622 ok = 1;
1623 }
1624 else if ((0 == rps_peer->index) &&
1625 (0 != memcmp (&peers[i],
1626 &rps_peers[0].peer_id,
1627 sizeof(struct GNUNET_PeerIdentity))))
1628 {
1629 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1630 "Received a peer id outside sub (lonely)\n");
1631 ok = 1;
1632 }
1633 }
1634}
1635
1636
1637static void
1638sub_post (struct RPSPeer *rps_peer)
1639{
1640 if (0 != rps_peer->index)
1641 GNUNET_RPS_sub_stop (rps_peer->rps_handle, "test");
1642 else
1643 GNUNET_RPS_sub_stop (rps_peer->rps_handle, "lonely");
1644}
1645
1646
1647static void
1648sub_pre (struct RPSPeer *rps_peer, struct GNUNET_RPS_Handle *h)
1649{
1650 (void) rps_peer;
1651
1652 if (0 != rps_peer->index)
1653 GNUNET_RPS_sub_start (h, "test");
1654 else
1655 GNUNET_RPS_sub_start (h, "lonely"); /* have a group of one */
1656 rps_peer->rps_srh = GNUNET_RPS_stream_request (h,
1657 &got_stream_peer_cb,
1658 rps_peer);
1659}
1660
1661
1662/***********************************
1663* PROFILER
1664***********************************/
1665
1666/**
1667 * Callback to be called when RPS service is started or stopped at peers
1668 *
1669 * @param cls NULL
1670 * @param op the operation handle
1671 * @param emsg NULL on success; otherwise an error description
1672 */
1673static void
1674churn_cb (void *cls,
1675 struct GNUNET_TESTBED_Operation *op,
1676 const char *emsg)
1677{
1678 (void) op;
1679 // FIXME
1680 struct OpListEntry *entry = cls;
1681
1682 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1683 {
1684 return;
1685 }
1686
1687 GNUNET_TESTBED_operation_done (entry->op);
1688 if (NULL != emsg)
1689 {
1690 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1691 "Failed to start/stop RPS at a peer\n");
1692 GNUNET_SCHEDULER_shutdown ();
1693 return;
1694 }
1695 GNUNET_assert (0 != entry->delta);
1696
1697 num_peers_online += entry->delta;
1698
1699 if (PEER_GO_OFFLINE == entry->delta)
1700 { /* Peer hopefully just went offline */
1701 if (GNUNET_YES != rps_peers[entry->index].online)
1702 {
1703 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1704 "peer %s was expected to go offline but is still marked as online\n",
1705 GNUNET_i2s (rps_peers[entry->index].peer_id));
1706 GNUNET_break (0);
1707 }
1708 else
1709 {
1710 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1711 "peer %s probably went offline as expected\n",
1712 GNUNET_i2s (rps_peers[entry->index].peer_id));
1713 }
1714 rps_peers[entry->index].online = GNUNET_NO;
1715 }
1716
1717 else if (PEER_GO_ONLINE < entry->delta)
1718 { /* Peer hopefully just went online */
1719 if (GNUNET_NO != rps_peers[entry->index].online)
1720 {
1721 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1722 "peer %s was expected to go online but is still marked as offline\n",
1723 GNUNET_i2s (rps_peers[entry->index].peer_id));
1724 GNUNET_break (0);
1725 }
1726 else
1727 {
1728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1729 "peer %s probably went online as expected\n",
1730 GNUNET_i2s (rps_peers[entry->index].peer_id));
1731 if (NULL != cur_test_run.pre_test)
1732 {
1733 cur_test_run.pre_test (&rps_peers[entry->index],
1734 rps_peers[entry->index].rps_handle);
1735 schedule_missing_requests (&rps_peers[entry->index]);
1736 }
1737 }
1738 rps_peers[entry->index].online = GNUNET_YES;
1739 }
1740 else
1741 {
1742 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1743 "Invalid value for delta: %i\n", entry->delta);
1744 GNUNET_break (0);
1745 }
1746
1747 GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry);
1748 rps_peers[entry->index].entry_op_manage = NULL;
1749 GNUNET_free (entry);
1750 // if (num_peers_in_round[current_round] == peers_running)
1751 // run_round ();
1752}
1753
1754
1755/**
1756 * @brief Set the rps-service up or down for a specific peer
1757 *
1758 * @param i index of action
1759 * @param j index of peer
1760 * @param delta (#PEER_ONLINE_DELTA) down (-1) or up (1)
1761 * @param prob_go_on_off the probability of the action
1762 */
1763static void
1764manage_service_wrapper (unsigned int i, unsigned int j,
1765 enum PEER_ONLINE_DELTA delta,
1766 double prob_go_on_off)
1767{
1768 struct OpListEntry *entry = NULL;
1769 uint32_t prob;
1770
1771 /* make sure that management operation is not already scheduled */
1772 if (NULL != rps_peers[j].entry_op_manage)
1773 {
1774 return;
1775 }
1776
1777 prob = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1778 UINT32_MAX);
1779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1780 "%u. selected peer (%u: %s) is %s.\n",
1781 i,
1782 j,
1783 GNUNET_i2s (rps_peers[j].peer_id),
1784 (PEER_GO_ONLINE == delta) ? "online" : "offline");
1785 if (prob < prob_go_on_off * UINT32_MAX)
1786 {
1787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1788 "%s goes %s\n",
1789 GNUNET_i2s (rps_peers[j].peer_id),
1790 (PEER_GO_OFFLINE == delta) ? "offline" : "online");
1791
1792 if (PEER_GO_OFFLINE == delta)
1793 cancel_pending_req_rep (&rps_peers[j]);
1794 entry = make_oplist_entry ();
1795 entry->delta = delta;
1796 entry->index = j;
1797 entry->op = GNUNET_TESTBED_peer_manage_service (NULL,
1798 testbed_peers[j],
1799 "rps",
1800 &churn_cb,
1801 entry,
1802 (PEER_GO_OFFLINE == delta) ?
1803 0 : 1);
1804 rps_peers[j].entry_op_manage = entry;
1805 }
1806}
1807
1808
1809static void
1810churn (void *cls)
1811{
1812 (void) cls;
1813 unsigned int i;
1814 unsigned int j;
1815 double portion_online;
1816 unsigned int *permut;
1817 double prob_go_offline;
1818 double portion_go_online;
1819 double portion_go_offline;
1820
1821 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1822 {
1823 return;
1824 }
1825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1826 "Churn function executing\n");
1827
1828 churn_task = NULL; /* Should be invalid by now */
1829
1830 /* Compute the probability for an online peer to go offline
1831 * this round */
1832 portion_online = num_peers_online * 1.0 / num_peers;
1833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1834 "Portion online: %f\n",
1835 portion_online);
1836 portion_go_online = ((1 - portion_online) * .5 * .66);
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1838 "Portion that should go online: %f\n",
1839 portion_go_online);
1840 portion_go_offline = (portion_online + portion_go_online) - .75;
1841 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1842 "Portion that probably goes offline: %f\n",
1843 portion_go_offline);
1844 prob_go_offline = portion_go_offline / (portion_online * .5);
1845 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1846 "Probability of a selected online peer to go offline: %f\n",
1847 prob_go_offline);
1848
1849 permut = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
1850 (unsigned int) num_peers);
1851
1852 /* Go over 50% randomly chosen peers */
1853 for (i = 0; i < .5 * num_peers; i++)
1854 {
1855 j = permut[i];
1856
1857 /* If online, shut down with certain probability */
1858 if (GNUNET_YES == rps_peers[j].online)
1859 {
1860 manage_service_wrapper (i, j, -1, prob_go_offline);
1861 }
1862
1863 /* If offline, restart with certain probability */
1864 else if (GNUNET_NO == rps_peers[j].online)
1865 {
1866 manage_service_wrapper (i, j, 1, 0.66);
1867 }
1868 }
1869
1870 GNUNET_free (permut);
1871
1872 churn_task = GNUNET_SCHEDULER_add_delayed (
1873 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2),
1874 churn,
1875 NULL);
1876}
1877
1878
1879/**
1880 * Initialise given RPSPeer
1881 */
1882static void
1883profiler_init_peer (struct RPSPeer *rps_peer)
1884{
1885 if (num_peers - 1 == rps_peer->index)
1886 rps_peer->num_ids_to_request = cur_test_run.num_requests;
1887}
1888
1889
1890/**
1891 * Callback to call on receipt of a reply
1892 *
1893 * @param cls closure
1894 * @param n number of peers
1895 * @param recv_peers the received peers
1896 */
1897static void
1898profiler_reply_handle (void *cls,
1899 uint64_t n,
1900 const struct GNUNET_PeerIdentity *recv_peers)
1901{
1902 struct RPSPeer *rps_peer;
1903 struct RPSPeer *rcv_rps_peer;
1904 char *file_name;
1905 char *file_name_dh;
1906 unsigned int i;
1907 struct PendingReply *pending_rep = (struct PendingReply *) cls;
1908
1909 rps_peer = pending_rep->rps_peer;
1910 file_name = "/tmp/rps/received_ids";
1911 file_name_dh = "/tmp/rps/diehard_input";
1912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913 "[%s] got %" PRIu64 " peers:\n",
1914 GNUNET_i2s (rps_peer->peer_id),
1915 n);
1916 for (i = 0; i < n; i++)
1917 {
1918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1919 "%u: %s\n",
1920 i,
1921 GNUNET_i2s (&recv_peers[i]));
1922 tofile (file_name,
1923 "%s\n",
1924 GNUNET_i2s_full (&recv_peers[i]));
1925 rcv_rps_peer = GNUNET_CONTAINER_multipeermap_get (peer_map, &recv_peers[i]);
1926 GNUNET_assert (NULL != rcv_rps_peer);
1927 tofile (file_name_dh,
1928 "%" PRIu32 "\n",
1929 (uint32_t) rcv_rps_peer->index);
1930 }
1931 default_reply_handle (cls, n, recv_peers);
1932}
1933
1934
1935static void
1936profiler_cb (struct RPSPeer *rps_peer)
1937{
1938 if ((GNUNET_YES == in_shutdown) || (GNUNET_YES == post_test))
1939 {
1940 return;
1941 }
1942
1943 /* Start churn */
1944 if ((HAVE_CHURN == cur_test_run.have_churn) && (NULL == churn_task))
1945 {
1946 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1947 "Starting churn task\n");
1948 churn_task = GNUNET_SCHEDULER_add_delayed (
1949 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
1950 churn,
1951 NULL);
1952 }
1953 else
1954 {
1955 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1956 "Not starting churn task\n");
1957 }
1958
1959 /* Only request peer ids at one peer.
1960 * (It's the before-last because last one is target of the focused attack.)
1961 */
1962 if (eval_peer == rps_peer)
1963 schedule_missing_requests (rps_peer);
1964}
1965
1966
1967/**
1968 * Function called from #profiler_eval with a filename.
1969 *
1970 * @param cls closure
1971 * @param filename complete filename (absolute path)
1972 * @return #GNUNET_OK to continue to iterate,
1973 * #GNUNET_NO to stop iteration with no error,
1974 * #GNUNET_SYSERR to abort iteration with error!
1975 */
1976int
1977file_name_cb (void *cls, const char *filename)
1978{
1979 (void) cls;
1980
1981 if (NULL != strstr (filename, "sampler_el"))
1982 {
1983 struct RPS_SamplerElement *s_elem;
1984 struct GNUNET_CRYPTO_AuthKey auth_key;
1985 const char *key_char;
1986 uint32_t i;
1987
1988 key_char = filename + 20; /* Length of "/tmp/rps/sampler_el-" */
1989 tofile (filename, "--------------------------\n");
1990
1991 auth_key = string_to_auth_key (key_char);
1992 s_elem = RPS_sampler_elem_create ();
1993 RPS_sampler_elem_set (s_elem, auth_key);
1994
1995 for (i = 0; i < num_peers; i++)
1996 {
1997 RPS_sampler_elem_next (s_elem, &rps_peer_ids[i]);
1998 }
1999 RPS_sampler_elem_destroy (s_elem);
2000 }
2001 return GNUNET_OK;
2002}
2003
2004
2005/**
2006 * This is run after the test finished.
2007 *
2008 * Compute all perfect samples.
2009 */
2010int
2011profiler_eval (void)
2012{
2013 /* Compute perfect sample for each sampler element */
2014 if (-1 == GNUNET_DISK_directory_scan ("/tmp/rps/", file_name_cb, NULL))
2015 {
2016 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Scan of directory failed\n");
2017 }
2018
2019 return evaluate ();
2020}
2021
2022
2023/**
2024 * @brief is b in view of a?
2025 *
2026 * @param a
2027 * @param b
2028 *
2029 * @return
2030 */
2031static int
2032is_in_view (uint32_t a, uint32_t b)
2033{
2034 uint32_t i;
2035
2036 for (i = 0; i < rps_peers[a].cur_view_count; i++)
2037 {
2038 if (0 == memcmp (rps_peers[b].peer_id,
2039 &rps_peers[a].cur_view[i],
2040 sizeof(struct GNUNET_PeerIdentity)))
2041 {
2042 return GNUNET_YES;
2043 }
2044 }
2045 return GNUNET_NO;
2046}
2047
2048
2049static uint32_t
2050get_idx_of_pid (const struct GNUNET_PeerIdentity *pid)
2051{
2052 uint32_t i;
2053
2054 for (i = 0; i < num_peers; i++)
2055 {
2056 if (0 == memcmp (pid,
2057 rps_peers[i].peer_id,
2058 sizeof(struct GNUNET_PeerIdentity)))
2059 {
2060 return i;
2061 }
2062 }
2063 // return 0; /* Should not happen - make compiler happy */
2064 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2065 "No known _PeerIdentity %s!\n",
2066 GNUNET_i2s_full (pid));
2067 GNUNET_assert (0);
2068}
2069
2070
2071/**
2072 * @brief Counts number of peers in view of a that have b in their view
2073 *
2074 * @param a
2075 * @param uint32_tb
2076 *
2077 * @return
2078 */
2079static uint32_t
2080count_containing_views (uint32_t a, uint32_t b)
2081{
2082 uint32_t i;
2083 uint32_t peer_idx;
2084 uint32_t count = 0;
2085
2086 for (i = 0; i < rps_peers[a].cur_view_count; i++)
2087 {
2088 peer_idx = get_idx_of_pid (&rps_peers[a].cur_view[i]);
2089 if (GNUNET_YES == is_in_view (peer_idx, b))
2090 {
2091 count++;
2092 }
2093 }
2094 return count;
2095}
2096
2097
2098/**
2099 * @brief Computes the probability for each other peer to be selected by the
2100 * sampling process based on the views of all peers
2101 *
2102 * @param peer_idx index of the peer that is about to sample
2103 */
2104static void
2105compute_probabilities (uint32_t peer_idx)
2106{
2107 // double probs[num_peers] = { 0 };
2108 double probs[num_peers];
2109 size_t probs_as_str_size = (num_peers * 10 + 1) * sizeof(char);
2110 char *probs_as_str = GNUNET_malloc (probs_as_str_size);
2111 char *probs_as_str_cpy;
2112 uint32_t i;
2113 double prob_push;
2114 double prob_pull;
2115 uint32_t view_size;
2116 uint32_t cont_views;
2117 uint32_t number_of_being_in_pull_events;
2118 int tmp;
2119 uint32_t count_non_zero_prob = 0;
2120
2121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2122 "Computing probabilities for peer %" PRIu32 "\n", peer_idx);
2123 /* Firstly without knowledge of old views */
2124 for (i = 0; i < num_peers; i++)
2125 {
2126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2127 "\tfor peer %" PRIu32 ":\n", i);
2128 view_size = rps_peers[i].cur_view_count;
2129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2130 "\t\tview_size: %" PRIu32 "\n", view_size);
2131 /* For peer i the probability of being sampled is
2132 * evenly distributed among all possibly observed peers. */
2133 /* We could have observed a peer in three cases:
2134 * 1. peer sent a push
2135 * 2. peer was contained in a pull reply
2136 * 3. peer was in history (sampler) - ignored for now */
2137 /* 1. Probability of having received a push from peer i */
2138 if ((GNUNET_YES == is_in_view (i, peer_idx)) &&
2139 (1 <= (0.45 * view_size)))
2140 {
2141 prob_push = 1.0 * binom (0.45 * view_size, 1)
2142 /
2143 binom (view_size, 0.45 * view_size);
2144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2145 "\t\t%" PRIu32 " is in %" PRIu32 "'s view, prob: %f\n",
2146 peer_idx,
2147 i,
2148 prob_push);
2149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2150 "\t\tposs choices from view: %" PRIu32 ", containing i: %"
2151 PRIu32 "\n",
2152 binom (view_size, 0.45 * view_size),
2153 binom (0.45 * view_size, 1));
2154 }
2155 else
2156 {
2157 prob_push = 0;
2158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2159 "\t\t%" PRIu32 " is not in %" PRIu32 "'s view, prob: 0\n",
2160 peer_idx,
2161 i);
2162 }
2163 /* 2. Probability of peer i being contained in pulls */
2164 view_size = rps_peers[peer_idx].cur_view_count;
2165 cont_views = count_containing_views (peer_idx, i);
2166 number_of_being_in_pull_events =
2167 (binom (view_size, 0.45 * view_size)
2168 - binom (view_size - cont_views, 0.45 * view_size));
2169 if (0 != number_of_being_in_pull_events)
2170 {
2171 prob_pull = number_of_being_in_pull_events
2172 /
2173 (1.0 * binom (view_size, 0.45 * view_size));
2174 }
2175 else
2176 {
2177 prob_pull = 0;
2178 }
2179 probs[i] = prob_push + prob_pull - (prob_push * prob_pull);
2180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2181 "\t\t%" PRIu32 " has %" PRIu32 " of %" PRIu32
2182 " peers in its view who know %" PRIu32 " prob: %f\n",
2183 peer_idx,
2184 cont_views,
2185 view_size,
2186 i,
2187 prob_pull);
2188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2189 "\t\tnumber of possible pull combinations: %" PRIu32 "\n",
2190 binom (view_size, 0.45 * view_size));
2191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2192 "\t\tnumber of possible pull combinations without %" PRIu32
2193 ": %" PRIu32 "\n",
2194 i,
2195 binom (view_size - cont_views, 0.45 * view_size));
2196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197 "\t\tnumber of possible pull combinations with %" PRIu32
2198 ": %" PRIu32 "\n",
2199 i,
2200 number_of_being_in_pull_events);
2201
2202 if (0 != probs[i])
2203 count_non_zero_prob++;
2204 }
2205 /* normalize */
2206 if (0 != count_non_zero_prob)
2207 {
2208 for (i = 0; i < num_peers; i++)
2209 {
2210 probs[i] = probs[i] * (1.0 / count_non_zero_prob);
2211 }
2212 }
2213 else
2214 {
2215 for (i = 0; i < num_peers; i++)
2216 {
2217 probs[i] = 0;
2218 }
2219 }
2220 /* str repr */
2221 for (i = 0; i < num_peers; i++)
2222 {
2223 probs_as_str_cpy = GNUNET_strndup (probs_as_str, probs_as_str_size);
2224 tmp = GNUNET_snprintf (probs_as_str,
2225 probs_as_str_size,
2226 "%s %7.6f", probs_as_str_cpy, probs[i]);
2227 GNUNET_free (probs_as_str_cpy);
2228 GNUNET_assert (0 <= tmp);
2229 }
2230
2231 to_file_w_len (rps_peers[peer_idx].file_name_probs,
2232 probs_as_str_size,
2233 probs_as_str);
2234 GNUNET_free (probs_as_str);
2235}
2236
2237
2238/**
2239 * @brief This counts the number of peers in which views a given peer occurs.
2240 *
2241 * It also stores this value in the rps peer.
2242 *
2243 * @param peer_idx the index of the peer to count the representation
2244 *
2245 * @return the number of occurrences
2246 */
2247static uint32_t
2248count_peer_in_views_2 (uint32_t peer_idx)
2249{
2250 uint32_t i, j;
2251 uint32_t count = 0;
2252
2253 for (i = 0; i < num_peers; i++) /* Peer in which view is counted */
2254 {
2255 for (j = 0; j < rps_peers[i].cur_view_count; j++) /* entry in view */
2256 {
2257 if (0 == memcmp (rps_peers[peer_idx].peer_id,
2258 &rps_peers[i].cur_view[j],
2259 sizeof(struct GNUNET_PeerIdentity)))
2260 {
2261 count++;
2262 break;
2263 }
2264 }
2265 }
2266 rps_peers[peer_idx].count_in_views = count;
2267 return count;
2268}
2269
2270
2271static uint32_t
2272cumulated_view_sizes ()
2273{
2274 uint32_t i;
2275
2276 view_sizes = 0;
2277 for (i = 0; i < num_peers; i++) /* Peer in which view is counted */
2278 {
2279 view_sizes += rps_peers[i].cur_view_count;
2280 }
2281 return view_sizes;
2282}
2283
2284
2285static void
2286count_peer_in_views (uint32_t *count_peers)
2287{
2288 uint32_t i, j;
2289
2290 for (i = 0; i < num_peers; i++) /* Peer in which view is counted */
2291 {
2292 for (j = 0; j < rps_peers[i].cur_view_count; j++) /* entry in view */
2293 {
2294 if (0 == memcmp (rps_peers[i].peer_id,
2295 &rps_peers[i].cur_view[j],
2296 sizeof(struct GNUNET_PeerIdentity)))
2297 {
2298 count_peers[i]++;
2299 }
2300 }
2301 }
2302}
2303
2304
2305void
2306compute_diversity ()
2307{
2308 uint32_t i;
2309 /* ith entry represents the number of occurrences in other peer's views */
2310 uint32_t *count_peers = GNUNET_new_array (num_peers, uint32_t);
2311 uint32_t views_total_size;
2312 double expected;
2313 /* deviation from expected number of peers */
2314 double *deviation = GNUNET_new_array (num_peers, double);
2315
2316 views_total_size = 0;
2317 expected = 0;
2318
2319 /* For each peer count its representation in other peer's views*/
2320 for (i = 0; i < num_peers; i++) /* Peer to count */
2321 {
2322 views_total_size += rps_peers[i].cur_view_count;
2323 count_peer_in_views (count_peers);
2324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2325 "Counted representation of %" PRIu32 "th peer [%s]: %" PRIu32
2326 "\n",
2327 i,
2328 GNUNET_i2s (rps_peers[i].peer_id),
2329 count_peers[i]);
2330 }
2331
2332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2333 "size of all views combined: %" PRIu32 "\n",
2334 views_total_size);
2335 expected = ((double) 1 / num_peers) * views_total_size;
2336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2337 "Expected number of occurrences of each peer in all views: %f\n",
2338 expected);
2339 for (i = 0; i < num_peers; i++) /* Peer to count */
2340 {
2341 deviation[i] = expected - count_peers[i];
2342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2343 "Deviation from expectation: %f\n", deviation[i]);
2344 }
2345 GNUNET_free (count_peers);
2346 GNUNET_free (deviation);
2347}
2348
2349
2350void
2351print_view_sizes ()
2352{
2353 uint32_t i;
2354
2355 for (i = 0; i < num_peers; i++) /* Peer to count */
2356 {
2357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2358 "View size of %" PRIu32 ". [%s] is %" PRIu32 "\n",
2359 i,
2360 GNUNET_i2s (rps_peers[i].peer_id),
2361 rps_peers[i].cur_view_count);
2362 }
2363}
2364
2365
2366void
2367all_views_updated_cb ()
2368{
2369 compute_diversity ();
2370 print_view_sizes ();
2371}
2372
2373
2374void
2375view_update_cb (void *cls,
2376 uint64_t view_size,
2377 const struct GNUNET_PeerIdentity *peers)
2378{
2379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2380 "View was updated (%" PRIu64 ")\n", view_size);
2381 struct RPSPeer *rps_peer = (struct RPSPeer *) cls;
2382 to_file ("/tmp/rps/view_sizes.txt",
2383 "%" PRIu32 " %" PRIu64 "",
2384 rps_peer->index,
2385 view_size);
2386 for (uint64_t i = 0; i < view_size; i++)
2387 {
2388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2389 "\t%s\n", GNUNET_i2s (&peers[i]));
2390 }
2391 GNUNET_array_grow (rps_peer->cur_view,
2392 rps_peer->cur_view_count,
2393 view_size);
2394 // *rps_peer->cur_view = *peers;
2395 GNUNET_memcpy (rps_peer->cur_view,
2396 peers,
2397 view_size * sizeof(struct GNUNET_PeerIdentity));
2398 to_file ("/tmp/rps/count_in_views.txt",
2399 "%" PRIu32 " %" PRIu32 "",
2400 rps_peer->index,
2401 count_peer_in_views_2 (rps_peer->index));
2402 cumulated_view_sizes ();
2403 if (0 != view_size)
2404 {
2405 to_file ("/tmp/rps/repr.txt",
2406 "%" PRIu32 /* index */
2407 " %" PRIu32 /* occurrence in views */
2408 " %" PRIu32 /* view sizes */
2409 " %f" /* fraction of repr in views */
2410 " %f" /* average view size */
2411 " %f" /* prob of occurrence in view slot */
2412 " %f" "", /* exp frac of repr in views */
2413 rps_peer->index,
2414 count_peer_in_views_2 (rps_peer->index),
2415 view_sizes,
2416 count_peer_in_views_2 (rps_peer->index) / (view_size * 1.0), /* fraction of representation in views */
2417 view_sizes / (view_size * 1.0), /* average view size */
2418 1.0 / view_size, /* prob of occurrence in view slot */
2419 (1.0 / view_size) * (view_sizes / view_size) /* expected fraction of repr in views */
2420 );
2421 }
2422 compute_probabilities (rps_peer->index);
2423 all_views_updated_cb ();
2424}
2425
2426
2427static void
2428pre_profiler (struct RPSPeer *rps_peer, struct GNUNET_RPS_Handle *h)
2429{
2430 rps_peer->file_name_probs =
2431 store_prefix_file_name (rps_peer->index, "probs");
2432 GNUNET_RPS_view_request (h, 0, view_update_cb, rps_peer);
2433}
2434
2435
2436void
2437write_final_stats (void)
2438{
2439 uint32_t i;
2440
2441 for (i = 0; i < num_peers; i++)
2442 {
2443 to_file ("/tmp/rps/final_stats.dat",
2444 "%" PRIu32 " " /* index */
2445 "%s %" /* id */
2446 PRIu64 " %" /* rounds */
2447 PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
2448 " %" /* blocking */
2449 PRIu64 " %" PRIu64 " %" PRIu64 " %" /* issued */
2450 PRIu64 " %" PRIu64 " %" PRIu64 " %" /* sent */
2451 PRIu64 " %" PRIu64 " %" PRIu64 /* recv */,
2452 i,
2453 GNUNET_i2s (rps_peers[i].peer_id),
2454 rps_peers[i].num_rounds,
2455 rps_peers[i].num_blocks,
2456 rps_peers[i].num_blocks_many_push,
2457 rps_peers[i].num_blocks_no_push,
2458 rps_peers[i].num_blocks_no_pull,
2459 rps_peers[i].num_blocks_many_push_no_pull,
2460 rps_peers[i].num_blocks_no_push_no_pull,
2461 rps_peers[i].num_issued_push,
2462 rps_peers[i].num_issued_pull_req,
2463 rps_peers[i].num_issued_pull_rep,
2464 rps_peers[i].num_sent_push,
2465 rps_peers[i].num_sent_pull_req,
2466 rps_peers[i].num_sent_pull_rep,
2467 rps_peers[i].num_recv_push,
2468 rps_peers[i].num_recv_pull_req,
2469 rps_peers[i].num_recv_pull_rep);
2470 }
2471}
2472
2473
2474/**
2475 * Continuation called by #GNUNET_STATISTICS_get() functions.
2476 *
2477 * Remembers that this specific statistics value was received for this peer.
2478 * Checks whether all peers received their statistics yet.
2479 * Issues the shutdown.
2480 *
2481 * @param cls closure
2482 * @param success #GNUNET_OK if statistics were
2483 * successfully obtained, #GNUNET_SYSERR if not.
2484 */
2485void
2486post_test_shutdown_ready_cb (void *cls,
2487 int success)
2488{
2489 struct STATcls *stat_cls = (struct STATcls *) cls;
2490 struct RPSPeer *rps_peer = stat_cls->rps_peer;
2491
2492 if (GNUNET_OK == success)
2493 {
2494 /* set flag that we we got the value */
2495 rps_peer->stat_collected_flags |= stat_cls->stat_type;
2496 }
2497 else
2498 {
2499 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2500 "Peer %u did not receive statistics value\n",
2501 rps_peer->index);
2502 GNUNET_free (stat_cls);
2503 GNUNET_break (0);
2504 }
2505
2506 if ((NULL != rps_peer->stat_op) &&
2507 (GNUNET_YES == check_statistics_collect_completed_single_peer (
2508 rps_peer)) )
2509 {
2510 GNUNET_TESTBED_operation_done (rps_peer->stat_op);
2511 }
2512
2513 write_final_stats ();
2514 if (GNUNET_YES == check_statistics_collect_completed ())
2515 {
2516 // write_final_stats ();
2517 GNUNET_free (stat_cls);
2518 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2519 "Shutting down\n");
2520 GNUNET_SCHEDULER_shutdown ();
2521 }
2522 else
2523 {
2524 GNUNET_free (stat_cls);
2525 }
2526}
2527
2528
2529/**
2530 * @brief Converts string representation to the corresponding #STAT_TYPE enum.
2531 *
2532 * @param stat_str string representation of statistics specifier
2533 *
2534 * @return corresponding enum
2535 */
2536enum STAT_TYPE
2537stat_str_2_type (const char *stat_str)
2538{
2539 if (0 == strncmp ("# rounds blocked - no pull replies", stat_str, strlen (
2540 "# rounds blocked - no pull replies")))
2541 {
2542 return STAT_TYPE_BLOCKS_NO_PULL;
2543 }
2544 else if (0 == strncmp ("# rounds blocked - too many pushes, no pull replies",
2545 stat_str, strlen (
2546 "# rounds blocked - too many pushes, no pull replies")))
2547 {
2548 return STAT_TYPE_BLOCKS_MANY_PUSH_NO_PULL;
2549 }
2550 else if (0 == strncmp ("# rounds blocked - too many pushes", stat_str,
2551 strlen ("# rounds blocked - too many pushes")))
2552 {
2553 return STAT_TYPE_BLOCKS_MANY_PUSH;
2554 }
2555 else if (0 == strncmp ("# rounds blocked - no pushes, no pull replies",
2556 stat_str, strlen (
2557 "# rounds blocked - no pushes, no pull replies")))
2558 {
2559 return STAT_TYPE_BLOCKS_NO_PUSH_NO_PULL;
2560 }
2561 else if (0 == strncmp ("# rounds blocked - no pushes", stat_str, strlen (
2562 "# rounds blocked - no pushes")))
2563 {
2564 return STAT_TYPE_BLOCKS_NO_PUSH;
2565 }
2566 else if (0 == strncmp ("# rounds blocked", stat_str, strlen (
2567 "# rounds blocked")))
2568 {
2569 return STAT_TYPE_BLOCKS;
2570 }
2571 else if (0 == strncmp ("# rounds", stat_str, strlen ("# rounds")))
2572 {
2573 return STAT_TYPE_ROUNDS;
2574 }
2575 else if (0 == strncmp ("# push send issued", stat_str, strlen (
2576 "# push send issued")))
2577 {
2578 return STAT_TYPE_ISSUED_PUSH_SEND;
2579 }
2580 else if (0 == strncmp ("# pull request send issued", stat_str, strlen (
2581 "# pull request send issued")))
2582 {
2583 return STAT_TYPE_ISSUED_PULL_REQ;
2584 }
2585 else if (0 == strncmp ("# pull reply send issued", stat_str, strlen (
2586 "# pull reply send issued")))
2587 {
2588 return STAT_TYPE_ISSUED_PULL_REP;
2589 }
2590 else if (0 == strncmp ("# pushes sent", stat_str, strlen ("# pushes sent")))
2591 {
2592 return STAT_TYPE_SENT_PUSH_SEND;
2593 }
2594 else if (0 == strncmp ("# pull requests sent", stat_str, strlen (
2595 "# pull requests sent")))
2596 {
2597 return STAT_TYPE_SENT_PULL_REQ;
2598 }
2599 else if (0 == strncmp ("# pull replies sent", stat_str, strlen (
2600 "# pull replies sent")))
2601 {
2602 return STAT_TYPE_SENT_PULL_REP;
2603 }
2604 else if (0 == strncmp ("# push message received", stat_str, strlen (
2605 "# push message received")))
2606 {
2607 return STAT_TYPE_RECV_PUSH_SEND;
2608 }
2609 else if (0 == strncmp ("# pull request message received", stat_str, strlen (
2610 "# pull request message received")))
2611 {
2612 return STAT_TYPE_RECV_PULL_REQ;
2613 }
2614 else if (0 == strncmp ("# pull reply messages received", stat_str, strlen (
2615 "# pull reply messages received")))
2616 {
2617 return STAT_TYPE_RECV_PULL_REP;
2618 }
2619 return STAT_TYPE_MAX;
2620}
2621
2622
2623/**
2624 * @brief Converts #STAT_TYPE enum to the equivalent string representation that
2625 * is stored with the statistics service.
2626 *
2627 * @param stat_type #STAT_TYPE enum
2628 *
2629 * @return string representation that matches statistics value
2630 */
2631char*
2632stat_type_2_str (enum STAT_TYPE stat_type)
2633{
2634 switch (stat_type)
2635 {
2636 case STAT_TYPE_ROUNDS:
2637 return "# rounds";
2638
2639 case STAT_TYPE_BLOCKS:
2640 return "# rounds blocked";
2641
2642 case STAT_TYPE_BLOCKS_MANY_PUSH:
2643 return "# rounds blocked - too many pushes";
2644
2645 case STAT_TYPE_BLOCKS_NO_PUSH:
2646 return "# rounds blocked - no pushes";
2647
2648 case STAT_TYPE_BLOCKS_NO_PULL:
2649 return "# rounds blocked - no pull replies";
2650
2651 case STAT_TYPE_BLOCKS_MANY_PUSH_NO_PULL:
2652 return "# rounds blocked - too many pushes, no pull replies";
2653
2654 case STAT_TYPE_BLOCKS_NO_PUSH_NO_PULL:
2655 return "# rounds blocked - no pushes, no pull replies";
2656
2657 case STAT_TYPE_ISSUED_PUSH_SEND:
2658 return "# push send issued";
2659
2660 case STAT_TYPE_ISSUED_PULL_REQ:
2661 return "# pull request send issued";
2662
2663 case STAT_TYPE_ISSUED_PULL_REP:
2664 return "# pull reply send issued";
2665
2666 case STAT_TYPE_SENT_PUSH_SEND:
2667 return "# pushes sent";
2668
2669 case STAT_TYPE_SENT_PULL_REQ:
2670 return "# pull requests sent";
2671
2672 case STAT_TYPE_SENT_PULL_REP:
2673 return "# pull replies sent";
2674
2675 case STAT_TYPE_RECV_PUSH_SEND:
2676 return "# push message received";
2677
2678 case STAT_TYPE_RECV_PULL_REQ:
2679 return "# pull request message received";
2680
2681 case STAT_TYPE_RECV_PULL_REP:
2682 return "# pull reply messages received";
2683
2684 case STAT_TYPE_MAX:
2685 default:
2686 return "ERROR";
2687 ;
2688 }
2689}
2690
2691
2692/**
2693 * Callback function to process statistic values.
2694 *
2695 * @param cls closure
2696 * @param subsystem name of subsystem that created the statistic
2697 * @param name the name of the datum
2698 * @param value the current value
2699 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
2700 * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
2701 */
2702int
2703stat_iterator (void *cls,
2704 const char *subsystem,
2705 const char *name,
2706 uint64_t value,
2707 int is_persistent)
2708{
2709 (void) subsystem;
2710 (void) is_persistent;
2711 const struct STATcls *stat_cls = (const struct STATcls *) cls;
2712 struct RPSPeer *rps_peer = (struct RPSPeer *) stat_cls->rps_peer;
2713 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got stat value: %s - %" PRIu64 "\n",
2714 // stat_type_2_str (stat_cls->stat_type),
2715 name,
2716 value);
2717 to_file (rps_peer->file_name_stats,
2718 "%s: %" PRIu64 "\n",
2719 name,
2720 value);
2721 switch (stat_str_2_type (name))
2722 {
2723 case STAT_TYPE_ROUNDS:
2724 rps_peer->num_rounds = value;
2725 break;
2726
2727 case STAT_TYPE_BLOCKS:
2728 rps_peer->num_blocks = value;
2729 break;
2730
2731 case STAT_TYPE_BLOCKS_MANY_PUSH:
2732 rps_peer->num_blocks_many_push = value;
2733 break;
2734
2735 case STAT_TYPE_BLOCKS_NO_PUSH:
2736 rps_peer->num_blocks_no_push = value;
2737 break;
2738
2739 case STAT_TYPE_BLOCKS_NO_PULL:
2740 rps_peer->num_blocks_no_pull = value;
2741 break;
2742
2743 case STAT_TYPE_BLOCKS_MANY_PUSH_NO_PULL:
2744 rps_peer->num_blocks_many_push_no_pull = value;
2745 break;
2746
2747 case STAT_TYPE_BLOCKS_NO_PUSH_NO_PULL:
2748 rps_peer->num_blocks_no_push_no_pull = value;
2749 break;
2750
2751 case STAT_TYPE_ISSUED_PUSH_SEND:
2752 rps_peer->num_issued_push = value;
2753 break;
2754
2755 case STAT_TYPE_ISSUED_PULL_REQ:
2756 rps_peer->num_issued_pull_req = value;
2757 break;
2758
2759 case STAT_TYPE_ISSUED_PULL_REP:
2760 rps_peer->num_issued_pull_rep = value;
2761 break;
2762
2763 case STAT_TYPE_SENT_PUSH_SEND:
2764 rps_peer->num_sent_push = value;
2765 break;
2766
2767 case STAT_TYPE_SENT_PULL_REQ:
2768 rps_peer->num_sent_pull_req = value;
2769 break;
2770
2771 case STAT_TYPE_SENT_PULL_REP:
2772 rps_peer->num_sent_pull_rep = value;
2773 break;
2774
2775 case STAT_TYPE_RECV_PUSH_SEND:
2776 rps_peer->num_recv_push = value;
2777 break;
2778
2779 case STAT_TYPE_RECV_PULL_REQ:
2780 rps_peer->num_recv_pull_req = value;
2781 break;
2782
2783 case STAT_TYPE_RECV_PULL_REP:
2784 rps_peer->num_recv_pull_rep = value;
2785 break;
2786
2787 case STAT_TYPE_MAX:
2788 default:
2789 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2790 "Unknown statistics string: %s\n",
2791 name);
2792 break;
2793 }
2794 return GNUNET_OK;
2795}
2796
2797
2798void
2799post_profiler (struct RPSPeer *rps_peer)
2800{
2801 if (COLLECT_STATISTICS != cur_test_run.have_collect_statistics)
2802 {
2803 return;
2804 }
2805
2806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2807 "Going to request statistic values with mask 0x%" PRIx32 "\n",
2808 cur_test_run.stat_collect_flags);
2809
2810 struct STATcls *stat_cls;
2811 uint32_t stat_type;
2812 for (stat_type = STAT_TYPE_ROUNDS;
2813 stat_type < STAT_TYPE_MAX;
2814 stat_type = stat_type << 1)
2815 {
2816 if (stat_type & cur_test_run.stat_collect_flags)
2817 {
2818 stat_cls = GNUNET_malloc (sizeof(struct STATcls));
2819 stat_cls->rps_peer = rps_peer;
2820 stat_cls->stat_type = stat_type;
2821 rps_peer->file_name_stats =
2822 store_prefix_file_name (rps_peer->peer_id, "stats");
2823 GNUNET_STATISTICS_get (rps_peer->stats_h,
2824 "rps",
2825 stat_type_2_str (stat_type),
2826 post_test_shutdown_ready_cb,
2827 stat_iterator,
2828 (struct STATcls *) stat_cls);
2829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2830 "Requested statistics for %s (peer %" PRIu32 ")\n",
2831 stat_type_2_str (stat_type),
2832 rps_peer->index);
2833 }
2834 }
2835}
2836
2837
2838/***********************************************************************
2839* /Definition of tests
2840***********************************************************************/
2841
2842
2843/**
2844 * Actual "main" function for the testcase.
2845 *
2846 * @param cls closure
2847 * @param h the run handle
2848 * @param n_peers number of peers in 'peers'
2849 * @param peers handle to peers run in the testbed
2850 * @param links_succeeded the number of overlay link connection attempts that
2851 * succeeded
2852 * @param links_failed the number of overlay link connection attempts that
2853 * failed
2854 */
2855static void
2856run (void *cls,
2857 struct GNUNET_TESTBED_RunHandle *h,
2858 unsigned int n_peers,
2859 struct GNUNET_TESTBED_Peer **peers,
2860 unsigned int links_succeeded,
2861 unsigned int links_failed)
2862{
2863 (void) cls;
2864 (void) h;
2865 (void) links_failed;
2866 unsigned int i;
2867 struct OpListEntry *entry;
2868
2869 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "RUN was called\n");
2870
2871 /* Check whether we timed out */
2872 if ((n_peers != num_peers) ||
2873 (NULL == peers) ||
2874 (0 == links_succeeded) )
2875 {
2876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2877 "Going down due to args (eg. timeout)\n");
2878 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tn_peers: %u\n", n_peers);
2879 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tnum_peers: %" PRIu32 "\n",
2880 num_peers);
2881 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tpeers: %p\n", peers);
2882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tlinks_succeeded: %u\n",
2883 links_succeeded);
2884 GNUNET_SCHEDULER_shutdown ();
2885 return;
2886 }
2887
2888
2889 /* Initialize peers */
2890 testbed_peers = peers;
2891 num_peers_online = 0;
2892 for (i = 0; i < num_peers; i++)
2893 {
2894 entry = make_oplist_entry ();
2895 entry->index = i;
2896 rps_peers[i].index = i;
2897 if (NULL != cur_test_run.init_peer)
2898 cur_test_run.init_peer (&rps_peers[i]);
2899 if (NO_COLLECT_VIEW == cur_test_run.have_collect_view)
2900 {
2901 rps_peers->cur_view_count = 0;
2902 rps_peers->cur_view = NULL;
2903 }
2904 entry->op = GNUNET_TESTBED_peer_get_information (peers[i],
2905 GNUNET_TESTBED_PIT_IDENTITY,
2906 &info_cb,
2907 entry);
2908 }
2909
2910 /* Bring peers up */
2911 GNUNET_assert (num_peers == n_peers);
2912 for (i = 0; i < n_peers; i++)
2913 {
2914 rps_peers[i].index = i;
2915 rps_peers[i].op =
2916 GNUNET_TESTBED_service_connect (&rps_peers[i],
2917 peers[i],
2918 "rps",
2919 &rps_connect_complete_cb,
2920 &rps_peers[i],
2921 &rps_connect_adapter,
2922 &rps_disconnect_adapter,
2923 &rps_peers[i]);
2924 /* Connect all peers to statistics service */
2925 if (COLLECT_STATISTICS == cur_test_run.have_collect_statistics)
2926 {
2927 rps_peers[i].stat_op =
2928 GNUNET_TESTBED_service_connect (NULL,
2929 peers[i],
2930 "statistics",
2931 stat_complete_cb,
2932 &rps_peers[i],
2933 &stat_connect_adapter,
2934 &stat_disconnect_adapter,
2935 &rps_peers[i]);
2936 }
2937 }
2938
2939 if (NULL != churn_task)
2940 GNUNET_SCHEDULER_cancel (churn_task);
2941 post_test_task = GNUNET_SCHEDULER_add_delayed (timeout, &post_test_op, NULL);
2942 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
2943 (timeout_s * 1.2) + 0.1 * num_peers);
2944 shutdown_task = GNUNET_SCHEDULER_add_delayed (timeout, &shutdown_op, NULL);
2945}
2946
2947
2948/**
2949 * Entry point for the testcase, sets up the testbed.
2950 *
2951 * @param argc unused
2952 * @param argv unused
2953 * @return 0 on success
2954 */
2955int
2956main (int argc, char *argv[])
2957{
2958 int ret_value;
2959
2960 (void) argc;
2961
2962 /* Defaults for tests */
2963 num_peers = 5;
2964 cur_test_run.name = "test-rps-default";
2965 cur_test_run.init_peer = default_init_peer;
2966 cur_test_run.pre_test = NULL;
2967 cur_test_run.reply_handle = default_reply_handle;
2968 cur_test_run.eval_cb = default_eval_cb;
2969 cur_test_run.post_test = NULL;
2970 cur_test_run.have_churn = HAVE_CHURN;
2971 cur_test_run.have_collect_statistics = NO_COLLECT_STATISTICS;
2972 cur_test_run.stat_collect_flags = 0;
2973 cur_test_run.have_collect_view = NO_COLLECT_VIEW;
2974 churn_task = NULL;
2975 timeout_s = 30;
2976
2977 if (strstr (argv[0], "malicious") != NULL)
2978 {
2979 cur_test_run.pre_test = mal_pre;
2980 cur_test_run.main_test = mal_cb;
2981 cur_test_run.init_peer = mal_init_peer;
2982 timeout_s = 40;
2983
2984 if (strstr (argv[0], "_1") != NULL)
2985 {
2986 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test malicious peer type 1\n");
2987 cur_test_run.name = "test-rps-malicious_1";
2988 mal_type = 1;
2989 }
2990 else if (strstr (argv[0], "_2") != NULL)
2991 {
2992 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test malicious peer type 2\n");
2993 cur_test_run.name = "test-rps-malicious_2";
2994 mal_type = 2;
2995 }
2996 else if (strstr (argv[0], "_3") != NULL)
2997 {
2998 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test malicious peer type 3\n");
2999 cur_test_run.name = "test-rps-malicious_3";
3000 mal_type = 3;
3001 }
3002 }
3003
3004 else if (strstr (argv[0], "_single_req") != NULL)
3005 {
3006 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test single request\n");
3007 cur_test_run.name = "test-rps-single-req";
3008 cur_test_run.main_test = single_req_cb;
3009 cur_test_run.have_churn = HAVE_NO_CHURN;
3010 }
3011
3012 else if (strstr (argv[0], "_delayed_reqs") != NULL)
3013 {
3014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test delayed requests\n");
3015 cur_test_run.name = "test-rps-delayed-reqs";
3016 cur_test_run.main_test = delay_req_cb;
3017 cur_test_run.have_churn = HAVE_NO_CHURN;
3018 }
3019
3020 else if (strstr (argv[0], "_seed_big") != NULL)
3021 {
3022 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3023 "Test seeding (num_peers > GNUNET_MAX_MESSAGE_SIZE)\n");
3024 num_peers = 1;
3025 cur_test_run.name = "test-rps-seed-big";
3026 cur_test_run.main_test = seed_big_cb;
3027 cur_test_run.eval_cb = no_eval;
3028 cur_test_run.have_churn = HAVE_NO_CHURN;
3029 timeout_s = 10;
3030 }
3031
3032 else if (strstr (argv[0], "_single_peer_seed") != NULL)
3033 {
3034 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3035 "Test seeding and requesting on a single peer\n");
3036 cur_test_run.name = "test-rps-single-peer-seed";
3037 cur_test_run.main_test = single_peer_seed_cb;
3038 cur_test_run.have_churn = HAVE_NO_CHURN;
3039 }
3040
3041 else if (strstr (argv[0], "_seed_request") != NULL)
3042 {
3043 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3044 "Test seeding and requesting on multiple peers\n");
3045 cur_test_run.name = "test-rps-seed-request";
3046 cur_test_run.main_test = seed_req_cb;
3047 cur_test_run.have_churn = HAVE_NO_CHURN;
3048 }
3049
3050 else if (strstr (argv[0], "_seed") != NULL)
3051 {
3052 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test seeding\n");
3053 cur_test_run.name = "test-rps-seed";
3054 cur_test_run.main_test = seed_cb;
3055 cur_test_run.eval_cb = no_eval;
3056 cur_test_run.have_churn = HAVE_NO_CHURN;
3057 }
3058
3059 else if (strstr (argv[0], "_req_cancel") != NULL)
3060 {
3061 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test cancelling a request\n");
3062 cur_test_run.name = "test-rps-req-cancel";
3063 num_peers = 1;
3064 cur_test_run.main_test = req_cancel_cb;
3065 cur_test_run.eval_cb = no_eval;
3066 cur_test_run.have_churn = HAVE_NO_CHURN;
3067 timeout_s = 10;
3068 }
3069
3070 else if (strstr (argv[0], "_churn") != NULL)
3071 {
3072 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test churn\n");
3073 cur_test_run.name = "test-rps-churn";
3074 num_peers = 5;
3075 cur_test_run.init_peer = default_init_peer;
3076 cur_test_run.main_test = churn_test_cb;
3077 cur_test_run.reply_handle = default_reply_handle;
3078 cur_test_run.eval_cb = default_eval_cb;
3079 cur_test_run.have_churn = HAVE_NO_CHURN;
3080 cur_test_run.have_quick_quit = HAVE_NO_QUICK_QUIT;
3081 timeout_s = 40;
3082 }
3083
3084 else if (strstr (argv[0], "_sub") != NULL)
3085 {
3086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test subs\n");
3087 cur_test_run.name = "test-rps-sub";
3088 num_peers = 5;
3089 // cur_test_run.init_peer = &default_init_peer;
3090 cur_test_run.pre_test = &sub_pre;
3091 cur_test_run.main_test = &single_req_cb;
3092 // cur_test_run.reply_handle = default_reply_handle;
3093 cur_test_run.post_test = &sub_post;
3094 // cur_test_run.eval_cb = default_eval_cb;
3095 cur_test_run.have_churn = HAVE_NO_CHURN;
3096 cur_test_run.have_quick_quit = HAVE_QUICK_QUIT;
3097 }
3098
3099 else if (strstr (argv[0], "profiler") != NULL)
3100 {
3101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "This is the profiler\n");
3102 cur_test_run.name = "test-rps-profiler";
3103 num_peers = 16;
3104 mal_type = 3;
3105 cur_test_run.init_peer = profiler_init_peer;
3106 // cur_test_run.pre_test = mal_pre;
3107 cur_test_run.pre_test = pre_profiler;
3108 cur_test_run.main_test = profiler_cb;
3109 cur_test_run.reply_handle = profiler_reply_handle;
3110 cur_test_run.eval_cb = profiler_eval;
3111 cur_test_run.post_test = post_profiler;
3112 cur_test_run.request_interval = 2;
3113 cur_test_run.num_requests = 5;
3114 // cur_test_run.have_churn = HAVE_CHURN;
3115 cur_test_run.have_churn = HAVE_NO_CHURN;
3116 cur_test_run.have_quick_quit = HAVE_NO_QUICK_QUIT;
3117 cur_test_run.have_collect_statistics = COLLECT_STATISTICS;
3118 cur_test_run.stat_collect_flags = STAT_TYPE_ROUNDS
3119 | STAT_TYPE_BLOCKS
3120 | STAT_TYPE_BLOCKS_MANY_PUSH
3121 | STAT_TYPE_BLOCKS_NO_PUSH
3122 | STAT_TYPE_BLOCKS_NO_PULL
3123 | STAT_TYPE_BLOCKS_MANY_PUSH_NO_PULL
3124 | STAT_TYPE_BLOCKS_NO_PUSH_NO_PULL
3125 | STAT_TYPE_ISSUED_PUSH_SEND
3126 | STAT_TYPE_ISSUED_PULL_REQ
3127 | STAT_TYPE_ISSUED_PULL_REP
3128 | STAT_TYPE_SENT_PUSH_SEND
3129 | STAT_TYPE_SENT_PULL_REQ
3130 | STAT_TYPE_SENT_PULL_REP
3131 | STAT_TYPE_RECV_PUSH_SEND
3132 | STAT_TYPE_RECV_PULL_REQ
3133 | STAT_TYPE_RECV_PULL_REP;
3134 cur_test_run.have_collect_view = COLLECT_VIEW;
3135 timeout_s = 150;
3136
3137 /* 'Clean' directory */
3138 (void) GNUNET_DISK_directory_remove ("/tmp/rps/");
3139 GNUNET_DISK_directory_create ("/tmp/rps/");
3140 }
3141 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, timeout_s);
3142
3143 rps_peers = GNUNET_new_array (num_peers, struct RPSPeer);
3144 peer_map = GNUNET_CONTAINER_multipeermap_create (num_peers, GNUNET_NO);
3145 rps_peer_ids = GNUNET_new_array (num_peers, struct GNUNET_PeerIdentity);
3146 if ((2 == mal_type) ||
3147 (3 == mal_type))
3148 target_peer = &rps_peer_ids[num_peers - 2];
3149 if (profiler_eval == cur_test_run.eval_cb)
3150 eval_peer = &rps_peers[num_peers - 1]; /* FIXME: eval_peer could be a
3151 malicious peer if not careful
3152 with the malicious portion */
3153
3154 ok = 1;
3155 ret_value = GNUNET_TESTBED_test_run (cur_test_run.name,
3156 "test_rps.conf",
3157 num_peers,
3158 0, NULL, NULL,
3159 &run, NULL);
3160 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3161 "_test_run returned.\n");
3162 if (GNUNET_OK != ret_value)
3163 {
3164 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3165 "Test did not run successfully!\n");
3166 }
3167
3168 ret_value = cur_test_run.eval_cb ();
3169
3170 if (NO_COLLECT_VIEW == cur_test_run.have_collect_view)
3171 {
3172 GNUNET_array_grow (rps_peers->cur_view,
3173 rps_peers->cur_view_count,
3174 0);
3175 }
3176 GNUNET_free (rps_peers);
3177 GNUNET_free (rps_peer_ids);
3178 GNUNET_CONTAINER_multipeermap_destroy (peer_map);
3179 return ret_value;
3180}
3181
3182
3183/* end of test_rps.c */
diff --git a/src/contrib/service/rps/test_rps.conf b/src/contrib/service/rps/test_rps.conf
new file mode 100644
index 000000000..2b1101786
--- /dev/null
+++ b/src/contrib/service/rps/test_rps.conf
@@ -0,0 +1,135 @@
1[rps]
2#PREFIX = valgrind --leak-check=full --show-leak-kinds=all --log-file=/tmp/rps/valgrind!gnunet-service-rps!%p
3#PREFIX = valgrind --log-file=/tmp/rps/valgrind!gnunet-service-rps!%p
4#PREFIX = valgrind
5UNIXPATH = $GNUNET_TMP/gnunet-service-rps.sock
6HOME = $SERVICEHOME
7# PORT = 2106
8#@UNIXONLY@ PORT = 2087
9IMMEDIATE_START = YES
10START_ON_DEMAND = NO
11NOARMBIND = YES
12#OPTIONS=-l /tmp/rps_profiler_logs/rps-[]-%Y-%m-%d.log
13
14# This is the timeinterval between the rounds
15ROUNDINTERVAL = 100 ms
16FILENAME_VALID_PEERS = $GNUNET_DATA_HOME/rps/valid_peers.txt
17
18# This is the 'estimate' in the beginning.
19# This determines the size of the peers we keep in memory
20# until we receive the first estimate from NSE.
21# Keep in mind, that (networksize)^(1/3) should be enough.
22# So, 50 is enough for a network of size 50^3 = 125000
23MINSIZE = 4
24
25DESIRED_PROBABILITY = 0.75
26
27DEFICIENCY_FACTOR = 0.4
28
29
30
31[testbed]
32HOSTNAME = localhost
33
34# MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 100
35
36#OVERLAY_TOPOLOGY = CLIQUE
37
38OVERLAY_TOPOLOGY = SCALE_FREE
39SCALE_FREE_TOPOLOGY_CAP = 100
40SCALE_FREE_TOPOLOGY_M = 2
41
42#OVERLAY_TOPOLOGY = RANDOM
43#OVERLAY_RANDOM_LINKS = 25
44
45#OVERLAY_TOPOLOGY = SMALL_WORLD
46#OVERLAY_RANDOM_LINKS = 25
47
48SETUP_TIMEOUT = 2 m
49
50[nse]
51WORKBITS = 0
52
53[nat]
54# Use addresses from the local network interfaces (including loopback, but also others)
55USE_LOCALADDR = YES
56ENABLE_UPNP = NO
57
58# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
59RETURN_LOCAL_ADDRESSES = YES
60
61[transport]
62PLUGINS = unix
63
64[ats]
65# Network specific inbound/outbound quotas
66UNSPECIFIED_QUOTA_IN = unlimited
67UNSPECIFIED_QUOTA_OUT = unlimited
68# LOOPBACK
69LOOPBACK_QUOTA_IN = unlimited
70LOOPBACK_QUOTA_OUT = unlimited
71# LAN
72LAN_QUOTA_IN = unlimited
73LAN_QUOTA_OUT = unlimited
74#WAN
75WAN_QUOTA_OUT = unlimited
76WAN_QUOTA_IN = unlimited
77# WLAN
78WLAN_QUOTA_IN = unlimited
79WLAN_QUOTA_OUT = unlimited
80# BLUETOOTH
81BLUETOOTH_QUOTA_IN = unlimited
82BLUETOOTH_QUOTA_OUT = unlimited
83
84[dht]
85DISABLE_TRY_CONNECT = YES
86
87[cadet]
88#OPTIONS=-l /tmp/rps_profiler_logs/cadet-[]-%Y-%m-%d.log
89#PREFIX = valgrind --log-file=/tmp/rps/valgrind_gnunet-service-cadet_%p
90
91#[arm]
92#GLOBAL_POSTFIX=-l /tmp/rps_profiler_logs/other-[]-%Y-%m-%d.log
93
94#[statistics]
95#IMMEDIATE_START = NO
96#START_ON_DEMAND = NO
97
98[peerinfo]
99NO_IO = YES
100
101[hostlist]
102IMMEDIATE_START = NO
103START_ON_DEMAND = NO
104
105[zonemaster]
106IMMEDIATE_START = NO
107START_ON_DEMAND = NO
108
109[namecache]
110IMMEDIATE_START = NO
111START_ON_DEMAND = NO
112
113[namestore]
114IMMEDIATE_START = NO
115START_ON_DEMAND = NO
116
117[topology]
118IMMEDIATE_START = NO
119START_ON_DEMAND = NO
120
121[vpn]
122IMMEDIATE_START = NO
123START_ON_DEMAND = NO
124
125[revocation]
126IMMEDIATE_START = NO
127START_ON_DEMAND = NO
128
129[gns]
130IMMEDIATE_START = NO
131START_ON_DEMAND = NO
132
133[fs]
134IMMEDIATE_START = NO
135START_ON_DEMAND = NO
diff --git a/src/contrib/service/rps/test_rps_api.c b/src/contrib/service/rps/test_rps_api.c
new file mode 100644
index 000000000..06dad2887
--- /dev/null
+++ b/src/contrib/service/rps/test_rps_api.c
@@ -0,0 +1,87 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/test_rps_api.c
22 * @brief testcase for rps_api.c
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_rps_service.h"
27
28
29static int ok = 1;
30
31
32static void
33run (void *cls,
34 char *const *args,
35 const char *cfgfile,
36 const struct GNUNET_CONFIGURATION_Handle *cfg)
37{
38 ok = 0;
39}
40
41
42static int
43check ()
44{
45 char *const argv[] = { "test-rps-api", NULL };
46 struct GNUNET_GETOPT_CommandLineOption options[] = {
47 GNUNET_GETOPT_OPTION_END
48 };
49 struct GNUNET_OS_Process *proc;
50 char *path = GNUNET_OS_get_libexec_binary_path ("gnunet-service-rps");
51
52 if (NULL == path)
53 {
54 fprintf (stderr, "Service executable not found `%s'\n",
55 "gnunet-service-rps");
56 return;
57 }
58
59 proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL,
60 NULL, NULL, path, "gnunet-service-rps", NULL);
61
62 GNUNET_free (path);
63 GNUNET_assert (NULL != proc);
64 GNUNET_PROGRAM_run (1, argv, "test-rps-api", "nohelp",
65 options, &run, &ok);
66 if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
67 {
68 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
69 ok = 1;
70 }
71 GNUNET_OS_process_wait (proc);
72 GNUNET_OS_process_destroy (proc);
73 return ok;
74}
75
76
77int
78main (int argc, char *argv[])
79{
80 GNUNET_log_setup ("test_statistics_api",
81 "WARNING",
82 NULL);
83 return check ();
84}
85
86
87/* end of test_rps_api.c */
diff --git a/src/contrib/service/rps/test_service_rps_custommap.c b/src/contrib/service/rps/test_service_rps_custommap.c
new file mode 100644
index 000000000..82d75b7e0
--- /dev/null
+++ b/src/contrib/service/rps/test_service_rps_custommap.c
@@ -0,0 +1,128 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/test_service_rps_custommap.c
22 * @brief testcase for gnunet-service-rps_peers.c
23 */
24#include "platform.h"
25#include <platform.h>
26#include "gnunet-service-rps_custommap.h"
27
28#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
29 if (NULL != c_m) CustomPeerMap_destroy (c_m); return 1; }
30#define CHECK(c) { if (! (c)) ABORT (); }
31
32
33static int
34check ()
35{
36 struct CustomPeerMap *c_m;
37 struct GNUNET_PeerIdentity k1;
38 struct GNUNET_PeerIdentity k2;
39 unsigned int j;
40
41 CHECK (NULL != (c_m = CustomPeerMap_create (4)));
42 memset (&k1, 0, sizeof(k1));
43 memset (&k2, 1, sizeof(k2));
44 CHECK (GNUNET_NO == CustomPeerMap_contains_peer (c_m, &k1));
45 CHECK (GNUNET_NO == CustomPeerMap_contains_peer (c_m, &k2));
46 CHECK (GNUNET_NO == CustomPeerMap_remove_peer (c_m, &k1));
47 CHECK (GNUNET_NO == CustomPeerMap_remove_peer (c_m, &k2));
48 CHECK (GNUNET_NO == CustomPeerMap_remove_peer_by_index (c_m, 0));
49 CHECK (GNUNET_NO == CustomPeerMap_remove_peer_by_index (c_m, 0));
50 CHECK (GNUNET_NO == CustomPeerMap_remove_peer_by_index (c_m, 1));
51 CHECK (GNUNET_NO == CustomPeerMap_remove_peer_by_index (c_m, 1));
52 CHECK (NULL == CustomPeerMap_get_peer_by_index (c_m, 0));
53 CHECK (NULL == CustomPeerMap_get_peer_by_index (c_m, 0));
54 CHECK (NULL == CustomPeerMap_get_peer_by_index (c_m, 1));
55 CHECK (NULL == CustomPeerMap_get_peer_by_index (c_m, 1));
56 CustomPeerMap_clear (c_m); /* See if assertions trigger */
57 CHECK (0 == CustomPeerMap_size (c_m));
58
59 CHECK (GNUNET_OK == CustomPeerMap_put (c_m, &k1));
60 CHECK (1 == CustomPeerMap_size (c_m));
61 CHECK (GNUNET_NO == CustomPeerMap_put (c_m, &k1));
62 CHECK (1 == CustomPeerMap_size (c_m));
63 CHECK (GNUNET_YES == CustomPeerMap_contains_peer (c_m, &k1));
64 CHECK (GNUNET_OK == CustomPeerMap_remove_peer (c_m, &k1));
65 CHECK (0 == CustomPeerMap_size (c_m));
66 CHECK (GNUNET_NO == CustomPeerMap_contains_peer (c_m, &k1));
67 CHECK (GNUNET_NO == CustomPeerMap_contains_peer (c_m, &k2));
68
69 CHECK (GNUNET_OK == CustomPeerMap_put (c_m, &k1));
70 CHECK (1 == CustomPeerMap_size (c_m));
71 for (j = 0; j < 16; j++)
72 {
73 CHECK (GNUNET_NO == CustomPeerMap_put (c_m, &k1));
74 }
75 CHECK (1 == CustomPeerMap_size (c_m));
76 CHECK (GNUNET_OK == CustomPeerMap_put (c_m, &k2));
77 CHECK (2 == CustomPeerMap_size (c_m));
78 for (j = 0; j < 16; j++)
79 {
80 CHECK (GNUNET_NO == CustomPeerMap_put (c_m, &k2));
81 }
82 CHECK (2 == CustomPeerMap_size (c_m));
83
84 /* iterate */
85 for (j = 0; j < CustomPeerMap_size (c_m); j++)
86 {
87 CHECK (NULL != CustomPeerMap_get_peer_by_index (c_m, j));
88 }
89 CHECK ((0 == memcmp (CustomPeerMap_get_peer_by_index (c_m, 0),
90 &k1, sizeof(k1))));
91 CHECK ((0 == memcmp (CustomPeerMap_get_peer_by_index (c_m, 1),
92 &k2, sizeof(k2))));
93 CHECK (GNUNET_OK == CustomPeerMap_remove_peer (c_m, &k1));
94 CHECK (1 == CustomPeerMap_size (c_m));
95 CHECK (GNUNET_NO == CustomPeerMap_contains_peer (c_m, &k1));
96 CHECK (GNUNET_YES == CustomPeerMap_contains_peer (c_m, &k2));
97 CHECK (NULL != CustomPeerMap_get_peer_by_index (c_m, 0));
98
99 CustomPeerMap_clear (c_m);
100 CHECK (0 == CustomPeerMap_size (c_m));
101
102 CHECK (GNUNET_OK == CustomPeerMap_put (c_m, &k1));
103 CHECK (1 == CustomPeerMap_size (c_m));
104 CHECK (GNUNET_OK == CustomPeerMap_put (c_m, &k2));
105 CHECK (2 == CustomPeerMap_size (c_m));
106 CustomPeerMap_clear (c_m);
107 CHECK (0 == CustomPeerMap_size (c_m));
108
109 CustomPeerMap_destroy (c_m);
110
111 return 0;
112}
113
114
115int
116main (int argc, char *argv[])
117{
118 (void) argc;
119 (void) argv;
120
121 GNUNET_log_setup ("test_service_rps_peers",
122 "WARNING",
123 NULL);
124 return check ();
125}
126
127
128/* end of test_service_rps_custommap.c */
diff --git a/src/contrib/service/rps/test_service_rps_sampler_elem.c b/src/contrib/service/rps/test_service_rps_sampler_elem.c
new file mode 100644
index 000000000..afa17611c
--- /dev/null
+++ b/src/contrib/service/rps/test_service_rps_sampler_elem.c
@@ -0,0 +1,214 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/test_service_rps_sampler_elem.c
22 * @brief testcase for gnunet-service-rps_sampler_elem.c
23 */
24#include "platform.h"
25#include <platform.h>
26#include "gnunet_util_lib.h"
27#include "gnunet-service-rps_sampler_elem.h"
28
29#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
30 return 1; }
31#define CHECK(c) { if (! (c)) ABORT (); }
32
33
34static int
35check ()
36{
37 struct GNUNET_PeerIdentity pid0;
38 struct GNUNET_PeerIdentity pid1;
39 struct RPS_SamplerElement *s_elem;
40 struct GNUNET_CRYPTO_AuthKey auth_key;
41 struct GNUNET_CRYPTO_AuthKey auth_key2;
42 struct GNUNET_HashCode hash_code;
43 struct GNUNET_HashCode hash_code2;
44
45 memset (&pid0, 1, sizeof(pid0));
46 memset (&pid1, 0, sizeof(pid1));
47
48 /* Check if creation and destruction of an
49 * (empty) sampler element works */
50 s_elem = RPS_sampler_elem_create ();
51 CHECK (NULL != s_elem);
52 CHECK (EMPTY == s_elem->is_empty);
53 CHECK (NULL != &s_elem->auth_key);
54 auth_key = s_elem->auth_key;
55 RPS_sampler_elem_destroy (s_elem);
56
57
58 /* Check creation of another sampler element
59 * yields another (random) key */
60 s_elem = RPS_sampler_elem_create ();
61 CHECK (NULL != s_elem);
62 CHECK (EMPTY == s_elem->is_empty);
63 CHECK (NULL != &s_elem->auth_key);
64 CHECK (auth_key.key != s_elem->auth_key.key);
65 CHECK (0 != memcmp (auth_key.key, s_elem->auth_key.key,
66 GNUNET_CRYPTO_HASH_LENGTH));
67 auth_key = s_elem->auth_key;
68
69 /* Check that reinitialisation
70 * yields another (random) key */
71 RPS_sampler_elem_reinit (s_elem);
72 CHECK (NULL != s_elem);
73 CHECK (EMPTY == s_elem->is_empty);
74 CHECK (NULL != &s_elem->auth_key);
75 CHECK (auth_key.key != s_elem->auth_key.key);
76 CHECK (0 != memcmp (auth_key.key, s_elem->auth_key.key,
77 GNUNET_CRYPTO_HASH_LENGTH));
78 RPS_sampler_elem_destroy (s_elem);
79
80
81 /* Check that input of single peer id
82 * sets valid values */
83 s_elem = RPS_sampler_elem_create ();
84 CHECK (EMPTY == s_elem->is_empty);
85 CHECK (NULL != &s_elem->auth_key);
86 CHECK (auth_key.key != s_elem->auth_key.key);
87 /* This fails only with minimal chance */
88 CHECK (0 != memcmp (auth_key.key, s_elem->auth_key.key,
89 GNUNET_CRYPTO_HASH_LENGTH));
90 auth_key = s_elem->auth_key;
91
92 /* Check also that the hash of the peer id changed
93 * Also fails with minimal probability */
94 hash_code = s_elem->peer_id_hash;
95 RPS_sampler_elem_next (s_elem, &pid0);
96 CHECK (0 == memcmp (&pid0,
97 &s_elem->peer_id,
98 sizeof(struct GNUNET_PeerIdentity)));
99 CHECK (0 != memcmp (&hash_code,
100 &s_elem->peer_id_hash,
101 sizeof(struct GNUNET_HashCode)));
102 hash_code = s_elem->peer_id_hash;
103
104 /* We can only check that the peer id is one of both inputs */
105 RPS_sampler_elem_next (s_elem, &pid1);
106 CHECK ((0 == memcmp (&pid0,
107 &s_elem->peer_id,
108 sizeof(struct GNUNET_PeerIdentity))) ||
109 (0 == memcmp (&pid1,
110 &s_elem->peer_id,
111 sizeof(struct GNUNET_PeerIdentity))));
112
113 /* Check that hash stayed the same when peer id did not change */
114 if (0 == memcmp (&pid0,
115 &s_elem->peer_id,
116 sizeof(struct GNUNET_PeerIdentity)))
117 {
118 CHECK (0 == memcmp (&hash_code,
119 &s_elem->peer_id_hash,
120 sizeof(struct GNUNET_HashCode)));
121 }
122 else /* Check that hash changed */
123 {
124 CHECK (0 != memcmp (&hash_code,
125 &s_elem->peer_id_hash,
126 sizeof(struct GNUNET_HashCode)));
127 }
128
129 /* Check multiple inputs of same id
130 * hash should not change anymore */
131 hash_code2 = s_elem->peer_id_hash;
132 RPS_sampler_elem_next (s_elem, &pid0);
133 CHECK (0 == memcmp (&hash_code2,
134 &s_elem->peer_id_hash,
135 sizeof(struct GNUNET_HashCode)));
136 RPS_sampler_elem_next (s_elem, &pid1);
137 CHECK (0 == memcmp (&hash_code2,
138 &s_elem->peer_id_hash,
139 sizeof(struct GNUNET_HashCode)));
140 RPS_sampler_elem_next (s_elem, &pid0);
141 CHECK (0 == memcmp (&hash_code2,
142 &s_elem->peer_id_hash,
143 sizeof(struct GNUNET_HashCode)));
144 RPS_sampler_elem_next (s_elem, &pid0);
145 CHECK (0 == memcmp (&hash_code2,
146 &s_elem->peer_id_hash,
147 sizeof(struct GNUNET_HashCode)));
148 RPS_sampler_elem_next (s_elem, &pid0);
149 CHECK (0 == memcmp (&hash_code2,
150 &s_elem->peer_id_hash,
151 sizeof(struct GNUNET_HashCode)));
152 RPS_sampler_elem_next (s_elem, &pid1);
153 CHECK (0 == memcmp (&hash_code2,
154 &s_elem->peer_id_hash,
155 sizeof(struct GNUNET_HashCode)));
156 RPS_sampler_elem_next (s_elem, &pid1);
157 CHECK (0 == memcmp (&hash_code2,
158 &s_elem->peer_id_hash,
159 sizeof(struct GNUNET_HashCode)));
160 RPS_sampler_elem_next (s_elem, &pid1);
161 CHECK (0 == memcmp (&hash_code2,
162 &s_elem->peer_id_hash,
163 sizeof(struct GNUNET_HashCode)));
164
165 /* Check whether pid stayed the same all the time */
166 if (0 == memcmp (&hash_code,
167 &hash_code2,
168 sizeof(struct GNUNET_HashCode)))
169 {
170 CHECK (0 == memcmp (&pid0,
171 &s_elem->peer_id,
172 sizeof(struct GNUNET_PeerIdentity)));
173 }
174 else
175 {
176 CHECK (0 == memcmp (&pid1,
177 &s_elem->peer_id,
178 sizeof(struct GNUNET_PeerIdentity)));
179 }
180 RPS_sampler_elem_destroy (s_elem);
181
182 /* Check _set() */
183 s_elem = RPS_sampler_elem_create ();
184 CHECK (NULL != s_elem);
185 CHECK (EMPTY == s_elem->is_empty);
186 CHECK (NULL != &s_elem->auth_key);
187 auth_key = s_elem->auth_key;
188 memset (&auth_key2, 0, sizeof(auth_key2));
189 RPS_sampler_elem_set (s_elem, auth_key2);
190 CHECK (0 == memcmp (auth_key2.key,
191 s_elem->auth_key.key,
192 GNUNET_CRYPTO_HASH_LENGTH));
193 RPS_sampler_elem_destroy (s_elem);
194
195
196 /* TODO: deterministic tests (use _set() to set auth_key) */
197 return 0;
198}
199
200
201int
202main (int argc, char *argv[])
203{
204 (void) argc;
205 (void) argv;
206
207 GNUNET_log_setup ("test_service_rps_peers",
208 "WARNING",
209 NULL);
210 return check ();
211}
212
213
214/* end of test_service_rps_peers.c */
diff --git a/src/contrib/service/rps/test_service_rps_view.c b/src/contrib/service/rps/test_service_rps_view.c
new file mode 100644
index 000000000..bb1413168
--- /dev/null
+++ b/src/contrib/service/rps/test_service_rps_view.c
@@ -0,0 +1,146 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C)
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file rps/test_service_rps_view.c
22 * @brief testcase for gnunet-service-rps_view.c
23 */
24#include "platform.h"
25#include <platform.h>
26#include "gnunet-service-rps_view.h"
27
28#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
29 View_destroy (view); return 1; }
30#define CHECK(c) { if (! (c)) ABORT (); }
31
32
33static int
34check ()
35{
36 struct View *view;
37 struct GNUNET_PeerIdentity k1;
38 struct GNUNET_PeerIdentity k2;
39 const struct GNUNET_PeerIdentity *array;
40 unsigned int j;
41
42 view = View_create (3);
43 memset (&k1, 0, sizeof(k1));
44 memset (&k2, 1, sizeof(k2));
45 CHECK (GNUNET_NO == View_contains_peer (view, &k1));
46 CHECK (GNUNET_NO == View_contains_peer (view, &k2));
47 CHECK (GNUNET_NO == View_remove_peer (view, &k1));
48 CHECK (GNUNET_NO == View_remove_peer (view, &k2));
49 CHECK (NULL == View_get_peer_by_index (view, 0));
50 CHECK (NULL == View_get_peer_by_index (view, 1));
51 View_clear (view); /* See if assertions trigger */
52 CHECK (0 == View_size (view));
53
54 CHECK (GNUNET_OK == View_put (view, &k1));
55 CHECK (1 == View_size (view));
56 CHECK (GNUNET_NO == View_put (view, &k1));
57 CHECK (1 == View_size (view));
58 CHECK (GNUNET_YES == View_contains_peer (view, &k1));
59 CHECK (GNUNET_OK == View_remove_peer (view, &k1));
60 CHECK (0 == View_size (view));
61 CHECK (GNUNET_NO == View_contains_peer (view, &k1));
62 CHECK (GNUNET_NO == View_contains_peer (view, &k2));
63
64 CHECK (GNUNET_OK == View_put (view, &k1));
65 CHECK (1 == View_size (view));
66 for (j = 0; j < 16; j++)
67 {
68 CHECK (GNUNET_NO == View_put (view, &k1));
69 }
70 CHECK (1 == View_size (view));
71 CHECK (GNUNET_OK == View_put (view, &k2));
72 CHECK (2 == View_size (view));
73 for (j = 0; j < 16; j++)
74 {
75 CHECK (GNUNET_NO == View_put (view, &k2));
76 }
77 CHECK (2 == View_size (view));
78
79 /* iterate */
80 for (j = 0; j < View_size (view); j++)
81 {
82 CHECK (NULL != View_get_peer_by_index (view, j));
83 }
84 CHECK ((0 == memcmp (View_get_peer_by_index (view, 0),
85 &k1, sizeof(k1))));
86 CHECK ((0 == memcmp (View_get_peer_by_index (view, 1),
87 &k2, sizeof(k2))));
88 CHECK (GNUNET_OK == View_remove_peer (view, &k1));
89 CHECK (1 == View_size (view));
90 CHECK (GNUNET_NO == View_contains_peer (view, &k1));
91 CHECK (GNUNET_YES == View_contains_peer (view, &k2));
92 CHECK (NULL != View_get_peer_by_index (view, 0));
93 CHECK (NULL == View_get_peer_by_index (view, 1));
94
95 View_clear (view);
96 CHECK (0 == View_size (view));
97
98 CHECK (GNUNET_OK == View_put (view, &k1));
99 CHECK (1 == View_size (view));
100 CHECK (GNUNET_YES == View_contains_peer (view, &k1));
101 CHECK (GNUNET_OK == View_put (view, &k2));
102 CHECK (2 == View_size (view));
103 CHECK (GNUNET_YES == View_contains_peer (view, &k2));
104 array = View_get_as_array (view);
105 CHECK (0 == memcmp (&array[0], &k1, sizeof(k1)));
106 CHECK (0 == memcmp (&array[1], &k2, sizeof(k2)));
107 View_clear (view);
108 CHECK (0 == View_size (view));
109
110 /*View_change_len () */
111 CHECK (GNUNET_OK == View_put (view, &k1));
112 CHECK (GNUNET_OK == View_put (view, &k2));
113 CHECK (2 == View_size (view));
114 View_change_len (view, 4);
115 CHECK (2 == View_size (view));
116 CHECK (GNUNET_YES == View_contains_peer (view, &k1));
117 CHECK (GNUNET_YES == View_contains_peer (view, &k2));
118 array = View_get_as_array (view);
119 CHECK (0 == memcmp (&array[0], &k1, sizeof(k1)));
120 CHECK (0 == memcmp (&array[1], &k2, sizeof(k2)));
121 View_change_len (view, 1);
122 CHECK (1 == View_size (view));
123 CHECK (GNUNET_YES == View_contains_peer (view, &k1));
124 CHECK (GNUNET_NO == View_contains_peer (view, &k2));
125 array = View_get_as_array (view);
126 CHECK (0 == memcmp (&array[0], &k1, sizeof(k1)));
127 View_clear (view);
128 CHECK (0 == View_size (view));
129
130 View_destroy (view);
131
132 return 0;
133}
134
135
136int
137main ()
138{
139 GNUNET_log_setup ("test_service_rps_peers",
140 "WARNING",
141 NULL);
142 return check ();
143}
144
145
146/* end of test_service_rps_view.c */
diff --git a/src/contrib/service/scalarproduct/.gitignore b/src/contrib/service/scalarproduct/.gitignore
new file mode 100644
index 000000000..19909a3f9
--- /dev/null
+++ b/src/contrib/service/scalarproduct/.gitignore
@@ -0,0 +1,6 @@
1gnunet-service-scalarproduct-ecc-bob
2gnunet-scalarproduct
3gnunet-service-scalarproduct-alice
4gnunet-service-scalarproduct-bob
5gnunet-service-scalarproduct-ecc-alice
6test_ecc_scalarproduct
diff --git a/src/contrib/service/scalarproduct/Makefile.am b/src/contrib/service/scalarproduct/Makefile.am
new file mode 100644
index 000000000..b12dbace5
--- /dev/null
+++ b/src/contrib/service/scalarproduct/Makefile.am
@@ -0,0 +1,117 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 scalarproduct.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = -fprofile-arcs -ftest-coverage
13endif
14
15bin_PROGRAMS = \
16 gnunet-scalarproduct
17
18libexec_PROGRAMS = \
19 gnunet-service-scalarproduct-alice \
20 gnunet-service-scalarproduct-bob \
21 gnunet-service-scalarproduct-ecc-alice \
22 gnunet-service-scalarproduct-ecc-bob
23
24lib_LTLIBRARIES = \
25 libgnunetscalarproduct.la
26
27gnunet_scalarproduct_SOURCES = \
28 gnunet-scalarproduct.c
29gnunet_scalarproduct_LDADD = \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \
31 libgnunetscalarproduct.la \
32 $(LIBGCRYPT_LIBS) \
33 -lgcrypt \
34 $(GN_LIBINTL)
35
36gnunet_service_scalarproduct_alice_SOURCES = \
37 gnunet-service-scalarproduct.h \
38 gnunet-service-scalarproduct_alice.c
39gnunet_service_scalarproduct_alice_LDADD = \
40 $(top_builddir)/src/lib/util/libgnunetutil.la \
41 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
42 $(top_builddir)/src/service/seti/libgnunetseti.la \
43 $(LIBGCRYPT_LIBS) \
44 -lgcrypt \
45 $(GN_LIBINTL)
46
47gnunet_service_scalarproduct_bob_SOURCES = \
48 gnunet-service-scalarproduct.h \
49 gnunet-service-scalarproduct_bob.c
50gnunet_service_scalarproduct_bob_LDADD = \
51 $(top_builddir)/src/lib/util/libgnunetutil.la \
52 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
53 $(top_builddir)/src/service/seti/libgnunetseti.la \
54 $(LIBGCRYPT_LIBS) \
55 -lgcrypt \
56 $(GN_LIBINTL)
57
58gnunet_service_scalarproduct_ecc_alice_SOURCES = \
59 gnunet-service-scalarproduct-ecc.h \
60 gnunet-service-scalarproduct-ecc_alice.c
61gnunet_service_scalarproduct_ecc_alice_LDADD = \
62 $(top_builddir)/src/lib/util/libgnunetutil.la \
63 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
64 $(top_builddir)/src/service/seti/libgnunetseti.la \
65 $(LIBGCRYPT_LIBS) \
66 -lsodium \
67 -lgcrypt \
68 $(GN_LIBINTL)
69
70gnunet_service_scalarproduct_ecc_bob_SOURCES = \
71 gnunet-service-scalarproduct-ecc.h \
72 gnunet-service-scalarproduct-ecc_bob.c
73gnunet_service_scalarproduct_ecc_bob_LDADD = \
74 $(top_builddir)/src/lib/util/libgnunetutil.la \
75 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
76 $(top_builddir)/src/service/seti/libgnunetseti.la \
77 $(LIBGCRYPT_LIBS) \
78 -lsodium \
79 -lgcrypt \
80 $(GN_LIBINTL)
81
82libgnunetscalarproduct_la_SOURCES = \
83 scalarproduct_api.c \
84 scalarproduct.h
85libgnunetscalarproduct_la_LIBADD = \
86 $(top_builddir)/src/lib/util/libgnunetutil.la \
87 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
88 $(LIBGCRYPT_LIBS) \
89 -lgcrypt \
90 $(LTLIBINTL)
91libgnunetscalarproduct_la_LDFLAGS = \
92 $(GN_LIB_LDFLAGS)
93
94EXTRA_DIST = \
95 test_scalarproduct.conf \
96 $(check_SCRIPTS)
97
98# Need deprecated and removed testbed profiler
99#check_SCRIPTS = \
100# test_scalarproduct.sh \
101# test_scalarproduct_negative.sh \
102# test_scalarproduct_negativezero.sh
103
104check_PROGRAMS = \
105 # test_ecc_scalarproduct
106
107if ENABLE_TEST_RUN
108 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
109 TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
110endif
111
112
113test_ecc_scalarproduct_SOURCES = \
114 test_ecc_scalarproduct.c
115test_ecc_scalarproduct_LDADD = \
116 $(top_builddir)/src/lib/util/libgnunetutil.la \
117 -lsodium
diff --git a/src/contrib/service/scalarproduct/gnunet-scalarproduct.c b/src/contrib/service/scalarproduct/gnunet-scalarproduct.c
new file mode 100644
index 000000000..5ebab5baf
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-scalarproduct.c
@@ -0,0 +1,401 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file scalarproduct/gnunet-scalarproduct.c
23 * @brief scalarproduct client
24 * @author Christian M. Fuchs
25 */
26#define GCRYPT_NO_DEPRECATED
27#include "platform.h"
28#include <gcrypt.h>
29#include <inttypes.h>
30
31#include "gnunet_util_lib.h"
32#include "gnunet_scalarproduct_service.h"
33#include "gnunet_protocols.h"
34#include "scalarproduct.h"
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "gnunet-scalarproduct", \
37 __VA_ARGS__)
38
39
40/**
41 * the session key identifying this computation
42 */
43static struct GNUNET_HashCode session_key;
44
45/**
46 * PeerID we want to compute a scalar product with
47 */
48static struct GNUNET_PeerIdentity peer_id;
49
50/**
51 * Option -p: destination peer identity for checking message-ids with
52 */
53static char *input_peer_id;
54
55/**
56 * Option -p: destination peer identity for checking message-ids with
57 */
58static char *input_session_key;
59
60/**
61 * Option -e: vector to calculate a scalarproduct with
62 */
63static char *input_elements;
64
65/**
66 * Global return value
67 */
68static int ret = -1;
69
70/**
71 * our Scalarproduct Computation handle
72 */
73static struct GNUNET_SCALARPRODUCT_ComputationHandle *computation;
74
75
76/**
77 * Callback called if we are initiating a new computation session
78 *
79 * @param cls unused
80 * @param status if our job was successfully processed
81 */
82static void
83responder_callback (void *cls,
84 enum GNUNET_SCALARPRODUCT_ResponseStatus status)
85{
86 switch (status)
87 {
88 case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
89 ret = 0;
90 LOG (GNUNET_ERROR_TYPE_INFO,
91 "Session %s concluded.\n",
92 GNUNET_h2s (&session_key));
93 break;
94
95 case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
96 LOG (GNUNET_ERROR_TYPE_ERROR,
97 "Session %s failed: invalid response\n",
98 GNUNET_h2s (&session_key));
99 break;
100
101 case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
102 LOG (GNUNET_ERROR_TYPE_ERROR,
103 "Session %s failed: service failure\n",
104 GNUNET_h2s (&session_key));
105 break;
106
107 case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
108 LOG (GNUNET_ERROR_TYPE_ERROR,
109 "Session %s failed: service disconnect!\n",
110 GNUNET_h2s (&session_key));
111 break;
112
113 default:
114 LOG (GNUNET_ERROR_TYPE_ERROR,
115 "Session %s failed: return code %d\n",
116 GNUNET_h2s (&session_key),
117 status);
118 }
119 computation = NULL;
120 GNUNET_SCHEDULER_shutdown ();
121}
122
123
124/**
125 * Callback called if we are initiating a new computation session
126 *
127 * @param cls unused
128 * @param status if our job was successfully processed
129 * @param result the result in gnu/gcry MPI format
130 */
131static void
132requester_callback (void *cls,
133 enum GNUNET_SCALARPRODUCT_ResponseStatus status,
134 gcry_mpi_t result)
135{
136 unsigned char *buf;
137 gcry_error_t rc;
138
139 switch (status)
140 {
141 case GNUNET_SCALARPRODUCT_STATUS_SUCCESS:
142 if (0 == (rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result)))
143 {
144 ret = 0;
145 fprintf (stdout,
146 "%s\n",
147 buf);
148 fflush (stdout);
149 }
150 else
151 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
152 "gcry_mpi_aprint",
153 rc);
154 break;
155
156 case GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE:
157 LOG (GNUNET_ERROR_TYPE_ERROR,
158 "Session %s with peer %s failed: invalid response received\n",
159 GNUNET_h2s (&session_key),
160 GNUNET_i2s (&peer_id));
161 break;
162
163 case GNUNET_SCALARPRODUCT_STATUS_FAILURE:
164 LOG (GNUNET_ERROR_TYPE_ERROR,
165 "Session %s with peer %s failed: API failure\n",
166 GNUNET_h2s (&session_key),
167 GNUNET_i2s (&peer_id));
168 break;
169
170 case GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED:
171 LOG (GNUNET_ERROR_TYPE_ERROR,
172 "Session %s with peer %s was disconnected from service.\n",
173 GNUNET_h2s (&session_key),
174 GNUNET_i2s (&peer_id));
175 break;
176
177 default:
178 LOG (GNUNET_ERROR_TYPE_ERROR,
179 "Session %s with peer %s failed: return code %d\n",
180 GNUNET_h2s (&session_key),
181 GNUNET_i2s (&peer_id),
182 status);
183 }
184 computation = NULL;
185 GNUNET_SCHEDULER_shutdown ();
186}
187
188
189/**
190 * Task run during shutdown.
191 *
192 * @param cls unused
193 */
194static void
195shutdown_task (void *cls)
196{
197 if (NULL != computation)
198 {
199 GNUNET_SCALARPRODUCT_cancel (computation);
200 ret = 1; /* aborted */
201 }
202}
203
204
205/**
206 * Main function that will be run by the scheduler.
207 *
208 * @param cls closure
209 * @param args remaining command-line arguments
210 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
211 * @param cfg configuration
212 */
213static void
214run (void *cls,
215 char *const *args,
216 const char *cfgfile,
217 const struct GNUNET_CONFIGURATION_Handle *cfg)
218{
219 char *begin = input_elements;
220 char *end;
221 unsigned int i;
222 struct GNUNET_SCALARPRODUCT_Element *elements;
223 uint32_t element_count = 0;
224
225 if (NULL == input_elements)
226 {
227 LOG (GNUNET_ERROR_TYPE_ERROR,
228 _ ("You must specify at least one message ID to check!\n"));
229 return;
230 }
231 if ((NULL == input_session_key) ||
232 (0 == strlen (input_session_key)))
233 {
234 LOG (GNUNET_ERROR_TYPE_ERROR,
235 _ (
236 "This program needs a session identifier for comparing vectors.\n"));
237 return;
238 }
239 GNUNET_CRYPTO_hash (input_session_key,
240 strlen (input_session_key),
241 &session_key);
242 if ((NULL != input_peer_id) &&
243 (GNUNET_OK !=
244 GNUNET_CRYPTO_eddsa_public_key_from_string (input_peer_id,
245 strlen (input_peer_id),
246 &peer_id.public_key)))
247 {
248 LOG (GNUNET_ERROR_TYPE_ERROR,
249 _ ("Tried to set initiator mode, as peer ID was given. "
250 "However, `%s' is not a valid peer identifier.\n"),
251 input_peer_id);
252 return;
253 }
254 if (('\'' == *begin) &&
255 ('\'' == begin[strlen (begin) - 1]))
256 {
257 begin[strlen (begin) - 1] = '\0';
258 if (strlen (begin) > 0)
259 begin++;
260 }
261 for (end = begin; 0 != *end; end++)
262 if (*end == ';')
263 element_count++;
264 if (0 == element_count)
265 {
266 LOG (GNUNET_ERROR_TYPE_ERROR,
267 _ ("Need elements to compute the scalarproduct, got none.\n"));
268 return;
269 }
270
271 elements = GNUNET_malloc (sizeof(struct GNUNET_SCALARPRODUCT_Element)
272 * element_count);
273
274 for (i = 0; i < element_count; i++)
275 {
276 struct GNUNET_SCALARPRODUCT_Element element;
277 char*separator = NULL;
278
279 /* get the length of the current key,value; tuple */
280 for (end = begin; *end != ';'; end++)
281 if (*end == ',')
282 separator = end;
283
284 /* final element */
285 if ((NULL == separator) ||
286 (begin == separator) ||
287 (separator == end - 1))
288 {
289 LOG (GNUNET_ERROR_TYPE_ERROR,
290 _ ("Malformed input, could not parse `%s'\n"),
291 begin);
292 GNUNET_free (elements);
293 return;
294 }
295 *separator = 0;
296 /* read the element's key */
297 GNUNET_CRYPTO_hash (begin,
298 strlen (begin),
299 &element.key);
300
301 /* read the element's value */
302 if (1 !=
303 sscanf (separator + 1,
304 "%" SCNd64 ";",
305 &element.value))
306 {
307 LOG (GNUNET_ERROR_TYPE_ERROR,
308 _ ("Could not convert `%s' to int64_t.\n"),
309 begin);
310 GNUNET_free (elements);
311 return;
312 }
313 element.value = GNUNET_htonll (element.value);
314 elements[i] = element;
315 begin = end + 1;
316 }
317
318 if (((NULL != input_peer_id) &&
319 (NULL == (computation
320 = GNUNET_SCALARPRODUCT_start_computation (cfg,
321 &session_key,
322 &peer_id,
323 elements,
324 element_count,
325 &requester_callback,
326 NULL)))) ||
327 ((NULL == input_peer_id) &&
328 (NULL == (computation
329 = GNUNET_SCALARPRODUCT_accept_computation (cfg,
330 &session_key,
331 elements,
332 element_count,
333 &
334 responder_callback,
335 NULL)))))
336 {
337 fprintf (stderr,
338 _ ("Failed to initiate computation, were all keys unique?\n"));
339 GNUNET_free (elements);
340 return;
341 }
342 GNUNET_free (elements);
343 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
344 NULL);
345 ret = 0;
346}
347
348
349/**
350 * The main function to the scalarproduct client.
351 *
352 * @param argc number of arguments from the command line
353 * @param argv command line arguments
354 * @return 0 ok, 1 on error
355 */
356int
357main (int argc, char *const *argv)
358{
359 struct GNUNET_GETOPT_CommandLineOption options[] = {
360 GNUNET_GETOPT_option_string ('e',
361 "elements",
362 "\"key1,val1;key2,val2;...,keyn,valn;\"",
363 gettext_noop (
364 "A comma separated list of elements to compare as vector with our remote peer."),
365 &input_elements),
366
367 GNUNET_GETOPT_option_string ('e',
368 "elements",
369 "\"key1,val1;key2,val2;...,keyn,valn;\"",
370 gettext_noop (
371 "A comma separated list of elements to compare as vector with our remote peer."),
372 &input_elements),
373
374 GNUNET_GETOPT_option_string ('p',
375 "peer",
376 "PEERID",
377 gettext_noop (
378 "[Optional] peer to calculate our scalarproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."),
379 &input_peer_id),
380
381 GNUNET_GETOPT_option_string ('k',
382 "key",
383 "TRANSACTION_ID",
384 gettext_noop (
385 "Transaction ID shared with peer."),
386 &input_session_key),
387
388 GNUNET_GETOPT_OPTION_END
389 };
390
391 return (GNUNET_OK ==
392 GNUNET_PROGRAM_run (argc,
393 argv,
394 "gnunet-scalarproduct",
395 gettext_noop (
396 "Calculate the Vectorproduct with a GNUnet peer."),
397 options, &run, NULL)) ? ret : 1;
398}
399
400
401/* end of gnunet-scalarproduct.c */
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h
new file mode 100644
index 000000000..5c7a8d8f1
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc.h
@@ -0,0 +1,118 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct-ecc.h
22 * @brief scalarproduct service P2P messages
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SCALARPRODUCT_ECC_H
27#define GNUNET_SERVICE_SCALARPRODUCT_ECC_H
28
29
30GNUNET_NETWORK_STRUCT_BEGIN
31
32/**
33 * Message type passed from requesting service Alice to responding
34 * service Bob to initiate a request and make Bob participate in our
35 * protocol. Afterwards, Bob is expected to perform the set
36 * intersection with Alice. Once that has succeeded, Alice will
37 * send a `struct AliceCryptodataMessage *`. Bob is not expected
38 * to respond via CADET in the meantime.
39 */
40struct EccServiceRequestMessage
41{
42 /**
43 * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION
44 */
45 struct GNUNET_MessageHeader header;
46
47 /**
48 * For alignment. Always zero.
49 */
50 uint32_t reserved;
51
52 /**
53 * The transaction/session key used to identify a session
54 */
55 struct GNUNET_HashCode session_id;
56};
57
58
59/**
60 * Vector of ECC-encrypted values sent by Alice to Bob
61 * (after set intersection). Alice may send messages of this
62 * type repeatedly to transmit all values.
63 */
64struct EccAliceCryptodataMessage
65{
66 /**
67 * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA
68 */
69 struct GNUNET_MessageHeader header;
70
71 /**
72 * How many elements we appended to this message? In NBO.
73 */
74 uint32_t contained_element_count GNUNET_PACKED;
75
76 /**
77 * struct GNUNET_CRYPTO_EccPoint[contained_element_count]
78 */
79};
80
81
82/**
83 * Message type passed from responding service Bob to responding
84 * service Alice to complete a request and allow Alice to compute the
85 * result. If Bob's reply does not fit into this one message, the
86 * conversation may be continued with `struct BobCryptodataMultipartMessage`
87 * messages afterwards.
88 */
89struct EccBobCryptodataMessage
90{
91 /**
92 * GNUNET message header with type
93 * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA.
94 */
95 struct GNUNET_MessageHeader header;
96
97 /**
98 * How many elements this individual message delivers (in NBO),
99 * always TWO.
100 */
101 uint32_t contained_element_count GNUNET_PACKED;
102
103 /**
104 * The product of the g_i^{b_i} values.
105 */
106 struct GNUNET_CRYPTO_EccPoint prod_g_i_b_i;
107
108 /**
109 * The product of the h_i^{b_i} values.
110 */
111 struct GNUNET_CRYPTO_EccPoint prod_h_i_b_i;
112};
113
114
115GNUNET_NETWORK_STRUCT_END
116
117
118#endif
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c
new file mode 100644
index 000000000..b8bac0803
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c
@@ -0,0 +1,1150 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013-2017, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct-ecc_alice.c
22 * @brief scalarproduct service implementation
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <limits.h>
28#include <gcrypt.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_core_service.h"
31#include "gnunet_cadet_service.h"
32#include "gnunet_applications.h"
33#include "gnunet_protocols.h"
34#include "gnunet_scalarproduct_service.h"
35#include "gnunet_seti_service.h"
36#include "scalarproduct.h"
37#include "gnunet-service-scalarproduct-ecc.h"
38#include "gnunet_constants.h"
39
40#define LOG(kind, ...) \
41 GNUNET_log_from (kind, "scalarproduct-alice", __VA_ARGS__)
42
43/**
44 * Maximum allowed result value for the scalarproduct computation.
45 * DLOG will fail if the result is bigger. At 1 million, the
46 * precomputation takes about 2s on a fast machine.
47 */
48#define MAX_RESULT (1024 * 1024)
49
50/**
51 * How many values should DLOG store in memory (determines baseline
52 * RAM consumption, roughly 100 bytes times the value given here).
53 * Should be about SQRT (MAX_RESULT), larger values will make the
54 * online computation faster.
55 */
56#define MAX_RAM (1024)
57
58/**
59 * An encrypted element key-value pair.
60 */
61struct MpiElement
62{
63 /**
64 * Key used to identify matching pairs of values to multiply.
65 * Points into an existing data structure, to avoid copying
66 * and doubling memory use.
67 */
68 const struct GNUNET_HashCode *key;
69
70 /**
71 * a_i value, not disclosed to Bob.
72 */
73 int64_t value;
74};
75
76
77/**
78 * A scalarproduct session which tracks
79 * a request form the client to our final response.
80 */
81struct AliceServiceSession
82{
83 /**
84 * (hopefully) unique transaction ID
85 */
86 struct GNUNET_HashCode session_id;
87
88 /**
89 * Alice or Bob's peerID
90 */
91 struct GNUNET_PeerIdentity peer;
92
93 /**
94 * The client this request is related to.
95 */
96 struct GNUNET_SERVICE_Client *client;
97
98 /**
99 * The message queue for the client.
100 */
101 struct GNUNET_MQ_Handle *client_mq;
102
103 /**
104 * The message queue for CADET.
105 */
106 struct GNUNET_MQ_Handle *cadet_mq;
107
108 /**
109 * all non-0-value'd elements transmitted to us.
110 * Values are of type `struct GNUNET_SCALARPRODUCT_Element *`
111 */
112 struct GNUNET_CONTAINER_MultiHashMap *intersected_elements;
113
114 /**
115 * Set of elements for which will conduction an intersection.
116 * the resulting elements are then used for computing the scalar product.
117 */
118 struct GNUNET_SETI_Handle *intersection_set;
119
120 /**
121 * Set of elements for which will conduction an intersection.
122 * the resulting elements are then used for computing the scalar product.
123 */
124 struct GNUNET_SETI_OperationHandle *intersection_op;
125
126 /**
127 * Handle to Alice's Intersection operation listening for Bob
128 */
129 struct GNUNET_SETI_ListenHandle *intersection_listen;
130
131 /**
132 * channel-handle associated with our cadet handle
133 */
134 struct GNUNET_CADET_Channel *channel;
135
136 /**
137 * a(Alice), sorted array by key of length @e used_element_count.
138 */
139 struct MpiElement *sorted_elements;
140
141 /**
142 * The computed scalar product. INT_MAX if the computation failed.
143 */
144 int product;
145
146 /**
147 * How many elements we were supplied with from the client (total
148 * count before intersection).
149 */
150 uint32_t total;
151
152 /**
153 * How many elements actually are used for the scalar product.
154 * Size of the arrays in @e r and @e r_prime. Sometimes also
155 * reset to 0 and used as a counter!
156 */
157 uint32_t used_element_count;
158
159 /**
160 * Already transferred elements from client to us.
161 * Less or equal than @e total.
162 */
163 uint32_t client_received_element_count;
164
165 /**
166 * State of this session. In
167 * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is
168 * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or
169 * #GNUNET_SCALARPRODUCT_STATUS_FAILURE.
170 */
171 enum GNUNET_SCALARPRODUCT_ResponseStatus status;
172
173 /**
174 * Flag to prevent recursive calls to #destroy_service_session() from
175 * doing harm.
176 */
177 int in_destroy;
178};
179
180
181/**
182 * GNUnet configuration handle
183 */
184static const struct GNUNET_CONFIGURATION_Handle *cfg;
185
186/**
187 * Context for DLOG operations on a curve.
188 */
189static struct GNUNET_CRYPTO_EccDlogContext *edc;
190
191/**
192 * Alice's private key ('a').
193 */
194static struct GNUNET_CRYPTO_EccScalar my_privkey;
195
196/**
197 * Inverse of Alice's private key ('a_inv').
198 */
199static struct GNUNET_CRYPTO_EccScalar my_privkey_inv;
200
201/**
202 * Handle to the CADET service.
203 */
204static struct GNUNET_CADET_Handle *my_cadet;
205
206
207/**
208 * Iterator called to free elements.
209 *
210 * @param cls the `struct AliceServiceSession *` (unused)
211 * @param key the key (unused)
212 * @param value value to free
213 * @return #GNUNET_OK (continue to iterate)
214 */
215static int
216free_element_cb (void *cls,
217 const struct GNUNET_HashCode *key,
218 void *value)
219{
220 struct GNUNET_SCALARPRODUCT_Element *e = value;
221
222 GNUNET_free (e);
223 return GNUNET_OK;
224}
225
226
227/**
228 * Destroy session state, we are done with it.
229 *
230 * @param s the session to free elements from
231 */
232static void
233destroy_service_session (struct AliceServiceSession *s)
234{
235 if (GNUNET_YES == s->in_destroy)
236 return;
237 s->in_destroy = GNUNET_YES;
238 if (NULL != s->client)
239 {
240 struct GNUNET_SERVICE_Client *c = s->client;
241
242 s->client = NULL;
243 GNUNET_SERVICE_client_drop (c);
244 }
245 if (NULL != s->channel)
246 {
247 GNUNET_CADET_channel_destroy (s->channel);
248 s->channel = NULL;
249 }
250 if (NULL != s->intersected_elements)
251 {
252 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
253 &free_element_cb,
254 s);
255 GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements);
256 s->intersected_elements = NULL;
257 }
258 if (NULL != s->intersection_listen)
259 {
260 GNUNET_SETI_listen_cancel (s->intersection_listen);
261 s->intersection_listen = NULL;
262 }
263 if (NULL != s->intersection_op)
264 {
265 LOG (GNUNET_ERROR_TYPE_DEBUG,
266 "Set intersection, op still ongoing!\n");
267 GNUNET_SETI_operation_cancel (s->intersection_op);
268 s->intersection_op = NULL;
269 }
270 if (NULL != s->intersection_set)
271 {
272 GNUNET_SETI_destroy (s->intersection_set);
273 s->intersection_set = NULL;
274 }
275 if (NULL != s->sorted_elements)
276 {
277 GNUNET_free (s->sorted_elements);
278 s->sorted_elements = NULL;
279 }
280 GNUNET_free (s);
281}
282
283
284/**
285 * Notify the client that the session has failed. A message gets sent
286 * to Alice's client if we encountered any error.
287 *
288 * @param session the associated client session to fail or succeed
289 */
290static void
291prepare_client_end_notification (struct AliceServiceSession *session)
292{
293 struct ClientResponseMessage *msg;
294 struct GNUNET_MQ_Envelope *e;
295
296 if (NULL == session->client_mq)
297 return; /* no client left to be notified */
298 GNUNET_log (
299 GNUNET_ERROR_TYPE_DEBUG,
300 "Sending session-end notification with status %d to client for session %s\n",
301 session->status,
302 GNUNET_h2s (&session->session_id));
303 e = GNUNET_MQ_msg (msg,
304 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
305 msg->product_length = htonl (0);
306 msg->status = htonl (session->status);
307 GNUNET_MQ_send (session->client_mq,
308 e);
309}
310
311
312/**
313 * Prepare the final (positive) response we will send to Alice's
314 * client.
315 *
316 * @param s the session associated with our client.
317 */
318static void
319transmit_client_response (struct AliceServiceSession *s)
320{
321 struct ClientResponseMessage *msg;
322 struct GNUNET_MQ_Envelope *e;
323 unsigned char *product_exported = NULL;
324 size_t product_length = 0;
325 int32_t range;
326 gcry_error_t rc;
327 gcry_mpi_t value;
328
329 if (INT_MAX == s->product)
330 {
331 GNUNET_break (0);
332 prepare_client_end_notification (s);
333 return;
334 }
335 value = gcry_mpi_new (32);
336 if (0 > s->product)
337 {
338 range = -1;
339 gcry_mpi_set_ui (value,
340 -s->product);
341 }
342 else if (0 < s->product)
343 {
344 range = 1;
345 gcry_mpi_set_ui (value,
346 s->product);
347 }
348 else
349 {
350 /* result is exactly zero */
351 range = 0;
352 }
353 if ( (0 != range) &&
354 (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD,
355 &product_exported,
356 &product_length,
357 value))))
358 {
359 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
360 "gcry_mpi_scan",
361 rc);
362 prepare_client_end_notification (s);
363 return;
364 }
365 gcry_mpi_release (value);
366 e = GNUNET_MQ_msg_extra (msg,
367 product_length,
368 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
369 msg->status = htonl (GNUNET_SCALARPRODUCT_STATUS_SUCCESS);
370 msg->range = htonl (range);
371 msg->product_length = htonl (product_length);
372 if (NULL != product_exported)
373 {
374 GNUNET_memcpy (&msg[1],
375 product_exported,
376 product_length);
377 GNUNET_free (product_exported);
378 }
379 GNUNET_MQ_send (s->client_mq,
380 e);
381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382 "Sent result to client, session %s has ended!\n",
383 GNUNET_h2s (&s->session_id));
384}
385
386
387/**
388 * Function called whenever a channel is destroyed. Should clean up
389 * any associated state.
390 *
391 * It must NOT call #GNUNET_CADET_channel_destroy() on the channel.
392 *
393 * @param cls the `struct AliceServiceSession`
394 * @param channel connection to the other end (henceforth invalid)
395 */
396static void
397cb_channel_destruction (void *cls,
398 const struct GNUNET_CADET_Channel *channel)
399{
400 struct AliceServiceSession *s = cls;
401
402 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403 "Peer disconnected, terminating session %s with peer %s\n",
404 GNUNET_h2s (&s->session_id),
405 GNUNET_i2s (&s->peer));
406 s->channel = NULL;
407 if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status)
408 {
409 /* We didn't get an answer yet, fail with error */
410 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
411 prepare_client_end_notification (s);
412 }
413}
414
415
416/**
417 * Handle a response we got from another service we wanted to
418 * calculate a scalarproduct with.
419 *
420 * @param cls the `struct AliceServiceSession *`
421 * @param msg the actual message
422 */
423static void
424handle_bobs_cryptodata_message (void *cls,
425 const struct EccBobCryptodataMessage *msg)
426{
427 struct AliceServiceSession *s = cls;
428 uint32_t contained;
429
430 contained = ntohl (msg->contained_element_count);
431 if (2 != contained)
432 {
433 GNUNET_break_op (0);
434 destroy_service_session (s);
435 return;
436 }
437 if (NULL == s->sorted_elements)
438 {
439 /* we're not ready yet, how can Bob be? */
440 GNUNET_break_op (0);
441 destroy_service_session (s);
442 return;
443 }
444 if (s->total != s->client_received_element_count)
445 {
446 /* we're not ready yet, how can Bob be? */
447 GNUNET_break_op (0);
448 destroy_service_session (s);
449 return;
450 }
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452 "Received %u crypto values from Bob\n",
453 (unsigned int) contained);
454 GNUNET_CADET_receive_done (s->channel);
455 {
456 struct GNUNET_CRYPTO_EccPoint g_i_b_i_a_inv;
457 struct GNUNET_CRYPTO_EccPoint g_ai_bi;
458
459 GNUNET_assert (
460 GNUNET_OK ==
461 GNUNET_CRYPTO_ecc_pmul_mpi (&msg->prod_g_i_b_i,
462 &my_privkey_inv,
463 &g_i_b_i_a_inv));
464 GNUNET_assert (
465 GNUNET_OK ==
466 GNUNET_CRYPTO_ecc_add (&g_i_b_i_a_inv,
467 &msg->prod_h_i_b_i,
468 &g_ai_bi));
469 s->product = GNUNET_CRYPTO_ecc_dlog (edc,
470 &g_ai_bi);
471 if (INT_MAX == s->product)
472 {
473 /* result too big */
474 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
475 "Scalar product result out of range\n");
476 }
477 }
478 transmit_client_response (s);
479}
480
481
482/**
483 * Iterator to copy over messages from the hash map
484 * into an array for sorting.
485 *
486 * @param cls the `struct AliceServiceSession *`
487 * @param key the key (unused)
488 * @param value the `struct GNUNET_SCALARPRODUCT_Element *`
489 */
490static int
491copy_element_cb (void *cls,
492 const struct GNUNET_HashCode *key,
493 void *value)
494{
495 struct AliceServiceSession *s = cls;
496 struct GNUNET_SCALARPRODUCT_Element *e = value;
497
498 s->sorted_elements[s->used_element_count].value = (int64_t) GNUNET_ntohll (
499 e->value);
500 s->sorted_elements[s->used_element_count].key = &e->key;
501 s->used_element_count++;
502 return GNUNET_OK;
503}
504
505
506/**
507 * Compare two `struct MpiValue`s by key for sorting.
508 *
509 * @param a pointer to first `struct MpiValue *`
510 * @param b pointer to first `struct MpiValue *`
511 * @return -1 for a < b, 0 for a=b, 1 for a > b.
512 */
513static int
514element_cmp (const void *a,
515 const void *b)
516{
517 const struct MpiElement *ma = a;
518 const struct MpiElement *mb = b;
519
520 return GNUNET_CRYPTO_hash_cmp (ma->key,
521 mb->key);
522}
523
524
525/**
526 * Maximum number of elements we can put into a single cryptodata
527 * message
528 */
529#define ELEMENT_CAPACITY \
530 ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \
531 - sizeof(struct EccAliceCryptodataMessage)) \
532 / sizeof(struct GNUNET_CRYPTO_EccPoint))
533
534
535/**
536 * Send the cryptographic data from Alice to Bob.
537 * Does nothing if we already transferred all elements.
538 *
539 * @param s the associated service session
540 */
541static void
542send_alices_cryptodata_message (struct AliceServiceSession *s)
543{
544 struct EccAliceCryptodataMessage *msg;
545 struct GNUNET_MQ_Envelope *e;
546 struct GNUNET_CRYPTO_EccPoint *payload;
547 struct GNUNET_CRYPTO_EccScalar r_ia;
548 struct GNUNET_CRYPTO_EccScalar r_ia_ai;
549 unsigned int off;
550 unsigned int todo_count;
551
552 s->sorted_elements = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size (
553 s->intersected_elements),
554 struct MpiElement);
555 s->used_element_count = 0;
556 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
557 &copy_element_cb,
558 s);
559 LOG (GNUNET_ERROR_TYPE_DEBUG,
560 "Finished intersection, %d items remain\n",
561 s->used_element_count);
562 qsort (s->sorted_elements,
563 s->used_element_count,
564 sizeof(struct MpiElement),
565 &element_cmp);
566 off = 0;
567 while (off < s->used_element_count)
568 {
569 todo_count = s->used_element_count - off;
570 if (todo_count > ELEMENT_CAPACITY)
571 todo_count = ELEMENT_CAPACITY;
572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573 "Sending %u/%u crypto values to Bob\n",
574 (unsigned int) todo_count,
575 (unsigned int) s->used_element_count);
576
577 e =
578 GNUNET_MQ_msg_extra (msg,
579 todo_count * 2
580 * sizeof(struct GNUNET_CRYPTO_EccPoint),
581 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA);
582 msg->contained_element_count = htonl (todo_count);
583 payload = (struct GNUNET_CRYPTO_EccPoint *) &msg[1];
584 for (unsigned int i = off; i < off + todo_count; i++)
585 {
586 struct GNUNET_CRYPTO_EccScalar r_i;
587 struct GNUNET_CRYPTO_EccPoint g_i;
588 struct GNUNET_CRYPTO_EccPoint h_i;
589
590 /* r_i = random() mod n */
591 GNUNET_CRYPTO_ecc_random_mod_n (&r_i);
592 /* g_i = g^{r_i} */
593 GNUNET_assert (GNUNET_OK ==
594 GNUNET_CRYPTO_ecc_dexp_mpi (&r_i,
595 &g_i));
596 /* r_ia = r_i * a */
597 crypto_core_ed25519_scalar_mul (r_ia.v,
598 r_i.v,
599 my_privkey.v);
600 /* r_ia_ai = r_ia + a_i */
601 {
602 int64_t val = s->sorted_elements[i].value;
603 struct GNUNET_CRYPTO_EccScalar vali;
604
605 GNUNET_assert (INT64_MIN != val);
606 GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val,
607 &vali);
608 if (val > 0)
609 crypto_core_ed25519_scalar_add (r_ia_ai.v,
610 r_ia.v,
611 vali.v);
612 else
613 crypto_core_ed25519_scalar_sub (r_ia_ai.v,
614 r_ia.v,
615 vali.v);
616 }
617 /* h_i = g^{r_ia_ai} */
618 GNUNET_assert (GNUNET_OK ==
619 GNUNET_CRYPTO_ecc_dexp_mpi (&r_ia_ai,
620 &h_i));
621 memcpy (&payload[(i - off) * 2],
622 &g_i,
623 sizeof (g_i));
624 memcpy (&payload[(i - off) * 2 + 1],
625 &h_i,
626 sizeof (h_i));
627 }
628 off += todo_count;
629 GNUNET_MQ_send (s->cadet_mq,
630 e);
631 }
632}
633
634
635/**
636 * Callback for set operation results. Called for each element
637 * that should be removed from the result set, and then once
638 * to indicate that the set intersection operation is done.
639 *
640 * @param cls closure with the `struct AliceServiceSession`
641 * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK
642 * @param current_size current set size
643 * @param status what has happened with the set intersection?
644 */
645static void
646cb_intersection_element_removed (void *cls,
647 const struct GNUNET_SETI_Element *element,
648 uint64_t current_size,
649 enum GNUNET_SETI_Status status)
650{
651 struct AliceServiceSession *s = cls;
652 struct GNUNET_SCALARPRODUCT_Element *se;
653
654 switch (status)
655 {
656 case GNUNET_SETI_STATUS_DEL_LOCAL:
657 /* this element has been removed from the set */
658 se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements,
659 element->data);
660 GNUNET_assert (NULL != se);
661 LOG (GNUNET_ERROR_TYPE_DEBUG,
662 "Intersection removed element with key %s and value %lld\n",
663 GNUNET_h2s (&se->key),
664 (long long) GNUNET_ntohll (se->value));
665 GNUNET_assert (
666 GNUNET_YES ==
667 GNUNET_CONTAINER_multihashmap_remove (s->intersected_elements,
668 element->data,
669 se));
670 GNUNET_free (se);
671 return;
672 case GNUNET_SETI_STATUS_DONE:
673 s->intersection_op = NULL;
674 if (NULL != s->intersection_set)
675 {
676 GNUNET_SETI_destroy (s->intersection_set);
677 s->intersection_set = NULL;
678 }
679 send_alices_cryptodata_message (s);
680 return;
681 case GNUNET_SETI_STATUS_FAILURE:
682 /* unhandled status code */
683 LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n");
684 if (NULL != s->intersection_listen)
685 {
686 GNUNET_SETI_listen_cancel (s->intersection_listen);
687 s->intersection_listen = NULL;
688 }
689 s->intersection_op = NULL;
690 if (NULL != s->intersection_set)
691 {
692 GNUNET_SETI_destroy (s->intersection_set);
693 s->intersection_set = NULL;
694 }
695 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
696 prepare_client_end_notification (s);
697 return;
698
699 default:
700 GNUNET_break (0);
701 return;
702 }
703}
704
705
706/**
707 * Called when another peer wants to do a set operation with the
708 * local peer. If a listen error occurs, the @a request is NULL.
709 *
710 * @param cls closure with the `struct AliceServiceSession *`
711 * @param other_peer the other peer
712 * @param context_msg message with application specific information from
713 * the other peer
714 * @param request request from the other peer (never NULL), use GNUNET_SETI_accept()
715 * to accept it, otherwise the request will be refused
716 * Note that we can't just return value from the listen callback,
717 * as it is also necessary to specify the set we want to do the
718 * operation with, which sometimes can be derived from the context
719 * message. It's necessary to specify the timeout.
720 */
721static void
722cb_intersection_request_alice (void *cls,
723 const struct GNUNET_PeerIdentity *other_peer,
724 const struct GNUNET_MessageHeader *context_msg,
725 struct GNUNET_SETI_Request *request)
726{
727 struct AliceServiceSession *s = cls;
728
729 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730 "Received intersection request from %s!\n",
731 GNUNET_i2s (other_peer));
732 if (0 != GNUNET_memcmp (other_peer,
733 &s->peer))
734 {
735 GNUNET_break_op (0);
736 return;
737 }
738 s->intersection_op
739 = GNUNET_SETI_accept (request,
740 (struct GNUNET_SETI_Option[]){ { 0 } },
741 &cb_intersection_element_removed,
742 s);
743 if (NULL == s->intersection_op)
744 {
745 GNUNET_break (0);
746 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
747 prepare_client_end_notification (s);
748 return;
749 }
750 if (GNUNET_OK !=
751 GNUNET_SETI_commit (s->intersection_op,
752 s->intersection_set))
753 {
754 GNUNET_break (0);
755 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
756 prepare_client_end_notification (s);
757 return;
758 }
759}
760
761
762/**
763 * Our client has finished sending us its multipart message.
764 *
765 * @param s the service session context
766 */
767static void
768client_request_complete_alice (struct AliceServiceSession *s)
769{
770 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
771 GNUNET_MQ_hd_fixed_size (bobs_cryptodata_message,
772 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA,
773 struct EccBobCryptodataMessage,
774 s),
775 GNUNET_MQ_handler_end ()
776 };
777 struct EccServiceRequestMessage *msg;
778 struct GNUNET_MQ_Envelope *e;
779 struct GNUNET_HashCode set_sid;
780
781 GNUNET_CRYPTO_hash (&s->session_id,
782 sizeof(struct GNUNET_HashCode),
783 &set_sid);
784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785 "Creating new channel for session with key %s.\n",
786 GNUNET_h2s (&s->session_id));
787 s->channel = GNUNET_CADET_channel_create (my_cadet,
788 s,
789 &s->peer,
790 &s->session_id,
791 NULL,
792 &cb_channel_destruction,
793 cadet_handlers);
794 if (NULL == s->channel)
795 {
796 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
797 prepare_client_end_notification (s);
798 return;
799 }
800 s->cadet_mq = GNUNET_CADET_get_mq (s->channel);
801 s->intersection_listen = GNUNET_SETI_listen (cfg,
802 &set_sid,
803 &cb_intersection_request_alice,
804 s);
805 if (NULL == s->intersection_listen)
806 {
807 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
808 GNUNET_CADET_channel_destroy (s->channel);
809 s->channel = NULL;
810 prepare_client_end_notification (s);
811 return;
812 }
813
814 e =
815 GNUNET_MQ_msg (msg,
816 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION);
817 GNUNET_MQ_env_set_options (e, GNUNET_MQ_PRIO_CRITICAL_CONTROL);
818 msg->session_id = s->session_id;
819 GNUNET_MQ_send (s->cadet_mq, e);
820}
821
822
823/**
824 * We're receiving additional set data. Check if
825 * @a msg is well-formed.
826 *
827 * @param cls client identification of the client
828 * @param msg the actual message
829 * @return #GNUNET_OK if @a msg is well-formed
830 */
831static int
832check_alice_client_message_multipart (
833 void *cls,
834 const struct ComputationBobCryptodataMultipartMessage *msg)
835{
836 struct AliceServiceSession *s = cls;
837 uint32_t contained_count;
838 uint16_t msize;
839
840 msize = ntohs (msg->header.size);
841 contained_count = ntohl (msg->element_count_contained);
842 if ((msize !=
843 (sizeof(struct ComputationBobCryptodataMultipartMessage)
844 + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))) ||
845 (0 == contained_count) ||
846 (s->total == s->client_received_element_count) ||
847 (s->total < s->client_received_element_count + contained_count))
848 {
849 GNUNET_break_op (0);
850 return GNUNET_SYSERR;
851 }
852 return GNUNET_OK;
853}
854
855
856/**
857 * We're receiving additional set data. Add it to our
858 * set and if we are done, initiate the transaction.
859 *
860 * @param cls client identification of the client
861 * @param msg the actual message
862 */
863static void
864handle_alice_client_message_multipart (
865 void *cls,
866 const struct ComputationBobCryptodataMultipartMessage *msg)
867{
868 struct AliceServiceSession *s = cls;
869 uint32_t contained_count;
870 const struct GNUNET_SCALARPRODUCT_Element *elements;
871 struct GNUNET_SETI_Element set_elem;
872 struct GNUNET_SCALARPRODUCT_Element *elem;
873
874 contained_count = ntohl (msg->element_count_contained);
875 s->client_received_element_count += contained_count;
876 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
877 for (uint32_t i = 0; i < contained_count; i++)
878 {
879 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
880 GNUNET_memcpy (elem,
881 &elements[i],
882 sizeof(struct GNUNET_SCALARPRODUCT_Element));
883 if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
884 s->intersected_elements,
885 &elem->key,
886 elem,
887 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
888 {
889 GNUNET_break (0);
890 GNUNET_free (elem);
891 continue;
892 }
893 set_elem.data = &elem->key;
894 set_elem.size = sizeof(elem->key);
895 set_elem.element_type = 0;
896 GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL);
897 s->used_element_count++;
898 }
899 GNUNET_SERVICE_client_continue (s->client);
900 if (s->total != s->client_received_element_count)
901 {
902 /* more to come */
903 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
904 "Received client multipart data, waiting for more!\n");
905 return;
906 }
907 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching computation\n");
908 client_request_complete_alice (s);
909}
910
911
912/**
913 * Handler for Alice's client request message.
914 * Check that @a msg is well-formed.
915 *
916 * @param cls identification of the client
917 * @param msg the actual message
918 * @return #GNUNET_OK if @a msg is well-formed
919 */
920static int
921check_alice_client_message (void *cls,
922 const struct AliceComputationMessage *msg)
923{
924 struct AliceServiceSession *s = cls;
925 uint16_t msize;
926 uint32_t total_count;
927 uint32_t contained_count;
928
929 if (NULL != s->intersected_elements)
930 {
931 /* only one concurrent session per client connection allowed,
932 simplifies logic a lot... */
933 GNUNET_break (0);
934 return GNUNET_SYSERR;
935 }
936 msize = ntohs (msg->header.size);
937 total_count = ntohl (msg->element_count_total);
938 contained_count = ntohl (msg->element_count_contained);
939 if ((0 == total_count) || (0 == contained_count) ||
940 (msize !=
941 (sizeof(struct AliceComputationMessage)
942 + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))))
943 {
944 GNUNET_break_op (0);
945 return GNUNET_SYSERR;
946 }
947 return GNUNET_OK;
948}
949
950
951/**
952 * Handler for Alice's client request message.
953 * We are doing request-initiation to compute a scalar product with a peer.
954 *
955 * @param cls identification of the client
956 * @param msg the actual message
957 */
958static void
959handle_alice_client_message (void *cls,
960 const struct AliceComputationMessage *msg)
961{
962 struct AliceServiceSession *s = cls;
963 uint32_t contained_count;
964 uint32_t total_count;
965 const struct GNUNET_SCALARPRODUCT_Element *elements;
966 struct GNUNET_SETI_Element set_elem;
967 struct GNUNET_SCALARPRODUCT_Element *elem;
968
969 total_count = ntohl (msg->element_count_total);
970 contained_count = ntohl (msg->element_count_contained);
971 s->peer = msg->peer;
972 s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE;
973 s->total = total_count;
974 s->client_received_element_count = contained_count;
975 s->session_id = msg->session_key;
976 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
977 s->intersected_elements =
978 GNUNET_CONTAINER_multihashmap_create (s->total,
979 GNUNET_YES);
980 s->intersection_set = GNUNET_SETI_create (cfg);
981 for (uint32_t i = 0; i < contained_count; i++)
982 {
983 if (0 == GNUNET_ntohll (elements[i].value))
984 continue;
985 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
986 *elem = elements[i];
987 if (GNUNET_SYSERR ==
988 GNUNET_CONTAINER_multihashmap_put (
989 s->intersected_elements,
990 &elem->key,
991 elem,
992 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
993 {
994 /* element with same key encountered twice! */
995 GNUNET_break (0);
996 GNUNET_free (elem);
997 continue;
998 }
999 set_elem.data = &elem->key;
1000 set_elem.size = sizeof(elem->key);
1001 set_elem.element_type = 0;
1002 GNUNET_SETI_add_element (s->intersection_set,
1003 &set_elem,
1004 NULL,
1005 NULL);
1006 s->used_element_count++;
1007 }
1008 GNUNET_SERVICE_client_continue (s->client);
1009 if (s->total != s->client_received_element_count)
1010 {
1011 /* wait for multipart msg */
1012 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1013 "Received partial client request, waiting for more!\n");
1014 return;
1015 }
1016 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1017 "Launching computation\n");
1018 client_request_complete_alice (s);
1019}
1020
1021
1022/**
1023 * Task run during shutdown.
1024 *
1025 * @param cls unused
1026 * @param tc unused
1027 */
1028static void
1029shutdown_task (void *cls)
1030{
1031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1032 "Shutting down, initiating cleanup.\n");
1033 // FIXME: we have to cut our connections to CADET first!
1034 if (NULL != my_cadet)
1035 {
1036 GNUNET_CADET_disconnect (my_cadet);
1037 my_cadet = NULL;
1038 }
1039 if (NULL != edc)
1040 {
1041 GNUNET_CRYPTO_ecc_dlog_release (edc);
1042 edc = NULL;
1043 }
1044}
1045
1046
1047/**
1048 * A client connected.
1049 *
1050 * Setup the associated data structure.
1051 *
1052 * @param cls closure, NULL
1053 * @param client identification of the client
1054 * @param mq message queue to communicate with @a client
1055 * @return our `struct AliceServiceSession`
1056 */
1057static void *
1058client_connect_cb (void *cls,
1059 struct GNUNET_SERVICE_Client *client,
1060 struct GNUNET_MQ_Handle *mq)
1061{
1062 struct AliceServiceSession *s;
1063
1064 s = GNUNET_new (struct AliceServiceSession);
1065 s->client = client;
1066 s->client_mq = mq;
1067 return s;
1068}
1069
1070
1071/**
1072 * A client disconnected.
1073 *
1074 * Remove the associated session(s), release data structures
1075 * and cancel pending outgoing transmissions to the client.
1076 *
1077 * @param cls closure, NULL
1078 * @param client identification of the client
1079 * @param app_cls our `struct AliceServiceSession`
1080 */
1081static void
1082client_disconnect_cb (void *cls,
1083 struct GNUNET_SERVICE_Client *client,
1084 void *app_cls)
1085{
1086 struct AliceServiceSession *s = app_cls;
1087
1088 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1089 "Client %p disconnected from us.\n",
1090 client);
1091 s->client = NULL;
1092 s->client_mq = NULL;
1093 destroy_service_session (s);
1094}
1095
1096
1097/**
1098 * Initialization of the program and message handlers
1099 *
1100 * @param cls closure
1101 * @param c configuration to use
1102 * @param service the initialized service
1103 */
1104static void
1105run (void *cls,
1106 const struct GNUNET_CONFIGURATION_Handle *c,
1107 struct GNUNET_SERVICE_Handle *service)
1108{
1109 cfg = c;
1110 edc = GNUNET_CRYPTO_ecc_dlog_prepare (MAX_RESULT,
1111 MAX_RAM);
1112 /* Select a random 'a' value for Alice */
1113 GNUNET_CRYPTO_ecc_rnd_mpi (&my_privkey,
1114 &my_privkey_inv);
1115 my_cadet = GNUNET_CADET_connect (cfg);
1116 if (NULL == my_cadet)
1117 {
1118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1119 _ ("Connect to CADET failed\n"));
1120 GNUNET_SCHEDULER_shutdown ();
1121 return;
1122 }
1123 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1124 NULL);
1125}
1126
1127
1128/**
1129 * Define "main" method using service macro.
1130 */
1131GNUNET_SERVICE_MAIN (
1132 "scalarproduct-alice",
1133 GNUNET_SERVICE_OPTION_NONE,
1134 &run,
1135 &client_connect_cb,
1136 &client_disconnect_cb,
1137 NULL,
1138 GNUNET_MQ_hd_var_size (alice_client_message,
1139 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE,
1140 struct AliceComputationMessage,
1141 NULL),
1142 GNUNET_MQ_hd_var_size (
1143 alice_client_message_multipart,
1144 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE,
1145 struct ComputationBobCryptodataMultipartMessage,
1146 NULL),
1147 GNUNET_MQ_handler_end ());
1148
1149
1150/* end of gnunet-service-scalarproduct-ecc_alice.c */
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c
new file mode 100644
index 000000000..1945f1937
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c
@@ -0,0 +1,1060 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013-2017, 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct-ecc_bob.c
22 * @brief scalarproduct service implementation
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <limits.h>
28#include <gcrypt.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_core_service.h"
31#include "gnunet_cadet_service.h"
32#include "gnunet_applications.h"
33#include "gnunet_protocols.h"
34#include "gnunet_scalarproduct_service.h"
35#include "gnunet_seti_service.h"
36#include "scalarproduct.h"
37#include "gnunet-service-scalarproduct-ecc.h"
38
39#define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-bob", __VA_ARGS__)
40
41
42/**
43 * An encrypted element key-value pair.
44 */
45struct MpiElement
46{
47 /**
48 * Key used to identify matching pairs of values to multiply.
49 * Points into an existing data structure, to avoid copying
50 * and doubling memory use.
51 */
52 const struct GNUNET_HashCode *key;
53
54 /**
55 * Value represented (a).
56 */
57 int64_t value;
58};
59
60
61/**
62 * A scalarproduct session which tracks an offer for a
63 * multiplication service by a local client.
64 */
65struct BobServiceSession
66{
67 /**
68 * The client this request is related to.
69 */
70 struct GNUNET_SERVICE_Client *client;
71
72 /**
73 * Client message queue.
74 */
75 struct GNUNET_MQ_Handle *client_mq;
76
77 /**
78 * All non-0-value'd elements transmitted to us.
79 */
80 struct GNUNET_CONTAINER_MultiHashMap *intersected_elements;
81
82 /**
83 * Set of elements for which we will be conducting an intersection.
84 * The resulting elements are then used for computing the scalar product.
85 */
86 struct GNUNET_SETI_Handle *intersection_set;
87
88 /**
89 * Set of elements for which will conduction an intersection.
90 * the resulting elements are then used for computing the scalar product.
91 */
92 struct GNUNET_SETI_OperationHandle *intersection_op;
93
94 /**
95 * Our open port.
96 */
97 struct GNUNET_CADET_Port *port;
98
99 /**
100 * b(Bob)
101 */
102 struct MpiElement *sorted_elements;
103
104 /**
105 * Product of the g_i^{b_i}
106 */
107 struct GNUNET_CRYPTO_EccPoint prod_g_i_b_i;
108
109 /**
110 * Product of the h_i^{b_i}
111 */
112 struct GNUNET_CRYPTO_EccPoint prod_h_i_b_i;
113
114 /**
115 * How many elements will be supplied in total from the client.
116 */
117 uint32_t total;
118
119 /**
120 * Already transferred elements (received) for multipart
121 * messages from client. Always less than @e total.
122 */
123 uint32_t client_received_element_count;
124
125 /**
126 * How many elements actually are used for the scalar product.
127 * Size of the arrays in @e r and @e r_prime. Also sometimes
128 * used as an index into the arrays during construction.
129 */
130 uint32_t used_element_count;
131
132 /**
133 * Counts the number of values received from Alice by us.
134 * Always less than @e used_element_count.
135 */
136 uint32_t cadet_received_element_count;
137
138 /**
139 * State of this session. In
140 * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is
141 * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or
142 * #GNUNET_SCALARPRODUCT_STATUS_FAILURE.
143 */
144 enum GNUNET_SCALARPRODUCT_ResponseStatus status;
145
146 /**
147 * Are we already in #destroy_service_session()?
148 */
149 int in_destroy;
150
151 /**
152 * The CADET channel.
153 */
154 struct GNUNET_CADET_Channel *channel;
155
156 /**
157 * Originator's peer identity. (Only for diagnostics.)
158 */
159 struct GNUNET_PeerIdentity peer;
160
161 /**
162 * (hopefully) unique transaction ID
163 */
164 struct GNUNET_HashCode session_id;
165
166 /**
167 * The message queue for this channel.
168 */
169 struct GNUNET_MQ_Handle *cadet_mq;
170};
171
172
173/**
174 * GNUnet configuration handle
175 */
176static const struct GNUNET_CONFIGURATION_Handle *cfg;
177
178/**
179 * Handle to the CADET service.
180 */
181static struct GNUNET_CADET_Handle *my_cadet;
182
183/**
184 * Context for DLOG operations on a curve.
185 */
186static struct GNUNET_CRYPTO_EccDlogContext *edc;
187
188
189/**
190 * Callback used to free the elements in the map.
191 *
192 * @param cls NULL
193 * @param key key of the element
194 * @param value the value to free
195 */
196static int
197free_element_cb (void *cls,
198 const struct GNUNET_HashCode *key,
199 void *value)
200{
201 struct GNUNET_SCALARPRODUCT_Element *element = value;
202
203 GNUNET_free (element);
204 return GNUNET_OK;
205}
206
207
208/**
209 * Destroy session state, we are done with it.
210 *
211 * @param s the session to free elements from
212 */
213static void
214destroy_service_session (struct BobServiceSession *s)
215{
216 if (GNUNET_YES == s->in_destroy)
217 return;
218 s->in_destroy = GNUNET_YES;
219 if (NULL != s->client)
220 {
221 struct GNUNET_SERVICE_Client *c = s->client;
222
223 s->client = NULL;
224 GNUNET_SERVICE_client_drop (c);
225 }
226 if (NULL != s->intersected_elements)
227 {
228 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
229 &free_element_cb,
230 NULL);
231 GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements);
232 s->intersected_elements = NULL;
233 }
234 if (NULL != s->intersection_op)
235 {
236 GNUNET_SETI_operation_cancel (s->intersection_op);
237 s->intersection_op = NULL;
238 }
239 if (NULL != s->intersection_set)
240 {
241 GNUNET_SETI_destroy (s->intersection_set);
242 s->intersection_set = NULL;
243 }
244 if (NULL != s->sorted_elements)
245 {
246 GNUNET_free (s->sorted_elements);
247 s->sorted_elements = NULL;
248 }
249 if (NULL != s->port)
250 {
251 GNUNET_CADET_close_port (s->port);
252 s->port = NULL;
253 }
254 if (NULL != s->channel)
255 {
256 GNUNET_CADET_channel_destroy (s->channel);
257 s->channel = NULL;
258 }
259 GNUNET_free (s);
260}
261
262
263/**
264 * Notify the client that the session has succeeded or failed. This
265 * message gets sent to Bob's client if the operation completed or
266 * Alice disconnected.
267 *
268 * @param session the associated client session to fail or succeed
269 */
270static void
271prepare_client_end_notification (struct BobServiceSession *session)
272{
273 struct ClientResponseMessage *msg;
274 struct GNUNET_MQ_Envelope *e;
275
276 if (NULL == session->client_mq)
277 return; /* no client left to be notified */
278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279 "Sending session-end notification with status %d to client for session %s\n",
280 session->status,
281 GNUNET_h2s (&session->session_id));
282 e = GNUNET_MQ_msg (msg,
283 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
284 msg->range = 0;
285 msg->product_length = htonl (0);
286 msg->status = htonl (session->status);
287 GNUNET_MQ_send (session->client_mq,
288 e);
289}
290
291
292/**
293 * Function called whenever a channel is destroyed. Should clean up
294 * any associated state.
295 *
296 * It must NOT call #GNUNET_CADET_channel_destroy() on the channel.
297 *
298 * @param cls the `struct BobServiceSession`
299 * @param channel connection to the other end (henceforth invalid)
300 */
301static void
302cb_channel_destruction (void *cls,
303 const struct GNUNET_CADET_Channel *channel)
304{
305 struct BobServiceSession *s = cls;
306
307 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308 "Peer disconnected, terminating session %s with peer %s\n",
309 GNUNET_h2s (&s->session_id),
310 GNUNET_i2s (&s->peer));
311 s->channel = NULL;
312 if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status)
313 {
314 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
315 prepare_client_end_notification (s);
316 }
317 destroy_service_session (s);
318}
319
320
321/**
322 * MQ finished giving our last message to CADET, now notify
323 * the client that we are finished.
324 */
325static void
326bob_cadet_done_cb (void *cls)
327{
328 struct BobServiceSession *session = cls;
329
330 session->status = GNUNET_SCALARPRODUCT_STATUS_SUCCESS;
331 prepare_client_end_notification (session);
332}
333
334
335/**
336 * Bob generates the response message to be sent to Alice.
337 *
338 * @param s the associated requesting session with Alice
339 */
340static void
341transmit_bobs_cryptodata_message (struct BobServiceSession *s)
342{
343 struct EccBobCryptodataMessage *msg;
344 struct GNUNET_MQ_Envelope *e;
345
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347 "Sending response to Alice\n");
348 e = GNUNET_MQ_msg (msg,
349 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_BOB_CRYPTODATA);
350 msg->contained_element_count = htonl (2);
351 msg->prod_g_i_b_i = s->prod_g_i_b_i;
352 msg->prod_h_i_b_i = s->prod_h_i_b_i;
353 GNUNET_MQ_notify_sent (e,
354 &bob_cadet_done_cb,
355 s);
356 GNUNET_MQ_send (s->cadet_mq,
357 e);
358}
359
360
361/**
362 * Iterator to copy over messages from the hash map
363 * into an array for sorting.
364 *
365 * @param cls the `struct AliceServiceSession *`
366 * @param key the key (unused)
367 * @param value the `struct GNUNET_SCALARPRODUCT_Element *`
368 */
369static int
370copy_element_cb (void *cls,
371 const struct GNUNET_HashCode *key,
372 void *value)
373{
374 struct BobServiceSession *s = cls;
375 struct GNUNET_SCALARPRODUCT_Element *e = value;
376
377 s->sorted_elements[s->used_element_count].value = (int64_t) GNUNET_ntohll (
378 e->value);
379 s->sorted_elements[s->used_element_count].key = &e->key;
380 s->used_element_count++;
381 return GNUNET_OK;
382}
383
384
385/**
386 * Compare two `struct MpiValue`s by key for sorting.
387 *
388 * @param a pointer to first `struct MpiValue *`
389 * @param b pointer to first `struct MpiValue *`
390 * @return -1 for a < b, 0 for a=b, 1 for a > b.
391 * TODO: code duplication with Alice!
392 */
393static int
394element_cmp (const void *a,
395 const void *b)
396{
397 const struct MpiElement *ma = a;
398 const struct MpiElement *mb = b;
399
400 return GNUNET_CRYPTO_hash_cmp (ma->key,
401 mb->key);
402}
403
404
405/**
406 * Check a multipart-chunk of a request from another service to
407 * calculate a scalarproduct with us.
408 *
409 * @param cls closure (set from #GNUNET_CADET_connect)
410 * @param msg the actual message
411 * @return #GNUNET_OK to keep the connection open,
412 * #GNUNET_SYSERR to close it (signal serious error)
413 */
414static int
415check_alices_cryptodata_message (void *cls,
416 const struct EccAliceCryptodataMessage *msg)
417{
418 struct BobServiceSession *s = cls;
419 uint32_t contained_elements;
420 size_t msg_length;
421 uint16_t msize;
422 unsigned int max;
423
424 msize = ntohs (msg->header.size);
425 if (msize <= sizeof(struct EccAliceCryptodataMessage))
426 {
427 GNUNET_break_op (0);
428 return GNUNET_SYSERR;
429 }
430 contained_elements = ntohl (msg->contained_element_count);
431 /* Our intersection may still be ongoing, but this is nevertheless
432 an upper bound on the required array size */
433 max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements);
434 msg_length = sizeof(struct EccAliceCryptodataMessage)
435 + contained_elements * sizeof(struct GNUNET_CRYPTO_EccPoint) * 2;
436 if ((msize != msg_length) ||
437 (0 == contained_elements) ||
438 (contained_elements > UINT16_MAX) ||
439 (max < contained_elements + s->cadet_received_element_count))
440 {
441 GNUNET_break_op (0);
442 return GNUNET_SYSERR;
443 }
444 return GNUNET_OK;
445}
446
447
448/**
449 * Handle a multipart-chunk of a request from another service to
450 * calculate a scalarproduct with us.
451 *
452 * @param cls closure (set from #GNUNET_CADET_connect)
453 * @param msg the actual message
454 */
455static void
456handle_alices_cryptodata_message (void *cls,
457 const struct EccAliceCryptodataMessage *msg)
458{
459 struct BobServiceSession *s = cls;
460 const struct GNUNET_CRYPTO_EccPoint *payload;
461 uint32_t contained_elements;
462 unsigned int max;
463 const struct GNUNET_CRYPTO_EccPoint *g_i;
464 const struct GNUNET_CRYPTO_EccPoint *h_i;
465 struct GNUNET_CRYPTO_EccPoint g_i_b_i;
466 struct GNUNET_CRYPTO_EccPoint h_i_b_i;
467
468 contained_elements = ntohl (msg->contained_element_count);
469 max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements);
470 /* sort our vector for the computation */
471 if (NULL == s->sorted_elements)
472 {
473 s->sorted_elements
474 = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size (
475 s->intersected_elements),
476 struct MpiElement);
477 s->used_element_count = 0;
478 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
479 &copy_element_cb,
480 s);
481 qsort (s->sorted_elements,
482 s->used_element_count,
483 sizeof(struct MpiElement),
484 &element_cmp);
485 }
486
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488 "Received %u crypto values from Alice\n",
489 (unsigned int) contained_elements);
490 payload = (const struct GNUNET_CRYPTO_EccPoint *) &msg[1];
491
492 for (unsigned int i = 0; i < contained_elements; i++)
493 {
494 int64_t val = s->sorted_elements[i + s->cadet_received_element_count].value;
495 struct GNUNET_CRYPTO_EccScalar vali;
496
497 GNUNET_assert (INT64_MIN != val);
498 GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val,
499 &vali);
500 if (val < 0)
501 crypto_core_ed25519_scalar_negate (vali.v,
502 vali.v);
503 g_i = &payload[i * 2];
504 /* g_i_b_i = g_i^vali */
505 GNUNET_assert (GNUNET_OK ==
506 GNUNET_CRYPTO_ecc_pmul_mpi (g_i,
507 &vali,
508 &g_i_b_i));
509 h_i = &payload[i * 2 + 1];
510 /* h_i_b_i = h_i^vali */
511 GNUNET_assert (GNUNET_OK ==
512 GNUNET_CRYPTO_ecc_pmul_mpi (h_i,
513 &vali,
514 &h_i_b_i));
515 if (0 == i + s->cadet_received_element_count)
516 {
517 /* first iteration, nothing to add */
518 s->prod_g_i_b_i = g_i_b_i;
519 s->prod_h_i_b_i = h_i_b_i;
520 }
521 else
522 {
523 /* further iterations, cummulate resulting value */
524 GNUNET_assert (GNUNET_OK ==
525 GNUNET_CRYPTO_ecc_add (&s->prod_g_i_b_i,
526 &g_i_b_i,
527 &s->prod_g_i_b_i));
528 GNUNET_assert (GNUNET_OK ==
529 GNUNET_CRYPTO_ecc_add (&s->prod_h_i_b_i,
530 &h_i_b_i,
531 &s->prod_h_i_b_i));
532 }
533 }
534 s->cadet_received_element_count += contained_elements;
535 if ((s->cadet_received_element_count == max) &&
536 (NULL == s->intersection_op))
537 {
538 /* intersection has finished also on our side, and
539 we got the full set, so we can proceed with the
540 CADET response(s) */
541 transmit_bobs_cryptodata_message (s);
542 }
543 GNUNET_CADET_receive_done (s->channel);
544}
545
546
547/**
548 * Callback for set operation results. Called for each element
549 * that needs to be removed from the result set.
550 *
551 * @param cls closure with the `struct BobServiceSession`
552 * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK
553 * @param current_size current set size
554 * @param status what has happened with the set intersection?
555 */
556static void
557cb_intersection_element_removed (void *cls,
558 const struct GNUNET_SETI_Element *element,
559 uint64_t current_size,
560 enum GNUNET_SETI_Status status)
561{
562 struct BobServiceSession *s = cls;
563 struct GNUNET_SCALARPRODUCT_Element *se;
564
565 switch (status)
566 {
567 case GNUNET_SETI_STATUS_DEL_LOCAL:
568 /* this element has been removed from the set */
569 se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements,
570 element->data);
571 GNUNET_assert (NULL != se);
572 LOG (GNUNET_ERROR_TYPE_DEBUG,
573 "Removed element with key %s and value %lld\n",
574 GNUNET_h2s (&se->key),
575 (long long) GNUNET_ntohll (se->value));
576 GNUNET_assert (GNUNET_YES ==
577 GNUNET_CONTAINER_multihashmap_remove (
578 s->intersected_elements,
579 element->data,
580 se));
581 GNUNET_free (se);
582 return;
583 case GNUNET_SETI_STATUS_DONE:
584 s->intersection_op = NULL;
585 GNUNET_break (NULL == s->intersection_set);
586 GNUNET_CADET_receive_done (s->channel);
587 LOG (GNUNET_ERROR_TYPE_DEBUG,
588 "Finished intersection, %d items remain\n",
589 GNUNET_CONTAINER_multihashmap_size (s->intersected_elements));
590 if (s->client_received_element_count ==
591 GNUNET_CONTAINER_multihashmap_size (s->intersected_elements))
592 {
593 /* CADET transmission from Alice is also already done,
594 start with our own reply */
595 transmit_bobs_cryptodata_message (s);
596 }
597 return;
598 case GNUNET_SETI_STATUS_FAILURE:
599 /* unhandled status code */
600 LOG (GNUNET_ERROR_TYPE_DEBUG,
601 "Set intersection failed!\n");
602 s->intersection_op = NULL;
603 if (NULL != s->intersection_set)
604 {
605 GNUNET_SETI_destroy (s->intersection_set);
606 s->intersection_set = NULL;
607 }
608 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
609 prepare_client_end_notification (s);
610 return;
611
612 default:
613 GNUNET_break (0);
614 return;
615 }
616}
617
618
619/**
620 * We've paired up a client session with an incoming CADET request.
621 * Initiate set intersection work.
622 *
623 * @param s client session to start intersection for
624 */
625static void
626start_intersection (struct BobServiceSession *s)
627{
628 struct GNUNET_HashCode set_sid;
629
630 GNUNET_CRYPTO_hash (&s->session_id,
631 sizeof(struct GNUNET_HashCode),
632 &set_sid);
633 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634 "Got session with key %s and %u elements, starting intersection.\n",
635 GNUNET_h2s (&s->session_id),
636 (unsigned int) s->total);
637
638 s->intersection_op
639 = GNUNET_SETI_prepare (&s->peer,
640 &set_sid,
641 NULL,
642 (struct GNUNET_SETI_Option[]) { { 0 } },
643 &cb_intersection_element_removed,
644 s);
645 if (GNUNET_OK !=
646 GNUNET_SETI_commit (s->intersection_op,
647 s->intersection_set))
648 {
649 GNUNET_break (0);
650 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
651 prepare_client_end_notification (s);
652 return;
653 }
654 GNUNET_SETI_destroy (s->intersection_set);
655 s->intersection_set = NULL;
656}
657
658
659/**
660 * Handle a request from Alice to calculate a scalarproduct with us (Bob).
661 *
662 * @param cls closure (set from #GNUNET_CADET_connect)
663 * @param msg the actual message
664 */
665static void
666handle_alices_computation_request (void *cls,
667 const struct EccServiceRequestMessage *msg)
668{
669 struct BobServiceSession *s = cls;
670
671 s->session_id = msg->session_id; // ??
672 if (s->client_received_element_count < s->total)
673 {
674 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
675 "Alice ready, still waiting for Bob client data!\n");
676 return;
677 }
678 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
679 "Both ready, launching intersection!\n");
680 start_intersection (s);
681}
682
683
684/**
685 * Function called for inbound channels on Bob's end. Does some
686 * preliminary initialization, more happens after we get Alice's first
687 * message.
688 *
689 * @param cls our `struct BobServiceSession`
690 * @param channel new handle to the channel
691 * @param initiator peer that started the channel
692 * @return session associated with the channel
693 */
694static void *
695cb_channel_incoming (void *cls,
696 struct GNUNET_CADET_Channel *channel,
697 const struct GNUNET_PeerIdentity *initiator)
698{
699 struct BobServiceSession *s = cls;
700
701 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702 "New incoming channel from peer %s.\n",
703 GNUNET_i2s (initiator));
704 GNUNET_CADET_close_port (s->port);
705 s->port = NULL;
706 s->peer = *initiator;
707 s->channel = channel;
708 s->cadet_mq = GNUNET_CADET_get_mq (s->channel);
709 return s;
710}
711
712
713/**
714 * We're receiving additional set data. Check it is well-formed.
715 *
716 * @param cls identification of the client
717 * @param msg the actual message
718 * @return #GNUNET_OK if @a msg is well-formed
719 */
720static int
721check_bob_client_message_multipart (
722 void *cls,
723 const struct ComputationBobCryptodataMultipartMessage *msg)
724{
725 struct BobServiceSession *s = cls;
726 uint32_t contained_count;
727 uint16_t msize;
728
729 msize = ntohs (msg->header.size);
730 contained_count = ntohl (msg->element_count_contained);
731 if ((msize != (sizeof(struct ComputationBobCryptodataMultipartMessage)
732 + contained_count * sizeof(struct
733 GNUNET_SCALARPRODUCT_Element))) ||
734 (0 == contained_count) ||
735 (UINT16_MAX < contained_count) ||
736 (s->total == s->client_received_element_count) ||
737 (s->total < s->client_received_element_count + contained_count))
738 {
739 GNUNET_break (0);
740 return GNUNET_SYSERR;
741 }
742 return GNUNET_OK;
743}
744
745
746/**
747 * We're receiving additional set data. Add it to our
748 * set and if we are done, initiate the transaction.
749 *
750 * @param cls identification of the client
751 * @param msg the actual message
752 */
753static void
754handle_bob_client_message_multipart (
755 void *cls,
756 const struct ComputationBobCryptodataMultipartMessage *msg)
757{
758 struct BobServiceSession *s = cls;
759 uint32_t contained_count;
760 const struct GNUNET_SCALARPRODUCT_Element *elements;
761 struct GNUNET_SETI_Element set_elem;
762 struct GNUNET_SCALARPRODUCT_Element *elem;
763
764 contained_count = ntohl (msg->element_count_contained);
765 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
766 for (uint32_t i = 0; i < contained_count; i++)
767 {
768 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
769 GNUNET_memcpy (elem,
770 &elements[i],
771 sizeof(struct GNUNET_SCALARPRODUCT_Element));
772 if (GNUNET_SYSERR ==
773 GNUNET_CONTAINER_multihashmap_put (s->intersected_elements,
774 &elem->key,
775 elem,
776 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
777 {
778 GNUNET_break (0);
779 GNUNET_free (elem);
780 continue;
781 }
782 set_elem.data = &elem->key;
783 set_elem.size = sizeof(elem->key);
784 set_elem.element_type = 0;
785 GNUNET_SETI_add_element (s->intersection_set,
786 &set_elem,
787 NULL, NULL);
788 }
789 s->client_received_element_count += contained_count;
790 GNUNET_SERVICE_client_continue (s->client);
791 if (s->total != s->client_received_element_count)
792 {
793 /* more to come */
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "Request still partial, waiting for more client data!\n");
796 return;
797 }
798 if (NULL == s->channel)
799 {
800 /* no Alice waiting for this request, wait for Alice */
801 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802 "Client ready, still waiting for Alice!\n");
803 return;
804 }
805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
806 "Both ready, launching intersection!\n");
807 start_intersection (s);
808}
809
810
811/**
812 * Handler for Bob's a client request message. Check @a msg is
813 * well-formed.
814 *
815 * @param cls identification of the client
816 * @param msg the actual message
817 * @return #GNUNET_OK if @a msg is well-formed
818 */
819static int
820check_bob_client_message (void *cls,
821 const struct BobComputationMessage *msg)
822{
823 struct BobServiceSession *s = cls;
824 uint32_t contained_count;
825 uint32_t total_count;
826 uint16_t msize;
827
828 if (GNUNET_SCALARPRODUCT_STATUS_INIT != s->status)
829 {
830 GNUNET_break (0);
831 return GNUNET_SYSERR;
832 }
833 msize = ntohs (msg->header.size);
834 total_count = ntohl (msg->element_count_total);
835 contained_count = ntohl (msg->element_count_contained);
836 if ((0 == total_count) ||
837 (0 == contained_count) ||
838 (UINT16_MAX < contained_count) ||
839 (msize != (sizeof(struct BobComputationMessage)
840 + contained_count * sizeof(struct
841 GNUNET_SCALARPRODUCT_Element))))
842 {
843 GNUNET_break_op (0);
844 return GNUNET_SYSERR;
845 }
846 return GNUNET_OK;
847}
848
849
850/**
851 * Handler for Bob's a client request message. Bob is in the response
852 * role, keep the values + session and waiting for a matching session
853 * or process a waiting request from Alice.
854 *
855 * @param cls identification of the client
856 * @param msg the actual message
857 */
858static void
859handle_bob_client_message (void *cls,
860 const struct BobComputationMessage *msg)
861{
862 struct BobServiceSession *s = cls;
863 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
864 GNUNET_MQ_hd_fixed_size (alices_computation_request,
865 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_SESSION_INITIALIZATION,
866 struct EccServiceRequestMessage,
867 s),
868 GNUNET_MQ_hd_var_size (alices_cryptodata_message,
869 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ECC_ALICE_CRYPTODATA,
870 struct EccAliceCryptodataMessage,
871 s),
872 GNUNET_MQ_handler_end ()
873 };
874 uint32_t contained_count;
875 uint32_t total_count;
876 const struct GNUNET_SCALARPRODUCT_Element *elements;
877 struct GNUNET_SETI_Element set_elem;
878 struct GNUNET_SCALARPRODUCT_Element *elem;
879
880 total_count = ntohl (msg->element_count_total);
881 contained_count = ntohl (msg->element_count_contained);
882
883 s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE;
884 s->total = total_count;
885 s->client_received_element_count = contained_count;
886 s->session_id = msg->session_key;
887 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
888 s->intersected_elements
889 = GNUNET_CONTAINER_multihashmap_create (s->total,
890 GNUNET_YES);
891 s->intersection_set = GNUNET_SETI_create (cfg);
892 for (uint32_t i = 0; i < contained_count; i++)
893 {
894 if (0 == GNUNET_ntohll (elements[i].value))
895 continue;
896 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
897 GNUNET_memcpy (elem,
898 &elements[i],
899 sizeof(struct GNUNET_SCALARPRODUCT_Element));
900 if (GNUNET_SYSERR ==
901 GNUNET_CONTAINER_multihashmap_put (s->intersected_elements,
902 &elem->key,
903 elem,
904 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
905 {
906 GNUNET_break (0);
907 GNUNET_free (elem);
908 continue;
909 }
910 set_elem.data = &elem->key;
911 set_elem.size = sizeof(elem->key);
912 set_elem.element_type = 0;
913 GNUNET_SETI_add_element (s->intersection_set,
914 &set_elem,
915 NULL, NULL);
916 s->used_element_count++;
917 }
918 GNUNET_SERVICE_client_continue (s->client);
919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920 "Received client request, opening port %s!\n",
921 GNUNET_h2s (&msg->session_key));
922 s->port = GNUNET_CADET_open_port (my_cadet,
923 &msg->session_key,
924 &cb_channel_incoming,
925 s,
926 NULL,
927 &cb_channel_destruction,
928 cadet_handlers);
929 if (NULL == s->port)
930 {
931 GNUNET_break (0);
932 GNUNET_SERVICE_client_drop (s->client);
933 return;
934 }
935}
936
937
938/**
939 * Task run during shutdown.
940 *
941 * @param cls unused
942 */
943static void
944shutdown_task (void *cls)
945{
946 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
947 "Shutting down, initiating cleanup.\n");
948 // FIXME: we have to cut our connections to CADET first!
949 if (NULL != my_cadet)
950 {
951 GNUNET_CADET_disconnect (my_cadet);
952 my_cadet = NULL;
953 }
954 if (NULL != edc)
955 {
956 GNUNET_CRYPTO_ecc_dlog_release (edc);
957 edc = NULL;
958 }
959}
960
961
962/**
963 * A client connected.
964 *
965 * Setup the associated data structure.
966 *
967 * @param cls closure, NULL
968 * @param client identification of the client
969 * @param mq message queue to communicate with @a client
970 * @return our `struct BobServiceSession`
971 */
972static void *
973client_connect_cb (void *cls,
974 struct GNUNET_SERVICE_Client *client,
975 struct GNUNET_MQ_Handle *mq)
976{
977 struct BobServiceSession *s;
978
979 s = GNUNET_new (struct BobServiceSession);
980 s->client = client;
981 s->client_mq = mq;
982 return s;
983}
984
985
986/**
987 * A client disconnected.
988 *
989 * Remove the associated session(s), release data structures
990 * and cancel pending outgoing transmissions to the client.
991 *
992 * @param cls closure, NULL
993 * @param client identification of the client
994 * @param app_cls our `struct BobServiceSession`
995 */
996static void
997client_disconnect_cb (void *cls,
998 struct GNUNET_SERVICE_Client *client,
999 void *app_cls)
1000{
1001 struct BobServiceSession *s = app_cls;
1002
1003 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1004 "Client disconnected from us.\n");
1005 s->client = NULL;
1006 destroy_service_session (s);
1007}
1008
1009
1010/**
1011 * Initialization of the program and message handlers
1012 *
1013 * @param cls closure
1014 * @param c configuration to use
1015 * @param service the initialized service
1016 */
1017static void
1018run (void *cls,
1019 const struct GNUNET_CONFIGURATION_Handle *c,
1020 struct GNUNET_SERVICE_Handle *service)
1021{
1022 cfg = c;
1023 /* We don't really do DLOG, so we can setup with very minimal resources */
1024 edc = GNUNET_CRYPTO_ecc_dlog_prepare (4 /* max value */,
1025 2 /* RAM */);
1026 my_cadet = GNUNET_CADET_connect (cfg);
1027 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1028 NULL);
1029 if (NULL == my_cadet)
1030 {
1031 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1032 _ ("Connect to CADET failed\n"));
1033 GNUNET_SCHEDULER_shutdown ();
1034 return;
1035 }
1036}
1037
1038
1039/**
1040 * Define "main" method using service macro.
1041 */
1042GNUNET_SERVICE_MAIN
1043 ("scalarproduct-bob",
1044 GNUNET_SERVICE_OPTION_NONE,
1045 &run,
1046 &client_connect_cb,
1047 &client_disconnect_cb,
1048 NULL,
1049 GNUNET_MQ_hd_var_size (bob_client_message,
1050 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB,
1051 struct BobComputationMessage,
1052 NULL),
1053 GNUNET_MQ_hd_var_size (bob_client_message_multipart,
1054 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB,
1055 struct ComputationBobCryptodataMultipartMessage,
1056 NULL),
1057 GNUNET_MQ_handler_end ());
1058
1059
1060/* end of gnunet-service-scalarproduct-ecc_bob.c */
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h
new file mode 100644
index 000000000..4e79afa2f
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct.h
@@ -0,0 +1,142 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct.h
22 * @brief scalarproduct service P2P messages
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SCALARPRODUCT_H
27#define GNUNET_SERVICE_SCALARPRODUCT_H
28
29
30GNUNET_NETWORK_STRUCT_BEGIN
31
32/**
33 * Message type passed from requesting service Alice to responding
34 * service Bob to initiate a request and make Bob participate in our
35 * protocol. Afterwards, Bob is expected to perform the set
36 * intersection with Alice. Once that has succeeded, Alice will
37 * send a `struct AliceCryptodataMessage *`. Bob is not expected
38 * to respond via CADET in the meantime.
39 */
40struct ServiceRequestMessage
41{
42 /**
43 * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION
44 */
45 struct GNUNET_MessageHeader header;
46
47 /**
48 * For alignment. Always zero.
49 */
50 uint32_t reserved;
51
52 /**
53 * The transaction/session key used to identify a session
54 */
55 struct GNUNET_HashCode session_id;
56
57 /**
58 * Alice's public key
59 */
60 struct GNUNET_CRYPTO_PaillierPublicKey public_key;
61};
62
63
64/**
65 * Vector of Pallier-encrypted values sent by Alice to Bob
66 * (after set intersection). Alice may send messages of this
67 * type repeatedly to transmit all values.
68 */
69struct AliceCryptodataMessage
70{
71 /**
72 * Type is #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA
73 */
74 struct GNUNET_MessageHeader header;
75
76 /**
77 * How many elements we appended to this message? In NBO.
78 */
79 uint32_t contained_element_count GNUNET_PACKED;
80
81 /**
82 * struct GNUNET_CRYPTO_PaillierCiphertext[contained_element_count]
83 */
84};
85
86
87/**
88 * Message type passed from responding service Bob to responding
89 * service Alice to complete a request and allow Alice to compute the
90 * result. If Bob's reply does not fit into this one message, the
91 * conversation may be continued with `struct BobCryptodataMultipartMessage`
92 * messages afterwards.
93 */
94struct BobCryptodataMessage
95{
96 /**
97 * GNUNET message header with type
98 * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA.
99 */
100 struct GNUNET_MessageHeader header;
101
102 /**
103 * How many elements this individual message delivers (in NBO).
104 */
105 uint32_t contained_element_count GNUNET_PACKED;
106
107 /**
108 * followed by s | s' | k[i][perm]
109 */
110};
111
112
113/**
114 * Multipart Message type passed between to supply additional elements
115 * for the peer. Send from Bob to Alice with additional elements
116 * of k[i][perm] after his `struct BobCryptodataMessage *`.
117 * Once all k-values have been transmitted, Bob is finished and
118 * Alice can transmit the final result to the client.
119 */
120struct BobCryptodataMultipartMessage
121{
122 /**
123 * GNUNET message header
124 */
125 struct GNUNET_MessageHeader header;
126
127 /**
128 * How many elements we supply within this message? In NBO.
129 */
130 uint32_t contained_element_count GNUNET_PACKED;
131
132 /**
133 * Followed by `struct
134 * GNUNET_CRYPTO_PaillierCiphertext[contained_element_count]`
135 */
136};
137
138
139GNUNET_NETWORK_STRUCT_END
140
141
142#endif
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c
new file mode 100644
index 000000000..0149f45ba
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_alice.c
@@ -0,0 +1,1388 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct_alice.c
22 * @brief scalarproduct service implementation
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <limits.h>
28#include <gcrypt.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_core_service.h"
31#include "gnunet_cadet_service.h"
32#include "gnunet_applications.h"
33#include "gnunet_protocols.h"
34#include "gnunet_scalarproduct_service.h"
35#include "gnunet_seti_service.h"
36#include "scalarproduct.h"
37#include "gnunet-service-scalarproduct.h"
38#include "gnunet_constants.h"
39
40#define LOG(kind, ...) \
41 GNUNET_log_from (kind, "scalarproduct-alice", __VA_ARGS__)
42
43/**
44 * An encrypted element key-value pair.
45 */
46struct MpiElement
47{
48 /**
49 * Key used to identify matching pairs of values to multiply.
50 * Points into an existing data structure, to avoid copying
51 * and doubling memory use.
52 */
53 const struct GNUNET_HashCode *key;
54
55 /**
56 * Value represented (a).
57 */
58 gcry_mpi_t value;
59};
60
61
62/**
63 * A scalarproduct session which tracks
64 * a request form the client to our final response.
65 */
66struct AliceServiceSession
67{
68 /**
69 * (hopefully) unique transaction ID
70 */
71 struct GNUNET_HashCode session_id;
72
73 /**
74 * Alice or Bob's peerID
75 */
76 struct GNUNET_PeerIdentity peer;
77
78 /**
79 * The client this request is related to.
80 */
81 struct GNUNET_SERVICE_Client *client;
82
83 /**
84 * The message queue for the client.
85 */
86 struct GNUNET_MQ_Handle *client_mq;
87
88 /**
89 * The message queue for CADET.
90 */
91 struct GNUNET_MQ_Handle *cadet_mq;
92
93 /**
94 * all non-0-value'd elements transmitted to us.
95 * Values are of type `struct GNUNET_SCALARPRODUCT_Element *`
96 */
97 struct GNUNET_CONTAINER_MultiHashMap *intersected_elements;
98
99 /**
100 * Set of elements for which will conduction an intersection.
101 * the resulting elements are then used for computing the scalar product.
102 */
103 struct GNUNET_SETI_Handle *intersection_set;
104
105 /**
106 * Set of elements for which will conduction an intersection.
107 * the resulting elements are then used for computing the scalar product.
108 */
109 struct GNUNET_SETI_OperationHandle *intersection_op;
110
111 /**
112 * Handle to Alice's Intersection operation listening for Bob
113 */
114 struct GNUNET_SETI_ListenHandle *intersection_listen;
115
116 /**
117 * channel-handle associated with our cadet handle
118 */
119 struct GNUNET_CADET_Channel *channel;
120
121 /**
122 * a(Alice), sorted array by key of length @e used_element_count.
123 */
124 struct MpiElement *sorted_elements;
125
126 /**
127 * Bob's permutation p of R
128 */
129 struct GNUNET_CRYPTO_PaillierCiphertext *r;
130
131 /**
132 * Bob's permutation q of R
133 */
134 struct GNUNET_CRYPTO_PaillierCiphertext *r_prime;
135
136 /**
137 * Bob's "s"
138 */
139 struct GNUNET_CRYPTO_PaillierCiphertext s;
140
141 /**
142 * Bob's "s'"
143 */
144 struct GNUNET_CRYPTO_PaillierCiphertext s_prime;
145
146 /**
147 * The computed scalar
148 */
149 gcry_mpi_t product;
150
151 /**
152 * How many elements we were supplied with from the client (total
153 * count before intersection).
154 */
155 uint32_t total;
156
157 /**
158 * How many elements actually are used for the scalar product.
159 * Size of the arrays in @e r and @e r_prime. Sometimes also
160 * reset to 0 and used as a counter!
161 */
162 uint32_t used_element_count;
163
164 /**
165 * Already transferred elements from client to us.
166 * Less or equal than @e total.
167 */
168 uint32_t client_received_element_count;
169
170 /**
171 * Already transferred elements from Bob to us.
172 * Less or equal than @e total.
173 */
174 uint32_t cadet_received_element_count;
175
176 /**
177 * State of this session. In
178 * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is
179 * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or
180 * #GNUNET_SCALARPRODUCT_STATUS_FAILURE.
181 */
182 enum GNUNET_SCALARPRODUCT_ResponseStatus status;
183
184 /**
185 * Flag to prevent recursive calls to #destroy_service_session() from
186 * doing harm.
187 */
188 int in_destroy;
189};
190
191
192/**
193 * GNUnet configuration handle
194 */
195static const struct GNUNET_CONFIGURATION_Handle *cfg;
196
197/**
198 * Service's own public key
199 */
200static struct GNUNET_CRYPTO_PaillierPublicKey my_pubkey;
201
202/**
203 * Service's own private key
204 */
205static struct GNUNET_CRYPTO_PaillierPrivateKey my_privkey;
206
207/**
208 * Service's offset for values that could possibly be negative but are plaintext for encryption.
209 */
210static gcry_mpi_t my_offset;
211
212/**
213 * Handle to the CADET service.
214 */
215static struct GNUNET_CADET_Handle *my_cadet;
216
217
218/**
219 * Iterator called to free elements.
220 *
221 * @param cls the `struct AliceServiceSession *` (unused)
222 * @param key the key (unused)
223 * @param value value to free
224 * @return #GNUNET_OK (continue to iterate)
225 */
226static int
227free_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
228{
229 struct GNUNET_SCALARPRODUCT_Element *e = value;
230
231 GNUNET_free (e);
232 return GNUNET_OK;
233}
234
235
236/**
237 * Destroy session state, we are done with it.
238 *
239 * @param s the session to free elements from
240 */
241static void
242destroy_service_session (struct AliceServiceSession *s)
243{
244 if (GNUNET_YES == s->in_destroy)
245 return;
246 s->in_destroy = GNUNET_YES;
247 if (NULL != s->client)
248 {
249 struct GNUNET_SERVICE_Client *c = s->client;
250
251 s->client = NULL;
252 GNUNET_SERVICE_client_drop (c);
253 }
254 if (NULL != s->channel)
255 {
256 GNUNET_CADET_channel_destroy (s->channel);
257 s->channel = NULL;
258 }
259 if (NULL != s->intersected_elements)
260 {
261 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
262 &free_element_cb,
263 s);
264 GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements);
265 s->intersected_elements = NULL;
266 }
267 if (NULL != s->intersection_listen)
268 {
269 GNUNET_SETI_listen_cancel (s->intersection_listen);
270 s->intersection_listen = NULL;
271 }
272 if (NULL != s->intersection_op)
273 {
274 GNUNET_SETI_operation_cancel (s->intersection_op);
275 s->intersection_op = NULL;
276 }
277 if (NULL != s->intersection_set)
278 {
279 GNUNET_SETI_destroy (s->intersection_set);
280 s->intersection_set = NULL;
281 }
282 if (NULL != s->sorted_elements)
283 {
284 for (unsigned int i = 0; i < s->used_element_count; i++)
285 gcry_mpi_release (s->sorted_elements[i].value);
286 GNUNET_free (s->sorted_elements);
287 s->sorted_elements = NULL;
288 }
289 if (NULL != s->r)
290 {
291 GNUNET_free (s->r);
292 s->r = NULL;
293 }
294 if (NULL != s->r_prime)
295 {
296 GNUNET_free (s->r_prime);
297 s->r_prime = NULL;
298 }
299 if (NULL != s->product)
300 {
301 gcry_mpi_release (s->product);
302 s->product = NULL;
303 }
304 GNUNET_free (s);
305}
306
307
308/**
309 * Notify the client that the session has failed. A message gets sent
310 * to Alice's client if we encountered any error.
311 *
312 * @param session the associated client session to fail or succeed
313 */
314static void
315prepare_client_end_notification (struct AliceServiceSession *session)
316{
317 struct ClientResponseMessage *msg;
318 struct GNUNET_MQ_Envelope *e;
319
320 if (NULL == session->client_mq)
321 return; /* no client left to be notified */
322 GNUNET_log (
323 GNUNET_ERROR_TYPE_DEBUG,
324 "Sending session-end notification with status %d to client for session %s\n",
325 session->status,
326 GNUNET_h2s (&session->session_id));
327 e = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
328 msg->product_length = htonl (0);
329 msg->status = htonl (session->status);
330 GNUNET_MQ_send (session->client_mq, e);
331}
332
333
334/**
335 * Prepare the final (positive) response we will send to Alice's
336 * client.
337 *
338 * @param s the session associated with our client.
339 */
340static void
341transmit_client_response (struct AliceServiceSession *s)
342{
343 struct ClientResponseMessage *msg;
344 struct GNUNET_MQ_Envelope *e;
345 unsigned char *product_exported = NULL;
346 size_t product_length = 0;
347 int32_t range;
348 gcry_error_t rc;
349 int sign;
350 gcry_mpi_t value;
351
352 if (NULL == s->product)
353 {
354 GNUNET_break (0);
355 prepare_client_end_notification (s);
356 return;
357 }
358 value = gcry_mpi_new (0);
359 sign = gcry_mpi_cmp_ui (s->product, 0);
360 if (0 > sign)
361 {
362 range = -1;
363 gcry_mpi_sub (value, value, s->product);
364 }
365 else if (0 < sign)
366 {
367 range = 1;
368 gcry_mpi_add (value, value, s->product);
369 }
370 else
371 {
372 /* result is exactly zero */
373 range = 0;
374 }
375 gcry_mpi_release (s->product);
376 s->product = NULL;
377
378 if ((0 != range) && (0 != (rc = gcry_mpi_aprint (GCRYMPI_FMT_STD,
379 &product_exported,
380 &product_length,
381 value))))
382 {
383 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
384 prepare_client_end_notification (s);
385 return;
386 }
387 gcry_mpi_release (value);
388 e = GNUNET_MQ_msg_extra (msg,
389 product_length,
390 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
391 msg->status = htonl (GNUNET_SCALARPRODUCT_STATUS_SUCCESS);
392 msg->range = htonl (range);
393 msg->product_length = htonl (product_length);
394 if (NULL != product_exported)
395 {
396 GNUNET_memcpy (&msg[1], product_exported, product_length);
397 GNUNET_free (product_exported);
398 }
399 GNUNET_MQ_send (s->client_mq, e);
400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401 "Sent result to client, session %s has ended!\n",
402 GNUNET_h2s (&s->session_id));
403}
404
405
406/**
407 * Function called whenever a channel is destroyed. Should clean up
408 * any associated state.
409 *
410 * It must NOT call #GNUNET_CADET_channel_destroy() on the channel.
411 *
412 * @param cls our `struct AliceServiceSession`
413 * @param channel connection to the other end (henceforth invalid)
414 */
415static void
416cb_channel_destruction (void *cls, const struct GNUNET_CADET_Channel *channel)
417{
418 struct AliceServiceSession *s = cls;
419
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 "Peer disconnected, terminating session %s with peer %s\n",
422 GNUNET_h2s (&s->session_id),
423 GNUNET_i2s (&s->peer));
424 if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status)
425 {
426 /* We didn't get an answer yet, fail with error */
427 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
428 prepare_client_end_notification (s);
429 }
430 s->channel = NULL;
431}
432
433
434/**
435 * Computes the square sum over a vector of a given length.
436 *
437 * @param vector the vector to compute over
438 * @param length the length of the vector
439 * @return an MPI value containing the calculated sum, never NULL
440 */
441static gcry_mpi_t
442compute_square_sum_mpi_elements (const struct MpiElement *vector,
443 uint32_t length)
444{
445 gcry_mpi_t elem;
446 gcry_mpi_t sum;
447 uint32_t i;
448
449 GNUNET_assert (NULL != (sum = gcry_mpi_new (0)));
450 GNUNET_assert (NULL != (elem = gcry_mpi_new (0)));
451 for (i = 0; i < length; i++)
452 {
453 gcry_mpi_mul (elem, vector[i].value, vector[i].value);
454 gcry_mpi_add (sum, sum, elem);
455 }
456 gcry_mpi_release (elem);
457 return sum;
458}
459
460
461/**
462 * Computes the square sum over a vector of a given length.
463 *
464 * @param vector the vector to compute over
465 * @param length the length of the vector
466 * @return an MPI value containing the calculated sum, never NULL
467 */
468static gcry_mpi_t
469compute_square_sum (const gcry_mpi_t *vector, uint32_t length)
470{
471 gcry_mpi_t elem;
472 gcry_mpi_t sum;
473 uint32_t i;
474
475 GNUNET_assert (NULL != (sum = gcry_mpi_new (0)));
476 GNUNET_assert (NULL != (elem = gcry_mpi_new (0)));
477 for (i = 0; i < length; i++)
478 {
479 gcry_mpi_mul (elem, vector[i], vector[i]);
480 gcry_mpi_add (sum, sum, elem);
481 }
482 gcry_mpi_release (elem);
483 return sum;
484}
485
486
487/**
488 * Compute our scalar product, done by Alice
489 *
490 * @param session the session associated with this computation
491 * @return product as MPI, never NULL
492 */
493static gcry_mpi_t
494compute_scalar_product (struct AliceServiceSession *session)
495{
496 uint32_t count;
497 gcry_mpi_t t;
498 gcry_mpi_t u;
499 gcry_mpi_t u_prime;
500 gcry_mpi_t p;
501 gcry_mpi_t p_prime;
502 gcry_mpi_t tmp;
503 gcry_mpi_t r[session->used_element_count];
504 gcry_mpi_t r_prime[session->used_element_count];
505 gcry_mpi_t s;
506 gcry_mpi_t s_prime;
507 unsigned int i;
508
509 count = session->used_element_count;
510 // due to the introduced static offset S, we now also have to remove this
511 // from the E(a_pi)(+)E(-b_pi-r_pi) and E(a_qi)(+)E(-r_qi) twice each,
512 // the result is E((S + a_pi) + (S -b_pi-r_pi)) and E(S + a_qi + S - r_qi)
513 for (i = 0; i < count; i++)
514 {
515 r[i] = gcry_mpi_new (0);
516 GNUNET_CRYPTO_paillier_decrypt (&my_privkey,
517 &my_pubkey,
518 &session->r[i],
519 r[i]);
520 gcry_mpi_sub (r[i], r[i], my_offset);
521 gcry_mpi_sub (r[i], r[i], my_offset);
522 r_prime[i] = gcry_mpi_new (0);
523 GNUNET_CRYPTO_paillier_decrypt (&my_privkey,
524 &my_pubkey,
525 &session->r_prime[i],
526 r_prime[i]);
527 gcry_mpi_sub (r_prime[i], r_prime[i], my_offset);
528 gcry_mpi_sub (r_prime[i], r_prime[i], my_offset);
529 }
530
531 // calculate t = sum(ai)
532 t = compute_square_sum_mpi_elements (session->sorted_elements, count);
533 // calculate U
534 u = gcry_mpi_new (0);
535 tmp = compute_square_sum (r, count);
536 gcry_mpi_sub (u, u, tmp);
537 gcry_mpi_release (tmp);
538
539 // calculate U'
540 u_prime = gcry_mpi_new (0);
541 tmp = compute_square_sum (r_prime, count);
542 gcry_mpi_sub (u_prime, u_prime, tmp);
543
544 GNUNET_assert (p = gcry_mpi_new (0));
545 GNUNET_assert (p_prime = gcry_mpi_new (0));
546 GNUNET_assert (s = gcry_mpi_new (0));
547 GNUNET_assert (s_prime = gcry_mpi_new (0));
548
549 // compute P
550 GNUNET_CRYPTO_paillier_decrypt (&my_privkey, &my_pubkey, &session->s, s);
551 GNUNET_CRYPTO_paillier_decrypt (&my_privkey,
552 &my_pubkey,
553 &session->s_prime,
554 s_prime);
555
556 // compute P
557 gcry_mpi_add (p, s, t);
558 gcry_mpi_add (p, p, u);
559
560 // compute P'
561 gcry_mpi_add (p_prime, s_prime, t);
562 gcry_mpi_add (p_prime, p_prime, u_prime);
563
564 gcry_mpi_release (t);
565 gcry_mpi_release (u);
566 gcry_mpi_release (u_prime);
567 gcry_mpi_release (s);
568 gcry_mpi_release (s_prime);
569
570 // compute product
571 gcry_mpi_sub (p, p, p_prime);
572 gcry_mpi_release (p_prime);
573 tmp = gcry_mpi_set_ui (tmp, 2);
574 gcry_mpi_div (p, NULL, p, tmp, 0);
575
576 gcry_mpi_release (tmp);
577 for (i = 0; i < count; i++)
578 {
579 gcry_mpi_release (session->sorted_elements[i].value);
580 gcry_mpi_release (r[i]);
581 gcry_mpi_release (r_prime[i]);
582 }
583 GNUNET_free (session->sorted_elements);
584 session->sorted_elements = NULL;
585 GNUNET_free (session->r);
586 session->r = NULL;
587 GNUNET_free (session->r_prime);
588 session->r_prime = NULL;
589
590 return p;
591}
592
593
594/**
595 * Check a multipart chunk of a response we got from another service
596 * we wanted to calculate a scalarproduct with.
597 *
598 * @param cls the `struct AliceServiceSession`
599 * @param msg the actual message
600 * @return #GNUNET_OK to keep the connection open,
601 * #GNUNET_SYSERR to close it (signal serious error)
602 */
603static int
604check_bobs_cryptodata_multipart (
605 void *cls,
606 const struct BobCryptodataMultipartMessage *msg)
607{
608 struct AliceServiceSession *s = cls;
609 uint32_t contained;
610 size_t msg_size;
611 size_t required_size;
612
613 msg_size = ntohs (msg->header.size);
614 contained = ntohl (msg->contained_element_count);
615 required_size =
616 sizeof(struct BobCryptodataMultipartMessage)
617 + 2 * contained * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
618 if ((required_size != msg_size) ||
619 (s->cadet_received_element_count + contained > s->used_element_count))
620 {
621 GNUNET_break (0);
622 return GNUNET_SYSERR;
623 }
624 return GNUNET_OK;
625}
626
627
628/**
629 * Handle a multipart chunk of a response we got from another service
630 * we wanted to calculate a scalarproduct with.
631 *
632 * @param cls the `struct AliceServiceSession`
633 * @param msg the actual message
634 */
635static void
636handle_bobs_cryptodata_multipart (
637 void *cls,
638 const struct BobCryptodataMultipartMessage *msg)
639{
640 struct AliceServiceSession *s = cls;
641 const struct GNUNET_CRYPTO_PaillierCiphertext *payload;
642 size_t i;
643 uint32_t contained;
644
645 contained = ntohl (msg->contained_element_count);
646 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647 "Received %u additional crypto values from Bob\n",
648 (unsigned int) contained);
649
650 payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
651 /* Convert each k[][perm] to its MPI_value */
652 for (i = 0; i < contained; i++)
653 {
654 GNUNET_memcpy (&s->r[s->cadet_received_element_count + i],
655 &payload[2 * i],
656 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
657 GNUNET_memcpy (&s->r_prime[s->cadet_received_element_count + i],
658 &payload[2 * i],
659 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
660 }
661 s->cadet_received_element_count += contained;
662 GNUNET_CADET_receive_done (s->channel);
663 if (s->cadet_received_element_count != s->used_element_count)
664 return; /* more to come */
665
666 s->product = compute_scalar_product (s);
667 transmit_client_response (s);
668}
669
670
671/**
672 * Check a response we got from another service we wanted to
673 * calculate a scalarproduct with.
674 *
675 * @param cls our `struct AliceServiceSession`
676 * @param msg the actual message
677 * @return #GNUNET_OK to keep the connection open,
678 * #GNUNET_SYSERR to close it (we are done)
679 */
680static int
681check_bobs_cryptodata_message (void *cls,
682 const struct BobCryptodataMessage *msg)
683{
684 struct AliceServiceSession *s = cls;
685 uint32_t contained;
686 uint16_t msg_size;
687 size_t required_size;
688
689 msg_size = ntohs (msg->header.size);
690 contained = ntohl (msg->contained_element_count);
691 required_size =
692 sizeof(struct BobCryptodataMessage)
693 + 2 * contained * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)
694 + 2 * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext);
695 if ((msg_size != required_size) || (contained > UINT16_MAX) ||
696 (s->used_element_count < contained))
697 {
698 GNUNET_break_op (0);
699 return GNUNET_SYSERR;
700 }
701 if (NULL == s->sorted_elements)
702 {
703 /* we're not ready yet, how can Bob be? */
704 GNUNET_break_op (0);
705 return GNUNET_SYSERR;
706 }
707 if (s->total != s->client_received_element_count)
708 {
709 /* we're not ready yet, how can Bob be? */
710 GNUNET_break_op (0);
711 return GNUNET_SYSERR;
712 }
713 return GNUNET_OK;
714}
715
716
717/**
718 * Handle a response we got from another service we wanted to
719 * calculate a scalarproduct with.
720 *
721 * @param cls our `struct AliceServiceSession`
722 * @param msg the actual message
723 */
724static void
725handle_bobs_cryptodata_message (void *cls,
726 const struct BobCryptodataMessage *msg)
727{
728 struct AliceServiceSession *s = cls;
729 const struct GNUNET_CRYPTO_PaillierCiphertext *payload;
730 uint32_t i;
731 uint32_t contained;
732
733 contained = ntohl (msg->contained_element_count);
734 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
735 "Received %u crypto values from Bob\n",
736 (unsigned int) contained);
737 payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
738 GNUNET_memcpy (&s->s,
739 &payload[0],
740 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
741 GNUNET_memcpy (&s->s_prime,
742 &payload[1],
743 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
744 payload = &payload[2];
745
746 s->r = GNUNET_new_array (s->used_element_count,
747 struct GNUNET_CRYPTO_PaillierCiphertext);
748 s->r_prime = GNUNET_new_array (s->used_element_count,
749 struct GNUNET_CRYPTO_PaillierCiphertext);
750 for (i = 0; i < contained; i++)
751 {
752 GNUNET_memcpy (&s->r[i],
753 &payload[2 * i],
754 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
755 GNUNET_memcpy (&s->r_prime[i],
756 &payload[2 * i + 1],
757 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
758 }
759 s->cadet_received_element_count = contained;
760 GNUNET_CADET_receive_done (s->channel);
761
762 if (s->cadet_received_element_count != s->used_element_count)
763 {
764 /* More to come */
765 return;
766 }
767 s->product = compute_scalar_product (s);
768 transmit_client_response (s);
769}
770
771
772/**
773 * Iterator to copy over messages from the hash map
774 * into an array for sorting.
775 *
776 * @param cls the `struct AliceServiceSession *`
777 * @param key the key (unused)
778 * @param value the `struct GNUNET_SCALARPRODUCT_Element *`
779 */
780static int
781copy_element_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
782{
783 struct AliceServiceSession *s = cls;
784 struct GNUNET_SCALARPRODUCT_Element *e = value;
785 gcry_mpi_t mval;
786 int64_t val;
787
788 mval = gcry_mpi_new (0);
789 val = (int64_t) GNUNET_ntohll (e->value);
790 if (0 > val)
791 gcry_mpi_sub_ui (mval, mval, -val);
792 else
793 gcry_mpi_add_ui (mval, mval, val);
794 s->sorted_elements[s->used_element_count].value = mval;
795 s->sorted_elements[s->used_element_count].key = &e->key;
796 s->used_element_count++;
797 return GNUNET_OK;
798}
799
800
801/**
802 * Compare two `struct MpiValue`s by key for sorting.
803 *
804 * @param a pointer to first `struct MpiValue *`
805 * @param b pointer to first `struct MpiValue *`
806 * @return -1 for a < b, 0 for a=b, 1 for a > b.
807 */
808static int
809element_cmp (const void *a, const void *b)
810{
811 const struct MpiElement *ma = a;
812 const struct MpiElement *mb = b;
813
814 return GNUNET_CRYPTO_hash_cmp (ma->key, mb->key);
815}
816
817
818/**
819 * Maximum number of elements we can put into a single cryptodata
820 * message
821 */
822#define ELEMENT_CAPACITY \
823 ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \
824 - sizeof(struct AliceCryptodataMessage)) \
825 / sizeof(struct GNUNET_CRYPTO_PaillierCiphertext))
826
827
828/**
829 * Send the cryptographic data from Alice to Bob.
830 * Does nothing if we already transferred all elements.
831 *
832 * @param s the associated service session
833 */
834static void
835send_alices_cryptodata_message (struct AliceServiceSession *s)
836{
837 struct AliceCryptodataMessage *msg;
838 struct GNUNET_MQ_Envelope *e;
839 struct GNUNET_CRYPTO_PaillierCiphertext *payload;
840 unsigned int i;
841 uint32_t todo_count;
842 gcry_mpi_t a;
843 uint32_t off;
844
845 s->sorted_elements = GNUNET_malloc (
846 GNUNET_CONTAINER_multihashmap_size (s->intersected_elements)
847 * sizeof(struct MpiElement));
848 s->used_element_count = 0;
849 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
850 &copy_element_cb,
851 s);
852 LOG (GNUNET_ERROR_TYPE_DEBUG,
853 "Finished intersection, %d items remain\n",
854 s->used_element_count);
855 qsort (s->sorted_elements,
856 s->used_element_count,
857 sizeof(struct MpiElement),
858 &element_cmp);
859 off = 0;
860 while (off < s->used_element_count)
861 {
862 todo_count = s->used_element_count - off;
863 if (todo_count > ELEMENT_CAPACITY)
864 todo_count = ELEMENT_CAPACITY;
865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
866 "Sending %u/%u crypto values to Bob\n",
867 (unsigned int) todo_count,
868 (unsigned int) s->used_element_count);
869
870 e =
871 GNUNET_MQ_msg_extra (msg,
872 todo_count
873 * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext),
874 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA);
875 msg->contained_element_count = htonl (todo_count);
876 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
877 a = gcry_mpi_new (0);
878 for (i = off; i < off + todo_count; i++)
879 {
880 gcry_mpi_add (a, s->sorted_elements[i].value, my_offset);
881 GNUNET_assert (
882 3 ==
883 GNUNET_CRYPTO_paillier_encrypt (&my_pubkey, a, 3, &payload[i - off]));
884 }
885 gcry_mpi_release (a);
886 off += todo_count;
887 GNUNET_MQ_send (s->cadet_mq, e);
888 }
889}
890
891
892/**
893 * Callback for set operation results. Called for each element
894 * that should be removed from the result set, and then once
895 * to indicate that the set intersection operation is done.
896 *
897 * @param cls closure with the `struct AliceServiceSession`
898 * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK
899 * @param current_size current set size
900 * @param status what has happened with the set intersection?
901 */
902static void
903cb_intersection_element_removed (void *cls,
904 const struct GNUNET_SETI_Element *element,
905 uint64_t current_size,
906 enum GNUNET_SETI_Status status)
907{
908 struct AliceServiceSession *s = cls;
909 struct GNUNET_SCALARPRODUCT_Element *se;
910
911 switch (status)
912 {
913 case GNUNET_SETI_STATUS_DEL_LOCAL:
914 /* this element has been removed from the set */
915 se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements,
916 element->data);
917 GNUNET_assert (NULL != se);
918 LOG (GNUNET_ERROR_TYPE_DEBUG,
919 "Intersection removed element with key %s and value %lld\n",
920 GNUNET_h2s (&se->key),
921 (long long) GNUNET_ntohll (se->value));
922 GNUNET_assert (
923 GNUNET_YES ==
924 GNUNET_CONTAINER_multihashmap_remove (s->intersected_elements,
925 element->data,
926 se));
927 GNUNET_free (se);
928 return;
929
930 case GNUNET_SETI_STATUS_DONE:
931 s->intersection_op = NULL;
932 if (NULL != s->intersection_set)
933 {
934 GNUNET_SETI_destroy (s->intersection_set);
935 s->intersection_set = NULL;
936 }
937 send_alices_cryptodata_message (s);
938 return;
939 case GNUNET_SETI_STATUS_FAILURE:
940 /* unhandled status code */
941 LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n");
942 if (NULL != s->intersection_listen)
943 {
944 GNUNET_SETI_listen_cancel (s->intersection_listen);
945 s->intersection_listen = NULL;
946 }
947 s->intersection_op = NULL;
948 if (NULL != s->intersection_set)
949 {
950 GNUNET_SETI_destroy (s->intersection_set);
951 s->intersection_set = NULL;
952 }
953 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
954 prepare_client_end_notification (s);
955 return;
956
957 default:
958 GNUNET_break (0);
959 return;
960 }
961}
962
963
964/**
965 * Called when another peer wants to do a set operation with the
966 * local peer. If a listen error occurs, the @a request is NULL.
967 *
968 * @param cls closure with the `struct AliceServiceSession *`
969 * @param other_peer the other peer
970 * @param context_msg message with application specific information from
971 * the other peer
972 * @param request request from the other peer (never NULL), use GNUNET_SETI_accept()
973 * to accept it, otherwise the request will be refused
974 * Note that we can't just return value from the listen callback,
975 * as it is also necessary to specify the set we want to do the
976 * operation with, which sometimes can be derived from the context
977 * message. It's necessary to specify the timeout.
978 */
979static void
980cb_intersection_request_alice (void *cls,
981 const struct GNUNET_PeerIdentity *other_peer,
982 const struct GNUNET_MessageHeader *context_msg,
983 struct GNUNET_SETI_Request *request)
984{
985 struct AliceServiceSession *s = cls;
986
987 if (0 != GNUNET_memcmp (other_peer, &s->peer))
988 {
989 GNUNET_break_op (0);
990 return;
991 }
992 s->intersection_op = GNUNET_SETI_accept (request,
993 (struct
994 GNUNET_SETI_Option[]){ { 0 } },
995 &cb_intersection_element_removed,
996 s);
997 if (NULL == s->intersection_op)
998 {
999 GNUNET_break (0);
1000 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
1001 prepare_client_end_notification (s);
1002 return;
1003 }
1004 if (GNUNET_OK != GNUNET_SETI_commit (s->intersection_op, s->intersection_set))
1005 {
1006 GNUNET_break (0);
1007 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
1008 prepare_client_end_notification (s);
1009 return;
1010 }
1011}
1012
1013
1014/**
1015 * Our client has finished sending us its multipart message.
1016 *
1017 * @param session the service session context
1018 */
1019static void
1020client_request_complete_alice (struct AliceServiceSession *s)
1021{
1022 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1023 { GNUNET_MQ_hd_var_size (bobs_cryptodata_message,
1024 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA,
1025 struct BobCryptodataMessage,
1026 s),
1027 GNUNET_MQ_hd_var_size (
1028 bobs_cryptodata_multipart,
1029 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA_MULTIPART,
1030 struct BobCryptodataMultipartMessage,
1031 s),
1032 GNUNET_MQ_handler_end () };
1033 struct ServiceRequestMessage *msg;
1034 struct GNUNET_MQ_Envelope *e;
1035
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Creating new channel for session with key %s.\n",
1038 GNUNET_h2s (&s->session_id));
1039 s->channel = GNUNET_CADET_channel_create (my_cadet,
1040 s,
1041 &s->peer,
1042 &s->session_id,
1043 NULL,
1044 &cb_channel_destruction,
1045 cadet_handlers);
1046 if (NULL == s->channel)
1047 {
1048 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
1049 prepare_client_end_notification (s);
1050 return;
1051 }
1052 s->cadet_mq = GNUNET_CADET_get_mq (s->channel);
1053 s->intersection_listen = GNUNET_SETI_listen (cfg,
1054 &s->session_id,
1055 &cb_intersection_request_alice,
1056 s);
1057 if (NULL == s->intersection_listen)
1058 {
1059 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
1060 GNUNET_CADET_channel_destroy (s->channel);
1061 s->channel = NULL;
1062 prepare_client_end_notification (s);
1063 return;
1064 }
1065
1066 e = GNUNET_MQ_msg (msg,
1067 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION);
1068 msg->session_id = s->session_id;
1069 msg->public_key = my_pubkey;
1070 GNUNET_MQ_send (s->cadet_mq, e);
1071}
1072
1073
1074/**
1075 * We're receiving additional set data. Check if
1076 * @a msg is well-formed.
1077 *
1078 * @param cls client identification of the client
1079 * @param msg the actual message
1080 * @return #GNUNET_OK if @a msg is well-formed
1081 */
1082static int
1083check_alice_client_message_multipart (
1084 void *cls,
1085 const struct ComputationBobCryptodataMultipartMessage *msg)
1086{
1087 struct AliceServiceSession *s = cls;
1088 uint32_t contained_count;
1089 uint16_t msize;
1090
1091 msize = ntohs (msg->header.size);
1092 contained_count = ntohl (msg->element_count_contained);
1093 if ((msize !=
1094 (sizeof(struct ComputationBobCryptodataMultipartMessage)
1095 + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))) ||
1096 (0 == contained_count) ||
1097 (s->total == s->client_received_element_count) ||
1098 (s->total < s->client_received_element_count + contained_count))
1099 {
1100 GNUNET_break_op (0);
1101 return GNUNET_SYSERR;
1102 }
1103 return GNUNET_OK;
1104}
1105
1106
1107/**
1108 * We're receiving additional set data. Add it to our
1109 * set and if we are done, initiate the transaction.
1110 *
1111 * @param cls client identification of the client
1112 * @param msg the actual message
1113 */
1114static void
1115handle_alice_client_message_multipart (
1116 void *cls,
1117 const struct ComputationBobCryptodataMultipartMessage *msg)
1118{
1119 struct AliceServiceSession *s = cls;
1120 uint32_t contained_count;
1121 const struct GNUNET_SCALARPRODUCT_Element *elements;
1122 struct GNUNET_SETI_Element set_elem;
1123 struct GNUNET_SCALARPRODUCT_Element *elem;
1124
1125 contained_count = ntohl (msg->element_count_contained);
1126 s->client_received_element_count += contained_count;
1127 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
1128 for (uint32_t i = 0; i < contained_count; i++)
1129 {
1130 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
1131 GNUNET_memcpy (elem,
1132 &elements[i],
1133 sizeof(struct GNUNET_SCALARPRODUCT_Element));
1134 if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
1135 s->intersected_elements,
1136 &elem->key,
1137 elem,
1138 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1139 {
1140 GNUNET_break (0);
1141 GNUNET_free (elem);
1142 continue;
1143 }
1144 set_elem.data = &elem->key;
1145 set_elem.size = sizeof(elem->key);
1146 set_elem.element_type = 0;
1147 GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL);
1148 s->used_element_count++;
1149 }
1150 GNUNET_SERVICE_client_continue (s->client);
1151 if (s->total != s->client_received_element_count)
1152 {
1153 /* more to come */
1154 return;
1155 }
1156 client_request_complete_alice (s);
1157}
1158
1159
1160/**
1161 * Handler for Alice's client request message.
1162 * Check that @a msg is well-formed.
1163 *
1164 * @param cls identification of the client
1165 * @param msg the actual message
1166 * @return #GNUNET_OK if @a msg is well-formed
1167 */
1168static int
1169check_alice_client_message (void *cls,
1170 const struct AliceComputationMessage *msg)
1171{
1172 struct AliceServiceSession *s = cls;
1173 uint16_t msize;
1174 uint32_t total_count;
1175 uint32_t contained_count;
1176
1177 if (NULL != s->intersected_elements)
1178 {
1179 /* only one concurrent session per client connection allowed,
1180 simplifies logic a lot... */
1181 GNUNET_break (0);
1182 return GNUNET_SYSERR;
1183 }
1184 msize = ntohs (msg->header.size);
1185 total_count = ntohl (msg->element_count_total);
1186 contained_count = ntohl (msg->element_count_contained);
1187 if ((0 == total_count) || (0 == contained_count) ||
1188 (msize !=
1189 (sizeof(struct AliceComputationMessage)
1190 + contained_count * sizeof(struct GNUNET_SCALARPRODUCT_Element))))
1191 {
1192 GNUNET_break_op (0);
1193 return GNUNET_SYSERR;
1194 }
1195 return GNUNET_OK;
1196}
1197
1198
1199/**
1200 * Handler for Alice's client request message.
1201 * We are doing request-initiation to compute a scalar product with a peer.
1202 *
1203 * @param cls identification of the client
1204 * @param msg the actual message
1205 */
1206static void
1207handle_alice_client_message (void *cls,
1208 const struct AliceComputationMessage *msg)
1209{
1210 struct AliceServiceSession *s = cls;
1211 uint32_t contained_count;
1212 uint32_t total_count;
1213 const struct GNUNET_SCALARPRODUCT_Element *elements;
1214 struct GNUNET_SETI_Element set_elem;
1215 struct GNUNET_SCALARPRODUCT_Element *elem;
1216
1217 total_count = ntohl (msg->element_count_total);
1218 contained_count = ntohl (msg->element_count_contained);
1219 s->peer = msg->peer;
1220 s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE;
1221 s->total = total_count;
1222 s->client_received_element_count = contained_count;
1223 s->session_id = msg->session_key;
1224 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
1225 s->intersected_elements =
1226 GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES);
1227 s->intersection_set = GNUNET_SETI_create (cfg);
1228
1229 for (uint32_t i = 0; i < contained_count; i++)
1230 {
1231 if (0 == GNUNET_ntohll (elements[i].value))
1232 continue;
1233 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
1234 GNUNET_memcpy (elem,
1235 &elements[i],
1236 sizeof(struct GNUNET_SCALARPRODUCT_Element));
1237 if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
1238 s->intersected_elements,
1239 &elem->key,
1240 elem,
1241 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1242 {
1243 /* element with same key encountered twice! */
1244 GNUNET_break (0);
1245 GNUNET_free (elem);
1246 continue;
1247 }
1248 set_elem.data = &elem->key;
1249 set_elem.size = sizeof(elem->key);
1250 set_elem.element_type = 0;
1251 GNUNET_SETI_add_element (s->intersection_set,
1252 &set_elem,
1253 NULL,
1254 NULL);
1255 s->used_element_count++;
1256 }
1257 GNUNET_SERVICE_client_continue (s->client);
1258 if (s->total != s->client_received_element_count)
1259 {
1260 /* wait for multipart msg */
1261 return;
1262 }
1263 client_request_complete_alice (s);
1264}
1265
1266
1267/**
1268 * Task run during shutdown.
1269 *
1270 * @param cls unused
1271 */
1272static void
1273shutdown_task (void *cls)
1274{
1275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down, initiating cleanup.\n");
1276 // FIXME: we have to cut our connections to CADET first!
1277 if (NULL != my_cadet)
1278 {
1279 GNUNET_CADET_disconnect (my_cadet);
1280 my_cadet = NULL;
1281 }
1282}
1283
1284
1285/**
1286 * A client connected.
1287 *
1288 * Setup the associated data structure.
1289 *
1290 * @param cls closure, NULL
1291 * @param client identification of the client
1292 * @param mq message queue to communicate with @a client
1293 * @return our `struct AliceServiceSession`
1294 */
1295static void *
1296client_connect_cb (void *cls,
1297 struct GNUNET_SERVICE_Client *client,
1298 struct GNUNET_MQ_Handle *mq)
1299{
1300 struct AliceServiceSession *s;
1301
1302 s = GNUNET_new (struct AliceServiceSession);
1303 s->client = client;
1304 s->client_mq = mq;
1305 return s;
1306}
1307
1308
1309/**
1310 * A client disconnected.
1311 *
1312 * Remove the associated session(s), release data structures
1313 * and cancel pending outgoing transmissions to the client.
1314 *
1315 * @param cls closure, NULL
1316 * @param client identification of the client
1317 * @param app_cls our `struct AliceServiceSession`
1318 */
1319static void
1320client_disconnect_cb (void *cls,
1321 struct GNUNET_SERVICE_Client *client,
1322 void *app_cls)
1323{
1324 struct AliceServiceSession *s = app_cls;
1325
1326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1327 "Client %p disconnected from us.\n",
1328 client);
1329 s->client = NULL;
1330 s->client_mq = NULL;
1331 destroy_service_session (s);
1332}
1333
1334
1335/**
1336 * Initialization of the program and message handlers
1337 *
1338 * @param cls closure
1339 * @param c configuration to use
1340 * @param service the initialized service
1341 */
1342static void
1343run (void *cls,
1344 const struct GNUNET_CONFIGURATION_Handle *c,
1345 struct GNUNET_SERVICE_Handle *service)
1346{
1347 cfg = c;
1348 /*
1349 offset has to be sufficiently small to allow computation of:
1350 m1+m2 mod n == (S + a) + (S + b) mod n,
1351 if we have more complex operations, this factor needs to be lowered */
1352 my_offset = gcry_mpi_new (GNUNET_CRYPTO_PAILLIER_BITS / 3);
1353 gcry_mpi_set_bit (my_offset, GNUNET_CRYPTO_PAILLIER_BITS / 3);
1354 GNUNET_CRYPTO_paillier_create (&my_pubkey, &my_privkey);
1355 my_cadet = GNUNET_CADET_connect (cfg);
1356 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1357 if (NULL == my_cadet)
1358 {
1359 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to CADET failed\n"));
1360 GNUNET_SCHEDULER_shutdown ();
1361 return;
1362 }
1363}
1364
1365
1366/**
1367 * Define "main" method using service macro.
1368 */
1369GNUNET_SERVICE_MAIN (
1370 "scalarproduct-alice",
1371 GNUNET_SERVICE_OPTION_NONE,
1372 &run,
1373 &client_connect_cb,
1374 &client_disconnect_cb,
1375 NULL,
1376 GNUNET_MQ_hd_var_size (alice_client_message,
1377 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE,
1378 struct AliceComputationMessage,
1379 NULL),
1380 GNUNET_MQ_hd_var_size (
1381 alice_client_message_multipart,
1382 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE,
1383 struct ComputationBobCryptodataMultipartMessage,
1384 NULL),
1385 GNUNET_MQ_handler_end ());
1386
1387
1388/* end of gnunet-service-scalarproduct_alice.c */
diff --git a/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c
new file mode 100644
index 000000000..65e732675
--- /dev/null
+++ b/src/contrib/service/scalarproduct/gnunet-service-scalarproduct_bob.c
@@ -0,0 +1,1384 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/gnunet-service-scalarproduct_bob.c
22 * @brief scalarproduct service implementation
23 * @author Christian M. Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <limits.h>
28#include <gcrypt.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_core_service.h"
31#include "gnunet_cadet_service.h"
32#include "gnunet_applications.h"
33#include "gnunet_protocols.h"
34#include "gnunet_scalarproduct_service.h"
35#include "gnunet_seti_service.h"
36#include "scalarproduct.h"
37#include "gnunet-service-scalarproduct.h"
38#include "gnunet_constants.h"
39
40#define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-bob", __VA_ARGS__)
41
42
43/**
44 * An encrypted element key-value pair.
45 */
46struct MpiElement
47{
48 /**
49 * Key used to identify matching pairs of values to multiply.
50 * Points into an existing data structure, to avoid copying
51 * and doubling memory use.
52 */
53 const struct GNUNET_HashCode *key;
54
55 /**
56 * Value represented (a).
57 */
58 gcry_mpi_t value;
59};
60
61
62/**
63 * A scalarproduct session which tracks an offer for a
64 * multiplication service by a local client.
65 */
66struct BobServiceSession
67{
68 /**
69 * (hopefully) unique transaction ID
70 */
71 struct GNUNET_HashCode session_id;
72
73 /**
74 * The client this request is related to.
75 */
76 struct GNUNET_SERVICE_Client *client;
77
78 /**
79 * Client message queue.
80 */
81 struct GNUNET_MQ_Handle *client_mq;
82
83 /**
84 * All non-0-value'd elements transmitted to us.
85 */
86 struct GNUNET_CONTAINER_MultiHashMap *intersected_elements;
87
88 /**
89 * Set of elements for which we will be conducting an intersection.
90 * The resulting elements are then used for computing the scalar product.
91 */
92 struct GNUNET_SETI_Handle *intersection_set;
93
94 /**
95 * Set of elements for which will conduction an intersection.
96 * the resulting elements are then used for computing the scalar product.
97 */
98 struct GNUNET_SETI_OperationHandle *intersection_op;
99
100 /**
101 * CADET port we are listening on.
102 */
103 struct GNUNET_CADET_Port *port;
104
105 /**
106 * a(Alice)
107 */
108 struct MpiElement *sorted_elements;
109
110 /**
111 * E(ai)(Bob) after applying the mask
112 */
113 struct GNUNET_CRYPTO_PaillierCiphertext *e_a;
114
115 /**
116 * Bob's permutation p of R
117 */
118 struct GNUNET_CRYPTO_PaillierCiphertext *r;
119
120 /**
121 * Bob's permutation q of R
122 */
123 struct GNUNET_CRYPTO_PaillierCiphertext *r_prime;
124
125 /**
126 * Bob's "s"
127 */
128 struct GNUNET_CRYPTO_PaillierCiphertext s;
129
130 /**
131 * Bob's "s'"
132 */
133 struct GNUNET_CRYPTO_PaillierCiphertext s_prime;
134
135 /**
136 * Handle for our associated incoming CADET session, or NULL
137 * if we have not gotten one yet.
138 */
139 struct CadetIncomingSession *cadet;
140
141 /**
142 * How many elements will be supplied in total from the client.
143 */
144 uint32_t total;
145
146 /**
147 * Already transferred elements (received) for multipart
148 * messages from client. Always less than @e total.
149 */
150 uint32_t client_received_element_count;
151
152 /**
153 * How many elements actually are used for the scalar product.
154 * Size of the arrays in @e r and @e r_prime. Also sometimes
155 * used as an index into the arrays during construction.
156 */
157 uint32_t used_element_count;
158
159 /**
160 * Counts the number of values received from Alice by us.
161 * Always less than @e used_element_count.
162 */
163 uint32_t cadet_received_element_count;
164
165 /**
166 * Counts the number of values transmitted from us to Alice.
167 * Always less than @e used_element_count.
168 */
169 uint32_t cadet_transmitted_element_count;
170
171 /**
172 * State of this session. In
173 * #GNUNET_SCALARPRODUCT_STATUS_ACTIVE while operation is
174 * ongoing, afterwards in #GNUNET_SCALARPRODUCT_STATUS_SUCCESS or
175 * #GNUNET_SCALARPRODUCT_STATUS_FAILURE.
176 */
177 enum GNUNET_SCALARPRODUCT_ResponseStatus status;
178
179 /**
180 * Are we already in #destroy_service_session()?
181 */
182 int in_destroy;
183
184 /**
185 * The CADET channel.
186 */
187 struct GNUNET_CADET_Channel *channel;
188
189 /**
190 * Originator's peer identity. (Only for diagnostics.)
191 */
192 struct GNUNET_PeerIdentity peer;
193
194 /**
195 * Public key of the remote service.
196 */
197 struct GNUNET_CRYPTO_PaillierPublicKey remote_pubkey;
198
199 /**
200 * The message queue for this channel.
201 */
202 struct GNUNET_MQ_Handle *cadet_mq;
203};
204
205
206/**
207 * GNUnet configuration handle
208 */
209static const struct GNUNET_CONFIGURATION_Handle *cfg;
210
211/**
212 * Service's own public key
213 */
214static struct GNUNET_CRYPTO_PaillierPublicKey my_pubkey;
215
216/**
217 * Service's own private key
218 */
219static struct GNUNET_CRYPTO_PaillierPrivateKey my_privkey;
220
221/**
222 * Service's offset for values that could possibly be negative but are plaintext for encryption.
223 */
224static gcry_mpi_t my_offset;
225
226/**
227 * Handle to the CADET service.
228 */
229static struct GNUNET_CADET_Handle *my_cadet;
230
231
232/**
233 * Callback used to free the elements in the map.
234 *
235 * @param cls NULL
236 * @param key key of the element
237 * @param value the value to free
238 */
239static int
240free_element_cb (void *cls,
241 const struct GNUNET_HashCode *key,
242 void *value)
243{
244 struct GNUNET_SCALARPRODUCT_Element *element = value;
245
246 GNUNET_free (element);
247 return GNUNET_OK;
248}
249
250
251/**
252 * Destroy session state, we are done with it.
253 *
254 * @param session the session to free elements from
255 */
256static void
257destroy_service_session (struct BobServiceSession *s)
258{
259 unsigned int i;
260
261 if (GNUNET_YES == s->in_destroy)
262 return;
263 s->in_destroy = GNUNET_YES;
264 if (NULL != s->client)
265 {
266 struct GNUNET_SERVICE_Client *c = s->client;
267
268 s->client = NULL;
269 GNUNET_SERVICE_client_drop (c);
270 }
271 if (NULL != s->intersected_elements)
272 {
273 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
274 &free_element_cb,
275 NULL);
276 GNUNET_CONTAINER_multihashmap_destroy (s->intersected_elements);
277 s->intersected_elements = NULL;
278 }
279 if (NULL != s->intersection_op)
280 {
281 GNUNET_SETI_operation_cancel (s->intersection_op);
282 s->intersection_op = NULL;
283 }
284 if (NULL != s->intersection_set)
285 {
286 GNUNET_SETI_destroy (s->intersection_set);
287 s->intersection_set = NULL;
288 }
289 if (NULL != s->e_a)
290 {
291 GNUNET_free (s->e_a);
292 s->e_a = NULL;
293 }
294 if (NULL != s->sorted_elements)
295 {
296 for (i = 0; i < s->used_element_count; i++)
297 gcry_mpi_release (s->sorted_elements[i].value);
298 GNUNET_free (s->sorted_elements);
299 s->sorted_elements = NULL;
300 }
301 if (NULL != s->r)
302 {
303 GNUNET_free (s->r);
304 s->r = NULL;
305 }
306 if (NULL != s->r_prime)
307 {
308 GNUNET_free (s->r_prime);
309 s->r_prime = NULL;
310 }
311 if (NULL != s->port)
312 {
313 GNUNET_CADET_close_port (s->port);
314 s->port = NULL;
315 }
316 if (NULL != s->channel)
317 {
318 GNUNET_CADET_channel_destroy (s->channel);
319 s->channel = NULL;
320 }
321 GNUNET_free (s);
322}
323
324
325/**
326 * Notify the client that the session has succeeded or failed. This
327 * message gets sent to Bob's client if the operation completed or
328 * Alice disconnected.
329 *
330 * @param session the associated client session to fail or succeed
331 */
332static void
333prepare_client_end_notification (struct BobServiceSession *session)
334{
335 struct ClientResponseMessage *msg;
336 struct GNUNET_MQ_Envelope *e;
337
338 if (NULL == session->client_mq)
339 return; /* no client left to be notified */
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341 "Sending session-end notification with status %d to client for session %s\n",
342 session->status,
343 GNUNET_h2s (&session->session_id));
344 e = GNUNET_MQ_msg (msg,
345 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT);
346 msg->range = 0;
347 msg->product_length = htonl (0);
348 msg->status = htonl (session->status);
349 GNUNET_MQ_send (session->client_mq,
350 e);
351}
352
353
354/**
355 * Function called whenever a channel is destroyed. Should clean up
356 * any associated state.
357 *
358 * It must NOT call #GNUNET_CADET_channel_destroy() on the channel.
359 *
360 * @param cls the `struct BobServiceSession`
361 * @param channel connection to the other end (henceforth invalid)
362 */
363static void
364cb_channel_destruction (void *cls,
365 const struct GNUNET_CADET_Channel *channel)
366{
367 struct BobServiceSession *s = cls;
368
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Peer disconnected, terminating session %s with peer %s\n",
371 GNUNET_h2s (&s->session_id),
372 GNUNET_i2s (&s->peer));
373 if (GNUNET_SCALARPRODUCT_STATUS_ACTIVE == s->status)
374 {
375 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
376 prepare_client_end_notification (s);
377 }
378 s->channel = NULL;
379 destroy_service_session (s);
380}
381
382
383/**
384 * MQ finished giving our last message to CADET, now notify
385 * the client that we are finished.
386 */
387static void
388bob_cadet_done_cb (void *cls)
389{
390 struct BobServiceSession *session = cls;
391
392 session->status = GNUNET_SCALARPRODUCT_STATUS_SUCCESS;
393 prepare_client_end_notification (session);
394}
395
396
397/**
398 * Maximum count of elements we can put into a multipart message
399 */
400#define ELEMENT_CAPACITY ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 \
401 - sizeof(struct BobCryptodataMultipartMessage)) \
402 / sizeof(struct \
403 GNUNET_CRYPTO_PaillierCiphertext))
404
405
406/**
407 * Send a multipart chunk of a service response from Bob to Alice.
408 * This element only contains the two permutations of R, R'.
409 *
410 * @param s the associated service session
411 */
412static void
413transmit_bobs_cryptodata_message_multipart (struct BobServiceSession *s)
414{
415 struct GNUNET_CRYPTO_PaillierCiphertext *payload;
416 struct BobCryptodataMultipartMessage *msg;
417 struct GNUNET_MQ_Envelope *e;
418 unsigned int i;
419 unsigned int j;
420 uint32_t todo_count;
421
422 while (s->cadet_transmitted_element_count != s->used_element_count)
423 {
424 todo_count = s->used_element_count - s->cadet_transmitted_element_count;
425 if (todo_count > ELEMENT_CAPACITY / 2)
426 todo_count = ELEMENT_CAPACITY / 2;
427
428 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
429 "Sending %u additional crypto values to Alice\n",
430 (unsigned int) todo_count);
431 e = GNUNET_MQ_msg_extra (msg,
432 todo_count * sizeof(struct
433 GNUNET_CRYPTO_PaillierCiphertext)
434 * 2,
435 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA_MULTIPART);
436 msg->contained_element_count = htonl (todo_count);
437 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
438 for (i = s->cadet_transmitted_element_count, j = 0; i <
439 s->cadet_transmitted_element_count + todo_count; i++)
440 {
441 // r[i][p] and r[i][q]
442 GNUNET_memcpy (&payload[j++],
443 &s->r[i],
444 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
445 GNUNET_memcpy (&payload[j++],
446 &s->r_prime[i],
447 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
448 }
449 s->cadet_transmitted_element_count += todo_count;
450 if (s->cadet_transmitted_element_count == s->used_element_count)
451 GNUNET_MQ_notify_sent (e,
452 &bob_cadet_done_cb,
453 s);
454 GNUNET_MQ_send (s->cadet_mq,
455 e);
456 }
457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458 "All values queued for Alice, Bob is done\n");
459}
460
461
462/**
463 * Bob generates the response message to be sent to Alice after
464 * computing the values (1), (2), S and S'.
465 *
466 * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
467 * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
468 * S: $S := E_A(sum (r_i + b_i)^2)$
469 * S': $S' := E_A(sum r_i^2)$
470 *
471 * @param s the associated requesting session with Alice
472 */
473static void
474transmit_bobs_cryptodata_message (struct BobServiceSession *s)
475{
476 struct BobCryptodataMessage *msg;
477 struct GNUNET_MQ_Envelope *e;
478 struct GNUNET_CRYPTO_PaillierCiphertext *payload;
479 unsigned int i;
480
481 s->cadet_transmitted_element_count
482 = ((GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE - 1 - sizeof(struct
483 BobCryptodataMessage))
484 / sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) / 2) - 1;
485 if (s->cadet_transmitted_element_count > s->used_element_count)
486 s->cadet_transmitted_element_count = s->used_element_count;
487
488 e = GNUNET_MQ_msg_extra (msg,
489 (2 + s->cadet_transmitted_element_count * 2)
490 * sizeof(struct GNUNET_CRYPTO_PaillierCiphertext),
491 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_BOB_CRYPTODATA);
492 msg->contained_element_count = htonl (s->cadet_transmitted_element_count);
493
494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495 "Sending %u/%u crypto values to Alice\n",
496 (unsigned int) s->cadet_transmitted_element_count,
497 (unsigned int) s->used_element_count);
498
499 payload = (struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
500 GNUNET_memcpy (&payload[0],
501 &s->s,
502 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
503 GNUNET_memcpy (&payload[1],
504 &s->s_prime,
505 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
506
507 payload = &payload[2];
508 // convert k[][]
509 for (i = 0; i < s->cadet_transmitted_element_count; i++)
510 {
511 // k[i][p] and k[i][q]
512 GNUNET_memcpy (&payload[i * 2],
513 &s->r[i],
514 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
515 GNUNET_memcpy (&payload[i * 2 + 1],
516 &s->r_prime[i],
517 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext));
518 }
519 if (s->cadet_transmitted_element_count == s->used_element_count)
520 GNUNET_MQ_notify_sent (e,
521 &bob_cadet_done_cb,
522 s);
523 GNUNET_MQ_send (s->cadet_mq,
524 e);
525 transmit_bobs_cryptodata_message_multipart (s);
526}
527
528
529#undef ELEMENT_CAPACITY
530
531
532/**
533 * Computes the square sum over a vector of a given length.
534 *
535 * @param vector the vector to compute over
536 * @param length the length of the vector
537 * @return an MPI value containing the calculated sum, never NULL
538 * TODO: code duplication with Alice!
539 */
540static gcry_mpi_t
541compute_square_sum (const gcry_mpi_t *vector,
542 uint32_t length)
543{
544 gcry_mpi_t elem;
545 gcry_mpi_t sum;
546 uint32_t i;
547
548 GNUNET_assert (NULL != (sum = gcry_mpi_new (0)));
549 GNUNET_assert (NULL != (elem = gcry_mpi_new (0)));
550 for (i = 0; i < length; i++)
551 {
552 gcry_mpi_mul (elem, vector[i], vector[i]);
553 gcry_mpi_add (sum, sum, elem);
554 }
555 gcry_mpi_release (elem);
556 return sum;
557}
558
559
560/**
561 * Compute the values
562 * (1)[]: $E_A(a_{pi(i)}) otimes E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$
563 * (2)[]: $E_A(a_{pi'(i)}) otimes E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$
564 * S: $S := E_A(sum (r_i + b_i)^2)$
565 * S': $S' := E_A(sum r_i^2)$
566 *
567 * @param session the requesting session + bob's requesting peer
568 * @return #GNUNET_OK on success
569 */
570static int
571compute_service_response (struct BobServiceSession *session)
572{
573 uint32_t i;
574 unsigned int *p;
575 unsigned int *q;
576 uint32_t count;
577 gcry_mpi_t *rand;
578 gcry_mpi_t tmp;
579 const struct MpiElement *b;
580 struct GNUNET_CRYPTO_PaillierCiphertext *a;
581 struct GNUNET_CRYPTO_PaillierCiphertext *r;
582 struct GNUNET_CRYPTO_PaillierCiphertext *r_prime;
583
584 count = session->used_element_count;
585 a = session->e_a;
586 b = session->sorted_elements;
587 q = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
588 count);
589 p = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
590 count);
591 rand = GNUNET_malloc (sizeof(gcry_mpi_t) * count);
592 for (i = 0; i < count; i++)
593 GNUNET_assert (NULL != (rand[i] = gcry_mpi_new (0)));
594 r = GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_PaillierCiphertext) * count);
595 r_prime = GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)
596 * count);
597
598 for (i = 0; i < count; i++)
599 {
600 int32_t svalue;
601
602 svalue = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
603 UINT32_MAX);
604 // long to gcry_mpi_t
605 if (svalue < 0)
606 gcry_mpi_sub_ui (rand[i],
607 rand[i],
608 -svalue);
609 else
610 rand[i] = gcry_mpi_set_ui (rand[i], svalue);
611 }
612
613 tmp = gcry_mpi_new (0);
614 // encrypt the element
615 // for the sake of readability I decided to have dedicated permutation
616 // vectors, which get rid of all the lookups in p/q.
617 // however, ap/aq are not absolutely necessary but are just abstraction
618 // Calculate Kp = E(S + a_pi) (+) E(S - r_pi - b_pi)
619 for (i = 0; i < count; i++)
620 {
621 // E(S - r_pi - b_pi)
622 gcry_mpi_sub (tmp, my_offset, rand[p[i]]);
623 gcry_mpi_sub (tmp, tmp, b[p[i]].value);
624 GNUNET_assert (2 ==
625 GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey,
626 tmp,
627 2,
628 &r[i]));
629
630 // E(S - r_pi - b_pi) * E(S + a_pi) == E(2*S + a - r - b)
631 if (GNUNET_OK !=
632 GNUNET_CRYPTO_paillier_hom_add (&session->remote_pubkey,
633 &r[i],
634 &a[p[i]],
635 &r[i]))
636 {
637 GNUNET_break_op (0);
638 goto error_cleanup;
639 }
640 }
641
642 // Calculate Kq = E(S + a_qi) (+) E(S - r_qi)
643 for (i = 0; i < count; i++)
644 {
645 // E(S - r_qi)
646 gcry_mpi_sub (tmp, my_offset, rand[q[i]]);
647 GNUNET_assert (2 ==
648 GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey,
649 tmp,
650 2,
651 &r_prime[i]));
652
653 // E(S - r_qi) * E(S + a_qi) == E(2*S + a_qi - r_qi)
654 if (GNUNET_OK !=
655 GNUNET_CRYPTO_paillier_hom_add (&session->remote_pubkey,
656 &r_prime[i],
657 &a[q[i]],
658 &r_prime[i]))
659 {
660 GNUNET_break_op (0);
661 goto error_cleanup;
662 }
663 }
664 gcry_mpi_release (tmp);
665
666 // Calculate S' = E(SUM( r_i^2 ))
667 tmp = compute_square_sum (rand, count);
668 GNUNET_assert (1 ==
669 GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey,
670 tmp,
671 1,
672 &session->s_prime));
673 gcry_mpi_release (tmp);
674
675 // Calculate S = E(SUM( (r_i + b_i)^2 ))
676 for (i = 0; i < count; i++)
677 gcry_mpi_add (rand[i], rand[i], b[i].value);
678 tmp = compute_square_sum (rand, count);
679 GNUNET_assert (1 ==
680 GNUNET_CRYPTO_paillier_encrypt (&session->remote_pubkey,
681 tmp,
682 1,
683 &session->s));
684 gcry_mpi_release (tmp);
685
686 session->r = r;
687 session->r_prime = r_prime;
688
689 for (i = 0; i < count; i++)
690 gcry_mpi_release (rand[i]);
691 GNUNET_free (session->e_a);
692 session->e_a = NULL;
693 GNUNET_free (p);
694 GNUNET_free (q);
695 GNUNET_free (rand);
696 return GNUNET_OK;
697
698error_cleanup:
699 GNUNET_free (r);
700 GNUNET_free (r_prime);
701 gcry_mpi_release (tmp);
702 GNUNET_free (p);
703 GNUNET_free (q);
704 for (i = 0; i < count; i++)
705 gcry_mpi_release (rand[i]);
706 GNUNET_free (rand);
707 return GNUNET_SYSERR;
708}
709
710
711/**
712 * Iterator to copy over messages from the hash map
713 * into an array for sorting.
714 *
715 * @param cls the `struct BobServiceSession *`
716 * @param key the key (unused)
717 * @param value the `struct GNUNET_SCALARPRODUCT_Element *`
718 * TODO: code duplication with Alice!
719 */
720static int
721copy_element_cb (void *cls,
722 const struct GNUNET_HashCode *key,
723 void *value)
724{
725 struct BobServiceSession *s = cls;
726 struct GNUNET_SCALARPRODUCT_Element *e = value;
727 gcry_mpi_t mval;
728 int64_t val;
729
730 mval = gcry_mpi_new (0);
731 val = (int64_t) GNUNET_ntohll (e->value);
732 if (0 > val)
733 gcry_mpi_sub_ui (mval, mval, -val);
734 else
735 gcry_mpi_add_ui (mval, mval, val);
736 s->sorted_elements [s->used_element_count].value = mval;
737 s->sorted_elements [s->used_element_count].key = &e->key;
738 s->used_element_count++;
739 return GNUNET_OK;
740}
741
742
743/**
744 * Compare two `struct MpiValue`s by key for sorting.
745 *
746 * @param a pointer to first `struct MpiValue *`
747 * @param b pointer to first `struct MpiValue *`
748 * @return -1 for a < b, 0 for a=b, 1 for a > b.
749 * TODO: code duplication with Alice!
750 */
751static int
752element_cmp (const void *a,
753 const void *b)
754{
755 const struct MpiElement *ma = a;
756 const struct MpiElement *mb = b;
757
758 return GNUNET_CRYPTO_hash_cmp (ma->key,
759 mb->key);
760}
761
762
763/**
764 * Intersection operation and receiving data via CADET from
765 * Alice are both done, compute and transmit our reply via
766 * CADET.
767 *
768 * @param s session to transmit reply for.
769 */
770static void
771transmit_cryptographic_reply (struct BobServiceSession *s)
772{
773 struct GNUNET_CADET_Channel *channel;
774
775 /* TODO: code duplication with Alice! */
776 LOG (GNUNET_ERROR_TYPE_DEBUG,
777 "Received everything, building reply for Alice\n");
778 s->sorted_elements
779 = GNUNET_malloc (GNUNET_CONTAINER_multihashmap_size (
780 s->intersected_elements)
781 * sizeof(struct MpiElement));
782 s->used_element_count = 0;
783 GNUNET_CONTAINER_multihashmap_iterate (s->intersected_elements,
784 &copy_element_cb,
785 s);
786 qsort (s->sorted_elements,
787 s->used_element_count,
788 sizeof(struct MpiElement),
789 &element_cmp);
790 if (GNUNET_OK !=
791 compute_service_response (s))
792 {
793 channel = s->channel;
794 s->channel = NULL;
795 GNUNET_CADET_channel_destroy (channel);
796 return;
797 }
798 transmit_bobs_cryptodata_message (s);
799}
800
801
802/**
803 * Check a multipart-chunk of a request from another service to
804 * calculate a scalarproduct with us.
805 *
806 * @param cls the `struct BobServiceSession *`
807 * @param msg the actual message
808 * @return #GNUNET_OK to keep the connection open,
809 * #GNUNET_SYSERR to close it (signal serious error)
810 */
811static int
812check_alices_cryptodata_message (void *cls,
813 const struct AliceCryptodataMessage *msg)
814{
815 struct BobServiceSession *s = cls;
816 uint32_t contained_elements;
817 size_t msg_length;
818 uint16_t msize;
819 unsigned int max;
820
821 msize = ntohs (msg->header.size);
822 contained_elements = ntohl (msg->contained_element_count);
823 /* Our intersection may still be ongoing, but this is nevertheless
824 an upper bound on the required array size */
825 max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements);
826 msg_length = sizeof(struct AliceCryptodataMessage)
827 + contained_elements * sizeof(struct
828 GNUNET_CRYPTO_PaillierCiphertext);
829 if ((msize != msg_length) ||
830 (0 == contained_elements) ||
831 (contained_elements > UINT16_MAX) ||
832 (max < contained_elements + s->cadet_received_element_count))
833 {
834 GNUNET_break_op (0);
835 return GNUNET_SYSERR;
836 }
837 return GNUNET_OK;
838}
839
840
841/**
842 * Handle a multipart-chunk of a request from another service to
843 * calculate a scalarproduct with us.
844 *
845 * @param cls the `struct BobServiceSession *`
846 * @param msg the actual message
847 */
848static void
849handle_alices_cryptodata_message (void *cls,
850 const struct AliceCryptodataMessage *msg)
851{
852 struct BobServiceSession *s = cls;
853 const struct GNUNET_CRYPTO_PaillierCiphertext *payload;
854 uint32_t contained_elements;
855 unsigned int max;
856
857 contained_elements = ntohl (msg->contained_element_count);
858 /* Our intersection may still be ongoing, but this is nevertheless
859 an upper bound on the required array size */
860 max = GNUNET_CONTAINER_multihashmap_size (s->intersected_elements);
861 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
862 "Received %u crypto values from Alice\n",
863 (unsigned int) contained_elements);
864
865 payload = (const struct GNUNET_CRYPTO_PaillierCiphertext *) &msg[1];
866 if (NULL == s->e_a)
867 s->e_a = GNUNET_new_array (max,
868 struct GNUNET_CRYPTO_PaillierCiphertext);
869 GNUNET_memcpy (&s->e_a[s->cadet_received_element_count],
870 payload,
871 sizeof(struct GNUNET_CRYPTO_PaillierCiphertext)
872 * contained_elements);
873 s->cadet_received_element_count += contained_elements;
874
875 if ((s->cadet_received_element_count == max) &&
876 (NULL == s->intersection_op))
877 {
878 /* intersection has finished also on our side, and
879 we got the full set, so we can proceed with the
880 CADET response(s) */
881 transmit_cryptographic_reply (s);
882 }
883 GNUNET_CADET_receive_done (s->channel);
884}
885
886
887/**
888 * Callback for set operation results. Called for each element
889 * that needs to be removed from the result set.
890 *
891 * @param cls closure with the `struct BobServiceSession`
892 * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK
893 * @param current_size current set size
894 * @param status what has happened with the set intersection?
895 */
896static void
897cb_intersection_element_removed (void *cls,
898 const struct GNUNET_SETI_Element *element,
899 uint64_t current_size,
900 enum GNUNET_SETI_Status status)
901{
902 struct BobServiceSession *s = cls;
903 struct GNUNET_SCALARPRODUCT_Element *se;
904
905 switch (status)
906 {
907 case GNUNET_SETI_STATUS_DEL_LOCAL:
908 /* this element has been removed from the set */
909 se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements,
910 element->data);
911 GNUNET_assert (NULL != se);
912 LOG (GNUNET_ERROR_TYPE_DEBUG,
913 "Removed element with key %s and value %lld\n",
914 GNUNET_h2s (&se->key),
915 (long long) GNUNET_ntohll (se->value));
916 GNUNET_assert (GNUNET_YES ==
917 GNUNET_CONTAINER_multihashmap_remove (
918 s->intersected_elements,
919 element->data,
920 se));
921 GNUNET_free (se);
922 return;
923 case GNUNET_SETI_STATUS_DONE:
924 s->intersection_op = NULL;
925 GNUNET_break (NULL == s->intersection_set);
926 GNUNET_CADET_receive_done (s->channel);
927 LOG (GNUNET_ERROR_TYPE_DEBUG,
928 "Finished intersection, %d items remain\n",
929 GNUNET_CONTAINER_multihashmap_size (s->intersected_elements));
930 if (s->client_received_element_count ==
931 GNUNET_CONTAINER_multihashmap_size (s->intersected_elements))
932 {
933 /* CADET transmission from Alice is also already done,
934 start with our own reply */
935 transmit_cryptographic_reply (s);
936 }
937 return;
938 case GNUNET_SETI_STATUS_FAILURE:
939 /* unhandled status code */
940 LOG (GNUNET_ERROR_TYPE_DEBUG,
941 "Set intersection failed!\n");
942 s->intersection_op = NULL;
943 if (NULL != s->intersection_set)
944 {
945 GNUNET_SETI_destroy (s->intersection_set);
946 s->intersection_set = NULL;
947 }
948 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
949 prepare_client_end_notification (s);
950 return;
951 default:
952 GNUNET_break (0);
953 return;
954 }
955}
956
957
958/**
959 * We've paired up a client session with an incoming CADET request.
960 * Initiate set intersection work.
961 *
962 * @param s client session to start intersection for
963 */
964static void
965start_intersection (struct BobServiceSession *s)
966{
967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
968 "Got session with key %s and %u elements, starting intersection.\n",
969 GNUNET_h2s (&s->session_id),
970 (unsigned int) s->total);
971
972 s->intersection_op
973 = GNUNET_SETI_prepare (&s->peer,
974 &s->session_id,
975 NULL,
976 (struct GNUNET_SETI_Option[]) { { 0 } },
977 &cb_intersection_element_removed,
978 s);
979 if (GNUNET_OK !=
980 GNUNET_SETI_commit (s->intersection_op,
981 s->intersection_set))
982 {
983 GNUNET_break (0);
984 s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE;
985 prepare_client_end_notification (s);
986 return;
987 }
988 GNUNET_SETI_destroy (s->intersection_set);
989 s->intersection_set = NULL;
990}
991
992
993/**
994 * Handle a request from Alice to calculate a scalarproduct with us (Bob).
995 *
996 * @param cls the `struct BobServiceSession *`
997 * @param msg the actual message
998 */
999static void
1000handle_alices_computation_request (void *cls,
1001 const struct ServiceRequestMessage *msg)
1002{
1003 struct BobServiceSession *s = cls;
1004
1005 s->session_id = msg->session_id; // ??
1006 s->remote_pubkey = msg->public_key;
1007 if (s->client_received_element_count == s->total)
1008 start_intersection (s);
1009}
1010
1011
1012/**
1013 * Function called for inbound channels on Bob's end. Does some
1014 * preliminary initialization, more happens after we get Alice's first
1015 * message.
1016 *
1017 * @param cls closure with the `struct BobServiceSession`
1018 * @param channel new handle to the channel
1019 * @param initiator peer that started the channel
1020 * @return session associated with the channel
1021 */
1022static void *
1023cb_channel_incoming (void *cls,
1024 struct GNUNET_CADET_Channel *channel,
1025 const struct GNUNET_PeerIdentity *initiator)
1026{
1027 struct BobServiceSession *s = cls;
1028
1029 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1030 "New incoming channel from peer %s.\n",
1031 GNUNET_i2s (initiator));
1032 GNUNET_CADET_close_port (s->port);
1033 s->port = NULL;
1034 s->channel = channel;
1035 s->peer = *initiator;
1036 s->cadet_mq = GNUNET_CADET_get_mq (s->channel);
1037 return s;
1038}
1039
1040
1041/**
1042 * We're receiving additional set data. Check it is well-formed.
1043 *
1044 * @param cls identification of the client
1045 * @param msg the actual message
1046 * @return #GNUNET_OK if @a msg is well-formed
1047 */
1048static int
1049check_bob_client_message_multipart (void *cls,
1050 const struct
1051 ComputationBobCryptodataMultipartMessage *
1052 msg)
1053{
1054 struct BobServiceSession *s = cls;
1055 uint32_t contained_count;
1056 uint16_t msize;
1057
1058 msize = ntohs (msg->header.size);
1059 contained_count = ntohl (msg->element_count_contained);
1060 if ((msize != (sizeof(struct ComputationBobCryptodataMultipartMessage)
1061 + contained_count * sizeof(struct
1062 GNUNET_SCALARPRODUCT_Element))) ||
1063 (0 == contained_count) ||
1064 (UINT16_MAX < contained_count) ||
1065 (s->total == s->client_received_element_count) ||
1066 (s->total < s->client_received_element_count + contained_count))
1067 {
1068 GNUNET_break (0);
1069 return GNUNET_SYSERR;
1070 }
1071 return GNUNET_OK;
1072}
1073
1074
1075/**
1076 * We're receiving additional set data. Add it to our
1077 * set and if we are done, initiate the transaction.
1078 *
1079 * @param cls identification of the client
1080 * @param msg the actual message
1081 */
1082static void
1083handle_bob_client_message_multipart (void *cls,
1084 const struct
1085 ComputationBobCryptodataMultipartMessage *
1086 msg)
1087{
1088 struct BobServiceSession *s = cls;
1089 uint32_t contained_count;
1090 const struct GNUNET_SCALARPRODUCT_Element *elements;
1091 struct GNUNET_SETI_Element set_elem;
1092 struct GNUNET_SCALARPRODUCT_Element *elem;
1093
1094 contained_count = ntohl (msg->element_count_contained);
1095 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
1096 for (uint32_t i = 0; i < contained_count; i++)
1097 {
1098 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
1099 GNUNET_memcpy (elem,
1100 &elements[i],
1101 sizeof(struct GNUNET_SCALARPRODUCT_Element));
1102 if (GNUNET_SYSERR ==
1103 GNUNET_CONTAINER_multihashmap_put (s->intersected_elements,
1104 &elem->key,
1105 elem,
1106 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1107 {
1108 GNUNET_break (0);
1109 GNUNET_free (elem);
1110 continue;
1111 }
1112 set_elem.data = &elem->key;
1113 set_elem.size = sizeof(elem->key);
1114 set_elem.element_type = 0;
1115 GNUNET_SETI_add_element (s->intersection_set,
1116 &set_elem,
1117 NULL, NULL);
1118 }
1119 s->client_received_element_count += contained_count;
1120 GNUNET_SERVICE_client_continue (s->client);
1121 if (s->total != s->client_received_element_count)
1122 {
1123 /* more to come */
1124 return;
1125 }
1126 if (NULL == s->channel)
1127 {
1128 /* no Alice waiting for this request, wait for Alice */
1129 return;
1130 }
1131 start_intersection (s);
1132}
1133
1134
1135/**
1136 * Handler for Bob's a client request message. Check @a msg is
1137 * well-formed.
1138 *
1139 * @param cls identification of the client
1140 * @param msg the actual message
1141 * @return #GNUNET_OK if @a msg is well-formed
1142 */
1143static int
1144check_bob_client_message (void *cls,
1145 const struct BobComputationMessage *msg)
1146{
1147 struct BobServiceSession *s = cls;
1148 uint32_t contained_count;
1149 uint32_t total_count;
1150 uint16_t msize;
1151
1152 if (GNUNET_SCALARPRODUCT_STATUS_INIT != s->status)
1153 {
1154 GNUNET_break (0);
1155 return GNUNET_SYSERR;
1156 }
1157 msize = ntohs (msg->header.size);
1158 total_count = ntohl (msg->element_count_total);
1159 contained_count = ntohl (msg->element_count_contained);
1160 if ((0 == total_count) ||
1161 (0 == contained_count) ||
1162 (UINT16_MAX < contained_count) ||
1163 (msize != (sizeof(struct BobComputationMessage)
1164 + contained_count * sizeof(struct
1165 GNUNET_SCALARPRODUCT_Element))))
1166 {
1167 GNUNET_break_op (0);
1168 return GNUNET_SYSERR;
1169 }
1170 return GNUNET_OK;
1171}
1172
1173
1174/**
1175 * Handler for Bob's a client request message. Bob is in the response
1176 * role, keep the values + session and waiting for a matching session
1177 * or process a waiting request from Alice.
1178 *
1179 * @param cls identification of the client
1180 * @param msg the actual message
1181 */
1182static void
1183handle_bob_client_message (void *cls,
1184 const struct BobComputationMessage *msg)
1185{
1186 struct BobServiceSession *s = cls;
1187 struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
1188 GNUNET_MQ_hd_fixed_size (alices_computation_request,
1189 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_SESSION_INITIALIZATION,
1190 struct ServiceRequestMessage,
1191 NULL),
1192 GNUNET_MQ_hd_var_size (alices_cryptodata_message,
1193 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_ALICE_CRYPTODATA,
1194 struct AliceCryptodataMessage,
1195 NULL),
1196 GNUNET_MQ_handler_end ()
1197 };
1198 uint32_t contained_count;
1199 uint32_t total_count;
1200 const struct GNUNET_SCALARPRODUCT_Element *elements;
1201 struct GNUNET_SETI_Element set_elem;
1202 struct GNUNET_SCALARPRODUCT_Element *elem;
1203
1204 total_count = ntohl (msg->element_count_total);
1205 contained_count = ntohl (msg->element_count_contained);
1206
1207 s->status = GNUNET_SCALARPRODUCT_STATUS_ACTIVE;
1208 s->total = total_count;
1209 s->client_received_element_count = contained_count;
1210 s->session_id = msg->session_key;
1211 elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1];
1212 s->intersected_elements
1213 = GNUNET_CONTAINER_multihashmap_create (s->total,
1214 GNUNET_YES);
1215 s->intersection_set = GNUNET_SETI_create (cfg);
1216 for (uint32_t i = 0; i < contained_count; i++)
1217 {
1218 if (0 == GNUNET_ntohll (elements[i].value))
1219 continue;
1220 elem = GNUNET_new (struct GNUNET_SCALARPRODUCT_Element);
1221 GNUNET_memcpy (elem,
1222 &elements[i],
1223 sizeof(struct GNUNET_SCALARPRODUCT_Element));
1224 if (GNUNET_SYSERR ==
1225 GNUNET_CONTAINER_multihashmap_put (s->intersected_elements,
1226 &elem->key,
1227 elem,
1228 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1229 {
1230 GNUNET_break (0);
1231 GNUNET_free (elem);
1232 continue;
1233 }
1234 set_elem.data = &elem->key;
1235 set_elem.size = sizeof(elem->key);
1236 set_elem.element_type = 0;
1237 GNUNET_SETI_add_element (s->intersection_set,
1238 &set_elem,
1239 NULL, NULL);
1240 s->used_element_count++;
1241 }
1242 GNUNET_SERVICE_client_continue (s->client);
1243 /* We're ready, open the port */
1244 s->port = GNUNET_CADET_open_port (my_cadet,
1245 &msg->session_key,
1246 &cb_channel_incoming,
1247 s,
1248 NULL,
1249 &cb_channel_destruction,
1250 cadet_handlers);
1251 if (NULL == s->port)
1252 {
1253 GNUNET_break (0);
1254 GNUNET_SERVICE_client_drop (s->client);
1255 return;
1256 }
1257}
1258
1259
1260/**
1261 * Task run during shutdown.
1262 *
1263 * @param cls unused
1264 */
1265static void
1266shutdown_task (void *cls)
1267{
1268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269 "Shutting down, initiating cleanup.\n");
1270 // FIXME: we have to cut our connections to CADET first!
1271 if (NULL != my_cadet)
1272 {
1273 GNUNET_CADET_disconnect (my_cadet);
1274 my_cadet = NULL;
1275 }
1276}
1277
1278
1279/**
1280 * A client connected.
1281 *
1282 * Setup the associated data structure.
1283 *
1284 * @param cls closure, NULL
1285 * @param client identification of the client
1286 * @param mq message queue to communicate with @a client
1287 * @return our `struct BobServiceSession`
1288 */
1289static void *
1290client_connect_cb (void *cls,
1291 struct GNUNET_SERVICE_Client *client,
1292 struct GNUNET_MQ_Handle *mq)
1293{
1294 struct BobServiceSession *s;
1295
1296 s = GNUNET_new (struct BobServiceSession);
1297 s->client = client;
1298 s->client_mq = mq;
1299 return s;
1300}
1301
1302
1303/**
1304 * A client disconnected.
1305 *
1306 * Remove the associated session(s), release data structures
1307 * and cancel pending outgoing transmissions to the client.
1308 *
1309 * @param cls closure, NULL
1310 * @param client identification of the client
1311 * @param app_cls our `struct BobServiceSession`
1312 */
1313static void
1314client_disconnect_cb (void *cls,
1315 struct GNUNET_SERVICE_Client *client,
1316 void *app_cls)
1317{
1318 struct BobServiceSession *s = app_cls;
1319
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1321 "Client disconnected from us.\n");
1322 s->client = NULL;
1323 destroy_service_session (s);
1324}
1325
1326
1327/**
1328 * Initialization of the program and message handlers
1329 *
1330 * @param cls closure
1331 * @param c configuration to use
1332 * @param service the initialized service
1333 */
1334static void
1335run (void *cls,
1336 const struct GNUNET_CONFIGURATION_Handle *c,
1337 struct GNUNET_SERVICE_Handle *service)
1338{
1339 cfg = c;
1340 /*
1341 offset has to be sufficiently small to allow computation of:
1342 m1+m2 mod n == (S + a) + (S + b) mod n,
1343 if we have more complex operations, this factor needs to be lowered */
1344 my_offset = gcry_mpi_new (GNUNET_CRYPTO_PAILLIER_BITS / 3);
1345 gcry_mpi_set_bit (my_offset,
1346 GNUNET_CRYPTO_PAILLIER_BITS / 3);
1347
1348 GNUNET_CRYPTO_paillier_create (&my_pubkey,
1349 &my_privkey);
1350 my_cadet = GNUNET_CADET_connect (cfg);
1351 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1352 NULL);
1353 if (NULL == my_cadet)
1354 {
1355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1356 _ ("Connect to CADET failed\n"));
1357 GNUNET_SCHEDULER_shutdown ();
1358 return;
1359 }
1360}
1361
1362
1363/**
1364 * Define "main" method using service macro.
1365 */
1366GNUNET_SERVICE_MAIN
1367 ("scalarproduct-bob",
1368 GNUNET_SERVICE_OPTION_NONE,
1369 &run,
1370 &client_connect_cb,
1371 &client_disconnect_cb,
1372 NULL,
1373 GNUNET_MQ_hd_var_size (bob_client_message,
1374 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB,
1375 struct BobComputationMessage,
1376 NULL),
1377 GNUNET_MQ_hd_var_size (bob_client_message_multipart,
1378 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB,
1379 struct ComputationBobCryptodataMultipartMessage,
1380 NULL),
1381 GNUNET_MQ_handler_end ());
1382
1383
1384/* end of gnunet-service-scalarproduct_bob.c */
diff --git a/src/contrib/service/scalarproduct/meson.build b/src/contrib/service/scalarproduct/meson.build
new file mode 100644
index 000000000..a3d24cfa9
--- /dev/null
+++ b/src/contrib/service/scalarproduct/meson.build
@@ -0,0 +1,101 @@
1libgnunetscalarproduct_src = ['scalarproduct_api.c']
2
3gnunetservicescalarproducta_src = ['gnunet-service-scalarproduct_alice.c']
4gnunetservicescalarproductb_src = ['gnunet-service-scalarproduct_bob.c']
5gnunetservicescalarproductecca_src = ['gnunet-service-scalarproduct-ecc_alice.c']
6gnunetservicescalarproducteccb_src = ['gnunet-service-scalarproduct-ecc_bob.c']
7
8configure_file(input : 'scalarproduct.conf.in',
9 output : 'scalarproduct.conf',
10 configuration : cdata,
11 install: true,
12 install_dir: pkgcfgdir)
13
14
15if get_option('monolith')
16 foreach p : libgnunetscalarproduct_src + gnunetservicescalarproducta_src + gnunetservicescalarproductb_src + gnunetservicescalarproductecca_src + gnunetservicescalarproducteccb_src
17 gnunet_src += 'scalarproduct/' + p
18 endforeach
19endif
20
21libgnunetscalarproduct = library('gnunetscalarproduct',
22 libgnunetscalarproduct_src,
23 soversion: '0',
24 version: '0.0.0',
25 dependencies: [libgnunetutil_dep,
26 gcrypt_dep],
27 include_directories: [incdir, configuration_inc],
28 install: true,
29 install_dir: get_option('libdir'))
30pkg.generate(libgnunetscalarproduct, url: 'https://www.gnunet.org',
31 description : 'Provides API for accessing the scalarproduct service')
32libgnunetscalarproduct_dep = declare_dependency(link_with : libgnunetscalarproduct)
33
34executable ('gnunet-scalarproduct',
35 ['gnunet-scalarproduct.c'],
36 dependencies: [libgnunetscalarproduct_dep,
37 libgnunetutil_dep,
38 gcrypt_dep,
39 libgnunetstatistics_dep,
40 libgnunetcore_dep,
41 libgnunetcadet_dep,
42 libgnunetblock_dep],
43 include_directories: [incdir, configuration_inc],
44 install: true,
45 install_dir: get_option('bindir'))
46
47executable ('gnunet-service-scalarproduct-alice',
48 gnunetservicescalarproducta_src,
49 dependencies: [libgnunetscalarproduct_dep,
50 libgnunetutil_dep,
51 gcrypt_dep,
52 libgnunetseti_dep,
53 libgnunetstatistics_dep,
54 libgnunetcore_dep,
55 libgnunetcadet_dep,
56 libgnunetblock_dep],
57 include_directories: [incdir, configuration_inc],
58 install: true,
59 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
60executable ('gnunet-service-scalarproduct-bob',
61 gnunetservicescalarproductb_src,
62 dependencies: [libgnunetscalarproduct_dep,
63 libgnunetutil_dep,
64 gcrypt_dep,
65 libgnunetseti_dep,
66 libgnunetstatistics_dep,
67 libgnunetcore_dep,
68 libgnunetcadet_dep,
69 libgnunetblock_dep],
70 include_directories: [incdir, configuration_inc],
71 install: true,
72 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
73executable ('gnunet-service-scalarproduct-ecc-alice',
74 gnunetservicescalarproductecca_src,
75 dependencies: [libgnunetscalarproduct_dep,
76 libgnunetutil_dep,
77 libgnunetstatistics_dep,
78 libgnunetcore_dep,
79 gcrypt_dep,
80 sodium_dep,
81 libgnunetseti_dep,
82 libgnunetcadet_dep,
83 libgnunetblock_dep],
84 include_directories: [incdir, configuration_inc],
85 install: true,
86 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
87executable ('gnunet-service-scalarproduct-ecc-bob',
88 gnunetservicescalarproducteccb_src,
89 dependencies: [libgnunetscalarproduct_dep,
90 libgnunetutil_dep,
91 libgnunetstatistics_dep,
92 libgnunetcore_dep,
93 gcrypt_dep,
94 sodium_dep,
95 libgnunetseti_dep,
96 libgnunetcadet_dep,
97 libgnunetblock_dep],
98 include_directories: [incdir, configuration_inc],
99 install: true,
100 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
101
diff --git a/src/contrib/service/scalarproduct/perf_scalarproduct.sh b/src/contrib/service/scalarproduct/perf_scalarproduct.sh
new file mode 100755
index 000000000..b15465c9a
--- /dev/null
+++ b/src/contrib/service/scalarproduct/perf_scalarproduct.sh
@@ -0,0 +1,73 @@
1#!/bin/bash
2# Computes a simple scalar product, with configurable vector size.
3#
4# Some results (wall-clock for Alice+Bob, single-core, i7, libgcrypt):
5# SIZE 2048-H(s) 2048-O(s) 1024-O(s) ECC-2^20-H(s) ECC-2^28-H(s)
6# 25 10 14 3 2 29
7# 50 17 21 5 2 29
8# 100 32 39 7 2 29
9# 200 77 13 3 30
10# 400 149 23 OOR 31
11# 800 304 32 OOR 33
12
13# Bandwidth (including set intersection):
14# RSA-1024 RSA-2048 ECC
15# 800: 629 kb 1234 kb 65 kb
16#
17# LIBSODIUM, AMD Threadripper 1950:
18#
19# SIZE 2048-O(s) 1024-O(s) ECC-2^20-H(s) ECC-2^28-H(s)
20# 25 4.3 0.7 0.129 4.233
21# 50 7.7 1.2 0.143 4.267
22# 100 10.3 2.4 0.163 4.282
23# 200 19.8 3.0 0.192 4.326
24# 400 35.9 6.0 0.253 4.358
25# 800 73.7 12.6 0.379 4.533
26
27#
28#
29# Configure benchmark size:
30SIZE=800
31#
32# Construct input vectors:
33INPUTALICE="-k CCC -e '"
34INPUTBOB="-k CCC -e '"
35for X in `seq 1 $SIZE`
36do
37 INPUTALICE="${INPUTALICE}A${X},$X;"
38 INPUTBOB="${INPUTBOB}A${X},$X;"
39done
40INPUTALICE="${INPUTALICE}BC,-20000;RO,1000;FL,100;LOL,24;'"
41INPUTBOB="${INPUTBOB}AB,10;RO,3;FL,3;LOL,-1;'"
42
43# necessary to make the testing prefix deterministic, so we can access the config files
44PREFIX=/tmp/test-scalarproduct`date +%H%M%S`
45
46# where can we find the peers config files?
47CFGALICE="-c $PREFIX/0/config"
48CFGBOB="-c $PREFIX/1/config"
49
50# launch two peers in line topology non-interactively
51#
52# interactive mode would terminate the test immediately
53# because the rest of the script is already in stdin,
54# thus redirecting stdin does not suffice)
55#GNUNET_FORCE_LOG=';;;;ERROR'
56#GNUNET_FORCE_LOG='scalarproduct*;;;;DEBUG/cadet-api*;;;;DEBUG'
57GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 &
58PID=$!
59# sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe
60echo "Waiting for peers to start..."
61sleep 5
62# get Bob's peer ID, necessary for Alice
63PEERIDBOB=`gnunet-peerinfo -qs $CFGBOB`
64
65echo "Running problem of size $SIZE"
66gnunet-scalarproduct $CFGBOB $INPUTBOB &
67time RESULT=`gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB`
68gnunet-statistics $CFGALICE -s core | grep "bytes encrypted"
69gnunet-statistics $CFGBOB -s core | grep "bytes encrypted"
70
71echo "Terminating testbed..."
72# terminate the testbed
73kill $PID
diff --git a/src/contrib/service/scalarproduct/scalarproduct.conf.in b/src/contrib/service/scalarproduct/scalarproduct.conf.in
new file mode 100644
index 000000000..e2286b076
--- /dev/null
+++ b/src/contrib/service/scalarproduct/scalarproduct.conf.in
@@ -0,0 +1,27 @@
1[scalarproduct-alice]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-scalarproduct-ecc-alice
4UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-scalarproduct-alice.sock
5@UNIXONLY@ PORT = 2117
6#ACCEPT_FROM = 127.0.0.1;
7#ACCEPT_FROM6 = ::1;
8UNIX_MATCH_UID = NO
9UNIX_MATCH_GID = YES
10#OPTIONS = -L DEBUG
11#PREFIX = valgrind
12
13
14[scalarproduct-bob]
15START_ON_DEMAND = @START_ON_DEMAND@
16HOSTNAME = localhost
17BINARY = gnunet-service-scalarproduct-ecc-bob
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-scalarproduct-bob.sock
19@UNIXONLY@ PORT = 2118
20
21#ACCEPT_FROM = 127.0.0.1;
22#ACCEPT_FROM6 = ::1;
23UNIX_MATCH_UID = NO
24UNIX_MATCH_GID = YES
25#OPTIONS = -L DEBUG
26
27#PREFIX = valgrind
diff --git a/src/contrib/service/scalarproduct/scalarproduct.h b/src/contrib/service/scalarproduct/scalarproduct.h
new file mode 100644
index 000000000..f2311cda0
--- /dev/null
+++ b/src/contrib/service/scalarproduct/scalarproduct.h
@@ -0,0 +1,177 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2012, 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct.h
22 * @brief Scalar Product API Message Types
23 * @author Christian M. Fuchs
24 */
25#ifndef SCALARPRODUCT_H
26#define SCALARPRODUCT_H
27
28GNUNET_NETWORK_STRUCT_BEGIN
29
30/**
31 * Log an error message at log-level 'level' that indicates
32 * a failure of the command 'cmd' with the message given
33 * by gcry_strerror(rc).
34 */
35#define LOG_GCRY(level, cmd, rc) do { LOG (level, _ ( \
36 "`%s' failed at %s:%d with error: %s\n"), \
37 cmd, __FILE__, __LINE__, \
38 gcry_strerror (rc)); } while (0)
39
40
41/**
42 * Message type passed from client to service
43 * to initiate a request or responder role
44 */
45struct AliceComputationMessage
46{
47 /**
48 * GNUNET message header with type
49 * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE
50 */
51 struct GNUNET_MessageHeader header;
52
53 /**
54 * how many elements the vector in payload contains
55 */
56 uint32_t element_count_total GNUNET_PACKED;
57
58 /**
59 * contained elements the vector in payload contains
60 */
61 uint32_t element_count_contained GNUNET_PACKED;
62
63 /**
64 * Always zero.
65 */
66 uint32_t reserved GNUNET_PACKED;
67
68 /**
69 * the transaction/session key used to identify a session
70 */
71 struct GNUNET_HashCode session_key;
72
73 /**
74 * the identity of a remote peer we want to communicate with
75 */
76 struct GNUNET_PeerIdentity peer;
77
78 /**
79 * followed by struct GNUNET_SCALARPRODUCT_Element[]
80 */
81};
82
83
84/**
85 * Message type passed from client to service
86 * to initiate a request or responder role
87 */
88struct BobComputationMessage
89{
90 /**
91 * GNUNET message header with type
92 * #GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB
93 */
94 struct GNUNET_MessageHeader header;
95
96 /**
97 * how many elements the vector in payload contains
98 */
99 uint32_t element_count_total GNUNET_PACKED;
100
101 /**
102 * contained elements the vector in payload contains
103 */
104 uint32_t element_count_contained GNUNET_PACKED;
105
106 /**
107 * Always zero.
108 */
109 uint32_t reserved GNUNET_PACKED;
110
111 /**
112 * the transaction/session key used to identify a session
113 */
114 struct GNUNET_HashCode session_key;
115
116 /**
117 * followed by struct GNUNET_SCALARPRODUCT_Element[]
118 */
119};
120
121
122/**
123 * multipart messages following `struct ComputationMessage`
124 */
125struct ComputationBobCryptodataMultipartMessage
126{
127 /**
128 * GNUNET message header
129 */
130 struct GNUNET_MessageHeader header;
131
132 /**
133 * contained elements the vector in payload contains
134 */
135 uint32_t element_count_contained GNUNET_PACKED;
136
137 /**
138 * followed by struct GNUNET_SCALARPRODUCT_Element[]
139 */
140};
141
142
143/**
144 * Message type passed from service client
145 * to finalize a session as requester or responder
146 */
147struct ClientResponseMessage
148{
149 /**
150 * GNUNET message header
151 */
152 struct GNUNET_MessageHeader header;
153
154 /**
155 * 0 if no product attached
156 */
157 uint32_t product_length GNUNET_PACKED;
158
159 /**
160 * Status information about the outcome of this session,
161 * An `enum GNUNET_SCALARPRODUCT_ResponseStatus` (in NBO).
162 */
163 uint32_t status GNUNET_PACKED;
164
165 /**
166 * Workaround for libgcrypt: -1 if negative, 0 if zero, else 1
167 */
168 int32_t range GNUNET_PACKED;
169
170 /**
171 * followed by product of length product_length (or nothing)
172 */
173};
174
175GNUNET_NETWORK_STRUCT_END
176
177#endif /* SCALARPRODUCT_H */
diff --git a/src/contrib/service/scalarproduct/scalarproduct_api.c b/src/contrib/service/scalarproduct/scalarproduct_api.c
new file mode 100644
index 000000000..8c667a72e
--- /dev/null
+++ b/src/contrib/service/scalarproduct/scalarproduct_api.c
@@ -0,0 +1,468 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file scalarproduct/scalarproduct_api.c
22 * @brief API for the scalarproduct
23 * @author Christian Fuchs
24 * @author Gaurav Kukreja
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_statistics_service.h"
30#include "gnunet_scalarproduct_service.h"
31#include "gnunet_protocols.h"
32#include "scalarproduct.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "scalarproduct-api", __VA_ARGS__)
35
36
37/**
38 * The abstraction function for our internal callback
39 *
40 * @param h computation handle
41 * @param msg response we got, NULL on errors
42 * @param status processing status code
43 */
44typedef void
45(*GNUNET_SCALARPRODUCT_ResponseMessageHandler) (
46 struct GNUNET_SCALARPRODUCT_ComputationHandle *h,
47 const struct ClientResponseMessage *msg,
48 enum GNUNET_SCALARPRODUCT_ResponseStatus status);
49
50
51/**
52 * A handle returned for each computation
53 */
54struct GNUNET_SCALARPRODUCT_ComputationHandle
55{
56 /**
57 * Our configuration.
58 */
59 const struct GNUNET_CONFIGURATION_Handle *cfg;
60
61 /**
62 * Current connection to the scalarproduct service.
63 */
64 struct GNUNET_MQ_Handle *mq;
65
66 /**
67 * Function to call after transmission of the request (Bob).
68 */
69 GNUNET_SCALARPRODUCT_ContinuationWithStatus cont_status;
70
71 /**
72 * Function to call after transmission of the request (Alice).
73 */
74 GNUNET_SCALARPRODUCT_DatumProcessor cont_datum;
75
76 /**
77 * Closure for @e cont_status or @e cont_datum.
78 */
79 void *cont_cls;
80
81 /**
82 * API internal callback for results and failures to be forwarded to
83 * the client.
84 */
85 GNUNET_SCALARPRODUCT_ResponseMessageHandler response_proc;
86
87 /**
88 * The shared session key identifying this computation
89 */
90 struct GNUNET_HashCode key;
91};
92
93
94/**
95 * Called when a response is received from the service. Perform basic
96 * check that the message is well-formed.
97 *
98 * @param cls Pointer to the Master Context
99 * @param message Pointer to the data received in response
100 * @return #GNUNET_OK if @a message is well-formed
101 */
102static int
103check_response (void *cls,
104 const struct ClientResponseMessage *message)
105{
106 if (ntohs (message->header.size) !=
107 ntohl (message->product_length) + sizeof(struct ClientResponseMessage))
108 {
109 GNUNET_break (0);
110 return GNUNET_SYSERR;
111 }
112 return GNUNET_OK;
113}
114
115
116/**
117 * Handles the STATUS received from the service for a response, does
118 * not contain a payload. Called when we participate as "Bob" via
119 * #GNUNET_SCALARPRODUCT_accept_computation().
120 *
121 * @param h our Handle
122 * @param msg the response received
123 * @param status the condition the request was terminated with (eg: disconnect)
124 */
125static void
126process_status_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h,
127 const struct ClientResponseMessage *msg,
128 enum GNUNET_SCALARPRODUCT_ResponseStatus status)
129{
130 if (NULL != h->cont_status)
131 h->cont_status (h->cont_cls,
132 status);
133 GNUNET_SCALARPRODUCT_cancel (h);
134}
135
136
137/**
138 * Called when a response is received from the service. After basic
139 * check, the handler in `h->response_proc` is called. This functions
140 * handles the response to the client which used the API.
141 *
142 * @param cls Pointer to the Master Context
143 * @param message Pointer to the data received in response
144 */
145static void
146handle_response (void *cls,
147 const struct ClientResponseMessage *message)
148{
149 struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls;
150 enum GNUNET_SCALARPRODUCT_ResponseStatus status;
151
152 status = (enum GNUNET_SCALARPRODUCT_ResponseStatus) ntohl (message->status);
153 h->response_proc (h,
154 message,
155 status);
156}
157
158
159/**
160 * Check if the keys for all given elements are unique.
161 *
162 * @param elements elements to check
163 * @param element_count size of the @a elements array
164 * @return #GNUNET_OK if all keys are unique
165 */
166static int
167check_unique (const struct GNUNET_SCALARPRODUCT_Element *elements,
168 uint32_t element_count)
169{
170 struct GNUNET_CONTAINER_MultiHashMap *map;
171 int ok;
172
173 ok = GNUNET_OK;
174 map = GNUNET_CONTAINER_multihashmap_create (2 * element_count,
175 GNUNET_YES);
176 for (uint32_t i = 0; i < element_count; i++)
177 if (GNUNET_OK !=
178 GNUNET_CONTAINER_multihashmap_put (map,
179 &elements[i].key,
180 map,
181 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
182 {
183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
184 _ ("Keys given to SCALARPRODUCT not unique!\n"));
185 ok = GNUNET_SYSERR;
186 }
187 GNUNET_CONTAINER_multihashmap_destroy (map);
188 return ok;
189}
190
191
192/**
193 * We encountered an error communicating with the set service while
194 * performing a set operation. Report to the application.
195 *
196 * @param cls the `struct GNUNET_SCALARPRODUCT_ComputationHandle`
197 * @param error error code
198 */
199static void
200mq_error_handler (void *cls,
201 enum GNUNET_MQ_Error error)
202{
203 struct GNUNET_SCALARPRODUCT_ComputationHandle *h = cls;
204
205 LOG (GNUNET_ERROR_TYPE_INFO,
206 "Disconnected from SCALARPRODUCT service.\n");
207 h->response_proc (h,
208 NULL,
209 GNUNET_SCALARPRODUCT_STATUS_DISCONNECTED);
210}
211
212
213struct GNUNET_SCALARPRODUCT_ComputationHandle *
214GNUNET_SCALARPRODUCT_accept_computation (
215 const struct GNUNET_CONFIGURATION_Handle *cfg,
216 const struct GNUNET_HashCode *session_key,
217 const struct GNUNET_SCALARPRODUCT_Element *elements,
218 uint32_t element_count,
219 GNUNET_SCALARPRODUCT_ContinuationWithStatus cont,
220 void *cont_cls)
221{
222 struct GNUNET_SCALARPRODUCT_ComputationHandle *h
223 = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle);
224 struct GNUNET_MQ_MessageHandler handlers[] = {
225 GNUNET_MQ_hd_var_size (response,
226 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT,
227 struct ClientResponseMessage,
228 h),
229 GNUNET_MQ_handler_end ()
230 };
231 struct GNUNET_MQ_Envelope *env;
232 struct BobComputationMessage *msg;
233 struct ComputationBobCryptodataMultipartMessage *mmsg;
234 uint32_t size;
235 uint16_t possible;
236 uint16_t todo;
237 uint32_t element_count_transfered;
238
239
240 if (GNUNET_SYSERR == check_unique (elements,
241 element_count))
242 return NULL;
243 h->cont_status = cont;
244 h->cont_cls = cont_cls;
245 h->response_proc = &process_status_message;
246 h->cfg = cfg;
247 h->key = *session_key;
248 h->mq = GNUNET_CLIENT_connect (cfg,
249 "scalarproduct-bob",
250 handlers,
251 &mq_error_handler,
252 h);
253 if (NULL == h->mq)
254 {
255 /* scalarproduct configuration error */
256 GNUNET_break (0);
257 GNUNET_free (h);
258 return NULL;
259 }
260 possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct
261 BobComputationMessage))
262 / sizeof(struct GNUNET_SCALARPRODUCT_Element);
263 todo = GNUNET_MIN (possible,
264 element_count);
265 size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
266 env = GNUNET_MQ_msg_extra (msg,
267 size,
268 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_BOB);
269 msg->element_count_total = htonl (element_count);
270 msg->element_count_contained = htonl (todo);
271 msg->session_key = *session_key;
272 GNUNET_memcpy (&msg[1],
273 elements,
274 size);
275 element_count_transfered = todo;
276 GNUNET_MQ_send (h->mq,
277 env);
278 possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg))
279 / sizeof(struct GNUNET_SCALARPRODUCT_Element);
280 while (element_count_transfered < element_count)
281 {
282 todo = GNUNET_MIN (possible,
283 element_count - element_count_transfered);
284 size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
285 env = GNUNET_MQ_msg_extra (mmsg,
286 size,
287 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_BOB);
288 mmsg->element_count_contained = htonl (todo);
289 GNUNET_memcpy (&mmsg[1],
290 &elements[element_count_transfered],
291 size);
292 element_count_transfered += todo;
293 GNUNET_MQ_send (h->mq,
294 env);
295 }
296 return h;
297}
298
299
300/**
301 * Handles the RESULT received from the service for a request, should
302 * contain a result MPI value. Called when we participate as "Alice" via
303 * #GNUNET_SCALARPRODUCT_start_computation().
304 *
305 * @param h our Handle
306 * @param msg Pointer to the response received
307 * @param status the condition the request was terminated with (eg: disconnect)
308 */
309static void
310process_result_message (struct GNUNET_SCALARPRODUCT_ComputationHandle *h,
311 const struct ClientResponseMessage *msg,
312 enum GNUNET_SCALARPRODUCT_ResponseStatus status)
313{
314 uint32_t product_len;
315 gcry_mpi_t result = NULL;
316 gcry_error_t rc;
317 gcry_mpi_t num;
318 size_t rsize;
319
320 if (GNUNET_SCALARPRODUCT_STATUS_SUCCESS == status)
321 {
322 result = gcry_mpi_new (0);
323
324 product_len = ntohl (msg->product_length);
325 if (0 < product_len)
326 {
327 rsize = 0;
328 if (0 != (rc = gcry_mpi_scan (&num, GCRYMPI_FMT_STD,
329 &msg[1],
330 product_len,
331 &rsize)))
332 {
333 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
334 "gcry_mpi_scan",
335 rc);
336 gcry_mpi_release (result);
337 result = NULL;
338 status = GNUNET_SCALARPRODUCT_STATUS_INVALID_RESPONSE;
339 }
340 else
341 {
342 if (0 < (int32_t) ntohl (msg->range))
343 gcry_mpi_add (result, result, num);
344 else
345 gcry_mpi_sub (result, result, num);
346 gcry_mpi_release (num);
347 }
348 }
349 }
350 if (NULL != h->cont_datum)
351 h->cont_datum (h->cont_cls,
352 status,
353 result);
354 if (NULL != result)
355 gcry_mpi_release (result);
356 GNUNET_SCALARPRODUCT_cancel (h);
357}
358
359
360struct GNUNET_SCALARPRODUCT_ComputationHandle *
361GNUNET_SCALARPRODUCT_start_computation (
362 const struct GNUNET_CONFIGURATION_Handle *cfg,
363 const struct GNUNET_HashCode *session_key,
364 const struct GNUNET_PeerIdentity *peer,
365 const struct GNUNET_SCALARPRODUCT_Element *elements,
366 uint32_t element_count,
367 GNUNET_SCALARPRODUCT_DatumProcessor cont,
368 void *cont_cls)
369{
370 struct GNUNET_SCALARPRODUCT_ComputationHandle *h
371 = GNUNET_new (struct GNUNET_SCALARPRODUCT_ComputationHandle);
372 struct GNUNET_MQ_MessageHandler handlers[] = {
373 GNUNET_MQ_hd_var_size (response,
374 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_RESULT,
375 struct ClientResponseMessage,
376 h),
377 GNUNET_MQ_handler_end ()
378 };
379 struct GNUNET_MQ_Envelope *env;
380 struct AliceComputationMessage *msg;
381 struct ComputationBobCryptodataMultipartMessage *mmsg;
382 uint32_t size;
383 uint16_t possible;
384 uint16_t todo;
385 uint32_t element_count_transfered;
386
387 if (GNUNET_SYSERR == check_unique (elements,
388 element_count))
389 return NULL;
390 h->mq = GNUNET_CLIENT_connect (cfg,
391 "scalarproduct-alice",
392 handlers,
393 &mq_error_handler,
394 h);
395 if (NULL == h->mq)
396 {
397 /* misconfigured scalarproduct service */
398 GNUNET_break (0);
399 GNUNET_free (h);
400 return NULL;
401 }
402 h->cont_datum = cont;
403 h->cont_cls = cont_cls;
404 h->response_proc = &process_result_message;
405 h->cfg = cfg;
406 h->key = *session_key;
407
408 possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(struct
409 AliceComputationMessage))
410 / sizeof(struct GNUNET_SCALARPRODUCT_Element);
411 todo = GNUNET_MIN (possible,
412 element_count);
413 size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
414 env = GNUNET_MQ_msg_extra (msg,
415 size,
416 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_TO_ALICE);
417 msg->element_count_total = htonl (element_count);
418 msg->element_count_contained = htonl (todo);
419 msg->reserved = htonl (0);
420 msg->peer = *peer;
421 msg->session_key = *session_key;
422 GNUNET_memcpy (&msg[1],
423 elements,
424 size);
425 GNUNET_MQ_send (h->mq,
426 env);
427 element_count_transfered = todo;
428 possible = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*mmsg))
429 / sizeof(struct GNUNET_SCALARPRODUCT_Element);
430 while (element_count_transfered < element_count)
431 {
432 todo = GNUNET_MIN (possible,
433 element_count - element_count_transfered);
434 size = todo * sizeof(struct GNUNET_SCALARPRODUCT_Element);
435 env = GNUNET_MQ_msg_extra (mmsg,
436 size,
437 GNUNET_MESSAGE_TYPE_SCALARPRODUCT_CLIENT_MULTIPART_ALICE);
438 mmsg->element_count_contained = htonl (todo);
439 GNUNET_memcpy (&mmsg[1],
440 &elements[element_count_transfered],
441 size);
442 element_count_transfered += todo;
443 GNUNET_MQ_send (h->mq,
444 env);
445 }
446 return h;
447}
448
449
450/**
451 * Cancel an ongoing computation or revoke our collaboration offer.
452 * Closes the connection to the service
453 *
454 * @param h computation handle to terminate
455 */
456void
457GNUNET_SCALARPRODUCT_cancel (struct GNUNET_SCALARPRODUCT_ComputationHandle *h)
458{
459 if (NULL != h->mq)
460 {
461 GNUNET_MQ_destroy (h->mq);
462 h->mq = NULL;
463 }
464 GNUNET_free (h);
465}
466
467
468/* end of scalarproduct_api.c */
diff --git a/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c b/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c
new file mode 100644
index 000000000..85460cb05
--- /dev/null
+++ b/src/contrib/service/scalarproduct/test_ecc_scalarproduct.c
@@ -0,0 +1,212 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19
20 */
21/**
22 * @file util/test_ecc_scalarproduct.c
23 * @brief testcase for math behind ECC SP calculation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include <gcrypt.h>
29
30/**
31 * Global context.
32 */
33static struct GNUNET_CRYPTO_EccDlogContext *edc;
34
35
36/**
37 * Perform SP calculation.
38 *
39 * @param avec 0-terminated vector of Alice's values
40 * @param bvec 0-terminated vector of Bob's values
41 * @return avec * bvec
42 */
43static int
44test_sp (const unsigned int *avec,
45 const unsigned int *bvec)
46{
47 unsigned int len;
48 struct GNUNET_CRYPTO_EccScalar a;
49 struct GNUNET_CRYPTO_EccScalar a_neg;
50 struct GNUNET_CRYPTO_EccPoint *g;
51 struct GNUNET_CRYPTO_EccPoint *h;
52 struct GNUNET_CRYPTO_EccPoint pg;
53 struct GNUNET_CRYPTO_EccPoint ph;
54
55 /* determine length */
56 for (len = 0; 0 != avec[len]; len++)
57 ;
58 if (0 == len)
59 return 0;
60
61 /* Alice */
62 GNUNET_CRYPTO_ecc_rnd_mpi (&a,
63 &a_neg);
64 g = GNUNET_new_array (len,
65 struct GNUNET_CRYPTO_EccPoint);
66 h = GNUNET_new_array (len,
67 struct GNUNET_CRYPTO_EccPoint);
68 for (unsigned int i = 0; i < len; i++)
69 {
70 struct GNUNET_CRYPTO_EccScalar tmp;
71 struct GNUNET_CRYPTO_EccScalar ri;
72 struct GNUNET_CRYPTO_EccScalar ria;
73
74 GNUNET_CRYPTO_ecc_random_mod_n (&ri);
75 GNUNET_assert (GNUNET_OK ==
76 GNUNET_CRYPTO_ecc_dexp_mpi (&ri,
77 &g[i]));
78 /* ria = ri * a mod L, where L is the order of the main subgroup */
79 crypto_core_ed25519_scalar_mul (ria.v,
80 ri.v,
81 a.v);
82 /* tmp = ria + avec[i] */
83 {
84 int64_t val = avec[i];
85 struct GNUNET_CRYPTO_EccScalar vali;
86
87 GNUNET_assert (INT64_MIN != val);
88 GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val,
89 &vali);
90 if (val > 0)
91 crypto_core_ed25519_scalar_add (tmp.v,
92 ria.v,
93 vali.v);
94 else
95 crypto_core_ed25519_scalar_sub (tmp.v,
96 ria.v,
97 vali.v);
98 }
99 /* h[i] = g^tmp = g^{ria + avec[i]} */
100 GNUNET_assert (GNUNET_OK ==
101 GNUNET_CRYPTO_ecc_dexp_mpi (&tmp,
102 &h[i]));
103 }
104
105 /* Bob */
106 for (unsigned int i = 0; i < len; i++)
107 {
108 struct GNUNET_CRYPTO_EccPoint gm;
109 struct GNUNET_CRYPTO_EccPoint hm;
110
111 {
112 int64_t val = bvec[i];
113 struct GNUNET_CRYPTO_EccScalar vali;
114
115 GNUNET_assert (INT64_MIN != val);
116 GNUNET_CRYPTO_ecc_scalar_from_int (val > 0 ? val : -val,
117 &vali);
118 if (val < 0)
119 crypto_core_ed25519_scalar_negate (vali.v,
120 vali.v);
121 /* gm = g[i]^vali */
122 GNUNET_assert (GNUNET_OK ==
123 GNUNET_CRYPTO_ecc_pmul_mpi (&g[i],
124 &vali,
125 &gm));
126 /* hm = h[i]^vali */
127 GNUNET_assert (GNUNET_OK ==
128 GNUNET_CRYPTO_ecc_pmul_mpi (&h[i],
129 &vali,
130 &hm));
131 }
132 if (0 != i)
133 {
134 /* pg += gm */
135 GNUNET_assert (GNUNET_OK ==
136 GNUNET_CRYPTO_ecc_add (&gm,
137 &pg,
138 &pg));
139 /* ph += hm */
140 GNUNET_assert (GNUNET_OK ==
141 GNUNET_CRYPTO_ecc_add (&hm,
142 &ph,
143 &ph));
144 }
145 else
146 {
147 pg = gm;
148 ph = hm;
149 }
150 }
151 GNUNET_free (g);
152 GNUNET_free (h);
153
154 /* Alice */
155 {
156 struct GNUNET_CRYPTO_EccPoint pgi;
157 struct GNUNET_CRYPTO_EccPoint gsp;
158
159 /* pgi = pg^inv */
160 GNUNET_assert (GNUNET_OK ==
161 GNUNET_CRYPTO_ecc_pmul_mpi (&pg,
162 &a_neg,
163 &pgi));
164 /* gsp = pgi + ph */
165 GNUNET_assert (GNUNET_OK ==
166 GNUNET_CRYPTO_ecc_add (&pgi,
167 &ph,
168 &gsp));
169 return GNUNET_CRYPTO_ecc_dlog (edc,
170 &gsp);
171 }
172}
173
174
175/**
176 * Macro that checks that @a want is equal to @a have and
177 * if not returns with a failure code.
178 */
179#define CHECK(want,have) do { \
180 if (want != have) { \
181 GNUNET_break (0); \
182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
183 "Wanted %d, got %d\n", want, have); \
184 GNUNET_CRYPTO_ecc_dlog_release (edc); \
185 return 1; \
186 } } while (0)
187
188
189int
190main (int argc, char *argv[])
191{
192 static unsigned int v11[] = { 1, 1, 0 };
193 static unsigned int v22[] = { 2, 2, 0 };
194 static unsigned int v35[] = { 3, 5, 0 };
195 static unsigned int v24[] = { 2, 4, 0 };
196
197 GNUNET_log_setup ("test-ecc-scalarproduct",
198 "WARNING",
199 NULL);
200 edc = GNUNET_CRYPTO_ecc_dlog_prepare (128, 128);
201 CHECK (2, test_sp (v11, v11));
202 CHECK (4, test_sp (v22, v11));
203 CHECK (8, test_sp (v35, v11));
204 CHECK (26, test_sp (v35, v24));
205 CHECK (26, test_sp (v24, v35));
206 CHECK (16, test_sp (v22, v35));
207 GNUNET_CRYPTO_ecc_dlog_release (edc);
208 return 0;
209}
210
211
212/* end of test_ecc_scalarproduct.c */
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct.conf b/src/contrib/service/scalarproduct/test_scalarproduct.conf
new file mode 100644
index 000000000..9d280d1cd
--- /dev/null
+++ b/src/contrib/service/scalarproduct/test_scalarproduct.conf
@@ -0,0 +1,23 @@
1@INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test-scalarproduct/
5
6[testbed]
7OVERLAY_TOPOLOGY = CLIQUE
8
9[nse]
10WORKBITS=0
11
12[scalarproduct-bob]
13#PREFIX = valgrind --leak-check=yes
14
15[scalarproduct-alice]
16#PREFIX = valgrind --leak-check=yes
17
18[ats]
19WAN_QUOTA_IN = unlimited
20WAN_QUOTA_OUT = unlimited
21
22[peerinfo]
23USE_INCLUDED_HELLOS = NO
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct.sh b/src/contrib/service/scalarproduct/test_scalarproduct.sh
new file mode 100755
index 000000000..ed91cd5c4
--- /dev/null
+++ b/src/contrib/service/scalarproduct/test_scalarproduct.sh
@@ -0,0 +1,48 @@
1#!/bin/bash
2# compute a simple scalar product
3# payload for this test:
4INPUTALICE="-k CCC -e 'AB,10;RO,3;FL,3;LOL,-1;'"
5INPUTBOB="-k CCC -e 'BC,-20000;RO,1000;FL,100;LOL,24;'"
6EXPECTED="0CCC"
7
8# necessary to make the testing prefix deterministic, so we can access the config files
9PREFIX=/tmp/test-scalarproduct`date +%H%M%S`
10
11# where can we find the peers config files?
12CFGALICE="-c $PREFIX/0/config"
13CFGBOB="-c $PREFIX/1/config"
14
15# launch two peers in line topology non-interactively
16#
17# interactive mode would terminate the test immediately
18# because the rest of the script is already in stdin,
19# thus redirecting stdin does not suffice)
20# GNUNET_FORCE_LOG='scalarproduct*;;;;DEBUG'
21GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 &
22PID=$!
23# sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe
24echo "Waiting for peers to start..."
25sleep 5
26echo "Running test..."
27
28which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15"
29
30# get bob's peer ID, necessary for alice
31PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB`
32
33#GNUNET_LOG=';;;;DEBUG'
34${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB &
35#GNUNET_LOG=';;;;DEBUG'
36RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB`
37
38# terminate the testbed
39kill $PID
40
41if [ "$RESULT" = "$EXPECTED" ]
42then
43 echo "OK"
44 exit 0
45else
46 echo "Result $RESULT, expected $EXPECTED - NOTOK"
47 exit 1
48fi
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh b/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh
new file mode 100755
index 000000000..459406836
--- /dev/null
+++ b/src/contrib/service/scalarproduct/test_scalarproduct_negative.sh
@@ -0,0 +1,49 @@
1#!/bin/bash
2# compute a simple scalar product
3# payload for this test:
4INPUTALICE="-k CCC -e 'AB,10;RO,-3;FL,-3;LOL,1;'"
5INPUTBOB="-k CCC -e 'BC,-20000;RO,1000;FL,100;LOL,24;'"
6
7# necessary to make the testing prefix deterministic, so we can access the config files
8unset XDG_DATA_HOME
9unset XDG_CONFIG_HOME
10
11PREFIX=/tmp/test-scalarproduct`date +%H%M%S`
12
13# where can we find the peers config files?
14CFGALICE="-c $PREFIX/0/config"
15CFGBOB="-c $PREFIX/1/config"
16
17which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15"
18
19# launch two peers in line topology non-interactively
20#
21# interactive mode would terminate the test immediately
22# because the rest of the script is already in stdin,
23# thus redirecting stdin does not suffice)
24#GNUNET_LOG='scalarproduct;;;;DEBUG'
25GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 &
26PID=$!
27# sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe
28sleep 5
29
30# get bob's peer ID, necessary for alice
31PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB`
32
33#GNUNET_LOG=';;;;DEBUG'
34${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB &
35#RESULT=`GNUNET_LOG=';;;;DEBUG'
36RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB`
37
38# terminate the testbed
39kill $PID
40
41EXPECTED="-0CCC"
42if [ "$RESULT" = "$EXPECTED" ]
43then
44 echo "OK"
45 exit 0
46else
47 echo "Result $RESULT, expected $EXPECTED NOTOK"
48 exit 1
49fi
diff --git a/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh b/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh
new file mode 100755
index 000000000..6f80b2ea2
--- /dev/null
+++ b/src/contrib/service/scalarproduct/test_scalarproduct_negativezero.sh
@@ -0,0 +1,46 @@
1#!/bin/bash
2# compute a simple scalar product
3# payload for this test:
4INPUTALICE="-k CCC -e 'AB,10;RO,-1;FL,1;LOL,1;'"
5INPUTBOB="-k CCC -e 'BC,20;RO,1;FL,1;LOL,0;'"
6
7# necessary to make the testing prefix deterministic, so we can access the config files
8PREFIX=/tmp/test-scalarproduct`date +%H%M%S`
9
10# where can we find the peers config files?
11CFGALICE="-c $PREFIX/0/config"
12CFGBOB="-c $PREFIX/1/config"
13
14# launch two peers in line topology non-interactively
15#
16# interactive mode would terminate the test immediately
17# because the rest of the script is already in stdin,
18# thus redirecting stdin does not suffice)
19# GNUNET_LOG='scalarproduct;;;;DEBUG'
20GNUNET_TESTING_PREFIX=$PREFIX ../testbed/gnunet-testbed-profiler -n -c test_scalarproduct.conf -p 2 &
21PID=$!
22# sleep 1 is too short on most systems, 2 works on most, 5 seems to be safe
23sleep 5
24
25which timeout >/dev/null 2>&1 && DO_TIMEOUT="timeout 15"
26
27# get bob's peer ID, necessary for alice
28PEERIDBOB=`${DO_TIMEOUT} gnunet-peerinfo -qs $CFGBOB`
29
30#GNUNET_LOG=';;;;DEBUG'
31${DO_TIMEOUT} gnunet-scalarproduct $CFGBOB $INPUTBOB &
32#GNUNET_LOG=';;;;DEBUG'
33RESULT=`${DO_TIMEOUT} gnunet-scalarproduct $CFGALICE $INPUTALICE -p $PEERIDBOB`
34
35# terminate the testbed
36kill $PID
37
38EXPECTED="00"
39if [ "$RESULT" = "$EXPECTED" ]
40then
41 echo "OK"
42 exit 0
43else
44 echo "Result $RESULT NOTOK"
45 exit 1
46fi
diff --git a/src/contrib/service/secretsharing/.gitignore b/src/contrib/service/secretsharing/.gitignore
new file mode 100644
index 000000000..fe9db53a4
--- /dev/null
+++ b/src/contrib/service/secretsharing/.gitignore
@@ -0,0 +1,3 @@
1gnunet-service-secretsharing
2gnunet-secretsharing-profiler
3test_secretsharing_api
diff --git a/src/contrib/service/secretsharing/Makefile.am b/src/contrib/service/secretsharing/Makefile.am
new file mode 100644
index 000000000..d9c2b37b4
--- /dev/null
+++ b/src/contrib/service/secretsharing/Makefile.am
@@ -0,0 +1,61 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 secretsharing.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = -fprofile-arcs -ftest-coverage
13endif
14
15
16libexec_PROGRAMS = \
17 gnunet-service-secretsharing
18
19lib_LTLIBRARIES = \
20 libgnunetsecretsharing.la
21
22
23gnunet_service_secretsharing_SOURCES = \
24 gnunet-service-secretsharing.c \
25 secretsharing_common.c \
26 secretsharing_protocol.h
27gnunet_service_secretsharing_CFLAGS = $(AM_CFLAGS)
28gnunet_service_secretsharing_LDADD = \
29 $(top_builddir)/src/lib/util/libgnunetutil.la \
30 $(top_builddir)/src/contrib/service/consensus/libgnunetconsensus.la \
31 $(LIBGCRYPT_LIBS) \
32 $(GN_LIBINTL)
33
34libgnunetsecretsharing_la_SOURCES = \
35 secretsharing_api.c \
36 secretsharing_common.c \
37 secretsharing.h
38libgnunetsecretsharing_la_LIBADD = \
39 $(top_builddir)/src/lib/util/libgnunetutil.la \
40 $(LIBGCRYPT_LIBS) \
41 $(LTLIBINTL)
42libgnunetsecretsharing_la_LDFLAGS = \
43 $(GN_LIB_LDFLAGS)
44
45check_PROGRAMS = \
46 # test_secretsharing_api
47
48if ENABLE_TEST_RUN
49AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
50TESTS = $(check_PROGRAMS)
51endif
52
53test_secretsharing_api_SOURCES = \
54 test_secretsharing_api.c
55test_secretsharing_api_LDADD = \
56 libgnunetsecretsharing.la \
57 $(top_builddir)/src/service/testing/libgnunettesting.la \
58 $(top_builddir)/src/lib/util/libgnunetutil.la
59
60EXTRA_DIST = \
61 test_secretsharing.conf
diff --git a/src/contrib/service/secretsharing/gnunet-service-secretsharing.c b/src/contrib/service/secretsharing/gnunet-service-secretsharing.c
new file mode 100644
index 000000000..84338bd11
--- /dev/null
+++ b/src/contrib/service/secretsharing/gnunet-service-secretsharing.c
@@ -0,0 +1,2414 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/gnunet-service-secretsharing.c
23 * @brief secret sharing service
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_time_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_consensus_service.h"
31#include "secretsharing.h"
32#include "secretsharing_protocol.h"
33#include <gcrypt.h>
34
35
36#define EXTRA_CHECKS 1
37
38
39/**
40 * Info about a peer in a key generation session.
41 */
42struct KeygenPeerInfo
43{
44 /**
45 * Peer identity of the peer.
46 */
47 struct GNUNET_PeerIdentity peer;
48
49 /**
50 * The peer's paillier public key.
51 * Freshly generated for each keygen session.
52 */
53 struct GNUNET_CRYPTO_PaillierPublicKey paillier_public_key;
54
55 /**
56 * The peer's commitment to its presecret.
57 */
58 gcry_mpi_t presecret_commitment;
59
60 /**
61 * Commitment to the preshare that is
62 * intended for our peer.
63 */
64 gcry_mpi_t preshare_commitment;
65
66 /**
67 * Sigma (exponentiated share) for this peer.
68 */
69 gcry_mpi_t sigma;
70
71 /**
72 * Did we successfully receive the round1 element
73 * of the peer?
74 */
75 int round1_valid;
76
77 /**
78 * Did we successfully receive the round2 element
79 * of the peer?
80 */
81 int round2_valid;
82};
83
84
85/**
86 * Information about a peer in a decrypt session.
87 */
88struct DecryptPeerInfo
89{
90 /**
91 * Identity of the peer.
92 */
93 struct GNUNET_PeerIdentity peer;
94
95 /**
96 * Original index in the key generation round.
97 * Necessary for computing the lagrange coefficients.
98 */
99 unsigned int original_index;
100
101 /**
102 * Set to the partial decryption of
103 * this peer, or NULL if we did not
104 * receive a partial decryption from this
105 * peer or the zero knowledge proof failed.
106 */
107 gcry_mpi_t partial_decryption;
108};
109
110
111/**
112 * State we keep per client.
113 */
114struct ClientState;
115
116
117/**
118 * Session to establish a threshold-shared secret.
119 */
120struct KeygenSession
121{
122 /**
123 * Current consensus, used for both DKG rounds.
124 */
125 struct GNUNET_CONSENSUS_Handle *consensus;
126
127 /**
128 * Which client is this for?
129 */
130 struct ClientState *cs;
131
132 /**
133 * Randomly generated coefficients of the polynomial for sharing our
134 * pre-secret, where 'preshares[0]' is our pre-secret. Contains 'threshold'
135 * elements, thus represents a polynomial of degree 'threshold-1', which can
136 * be interpolated with 'threshold' data points.
137 *
138 * The pre-secret-shares 'i=1,...,num_peers' are given by evaluating this
139 * polyomial at 'i' for share i.
140 */
141 gcry_mpi_t *presecret_polynomial;
142
143 /**
144 * Minimum number of shares required to restore the secret.
145 * Also the number of coefficients for the polynomial representing
146 * the sharing. Obviously, the polynomial then has degree threshold-1.
147 */
148 unsigned int threshold;
149
150 /**
151 * Total number of peers.
152 */
153 unsigned int num_peers;
154
155 /**
156 * Index of the local peer.
157 */
158 unsigned int local_peer;
159
160 /**
161 * Information about all participating peers.
162 * Array of size 'num_peers'.
163 */
164 struct KeygenPeerInfo *info;
165
166 /**
167 * List of all peers involved in the secret sharing session.
168 */
169 struct GNUNET_PeerIdentity *peers;
170
171 /**
172 * Identifier for this session.
173 */
174 struct GNUNET_HashCode session_id;
175
176 /**
177 * Paillier private key of our peer.
178 */
179 struct GNUNET_CRYPTO_PaillierPrivateKey paillier_private_key;
180
181 /**
182 * When would we like the key to be established?
183 */
184 struct GNUNET_TIME_Absolute deadline;
185
186 /**
187 * When does the DKG start? Necessary to compute fractions of the
188 * operation's desired time interval.
189 */
190 struct GNUNET_TIME_Absolute start_time;
191
192 /**
193 * Index of the local peer in the ordered list
194 * of peers in the session.
195 */
196 unsigned int local_peer_idx;
197
198 /**
199 * Share of our peer. Once preshares from other peers are received, they
200 * will be added to 'my'share.
201 */
202 gcry_mpi_t my_share;
203
204 /**
205 * Public key, will be updated when a round2 element arrives.
206 */
207 gcry_mpi_t public_key;
208};
209
210
211/**
212 * Session to cooperatively decrypt a value.
213 */
214struct DecryptSession
215{
216 /**
217 * Handle to the consensus over partial decryptions.
218 */
219 struct GNUNET_CONSENSUS_Handle *consensus;
220
221 /**
222 * Which client is this for?
223 */
224 struct ClientState *cs;
225
226 /**
227 * When should we start communicating for decryption?
228 */
229 struct GNUNET_TIME_Absolute start;
230
231 /**
232 * When would we like the ciphertext to be
233 * decrypted?
234 */
235 struct GNUNET_TIME_Absolute deadline;
236
237 /**
238 * Ciphertext we want to decrypt.
239 */
240 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
241
242 /**
243 * Share of the local peer.
244 * Contains other important information, such as
245 * the list of other peers.
246 */
247 struct GNUNET_SECRETSHARING_Share *share;
248
249 /**
250 * State information about other peers.
251 */
252 struct DecryptPeerInfo *info;
253};
254
255
256/**
257 * State we keep per client.
258 */
259struct ClientState
260{
261 /**
262 * Decrypt session of the client, if any.
263 */
264 struct DecryptSession *decrypt_session;
265
266 /**
267 * Keygen session of the client, if any.
268 */
269 struct KeygenSession *keygen_session;
270
271 /**
272 * Client this is about.
273 */
274 struct GNUNET_SERVICE_Client *client;
275
276 /**
277 * MQ to talk to @a client.
278 */
279 struct GNUNET_MQ_Handle *mq;
280};
281
282
283/**
284 * The ElGamal prime field order as libgcrypt mpi.
285 * Initialized in #init_crypto_constants.
286 */
287static gcry_mpi_t elgamal_q;
288
289/**
290 * Modulus of the prime field used for ElGamal.
291 * Initialized in #init_crypto_constants.
292 */
293static gcry_mpi_t elgamal_p;
294
295/**
296 * Generator for prime field of order 'elgamal_q'.
297 * Initialized in #init_crypto_constants.
298 */
299static gcry_mpi_t elgamal_g;
300
301/**
302 * Peer that runs this service.
303 */
304static struct GNUNET_PeerIdentity my_peer;
305
306/**
307 * Peer that runs this service.
308 */
309static struct GNUNET_CRYPTO_EddsaPrivateKey *my_peer_private_key;
310
311/**
312 * Configuration of this service.
313 */
314static const struct GNUNET_CONFIGURATION_Handle *cfg;
315
316
317/**
318 * Get the peer info belonging to a peer identity in a keygen session.
319 *
320 * @param ks The keygen session.
321 * @param peer The peer identity.
322 * @return The Keygen peer info, or NULL if the peer could not be found.
323 */
324static struct KeygenPeerInfo *
325get_keygen_peer_info (const struct KeygenSession *ks,
326 const struct GNUNET_PeerIdentity *peer)
327{
328 unsigned int i;
329
330 for (i = 0; i < ks->num_peers; i++)
331 if (0 == GNUNET_memcmp (peer, &ks->info[i].peer))
332 return &ks->info[i];
333 return NULL;
334}
335
336
337/**
338 * Get the peer info belonging to a peer identity in a decrypt session.
339 *
340 * @param ds The decrypt session.
341 * @param peer The peer identity.
342 * @return The decrypt peer info, or NULL if the peer could not be found.
343 */
344static struct DecryptPeerInfo *
345get_decrypt_peer_info (const struct DecryptSession *ds,
346 const struct GNUNET_PeerIdentity *peer)
347{
348 unsigned int i;
349
350 for (i = 0; i < ds->share->num_peers; i++)
351 if (0 == GNUNET_memcmp (peer, &ds->info[i].peer))
352 return &ds->info[i];
353 return NULL;
354}
355
356
357/**
358 * Interpolate between two points in time.
359 *
360 * @param start start time
361 * @param end end time
362 * @param num numerator of the scale factor
363 * @param denum denumerator of the scale factor
364 */
365static struct GNUNET_TIME_Absolute
366time_between (struct GNUNET_TIME_Absolute start,
367 struct GNUNET_TIME_Absolute end,
368 int num, int denum)
369{
370 struct GNUNET_TIME_Absolute result;
371 uint64_t diff;
372
373 GNUNET_assert (start.abs_value_us <= end.abs_value_us);
374 diff = end.abs_value_us - start.abs_value_us;
375 result.abs_value_us = start.abs_value_us + ((diff * num) / denum);
376
377 return result;
378}
379
380
381/**
382 * Compare two peer identities. Intended to be used with qsort or bsearch.
383 *
384 * @param p1 Some peer identity.
385 * @param p2 Some peer identity.
386 * @return 1 if p1 > p2, -1 if p1 < p2 and 0 if p1 == p2.
387 */
388static int
389peer_id_cmp (const void *p1, const void *p2)
390{
391 return memcmp (p1,
392 p2,
393 sizeof(struct GNUNET_PeerIdentity));
394}
395
396
397/**
398 * Get the index of a peer in an array of peers
399 *
400 * @param haystack Array of peers.
401 * @param n Size of @a haystack.
402 * @param needle Peer to find
403 * @return Index of @a needle in @a haystack, or -1 if peer
404 * is not in the list.
405 */
406static int
407peer_find (const struct GNUNET_PeerIdentity *haystack, unsigned int n,
408 const struct GNUNET_PeerIdentity *needle)
409{
410 unsigned int i;
411
412 for (i = 0; i < n; i++)
413 if (0 == GNUNET_memcmp (&haystack[i],
414 needle))
415 return i;
416 return -1;
417}
418
419
420/**
421 * Normalize the given list of peers, by including the local peer
422 * (if it is missing) and sorting the peers by their identity.
423 *
424 * @param listed Peers in the unnormalized list.
425 * @param num_listed Peers in the un-normalized list.
426 * @param[out] num_normalized Number of peers in the normalized list.
427 * @param[out] my_peer_idx Index of the local peer in the normalized list.
428 * @return Normalized list, must be free'd by the caller.
429 */
430static struct GNUNET_PeerIdentity *
431normalize_peers (struct GNUNET_PeerIdentity *listed,
432 unsigned int num_listed,
433 unsigned int *num_normalized,
434 unsigned int *my_peer_idx)
435{
436 unsigned int local_peer_in_list;
437 /* number of peers in the normalized list */
438 unsigned int n;
439 struct GNUNET_PeerIdentity *normalized;
440
441 local_peer_in_list = GNUNET_YES;
442 n = num_listed;
443 if (peer_find (listed, num_listed, &my_peer) < 0)
444 {
445 local_peer_in_list = GNUNET_NO;
446 n += 1;
447 }
448
449 normalized = GNUNET_new_array (n,
450 struct GNUNET_PeerIdentity);
451
452 if (GNUNET_NO == local_peer_in_list)
453 normalized[n - 1] = my_peer;
454
455 GNUNET_memcpy (normalized,
456 listed,
457 num_listed * sizeof(struct GNUNET_PeerIdentity));
458 qsort (normalized,
459 n,
460 sizeof(struct GNUNET_PeerIdentity),
461 &peer_id_cmp);
462
463 if (NULL != my_peer_idx)
464 *my_peer_idx = peer_find (normalized, n, &my_peer);
465 if (NULL != num_normalized)
466 *num_normalized = n;
467
468 return normalized;
469}
470
471
472/**
473 * Get a the j-th lagrange coefficient for a set of indices.
474 *
475 * @param[out] coeff the lagrange coefficient
476 * @param j lagrange coefficient we want to compute
477 * @param indices indices
478 * @param num number of indices in @a indices
479 */
480static void
481compute_lagrange_coefficient (gcry_mpi_t coeff, unsigned int j,
482 unsigned int *indices,
483 unsigned int num)
484{
485 unsigned int i;
486 /* numerator */
487 gcry_mpi_t n;
488 /* denominator */
489 gcry_mpi_t d;
490 /* temp value for l-j */
491 gcry_mpi_t tmp;
492
493 GNUNET_assert (0 != coeff);
494
495 GNUNET_assert (0 != (n = gcry_mpi_new (0)));
496 GNUNET_assert (0 != (d = gcry_mpi_new (0)));
497 GNUNET_assert (0 != (tmp = gcry_mpi_new (0)));
498
499 gcry_mpi_set_ui (n, 1);
500 gcry_mpi_set_ui (d, 1);
501
502 for (i = 0; i < num; i++)
503 {
504 unsigned int l = indices[i];
505 if (l == j)
506 continue;
507 gcry_mpi_mul_ui (n, n, l + 1);
508 // d <- d * (l-j)
509 gcry_mpi_set_ui (tmp, l + 1);
510 gcry_mpi_sub_ui (tmp, tmp, j + 1);
511 gcry_mpi_mul (d, d, tmp);
512 }
513
514 // gcry_mpi_invm does not like negative numbers ...
515 gcry_mpi_mod (d, d, elgamal_q);
516
517 GNUNET_assert (gcry_mpi_cmp_ui (d, 0) > 0);
518
519 // now we do the actual division, with everything mod q, as we
520 // are not operating on elements from <g>, but on exponents
521 GNUNET_assert (0 != gcry_mpi_invm (d, d, elgamal_q));
522
523 gcry_mpi_mulm (coeff, n, d, elgamal_q);
524
525 gcry_mpi_release (n);
526 gcry_mpi_release (d);
527 gcry_mpi_release (tmp);
528}
529
530
531/**
532 * Destroy a decrypt session, removing it from
533 * the linked list of decrypt sessions.
534 *
535 * @param ds decrypt session to destroy
536 */
537static void
538decrypt_session_destroy (struct DecryptSession *ds)
539{
540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541 "destroying decrypt session\n");
542 if (NULL != ds->cs)
543 {
544 ds->cs->decrypt_session = NULL;
545 ds->cs = NULL;
546 }
547 if (NULL != ds->consensus)
548 {
549 GNUNET_CONSENSUS_destroy (ds->consensus);
550 ds->consensus = NULL;
551 }
552
553 if (NULL != ds->info)
554 {
555 for (unsigned int i = 0; i < ds->share->num_peers; i++)
556 {
557 if (NULL != ds->info[i].partial_decryption)
558 {
559 gcry_mpi_release (ds->info[i].partial_decryption);
560 ds->info[i].partial_decryption = NULL;
561 }
562 }
563 GNUNET_free (ds->info);
564 ds->info = NULL;
565 }
566 if (NULL != ds->share)
567 {
568 GNUNET_SECRETSHARING_share_destroy (ds->share);
569 ds->share = NULL;
570 }
571
572 GNUNET_free (ds);
573}
574
575
576static void
577keygen_info_destroy (struct KeygenPeerInfo *info)
578{
579 if (NULL != info->sigma)
580 {
581 gcry_mpi_release (info->sigma);
582 info->sigma = NULL;
583 }
584 if (NULL != info->presecret_commitment)
585 {
586 gcry_mpi_release (info->presecret_commitment);
587 info->presecret_commitment = NULL;
588 }
589 if (NULL != info->preshare_commitment)
590 {
591 gcry_mpi_release (info->preshare_commitment);
592 info->preshare_commitment = NULL;
593 }
594}
595
596
597static void
598keygen_session_destroy (struct KeygenSession *ks)
599{
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 "destroying keygen session\n");
602
603 if (NULL != ks->cs)
604 {
605 ks->cs->keygen_session = NULL;
606 ks->cs = NULL;
607 }
608 if (NULL != ks->info)
609 {
610 for (unsigned int i = 0; i < ks->num_peers; i++)
611 keygen_info_destroy (&ks->info[i]);
612 GNUNET_free (ks->info);
613 ks->info = NULL;
614 }
615
616 if (NULL != ks->consensus)
617 {
618 GNUNET_CONSENSUS_destroy (ks->consensus);
619 ks->consensus = NULL;
620 }
621
622 if (NULL != ks->presecret_polynomial)
623 {
624 for (unsigned int i = 0; i < ks->threshold; i++)
625 {
626 GNUNET_assert (NULL != ks->presecret_polynomial[i]);
627 gcry_mpi_release (ks->presecret_polynomial[i]);
628 ks->presecret_polynomial[i] = NULL;
629 }
630 GNUNET_free (ks->presecret_polynomial);
631 ks->presecret_polynomial = NULL;
632 }
633 if (NULL != ks->my_share)
634 {
635 gcry_mpi_release (ks->my_share);
636 ks->my_share = NULL;
637 }
638 if (NULL != ks->public_key)
639 {
640 gcry_mpi_release (ks->public_key);
641 ks->public_key = NULL;
642 }
643 if (NULL != ks->peers)
644 {
645 GNUNET_free (ks->peers);
646 ks->peers = NULL;
647 }
648 GNUNET_free (ks);
649}
650
651
652/**
653 * Task run during shutdown.
654 *
655 * @param cls unused
656 */
657static void
658cleanup_task (void *cls)
659{
660 /* Nothing to do! */
661}
662
663
664/**
665 * Generate the random coefficients of our pre-secret polynomial
666 *
667 * @param ks the session
668 */
669static void
670generate_presecret_polynomial (struct KeygenSession *ks)
671{
672 int i;
673 gcry_mpi_t v;
674
675 GNUNET_assert (NULL == ks->presecret_polynomial);
676 ks->presecret_polynomial = GNUNET_new_array (ks->threshold,
677 gcry_mpi_t);
678 for (i = 0; i < ks->threshold; i++)
679 {
680 v = ks->presecret_polynomial[i] = gcry_mpi_new (
681 GNUNET_SECRETSHARING_ELGAMAL_BITS);
682 GNUNET_assert (NULL != v);
683 // Randomize v such that 0 < v < elgamal_q.
684 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
685 do
686 {
687 gcry_mpi_randomize (v, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
688 GCRY_WEAK_RANDOM);
689 }
690 while ((gcry_mpi_cmp_ui (v, 0) == 0) || (gcry_mpi_cmp (v, elgamal_q) >= 0));
691 }
692}
693
694
695/**
696 * Consensus element handler for round one.
697 * We should get one ephemeral key for each peer.
698 *
699 * @param cls Closure (keygen session).
700 * @param element The element from consensus, or
701 * NULL if consensus failed.
702 */
703static void
704keygen_round1_new_element (void *cls,
705 const struct GNUNET_SET_Element *element)
706{
707 const struct GNUNET_SECRETSHARING_KeygenCommitData *d;
708 struct KeygenSession *ks = cls;
709 struct KeygenPeerInfo *info;
710
711 if (NULL == element)
712 {
713 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "round1 consensus failed\n");
714 return;
715 }
716
717 /* elements have fixed size */
718 if (element->size != sizeof(struct GNUNET_SECRETSHARING_KeygenCommitData))
719 {
720 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
721 "keygen commit data with wrong size (%u) in consensus, %u expected\n",
722 (unsigned int) element->size,
723 (unsigned int) sizeof(struct
724 GNUNET_SECRETSHARING_KeygenCommitData));
725 return;
726 }
727
728 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got round1 element\n");
729
730 d = element->data;
731 info = get_keygen_peer_info (ks, &d->peer);
732
733 if (NULL == info)
734 {
735 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
736 "keygen commit data with wrong peer identity (%s) in consensus\n",
737 GNUNET_i2s (&d->peer));
738 return;
739 }
740
741 /* Check that the right amount of data has been signed. */
742 if (d->purpose.size !=
743 htonl (element->size - offsetof (struct
744 GNUNET_SECRETSHARING_KeygenCommitData,
745 purpose)))
746 {
747 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
748 "keygen commit data with wrong signature purpose size in consensus\n");
749 return;
750 }
751
752 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify_ (
753 GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG1,
754 &d->purpose, &d->signature,
755 &d->peer.public_key))
756 {
757 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
758 "keygen commit data with invalid signature in consensus\n");
759 return;
760 }
761 info->paillier_public_key = d->pubkey;
762 GNUNET_CRYPTO_mpi_scan_unsigned (&info->presecret_commitment, &d->commitment,
763 512 / 8);
764 info->round1_valid = GNUNET_YES;
765}
766
767
768/**
769 * Evaluate the polynomial with coefficients @a coeff at @a x.
770 * The i-th element in @a coeff corresponds to the coefficient of x^i.
771 *
772 * @param[out] z result of the evaluation
773 * @param coeff array of coefficients
774 * @param num_coeff number of coefficients
775 * @param x where to evaluate the polynomial
776 * @param m what group are we operating in?
777 */
778static void
779horner_eval (gcry_mpi_t z, gcry_mpi_t *coeff, unsigned int num_coeff, gcry_mpi_t
780 x, gcry_mpi_t m)
781{
782 unsigned int i;
783
784 gcry_mpi_set_ui (z, 0);
785 for (i = 0; i < num_coeff; i++)
786 {
787 // z <- zx + c
788 gcry_mpi_mul (z, z, x);
789 gcry_mpi_addm (z, z, coeff[num_coeff - i - 1], m);
790 }
791}
792
793
794static void
795keygen_round2_conclude (void *cls)
796{
797 struct KeygenSession *ks = cls;
798 struct GNUNET_SECRETSHARING_SecretReadyMessage *m;
799 struct GNUNET_MQ_Envelope *ev;
800 size_t share_size;
801 unsigned int i;
802 unsigned int j;
803 struct GNUNET_SECRETSHARING_Share *share;
804
805 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "round2 conclude\n");
806
807 GNUNET_CONSENSUS_destroy (ks->consensus);
808 ks->consensus = NULL;
809
810 share = GNUNET_new (struct GNUNET_SECRETSHARING_Share);
811
812 share->num_peers = 0;
813
814 for (i = 0; i < ks->num_peers; i++)
815 if (GNUNET_YES == ks->info[i].round2_valid)
816 share->num_peers++;
817
818 share->peers = GNUNET_new_array (share->num_peers,
819 struct GNUNET_PeerIdentity);
820 share->sigmas =
821 GNUNET_new_array (share->num_peers,
822 struct GNUNET_SECRETSHARING_FieldElement);
823 share->original_indices = GNUNET_new_array (share->num_peers,
824 uint16_t);
825
826 /* maybe we're not even in the list of peers? */
827 share->my_peer = share->num_peers;
828
829 j = 0; /* running index of valid peers */
830 for (i = 0; i < ks->num_peers; i++)
831 {
832 if (GNUNET_YES == ks->info[i].round2_valid)
833 {
834 share->peers[j] = ks->info[i].peer;
835 GNUNET_CRYPTO_mpi_print_unsigned (&share->sigmas[j],
836 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
837 ks->info[i].sigma);
838 share->original_indices[i] = j;
839 if (0 == GNUNET_memcmp (&share->peers[i], &my_peer))
840 share->my_peer = j;
841 j += 1;
842 }
843 }
844
845 if (share->my_peer == share->num_peers)
846 {
847 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "P%u: peer identity not in share\n",
848 ks->local_peer_idx);
849 }
850
851 GNUNET_CRYPTO_mpi_print_unsigned (&share->my_share,
852 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
853 ks->my_share);
854 GNUNET_CRYPTO_mpi_print_unsigned (&share->public_key,
855 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
856 ks->public_key);
857
858 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "keygen completed with %u peers\n",
859 share->num_peers);
860
861 /* Write the share. If 0 peers completed the dkg, an empty
862 * share will be sent. */
863
864 GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, NULL, 0,
865 &share_size));
866
867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "writing share of size %u\n",
868 (unsigned int) share_size);
869
870 ev = GNUNET_MQ_msg_extra (m, share_size,
871 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY);
872
873 GNUNET_assert (GNUNET_OK == GNUNET_SECRETSHARING_share_write (share, &m[1],
874 share_size,
875 NULL));
876
877 GNUNET_SECRETSHARING_share_destroy (share);
878 share = NULL;
879
880 GNUNET_MQ_send (ks->cs->mq,
881 ev);
882}
883
884
885static void
886restore_fair (const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
887 const struct GNUNET_SECRETSHARING_FairEncryption *fe,
888 gcry_mpi_t x, gcry_mpi_t xres)
889{
890 gcry_mpi_t a_1;
891 gcry_mpi_t a_2;
892 gcry_mpi_t b_1;
893 gcry_mpi_t b_2;
894 gcry_mpi_t big_a;
895 gcry_mpi_t big_b;
896 gcry_mpi_t big_t;
897 gcry_mpi_t n;
898 gcry_mpi_t t_1;
899 gcry_mpi_t t_2;
900 gcry_mpi_t t;
901 gcry_mpi_t r;
902 gcry_mpi_t v;
903
904
905 GNUNET_assert (NULL != (n = gcry_mpi_new (0)));
906 GNUNET_assert (NULL != (t = gcry_mpi_new (0)));
907 GNUNET_assert (NULL != (t_1 = gcry_mpi_new (0)));
908 GNUNET_assert (NULL != (t_2 = gcry_mpi_new (0)));
909 GNUNET_assert (NULL != (r = gcry_mpi_new (0)));
910 GNUNET_assert (NULL != (big_t = gcry_mpi_new (0)));
911 GNUNET_assert (NULL != (v = gcry_mpi_new (0)));
912 GNUNET_assert (NULL != (big_a = gcry_mpi_new (0)));
913 GNUNET_assert (NULL != (big_b = gcry_mpi_new (0)));
914
915 // a = (N,0)^T
916 GNUNET_CRYPTO_mpi_scan_unsigned (&a_1,
917 ppub,
918 sizeof(struct
919 GNUNET_CRYPTO_PaillierPublicKey));
920 GNUNET_assert (NULL != (a_2 = gcry_mpi_new (0)));
921 gcry_mpi_set_ui (a_2, 0);
922 // b = (x,1)^T
923 GNUNET_assert (NULL != (b_1 = gcry_mpi_new (0)));
924 gcry_mpi_set (b_1, x);
925 GNUNET_assert (NULL != (b_2 = gcry_mpi_new (0)));
926 gcry_mpi_set_ui (b_2, 1);
927
928 // A = a DOT a
929 gcry_mpi_mul (t, a_1, a_1);
930 gcry_mpi_mul (big_a, a_2, a_2);
931 gcry_mpi_add (big_a, big_a, t);
932
933 // B = b DOT b
934 gcry_mpi_mul (t, b_1, b_1);
935 gcry_mpi_mul (big_b, b_2, b_2);
936 gcry_mpi_add (big_b, big_b, t);
937
938 while (1)
939 {
940 // n = a DOT b
941 gcry_mpi_mul (t, a_1, b_1);
942 gcry_mpi_mul (n, a_2, b_2);
943 gcry_mpi_add (n, n, t);
944
945 // r = nearest(n/B)
946 gcry_mpi_div (r, NULL, n, big_b, 0);
947
948 // T := A - 2rn + rrB
949 gcry_mpi_mul (v, r, n);
950 gcry_mpi_mul_ui (v, v, 2);
951 gcry_mpi_sub (big_t, big_a, v);
952 gcry_mpi_mul (v, r, r);
953 gcry_mpi_mul (v, v, big_b);
954 gcry_mpi_add (big_t, big_t, v);
955
956 if (gcry_mpi_cmp (big_t, big_b) >= 0)
957 {
958 break;
959 }
960
961 // t = a - rb
962 gcry_mpi_mul (v, r, b_1);
963 gcry_mpi_sub (t_1, a_1, v);
964 gcry_mpi_mul (v, r, b_2);
965 gcry_mpi_sub (t_2, a_2, v);
966
967 // a = b
968 gcry_mpi_set (a_1, b_1);
969 gcry_mpi_set (a_2, b_2);
970 // b = t
971 gcry_mpi_set (b_1, t_1);
972 gcry_mpi_set (b_2, t_2);
973
974 gcry_mpi_set (big_a, big_b);
975 gcry_mpi_set (big_b, big_t);
976 }
977
978 gcry_mpi_set (xres, b_2);
979 gcry_mpi_invm (xres, xres, elgamal_q);
980 gcry_mpi_mulm (xres, xres, b_1, elgamal_q);
981
982 gcry_mpi_release (a_1);
983 gcry_mpi_release (a_2);
984 gcry_mpi_release (b_1);
985 gcry_mpi_release (b_2);
986 gcry_mpi_release (big_a);
987 gcry_mpi_release (big_b);
988 gcry_mpi_release (big_t);
989 gcry_mpi_release (n);
990 gcry_mpi_release (t_1);
991 gcry_mpi_release (t_2);
992 gcry_mpi_release (t);
993 gcry_mpi_release (r);
994 gcry_mpi_release (v);
995}
996
997
998static void
999get_fair_encryption_challenge (const struct
1000 GNUNET_SECRETSHARING_FairEncryption *fe,
1001 gcry_mpi_t *e)
1002{
1003 struct
1004 {
1005 struct GNUNET_CRYPTO_PaillierCiphertext c;
1006 char h[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1007 char t1[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1008 char t2[GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8];
1009 } hash_data;
1010 struct GNUNET_HashCode e_hash;
1011
1012 memset (&hash_data,
1013 0,
1014 sizeof(hash_data));
1015 GNUNET_memcpy (&hash_data.c, &fe->c, sizeof(struct
1016 GNUNET_CRYPTO_PaillierCiphertext));
1017 GNUNET_memcpy (&hash_data.h, &fe->h, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1018 GNUNET_memcpy (&hash_data.t1, &fe->t1, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1019 GNUNET_memcpy (&hash_data.t2, &fe->t2, GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8);
1020 GNUNET_CRYPTO_hash (&hash_data,
1021 sizeof(hash_data),
1022 &e_hash);
1023 /* This allocates "e" */
1024 GNUNET_CRYPTO_mpi_scan_unsigned (e,
1025 &e_hash,
1026 sizeof(struct GNUNET_HashCode));
1027 gcry_mpi_mod (*e, *e, elgamal_q);
1028}
1029
1030
1031static int
1032verify_fair (const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
1033 const struct GNUNET_SECRETSHARING_FairEncryption *fe)
1034{
1035 gcry_mpi_t n;
1036 gcry_mpi_t n_sq;
1037 gcry_mpi_t z;
1038 gcry_mpi_t t1;
1039 gcry_mpi_t t2;
1040 gcry_mpi_t e;
1041 gcry_mpi_t w;
1042 gcry_mpi_t tmp1;
1043 gcry_mpi_t tmp2;
1044 gcry_mpi_t y;
1045 gcry_mpi_t big_y;
1046 int res;
1047
1048 GNUNET_assert (NULL != (n_sq = gcry_mpi_new (0)));
1049 GNUNET_assert (NULL != (tmp1 = gcry_mpi_new (0)));
1050 GNUNET_assert (NULL != (tmp2 = gcry_mpi_new (0)));
1051
1052 get_fair_encryption_challenge (fe,
1053 &e /* this allocates e */);
1054
1055 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
1056 ppub,
1057 sizeof(struct
1058 GNUNET_CRYPTO_PaillierPublicKey));
1059 GNUNET_CRYPTO_mpi_scan_unsigned (&t1, fe->t1, GNUNET_CRYPTO_PAILLIER_BITS
1060 / 8);
1061 GNUNET_CRYPTO_mpi_scan_unsigned (&z, fe->z,
1062 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1063 GNUNET_CRYPTO_mpi_scan_unsigned (&y, fe->h,
1064 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1065 GNUNET_CRYPTO_mpi_scan_unsigned (&w, fe->w, GNUNET_CRYPTO_PAILLIER_BITS / 8);
1066 GNUNET_CRYPTO_mpi_scan_unsigned (&big_y, fe->c.bits,
1067 GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8);
1068 GNUNET_CRYPTO_mpi_scan_unsigned (&t2, fe->t2, GNUNET_CRYPTO_PAILLIER_BITS
1069 * 2 / 8);
1070 gcry_mpi_mul (n_sq, n, n);
1071
1072 // tmp1 = g^z
1073 gcry_mpi_powm (tmp1, elgamal_g, z, elgamal_p);
1074 // tmp2 = y^{-e}
1075 gcry_mpi_powm (tmp1, y, e, elgamal_p);
1076 gcry_mpi_invm (tmp1, tmp1, elgamal_p);
1077 // tmp1 = tmp1 * tmp2
1078 gcry_mpi_mulm (tmp1, tmp1, tmp2, elgamal_p);
1079
1080 if (0 == gcry_mpi_cmp (t1, tmp1))
1081 {
1082 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "fair encryption invalid (t1)\n");
1083 res = GNUNET_NO;
1084 goto cleanup;
1085 }
1086
1087 gcry_mpi_powm (big_y, big_y, e, n_sq);
1088 gcry_mpi_invm (big_y, big_y, n_sq);
1089
1090 gcry_mpi_add_ui (tmp1, n, 1);
1091 gcry_mpi_powm (tmp1, tmp1, z, n_sq);
1092
1093 gcry_mpi_powm (tmp2, w, n, n_sq);
1094
1095 gcry_mpi_mulm (tmp1, tmp1, tmp2, n_sq);
1096 gcry_mpi_mulm (tmp1, tmp1, big_y, n_sq);
1097
1098
1099 if (0 == gcry_mpi_cmp (t2, tmp1))
1100 {
1101 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "fair encryption invalid (t2)\n");
1102 res = GNUNET_NO;
1103 goto cleanup;
1104 }
1105
1106 res = GNUNET_YES;
1107
1108cleanup:
1109
1110 gcry_mpi_release (n);
1111 gcry_mpi_release (n_sq);
1112 gcry_mpi_release (z);
1113 gcry_mpi_release (t1);
1114 gcry_mpi_release (t2);
1115 gcry_mpi_release (e);
1116 gcry_mpi_release (w);
1117 gcry_mpi_release (tmp1);
1118 gcry_mpi_release (tmp2);
1119 gcry_mpi_release (y);
1120 gcry_mpi_release (big_y);
1121 return res;
1122}
1123
1124
1125/**
1126 * Create a fair Paillier encryption of then given ciphertext.
1127 *
1128 * @param v the ciphertext
1129 * @param[out] fe the fair encryption
1130 */
1131static void
1132encrypt_fair (gcry_mpi_t v,
1133 const struct GNUNET_CRYPTO_PaillierPublicKey *ppub,
1134 struct GNUNET_SECRETSHARING_FairEncryption *fe)
1135{
1136 gcry_mpi_t r;
1137 gcry_mpi_t s;
1138 gcry_mpi_t t1;
1139 gcry_mpi_t t2;
1140 gcry_mpi_t z;
1141 gcry_mpi_t w;
1142 gcry_mpi_t n;
1143 gcry_mpi_t e;
1144 gcry_mpi_t n_sq;
1145 gcry_mpi_t u;
1146 gcry_mpi_t Y;
1147 gcry_mpi_t G;
1148 gcry_mpi_t h;
1149
1150 GNUNET_assert (NULL != (r = gcry_mpi_new (0)));
1151 GNUNET_assert (NULL != (s = gcry_mpi_new (0)));
1152 GNUNET_assert (NULL != (t1 = gcry_mpi_new (0)));
1153 GNUNET_assert (NULL != (t2 = gcry_mpi_new (0)));
1154 GNUNET_assert (NULL != (z = gcry_mpi_new (0)));
1155 GNUNET_assert (NULL != (w = gcry_mpi_new (0)));
1156 GNUNET_assert (NULL != (n_sq = gcry_mpi_new (0)));
1157 GNUNET_assert (NULL != (u = gcry_mpi_new (0)));
1158 GNUNET_assert (NULL != (Y = gcry_mpi_new (0)));
1159 GNUNET_assert (NULL != (G = gcry_mpi_new (0)));
1160 GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
1161
1162 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
1163 ppub,
1164 sizeof(struct
1165 GNUNET_CRYPTO_PaillierPublicKey));
1166 gcry_mpi_mul (n_sq, n, n);
1167 gcry_mpi_add_ui (G, n, 1);
1168
1169 do
1170 {
1171 gcry_mpi_randomize (u, GNUNET_CRYPTO_PAILLIER_BITS, GCRY_WEAK_RANDOM);
1172 }
1173 while (gcry_mpi_cmp (u, n) >= 0);
1174
1175 gcry_mpi_powm (t1, G, v, n_sq);
1176 gcry_mpi_powm (t2, u, n, n_sq);
1177 gcry_mpi_mulm (Y, t1, t2, n_sq);
1178
1179 GNUNET_CRYPTO_mpi_print_unsigned (fe->c.bits,
1180 sizeof fe->c.bits,
1181 Y);
1182
1183
1184 gcry_mpi_randomize (r, 2048, GCRY_WEAK_RANDOM);
1185 do
1186 {
1187 gcry_mpi_randomize (s, GNUNET_CRYPTO_PAILLIER_BITS, GCRY_WEAK_RANDOM);
1188 }
1189 while (gcry_mpi_cmp (s, n) >= 0);
1190
1191 // compute t1
1192 gcry_mpi_mulm (t1, elgamal_g, r, elgamal_p);
1193 // compute t2 (use z and w as temp)
1194 gcry_mpi_powm (z, G, r, n_sq);
1195 gcry_mpi_powm (w, s, n, n_sq);
1196 gcry_mpi_mulm (t2, z, w, n_sq);
1197
1198
1199 gcry_mpi_powm (h, elgamal_g, v, elgamal_p);
1200
1201 GNUNET_CRYPTO_mpi_print_unsigned (fe->h,
1202 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1203 h);
1204
1205 GNUNET_CRYPTO_mpi_print_unsigned (fe->t1,
1206 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1207 t1);
1208
1209 GNUNET_CRYPTO_mpi_print_unsigned (fe->t2,
1210 GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8,
1211 t2);
1212
1213 get_fair_encryption_challenge (fe,
1214 &e /* This allocates "e" */);
1215
1216 // compute z
1217 gcry_mpi_mul (z, e, v);
1218 gcry_mpi_addm (z, z, r, elgamal_q);
1219 // compute w
1220 gcry_mpi_powm (w, u, e, n);
1221 gcry_mpi_mulm (w, w, s, n);
1222
1223 GNUNET_CRYPTO_mpi_print_unsigned (fe->z,
1224 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1225 z);
1226
1227 GNUNET_CRYPTO_mpi_print_unsigned (fe->w,
1228 GNUNET_CRYPTO_PAILLIER_BITS / 8,
1229 w);
1230
1231 gcry_mpi_release (n);
1232 gcry_mpi_release (r);
1233 gcry_mpi_release (s);
1234 gcry_mpi_release (t1);
1235 gcry_mpi_release (t2);
1236 gcry_mpi_release (z);
1237 gcry_mpi_release (w);
1238 gcry_mpi_release (e);
1239 gcry_mpi_release (n_sq);
1240 gcry_mpi_release (u);
1241 gcry_mpi_release (Y);
1242 gcry_mpi_release (G);
1243 gcry_mpi_release (h);
1244}
1245
1246
1247/**
1248 * Insert round 2 element in the consensus, consisting of
1249 * (1) The exponentiated pre-share polynomial coefficients A_{i,l}=g^{a_{i,l}}
1250 * (2) The exponentiated pre-shares y_{i,j}=g^{s_{i,j}}
1251 * (3) The encrypted pre-shares Y_{i,j}
1252 * (4) The zero knowledge proof for fairness of
1253 * the encryption
1254 *
1255 * @param ks session to use
1256 */
1257static void
1258insert_round2_element (struct KeygenSession *ks)
1259{
1260 struct GNUNET_SET_Element *element;
1261 struct GNUNET_SECRETSHARING_KeygenRevealData *d;
1262 unsigned char *pos;
1263 unsigned char *last_pos;
1264 size_t element_size;
1265 unsigned int i;
1266 gcry_mpi_t idx;
1267 gcry_mpi_t v;
1268
1269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: Inserting round2 element\n",
1270 ks->local_peer_idx);
1271
1272 GNUNET_assert (NULL != (v = gcry_mpi_new (
1273 GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1274 GNUNET_assert (NULL != (idx = gcry_mpi_new (
1275 GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1276
1277 element_size = (sizeof(struct GNUNET_SECRETSHARING_KeygenRevealData)
1278 + sizeof(struct GNUNET_SECRETSHARING_FairEncryption)
1279 * ks->num_peers
1280 + GNUNET_SECRETSHARING_ELGAMAL_BITS / 8 * ks->threshold);
1281
1282 element = GNUNET_malloc (sizeof(struct GNUNET_SET_Element) + element_size);
1283 element->size = element_size;
1284 element->data = (void *) &element[1];
1285
1286 d = (void *) element->data;
1287 d->peer = my_peer;
1288
1289 // start inserting vector elements
1290 // after the fixed part of the element's data
1291 pos = (void *) &d[1];
1292 last_pos = pos + element_size;
1293
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed exp preshares\n",
1295 ks->local_peer_idx);
1296
1297 // encrypted pre-shares
1298 // and fair encryption proof
1299 {
1300 for (i = 0; i < ks->num_peers; i++)
1301 {
1302 ptrdiff_t remaining = last_pos - pos;
1303 struct GNUNET_SECRETSHARING_FairEncryption *fe = (void *) pos;
1304
1305 GNUNET_assert (remaining > 0);
1306 memset (fe, 0, sizeof *fe);
1307 if (GNUNET_YES == ks->info[i].round1_valid)
1308 {
1309 gcry_mpi_set_ui (idx, i + 1);
1310 // evaluate the polynomial
1311 horner_eval (v, ks->presecret_polynomial, ks->threshold, idx,
1312 elgamal_q);
1313 // encrypt the result
1314 encrypt_fair (v, &ks->info[i].paillier_public_key, fe);
1315 }
1316 pos += sizeof *fe;
1317 }
1318 }
1319
1320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed enc preshares\n",
1321 ks->local_peer_idx);
1322
1323 // exponentiated coefficients
1324 for (i = 0; i < ks->threshold; i++)
1325 {
1326 ptrdiff_t remaining = last_pos - pos;
1327 GNUNET_assert (remaining > 0);
1328 gcry_mpi_powm (v, elgamal_g, ks->presecret_polynomial[i], elgamal_p);
1329 GNUNET_CRYPTO_mpi_print_unsigned (pos, GNUNET_SECRETSHARING_ELGAMAL_BITS
1330 / 8, v);
1331 pos += GNUNET_SECRETSHARING_ELGAMAL_BITS / 8;
1332 }
1333
1334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: computed exp coefficients\n",
1335 ks->local_peer_idx);
1336
1337
1338 d->purpose.size = htonl (element_size - offsetof (struct
1339 GNUNET_SECRETSHARING_KeygenRevealData,
1340 purpose));
1341 d->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG2);
1342 GNUNET_assert (GNUNET_OK ==
1343 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
1344 &d->purpose,
1345 &d->signature));
1346
1347 GNUNET_CONSENSUS_insert (ks->consensus, element, NULL, NULL);
1348 GNUNET_free (element); /* FIXME: maybe stack-allocate instead? */
1349
1350 gcry_mpi_release (v);
1351 gcry_mpi_release (idx);
1352}
1353
1354
1355static gcry_mpi_t
1356keygen_reveal_get_exp_coeff (struct KeygenSession *ks,
1357 const struct
1358 GNUNET_SECRETSHARING_KeygenRevealData *d,
1359 unsigned int idx)
1360{
1361 unsigned char *pos;
1362 gcry_mpi_t exp_coeff;
1363
1364 GNUNET_assert (idx < ks->threshold);
1365
1366 pos = (void *) &d[1];
1367 // skip encrypted pre-shares
1368 pos += sizeof(struct GNUNET_SECRETSHARING_FairEncryption) * ks->num_peers;
1369 // skip exp. coeffs we are not interested in
1370 pos += GNUNET_SECRETSHARING_ELGAMAL_BITS / 8 * idx;
1371 // the first exponentiated coefficient is the public key share
1372 GNUNET_CRYPTO_mpi_scan_unsigned (&exp_coeff, pos,
1373 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1374 return exp_coeff;
1375}
1376
1377
1378static struct GNUNET_SECRETSHARING_FairEncryption *
1379keygen_reveal_get_enc_preshare (struct KeygenSession *ks,
1380 const struct
1381 GNUNET_SECRETSHARING_KeygenRevealData *d,
1382 unsigned int idx)
1383{
1384 unsigned char *pos;
1385
1386 GNUNET_assert (idx < ks->num_peers);
1387
1388 pos = (void *) &d[1];
1389 // skip encrypted pre-shares we're not interested in
1390 pos += sizeof(struct GNUNET_SECRETSHARING_FairEncryption) * idx;
1391 return (struct GNUNET_SECRETSHARING_FairEncryption *) pos;
1392}
1393
1394
1395static gcry_mpi_t
1396keygen_reveal_get_exp_preshare (struct KeygenSession *ks,
1397 const struct
1398 GNUNET_SECRETSHARING_KeygenRevealData *d,
1399 unsigned int idx)
1400{
1401 gcry_mpi_t exp_preshare;
1402 struct GNUNET_SECRETSHARING_FairEncryption *fe;
1403
1404 GNUNET_assert (idx < ks->num_peers);
1405 fe = keygen_reveal_get_enc_preshare (ks, d, idx);
1406 GNUNET_CRYPTO_mpi_scan_unsigned (&exp_preshare, fe->h,
1407 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1408 return exp_preshare;
1409}
1410
1411
1412static void
1413keygen_round2_new_element (void *cls,
1414 const struct GNUNET_SET_Element *element)
1415{
1416 struct KeygenSession *ks = cls;
1417 const struct GNUNET_SECRETSHARING_KeygenRevealData *d;
1418 struct KeygenPeerInfo *info;
1419 size_t expected_element_size;
1420 unsigned int j;
1421 int cmp_result;
1422 gcry_mpi_t tmp;
1423 gcry_mpi_t public_key_share;
1424 gcry_mpi_t preshare;
1425
1426 if (NULL == element)
1427 {
1428 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "round2 consensus failed\n");
1429 return;
1430 }
1431
1432 expected_element_size = (sizeof(struct GNUNET_SECRETSHARING_KeygenRevealData)
1433 + sizeof(struct
1434 GNUNET_SECRETSHARING_FairEncryption)
1435 * ks->num_peers
1436 + GNUNET_SECRETSHARING_ELGAMAL_BITS / 8
1437 * ks->threshold);
1438
1439 if (element->size != expected_element_size)
1440 {
1441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1442 "keygen round2 data with wrong size (%u) in consensus, %u expected\n",
1443 (unsigned int) element->size,
1444 (unsigned int) expected_element_size);
1445 return;
1446 }
1447
1448 d = (const void *) element->data;
1449
1450 info = get_keygen_peer_info (ks, &d->peer);
1451
1452 if (NULL == info)
1453 {
1454 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1455 "keygen commit data with wrong peer identity (%s) in consensus\n",
1456 GNUNET_i2s (&d->peer));
1457 return;
1458 }
1459
1460 if (GNUNET_NO == info->round1_valid)
1461 {
1462 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1463 "ignoring round2 element from peer with invalid round1 element (%s)\n",
1464 GNUNET_i2s (&d->peer));
1465 return;
1466 }
1467
1468 if (GNUNET_YES == info->round2_valid)
1469 {
1470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1471 "ignoring duplicate round2 element (%s)\n",
1472 GNUNET_i2s (&d->peer));
1473 return;
1474 }
1475
1476 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got round2 element\n");
1477
1478 if (ntohl (d->purpose.size) !=
1479 element->size - offsetof (struct GNUNET_SECRETSHARING_KeygenRevealData,
1480 purpose))
1481 {
1482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1483 "keygen reveal data with wrong signature purpose size in consensus\n");
1484 return;
1485 }
1486
1487 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify_ (
1488 GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG2,
1489 &d->purpose, &d->signature,
1490 &d->peer.public_key))
1491 {
1492 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1493 "keygen reveal data with invalid signature in consensus\n");
1494 return;
1495 }
1496
1497 public_key_share = keygen_reveal_get_exp_coeff (ks, d, 0);
1498 info->preshare_commitment = keygen_reveal_get_exp_preshare (ks, d,
1499 ks->local_peer_idx);
1500
1501 if (NULL == ks->public_key)
1502 {
1503 GNUNET_assert (NULL != (ks->public_key = gcry_mpi_new (0)));
1504 gcry_mpi_set_ui (ks->public_key, 1);
1505 }
1506 gcry_mpi_mulm (ks->public_key, ks->public_key, public_key_share, elgamal_p);
1507
1508 gcry_mpi_release (public_key_share);
1509 public_key_share = NULL;
1510
1511 {
1512 struct GNUNET_SECRETSHARING_FairEncryption *fe =
1513 keygen_reveal_get_enc_preshare (ks, d, ks->local_peer_idx);
1514 GNUNET_assert (NULL != (preshare = gcry_mpi_new (0)));
1515 GNUNET_CRYPTO_paillier_decrypt (&ks->paillier_private_key,
1516 &ks->info[ks->local_peer_idx].
1517 paillier_public_key,
1518 &fe->c,
1519 preshare);
1520
1521 // FIXME: not doing the restoration is less expensive
1522 restore_fair (&ks->info[ks->local_peer_idx].paillier_public_key,
1523 fe,
1524 preshare,
1525 preshare);
1526 }
1527
1528 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
1529 gcry_mpi_powm (tmp, elgamal_g, preshare, elgamal_p);
1530
1531 cmp_result = gcry_mpi_cmp (tmp, info->preshare_commitment);
1532 gcry_mpi_release (tmp);
1533 tmp = NULL;
1534 if (0 != cmp_result)
1535 {
1536 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1537 "P%u: Got invalid presecret from P%u\n",
1538 (unsigned int) ks->local_peer_idx, (unsigned int) (info
1539 - ks->info));
1540 return;
1541 }
1542
1543 if (NULL == ks->my_share)
1544 {
1545 GNUNET_assert (NULL != (ks->my_share = gcry_mpi_new (0)));
1546 }
1547 gcry_mpi_addm (ks->my_share, ks->my_share, preshare, elgamal_q);
1548
1549 for (j = 0; j < ks->num_peers; j++)
1550 {
1551 gcry_mpi_t presigma;
1552 if (NULL == ks->info[j].sigma)
1553 {
1554 GNUNET_assert (NULL != (ks->info[j].sigma = gcry_mpi_new (0)));
1555 gcry_mpi_set_ui (ks->info[j].sigma, 1);
1556 }
1557 presigma = keygen_reveal_get_exp_preshare (ks, d, j);
1558 gcry_mpi_mulm (ks->info[j].sigma, ks->info[j].sigma, presigma, elgamal_p);
1559 gcry_mpi_release (presigma);
1560 }
1561
1562 gcry_mpi_t prod;
1563 GNUNET_assert (NULL != (prod = gcry_mpi_new (0)));
1564 gcry_mpi_t j_to_k;
1565 GNUNET_assert (NULL != (j_to_k = gcry_mpi_new (0)));
1566 // validate that the polynomial sharing matches the additive sharing
1567 for (j = 0; j < ks->num_peers; j++)
1568 {
1569 unsigned int k;
1570 int cmp_result;
1571 gcry_mpi_t exp_preshare;
1572 gcry_mpi_set_ui (prod, 1);
1573 for (k = 0; k < ks->threshold; k++)
1574 {
1575 // Using pow(double,double) is a bit sketchy.
1576 // We count players from 1, but shares from 0.
1577 gcry_mpi_t tmp;
1578 gcry_mpi_set_ui (j_to_k, (unsigned int) pow (j + 1, k));
1579 tmp = keygen_reveal_get_exp_coeff (ks, d, k);
1580 gcry_mpi_powm (tmp, tmp, j_to_k, elgamal_p);
1581 gcry_mpi_mulm (prod, prod, tmp, elgamal_p);
1582 gcry_mpi_release (tmp);
1583 }
1584 exp_preshare = keygen_reveal_get_exp_preshare (ks, d, j);
1585 gcry_mpi_mod (exp_preshare, exp_preshare, elgamal_p);
1586 cmp_result = gcry_mpi_cmp (prod, exp_preshare);
1587 gcry_mpi_release (exp_preshare);
1588 exp_preshare = NULL;
1589 if (0 != cmp_result)
1590 {
1591 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1592 "P%u: reveal data from P%u incorrect\n",
1593 ks->local_peer_idx, j);
1594 /* no need for further verification, round2 stays invalid ... */
1595 return;
1596 }
1597 }
1598
1599 // TODO: verify proof of fair encryption (once implemented)
1600 for (j = 0; j < ks->num_peers; j++)
1601 {
1602 struct GNUNET_SECRETSHARING_FairEncryption *fe =
1603 keygen_reveal_get_enc_preshare (ks, d, j);
1604 if (GNUNET_YES != verify_fair (&ks->info[j].paillier_public_key, fe))
1605 {
1606 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1607 "P%u: reveal data from P%u incorrect (fair encryption)\n",
1608 ks->local_peer_idx, j);
1609 return;
1610 }
1611 }
1612
1613 info->round2_valid = GNUNET_YES;
1614
1615 gcry_mpi_release (preshare);
1616 gcry_mpi_release (prod);
1617 gcry_mpi_release (j_to_k);
1618}
1619
1620
1621/**
1622 * Called when the first consensus round has concluded.
1623 * Will initiate the second round.
1624 *
1625 * @param cls closure
1626 */
1627static void
1628keygen_round1_conclude (void *cls)
1629{
1630 struct KeygenSession *ks = cls;
1631
1632 GNUNET_CONSENSUS_destroy (ks->consensus);
1633
1634 ks->consensus = GNUNET_CONSENSUS_create (cfg, ks->num_peers, ks->peers,
1635 &ks->session_id,
1636 time_between (ks->start_time,
1637 ks->deadline, 1, 2),
1638 ks->deadline,
1639 keygen_round2_new_element, ks);
1640
1641 insert_round2_element (ks);
1642
1643 GNUNET_CONSENSUS_conclude (ks->consensus,
1644 keygen_round2_conclude,
1645 ks);
1646}
1647
1648
1649/**
1650 * Insert the ephemeral key and the presecret commitment
1651 * of this peer in the consensus of the given session.
1652 *
1653 * @param ks session to use
1654 */
1655static void
1656insert_round1_element (struct KeygenSession *ks)
1657{
1658 struct GNUNET_SET_Element *element;
1659 struct GNUNET_SECRETSHARING_KeygenCommitData *d;
1660 // g^a_{i,0}
1661 gcry_mpi_t v;
1662 // big-endian representation of 'v'
1663 unsigned char v_data[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
1664
1665 element = GNUNET_malloc (sizeof *element + sizeof *d);
1666 d = (void *) &element[1];
1667 element->data = d;
1668 element->size = sizeof *d;
1669
1670 d->peer = my_peer;
1671
1672 GNUNET_assert (0 != (v = gcry_mpi_new (GNUNET_SECRETSHARING_ELGAMAL_BITS)));
1673
1674 gcry_mpi_powm (v, elgamal_g, ks->presecret_polynomial[0], elgamal_p);
1675
1676 GNUNET_CRYPTO_mpi_print_unsigned (v_data, GNUNET_SECRETSHARING_ELGAMAL_BITS
1677 / 8, v);
1678
1679 GNUNET_CRYPTO_hash (v_data, GNUNET_SECRETSHARING_ELGAMAL_BITS / 8,
1680 &d->commitment);
1681
1682 d->pubkey = ks->info[ks->local_peer_idx].paillier_public_key;
1683
1684 d->purpose.size = htonl ((sizeof *d) - offsetof (struct
1685 GNUNET_SECRETSHARING_KeygenCommitData,
1686 purpose));
1687 d->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DKG1);
1688 GNUNET_assert (GNUNET_OK ==
1689 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
1690 &d->purpose,
1691 &d->signature));
1692
1693 GNUNET_CONSENSUS_insert (ks->consensus, element, NULL, NULL);
1694
1695 gcry_mpi_release (v);
1696 GNUNET_free (element);
1697}
1698
1699
1700/**
1701 * Check that @a msg is well-formed.
1702 *
1703 * @param cls identification of the client
1704 * @param msg the actual message
1705 * @return #GNUNET_OK if @a msg is well-formed
1706 */
1707static int
1708check_client_keygen (void *cls,
1709 const struct GNUNET_SECRETSHARING_CreateMessage *msg)
1710{
1711 unsigned int num_peers = ntohs (msg->num_peers);
1712
1713 if (ntohs (msg->header.size) - sizeof(*msg) !=
1714 num_peers * sizeof(struct GNUNET_PeerIdentity))
1715 {
1716 GNUNET_break (0);
1717 return GNUNET_SYSERR;
1718 }
1719 return GNUNET_OK;
1720}
1721
1722
1723/**
1724 * Functions with this signature are called whenever a message is
1725 * received.
1726 *
1727 * @param cls identification of the client
1728 * @param msg the actual message
1729 */
1730static void
1731handle_client_keygen (void *cls,
1732 const struct GNUNET_SECRETSHARING_CreateMessage *msg)
1733{
1734 struct ClientState *cs = cls;
1735 struct KeygenSession *ks;
1736
1737 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1738 "client requested key generation\n");
1739 if (NULL != cs->keygen_session)
1740 {
1741 GNUNET_break (0);
1742 GNUNET_SERVICE_client_drop (cs->client);
1743 return;
1744 }
1745 ks = GNUNET_new (struct KeygenSession);
1746 ks->cs = cs;
1747 cs->keygen_session = ks;
1748 ks->deadline = GNUNET_TIME_absolute_ntoh (msg->deadline);
1749 ks->threshold = ntohs (msg->threshold);
1750 ks->num_peers = ntohs (msg->num_peers);
1751
1752 ks->peers = normalize_peers ((struct GNUNET_PeerIdentity *) &msg[1],
1753 ks->num_peers,
1754 &ks->num_peers,
1755 &ks->local_peer_idx);
1756
1757
1758 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1759 "first round of consensus with %u peers\n",
1760 ks->num_peers);
1761 ks->consensus = GNUNET_CONSENSUS_create (cfg,
1762 ks->num_peers,
1763 ks->peers,
1764 &msg->session_id,
1765 GNUNET_TIME_absolute_ntoh (
1766 msg->start),
1767 GNUNET_TIME_absolute_ntoh (
1768 msg->deadline),
1769 keygen_round1_new_element,
1770 ks);
1771
1772 ks->info = GNUNET_new_array (ks->num_peers,
1773 struct KeygenPeerInfo);
1774
1775 for (unsigned int i = 0; i < ks->num_peers; i++)
1776 ks->info[i].peer = ks->peers[i];
1777
1778 GNUNET_CRYPTO_paillier_create (
1779 &ks->info[ks->local_peer_idx].paillier_public_key,
1780 &ks->paillier_private_key);
1781
1782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1783 "P%u: Generated paillier key pair\n",
1784 ks->local_peer_idx);
1785 generate_presecret_polynomial (ks);
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 "P%u: Generated presecret polynomial\n",
1788 ks->local_peer_idx);
1789 insert_round1_element (ks);
1790 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1791 "P%u: Concluding for round 1\n",
1792 ks->local_peer_idx);
1793 GNUNET_CONSENSUS_conclude (ks->consensus,
1794 keygen_round1_conclude,
1795 ks);
1796 GNUNET_SERVICE_client_continue (cs->client);
1797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1798 "P%u: Waiting for round 1 elements ...\n",
1799 ks->local_peer_idx);
1800}
1801
1802
1803/**
1804 * Called when the partial decryption consensus concludes.
1805 */
1806static void
1807decrypt_conclude (void *cls)
1808{
1809 struct DecryptSession *ds = cls;
1810 struct GNUNET_SECRETSHARING_DecryptResponseMessage *msg;
1811 struct GNUNET_MQ_Envelope *ev;
1812 gcry_mpi_t lagrange;
1813 gcry_mpi_t m;
1814 gcry_mpi_t tmp;
1815 gcry_mpi_t c_2;
1816 gcry_mpi_t prod;
1817 unsigned int *indices;
1818 unsigned int num;
1819 unsigned int i;
1820 unsigned int j;
1821
1822 GNUNET_CONSENSUS_destroy (ds->consensus);
1823 ds->consensus = NULL;
1824
1825 GNUNET_assert (0 != (lagrange = gcry_mpi_new (0)));
1826 GNUNET_assert (0 != (m = gcry_mpi_new (0)));
1827 GNUNET_assert (0 != (tmp = gcry_mpi_new (0)));
1828 GNUNET_assert (0 != (prod = gcry_mpi_new (0)));
1829
1830 num = 0;
1831 for (i = 0; i < ds->share->num_peers; i++)
1832 if (NULL != ds->info[i].partial_decryption)
1833 num++;
1834
1835 indices = GNUNET_new_array (num,
1836 unsigned int);
1837 j = 0;
1838 for (i = 0; i < ds->share->num_peers; i++)
1839 if (NULL != ds->info[i].partial_decryption)
1840 indices[j++] = ds->info[i].original_index;
1841
1842 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1843 "P%u: decrypt conclude, with %u peers\n",
1844 ds->share->my_peer,
1845 num);
1846
1847 gcry_mpi_set_ui (prod, 1);
1848 for (i = 0; i < num; i++)
1849 {
1850 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1851 "P%u: index of %u: %u\n",
1852 ds->share->my_peer, i, indices[i]);
1853 compute_lagrange_coefficient (lagrange, indices[i], indices, num);
1854 // w_i^{\lambda_i}
1855 gcry_mpi_powm (tmp, ds->info[indices[i]].partial_decryption, lagrange,
1856 elgamal_p);
1857
1858 // product of all exponentiated partiel decryptions ...
1859 gcry_mpi_mulm (prod, prod, tmp, elgamal_p);
1860 }
1861
1862 GNUNET_CRYPTO_mpi_scan_unsigned (&c_2, ds->ciphertext.c2_bits,
1863 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
1864
1865 GNUNET_assert (0 != gcry_mpi_invm (prod, prod, elgamal_p));
1866 gcry_mpi_mulm (m, c_2, prod, elgamal_p);
1867 ev = GNUNET_MQ_msg (msg,
1868 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE);
1869 GNUNET_CRYPTO_mpi_print_unsigned (&msg->plaintext,
1870 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, m);
1871 msg->success = htonl (1);
1872 GNUNET_MQ_send (ds->cs->mq,
1873 ev);
1874
1875 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "sent decrypt done to client\n");
1876
1877 GNUNET_free (indices);
1878
1879 gcry_mpi_release (lagrange);
1880 gcry_mpi_release (m);
1881 gcry_mpi_release (tmp);
1882 gcry_mpi_release (prod);
1883 gcry_mpi_release (c_2);
1884
1885 // FIXME: what if not enough peers participated?
1886}
1887
1888
1889/**
1890 * Get a string representation of an MPI.
1891 * The caller must free the returned string.
1892 *
1893 * @param mpi mpi to convert to a string
1894 * @return string representation of @a mpi, must be free'd by the caller
1895 */
1896static char *
1897mpi_to_str (gcry_mpi_t mpi)
1898{
1899 unsigned char *buf;
1900
1901 GNUNET_assert (0 == gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, mpi));
1902 return (char *) buf;
1903}
1904
1905
1906/**
1907 * Called when a new partial decryption arrives.
1908 */
1909static void
1910decrypt_new_element (void *cls,
1911 const struct GNUNET_SET_Element *element)
1912{
1913 struct DecryptSession *session = cls;
1914 const struct GNUNET_SECRETSHARING_DecryptData *d;
1915 struct DecryptPeerInfo *info;
1916 struct GNUNET_HashCode challenge_hash;
1917
1918 /* nizk response */
1919 gcry_mpi_t r;
1920 /* nizk challenge */
1921 gcry_mpi_t challenge;
1922 /* nizk commit1, g^\beta */
1923 gcry_mpi_t commit1;
1924 /* nizk commit2, c_1^\beta */
1925 gcry_mpi_t commit2;
1926 /* homomorphic commitment to the peer's share,
1927 * public key share */
1928 gcry_mpi_t sigma;
1929 /* partial decryption we received */
1930 gcry_mpi_t w;
1931 /* ciphertext component #1 */
1932 gcry_mpi_t c1;
1933 /* temporary variable (for comparison) #1 */
1934 gcry_mpi_t tmp1;
1935 /* temporary variable (for comparison) #2 */
1936 gcry_mpi_t tmp2;
1937
1938 if (NULL == element)
1939 {
1940 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decryption failed\n");
1941 /* FIXME: destroy */
1942 return;
1943 }
1944
1945 if (element->size != sizeof *d)
1946 {
1947 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1948 "element of wrong size in decrypt consensus\n");
1949 return;
1950 }
1951
1952 d = element->data;
1953
1954 info = get_decrypt_peer_info (session, &d->peer);
1955
1956 if (NULL == info)
1957 {
1958 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1959 "decrypt element from invalid peer (%s)\n",
1960 GNUNET_i2s (&d->peer));
1961 return;
1962 }
1963
1964 if (NULL != info->partial_decryption)
1965 {
1966 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "decrypt element duplicate\n");
1967 return;
1968 }
1969
1970 if (0 != GNUNET_memcmp (&d->ciphertext, &session->ciphertext))
1971 {
1972 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1973 "P%u: got decrypt element with non-matching ciphertext from P%u\n",
1974 (unsigned int) session->share->my_peer, (unsigned int) (info
1975 -
1976 session
1977 ->info));
1978
1979 return;
1980 }
1981
1982
1983 GNUNET_CRYPTO_hash (offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1984 ciphertext) + (char *) d,
1985 offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1986 nizk_response)
1987 - offsetof (struct GNUNET_SECRETSHARING_DecryptData,
1988 ciphertext),
1989 &challenge_hash);
1990
1991 GNUNET_CRYPTO_mpi_scan_unsigned (&challenge, &challenge_hash,
1992 sizeof(struct GNUNET_HashCode));
1993
1994 GNUNET_CRYPTO_mpi_scan_unsigned (&sigma, &session->share->sigmas[info
1995 - session->
1996 info],
1997 sizeof(struct
1998 GNUNET_SECRETSHARING_FieldElement));
1999
2000 GNUNET_CRYPTO_mpi_scan_unsigned (&c1, session->ciphertext.c1_bits,
2001 sizeof(struct
2002 GNUNET_SECRETSHARING_FieldElement));
2003
2004 GNUNET_CRYPTO_mpi_scan_unsigned (&commit1, &d->nizk_commit1,
2005 sizeof(struct
2006 GNUNET_SECRETSHARING_FieldElement));
2007
2008 GNUNET_CRYPTO_mpi_scan_unsigned (&commit2, &d->nizk_commit2,
2009 sizeof(struct
2010 GNUNET_SECRETSHARING_FieldElement));
2011
2012 GNUNET_CRYPTO_mpi_scan_unsigned (&r, &d->nizk_response,
2013 sizeof(struct
2014 GNUNET_SECRETSHARING_FieldElement));
2015
2016 GNUNET_CRYPTO_mpi_scan_unsigned (&w, &d->partial_decryption,
2017 sizeof(struct
2018 GNUNET_SECRETSHARING_FieldElement));
2019
2020 GNUNET_assert (NULL != (tmp1 = gcry_mpi_new (0)));
2021 GNUNET_assert (NULL != (tmp2 = gcry_mpi_new (0)));
2022
2023 // tmp1 = g^r
2024 gcry_mpi_powm (tmp1, elgamal_g, r, elgamal_p);
2025
2026 // tmp2 = g^\beta * \sigma^challenge
2027 gcry_mpi_powm (tmp2, sigma, challenge, elgamal_p);
2028 gcry_mpi_mulm (tmp2, tmp2, commit1, elgamal_p);
2029
2030 if (0 != gcry_mpi_cmp (tmp1, tmp2))
2031 {
2032 char *tmp1_str;
2033 char *tmp2_str;
2034
2035 tmp1_str = mpi_to_str (tmp1);
2036 tmp2_str = mpi_to_str (tmp2);
2037 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2038 "P%u: Received invalid partial decryption from P%u (eqn 1), expected %s got %s\n",
2039 session->share->my_peer,
2040 (unsigned int) (info - session->info),
2041 tmp1_str,
2042 tmp2_str);
2043 GNUNET_free (tmp1_str);
2044 GNUNET_free (tmp2_str);
2045 goto cleanup;
2046 }
2047
2048
2049 gcry_mpi_powm (tmp1, c1, r, elgamal_p);
2050
2051 gcry_mpi_powm (tmp2, w, challenge, elgamal_p);
2052 gcry_mpi_mulm (tmp2, tmp2, commit2, elgamal_p);
2053
2054
2055 if (0 != gcry_mpi_cmp (tmp1, tmp2))
2056 {
2057 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2058 "P%u: Received invalid partial decryption from P%u (eqn 2)\n",
2059 session->share->my_peer,
2060 (unsigned int) (info - session->info));
2061 goto cleanup;
2062 }
2063
2064
2065 GNUNET_CRYPTO_mpi_scan_unsigned (&info->partial_decryption,
2066 &d->partial_decryption,
2067 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2068cleanup:
2069 gcry_mpi_release (tmp1);
2070 gcry_mpi_release (tmp2);
2071 gcry_mpi_release (sigma);
2072 gcry_mpi_release (commit1);
2073 gcry_mpi_release (commit2);
2074 gcry_mpi_release (r);
2075 gcry_mpi_release (w);
2076 gcry_mpi_release (challenge);
2077 gcry_mpi_release (c1);
2078}
2079
2080
2081static void
2082insert_decrypt_element (struct DecryptSession *ds)
2083{
2084 struct GNUNET_SECRETSHARING_DecryptData d;
2085 struct GNUNET_SET_Element element;
2086 /* our share */
2087 gcry_mpi_t s;
2088 /* partial decryption with our share */
2089 gcry_mpi_t w;
2090 /* first component of the elgamal ciphertext */
2091 gcry_mpi_t c1;
2092 /* nonce for dlog zkp */
2093 gcry_mpi_t beta;
2094 gcry_mpi_t tmp;
2095 gcry_mpi_t challenge;
2096 gcry_mpi_t sigma;
2097 struct GNUNET_HashCode challenge_hash;
2098
2099 /* make vagrind happy until we implement the real deal ... */
2100 memset (&d, 0, sizeof d);
2101
2102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "P%u: Inserting decrypt element\n",
2103 ds->share->my_peer);
2104
2105 GNUNET_assert (ds->share->my_peer < ds->share->num_peers);
2106
2107 GNUNET_CRYPTO_mpi_scan_unsigned (&c1, &ds->ciphertext.c1_bits,
2108 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2109 GNUNET_CRYPTO_mpi_scan_unsigned (&s, &ds->share->my_share,
2110 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2111 GNUNET_CRYPTO_mpi_scan_unsigned (&sigma,
2112 &ds->share->sigmas[ds->share->my_peer],
2113 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8);
2114
2115 GNUNET_assert (NULL != (w = gcry_mpi_new (0)));
2116 GNUNET_assert (NULL != (beta = gcry_mpi_new (0)));
2117 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
2118
2119 // FIXME: unnecessary, remove once crypto works
2120 gcry_mpi_powm (tmp, elgamal_g, s, elgamal_p);
2121 if (0 != gcry_mpi_cmp (tmp, sigma))
2122 {
2123 char *sigma_str = mpi_to_str (sigma);
2124 char *tmp_str = mpi_to_str (tmp);
2125 char *s_str = mpi_to_str (s);
2126 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2127 "Share of P%u is invalid, ref sigma %s, "
2128 "computed sigma %s, s %s\n",
2129 ds->share->my_peer,
2130 sigma_str, tmp_str, s_str);
2131 GNUNET_free (sigma_str);
2132 GNUNET_free (tmp_str);
2133 GNUNET_free (s_str);
2134 }
2135
2136 gcry_mpi_powm (w, c1, s, elgamal_p);
2137
2138 element.data = (void *) &d;
2139 element.size = sizeof(struct GNUNET_SECRETSHARING_DecryptData);
2140 element.element_type = 0;
2141
2142 d.ciphertext = ds->ciphertext;
2143 d.peer = my_peer;
2144 GNUNET_CRYPTO_mpi_print_unsigned (&d.partial_decryption,
2145 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, w);
2146
2147 // create the zero knowledge proof
2148 // randomly choose beta such that 0 < beta < q
2149 do
2150 {
2151 gcry_mpi_randomize (beta, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
2152 GCRY_WEAK_RANDOM);
2153 }
2154 while ((gcry_mpi_cmp_ui (beta, 0) == 0) || (gcry_mpi_cmp (beta, elgamal_q) >=
2155 0));
2156 // tmp = g^beta
2157 gcry_mpi_powm (tmp, elgamal_g, beta, elgamal_p);
2158 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_commit1,
2159 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2160 // tmp = (c_1)^beta
2161 gcry_mpi_powm (tmp, c1, beta, elgamal_p);
2162 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_commit2,
2163 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2164
2165 // the challenge is the hash of everything up to the response
2166 GNUNET_CRYPTO_hash (offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2167 ciphertext) + (char *) &d,
2168 offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2169 nizk_response)
2170 - offsetof (struct GNUNET_SECRETSHARING_DecryptData,
2171 ciphertext),
2172 &challenge_hash);
2173
2174 GNUNET_CRYPTO_mpi_scan_unsigned (&challenge, &challenge_hash,
2175 sizeof(struct GNUNET_HashCode));
2176
2177 // compute the response in tmp,
2178 // tmp = (c * s + beta) mod q
2179 gcry_mpi_mulm (tmp, challenge, s, elgamal_q);
2180 gcry_mpi_addm (tmp, tmp, beta, elgamal_q);
2181
2182 GNUNET_CRYPTO_mpi_print_unsigned (&d.nizk_response,
2183 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
2184
2185 d.purpose.size = htonl (element.size - offsetof (struct
2186 GNUNET_SECRETSHARING_DecryptData,
2187 purpose));
2188 d.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SECRETSHARING_DECRYPTION);
2189
2190 GNUNET_assert (GNUNET_OK ==
2191 GNUNET_CRYPTO_eddsa_sign_ (my_peer_private_key,
2192 &d.purpose,
2193 &d.signature));
2194
2195 GNUNET_CONSENSUS_insert (ds->consensus, &element, NULL, NULL);
2196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197 "P%u: Inserting decrypt element done!\n",
2198 ds->share->my_peer);
2199
2200 gcry_mpi_release (s);
2201 gcry_mpi_release (w);
2202 gcry_mpi_release (c1);
2203 gcry_mpi_release (beta);
2204 gcry_mpi_release (tmp);
2205 gcry_mpi_release (challenge);
2206 gcry_mpi_release (sigma);
2207}
2208
2209
2210/**
2211 * Check that @a msg is well-formed.
2212 *
2213 * @param cls identification of the client
2214 * @param msg the actual message
2215 * @return #GNUNET_OK (check deferred a bit)
2216 */
2217static int
2218check_client_decrypt (void *cls,
2219 const struct
2220 GNUNET_SECRETSHARING_DecryptRequestMessage *msg)
2221{
2222 /* we check later, it's complicated */
2223 return GNUNET_OK;
2224}
2225
2226
2227/**
2228 * Functions with this signature are called whenever a message is
2229 * received.
2230 *
2231 * @param cls identification of the client
2232 * @param msg the actual message
2233 */
2234static void
2235handle_client_decrypt (void *cls,
2236 const struct
2237 GNUNET_SECRETSHARING_DecryptRequestMessage *msg)
2238{
2239 struct ClientState *cs = cls;
2240 struct DecryptSession *ds;
2241 struct GNUNET_HashCode session_id;
2242
2243 if (NULL != cs->decrypt_session)
2244 {
2245 GNUNET_break (0);
2246 GNUNET_SERVICE_client_drop (cs->client);
2247 return;
2248 }
2249 ds = GNUNET_new (struct DecryptSession);
2250 cs->decrypt_session = ds;
2251 ds->cs = cs;
2252 ds->start = GNUNET_TIME_absolute_ntoh (msg->start);
2253 ds->deadline = GNUNET_TIME_absolute_ntoh (msg->deadline);
2254 ds->ciphertext = msg->ciphertext;
2255
2256 ds->share = GNUNET_SECRETSHARING_share_read (&msg[1],
2257 ntohs (msg->header.size)
2258 - sizeof(*msg),
2259 NULL);
2260 if (NULL == ds->share)
2261 {
2262 GNUNET_break (0);
2263 GNUNET_SERVICE_client_drop (cs->client);
2264 return;
2265 }
2266
2267 /* FIXME: this is probably sufficient, but kdf/hash with all values would be nicer ... */
2268 GNUNET_CRYPTO_hash (&msg->ciphertext,
2269 sizeof(struct GNUNET_SECRETSHARING_Ciphertext),
2270 &session_id);
2271 ds->consensus = GNUNET_CONSENSUS_create (cfg,
2272 ds->share->num_peers,
2273 ds->share->peers,
2274 &session_id,
2275 ds->start,
2276 ds->deadline,
2277 &decrypt_new_element,
2278 ds);
2279
2280
2281 ds->info = GNUNET_new_array (ds->share->num_peers,
2282 struct DecryptPeerInfo);
2283 for (unsigned int i = 0; i < ds->share->num_peers; i++)
2284 {
2285 ds->info[i].peer = ds->share->peers[i];
2286 ds->info[i].original_index = ds->share->original_indices[i];
2287 }
2288 insert_decrypt_element (ds);
2289 GNUNET_CONSENSUS_conclude (ds->consensus,
2290 decrypt_conclude,
2291 ds);
2292 GNUNET_SERVICE_client_continue (cs->client);
2293 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2294 "decrypting with %u peers\n",
2295 ds->share->num_peers);
2296}
2297
2298
2299static void
2300init_crypto_constants (void)
2301{
2302 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
2303 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0,
2304 NULL));
2305 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
2306 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0,
2307 NULL));
2308 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
2309 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0,
2310 NULL));
2311}
2312
2313
2314/**
2315 * Initialize secretsharing service.
2316 *
2317 * @param cls closure
2318 * @param c configuration to use
2319 * @param service the initialized service
2320 */
2321static void
2322run (void *cls,
2323 const struct GNUNET_CONFIGURATION_Handle *c,
2324 struct GNUNET_SERVICE_Handle *service)
2325{
2326 cfg = c;
2327 my_peer_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (c);
2328 if (NULL == my_peer_private_key)
2329 {
2330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2331 "could not access host private key\n");
2332 GNUNET_break (0);
2333 GNUNET_SCHEDULER_shutdown ();
2334 return;
2335 }
2336 init_crypto_constants ();
2337 if (GNUNET_OK !=
2338 GNUNET_CRYPTO_get_peer_identity (cfg,
2339 &my_peer))
2340 {
2341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2342 "could not retrieve host identity\n");
2343 GNUNET_break (0);
2344 GNUNET_SCHEDULER_shutdown ();
2345 return;
2346 }
2347 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
2348 NULL);
2349}
2350
2351
2352/**
2353 * Callback called when a client connects to the service.
2354 *
2355 * @param cls closure for the service
2356 * @param c the new client that connected to the service
2357 * @param mq the message queue used to send messages to the client
2358 * @return @a c
2359 */
2360static void *
2361client_connect_cb (void *cls,
2362 struct GNUNET_SERVICE_Client *c,
2363 struct GNUNET_MQ_Handle *mq)
2364{
2365 struct ClientState *cs = GNUNET_new (struct ClientState);;
2366
2367 cs->client = c;
2368 cs->mq = mq;
2369 return cs;
2370}
2371
2372
2373/**
2374 * Callback called when a client disconnected from the service
2375 *
2376 * @param cls closure for the service
2377 * @param c the client that disconnected
2378 * @param internal_cls should be equal to @a c
2379 */
2380static void
2381client_disconnect_cb (void *cls,
2382 struct GNUNET_SERVICE_Client *c,
2383 void *internal_cls)
2384{
2385 struct ClientState *cs = internal_cls;
2386
2387 if (NULL != cs->keygen_session)
2388 keygen_session_destroy (cs->keygen_session);
2389
2390 if (NULL != cs->decrypt_session)
2391 decrypt_session_destroy (cs->decrypt_session);
2392 GNUNET_free (cs);
2393}
2394
2395
2396/**
2397 * Define "main" method using service macro.
2398 */
2399GNUNET_SERVICE_MAIN
2400 ("secretsharing",
2401 GNUNET_SERVICE_OPTION_NONE,
2402 &run,
2403 &client_connect_cb,
2404 &client_disconnect_cb,
2405 NULL,
2406 GNUNET_MQ_hd_var_size (client_keygen,
2407 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE,
2408 struct GNUNET_SECRETSHARING_CreateMessage,
2409 NULL),
2410 GNUNET_MQ_hd_var_size (client_decrypt,
2411 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT,
2412 struct GNUNET_SECRETSHARING_DecryptRequestMessage,
2413 NULL),
2414 GNUNET_MQ_handler_end ());
diff --git a/src/contrib/service/secretsharing/meson.build b/src/contrib/service/secretsharing/meson.build
new file mode 100644
index 000000000..5f9935793
--- /dev/null
+++ b/src/contrib/service/secretsharing/meson.build
@@ -0,0 +1,45 @@
1libgnunetsecretsharing_src = ['secretsharing_api.c', 'secretsharing_common.c']
2
3gnunetservicesecretsharing_src = ['gnunet-service-secretsharing.c', 'secretsharing_common.c']
4
5configure_file(input : 'secretsharing.conf.in',
6 output : 'secretsharing.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11# FIXME needs new seti/setu
12if get_option('monolith')
13 #foreach p : libgnunetsecretsharing_src + gnunetservicesecretsharing_src
14 # gnunet_src += 'secretsharing/' + p
15 #endforeach
16endif
17
18libgnunetsecretsharing = library('gnunetsecretsharing',
19 libgnunetsecretsharing_src,
20 soversion: '0',
21 version: '0.0.0',
22 dependencies: [libgnunetutil_dep,
23 libgnunetstatistics_dep,
24 gcrypt_dep,
25 libgnunetdatacache_dep],
26 include_directories: [incdir, configuration_inc],
27 install: true,
28 install_dir: get_option('libdir'))
29libgnunetsecretsharing_dep = declare_dependency(link_with : libgnunetsecretsharing)
30pkg.generate(libgnunetsecretsharing, url: 'https://www.gnunet.org',
31 description : 'Provides API for the secretsharing service')
32
33executable ('gnunet-service-secretsharing',
34 gnunetservicesecretsharing_src,
35 dependencies: [libgnunetsecretsharing_dep,
36 libgnunetutil_dep,
37 gcrypt_dep,
38 m_dep,
39 libgnunetconsensus_dep,
40 libgnunetstatistics_dep,
41 libgnunetdatacache_dep],
42 include_directories: [incdir, configuration_inc],
43 install: true,
44 install_dir: get_option('libdir')/'gnunet'/'libexec')
45
diff --git a/src/contrib/service/secretsharing/secretsharing.conf.in b/src/contrib/service/secretsharing/secretsharing.conf.in
new file mode 100644
index 000000000..df8566abd
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing.conf.in
@@ -0,0 +1,20 @@
1[secretsharing]
2START_ON_DEMAND = NO
3@JAVAPORT@PORT = 2114
4HOSTNAME = localhost
5BINARY = gnunet-service-secretsharing
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-secretsharing.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
11# PREFIX = valgrind --leak-check=yes
12# DISABLE_SOCKET_FORWARDING = NO
13# USERNAME =
14# MAXBUF =
15# TIMEOUT =
16# DISABLEV6 =
17# BINDTO =
18# REJECT_FROM =
19# REJECT_FROM6 =
20# PREFIX =
diff --git a/src/contrib/service/secretsharing/secretsharing.h b/src/contrib/service/secretsharing/secretsharing.h
new file mode 100644
index 000000000..6e104ebfa
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing.h
@@ -0,0 +1,227 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Florian Dold
23 * @file secretsharing/secretsharing.h
24 * @brief messages used for the secretsharing api
25 */
26#ifndef SECRETSHARING_H
27#define SECRETSHARING_H
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_time_lib.h"
32#include "gnunet_common.h"
33#include "gnunet_secretsharing_service.h"
34
35
36GNUNET_NETWORK_STRUCT_BEGIN
37
38struct GNUNET_SECRETSHARING_FieldElement
39{
40 /**
41 * Value of an element in &lt;elgamal_g&gt;.
42 */
43 unsigned char bits[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
44};
45
46
47struct GNUNET_SECRETSHARING_CreateMessage
48{
49 /**
50 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE
51 */
52 struct GNUNET_MessageHeader header;
53
54 /**
55 * Session ID, will be used for consensus.
56 */
57 struct GNUNET_HashCode session_id GNUNET_PACKED;
58
59 /**
60 * Start time for communication with the other peers.
61 */
62 struct GNUNET_TIME_AbsoluteNBO start;
63
64 /**
65 * Deadline for the establishment of the crypto system.
66 */
67 struct GNUNET_TIME_AbsoluteNBO deadline;
68
69 /**
70 * Minimum number of cooperating peers to decrypt a
71 * value.
72 */
73 uint16_t threshold GNUNET_PACKED;
74
75 /**
76 * Number of peers at the end of this message.
77 */
78 uint16_t num_peers GNUNET_PACKED;
79
80 /* struct GNUNET_PeerIdentity[num_peers]; */
81};
82
83
84struct GNUNET_SECRETSHARING_ShareHeaderNBO
85{
86 /**
87 * Threshold for the key this share belongs to.
88 */
89 uint16_t threshold;
90
91 /**
92 * Peers that have the share.
93 */
94 uint16_t num_peers;
95
96 /**
97 * Index of our peer in the list.
98 */
99 uint16_t my_peer;
100
101 /**
102 * Public key. Must correspond to the product of
103 * the homomorphic share commitments.
104 */
105 struct GNUNET_SECRETSHARING_PublicKey public_key;
106
107 /**
108 * Share of 'my_peer'
109 */
110 struct GNUNET_SECRETSHARING_FieldElement my_share;
111};
112
113
114/**
115 * Notify the client that then threshold secret has been
116 * established.
117 */
118struct GNUNET_SECRETSHARING_SecretReadyMessage
119{
120 /**
121 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY
122 */
123 struct GNUNET_MessageHeader header;
124
125 /* rest: the serialized share */
126};
127
128
129struct GNUNET_SECRETSHARING_DecryptRequestMessage
130{
131 /**
132 * Type: GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_REQUEST
133 */
134 struct GNUNET_MessageHeader header;
135
136 /**
137 * Until when should the decryption start?
138 */
139 struct GNUNET_TIME_AbsoluteNBO start;
140
141 /**
142 * Until when should the decryption be finished?
143 */
144 struct GNUNET_TIME_AbsoluteNBO deadline;
145
146 /**
147 * Ciphertext we want to decrypt.
148 */
149 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
150
151 /* the share with payload */
152};
153
154
155struct GNUNET_SECRETSHARING_DecryptResponseMessage
156{
157 /**
158 * Type: #GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE
159 */
160 struct GNUNET_MessageHeader header;
161
162 /**
163 * Zero if decryption failed, non-zero if decryption succeeded.
164 * If the decryption failed, plaintext is also zero.
165 */
166 uint32_t success GNUNET_PACKED;
167
168 /**
169 * Decrypted plaintext.
170 */
171 struct GNUNET_SECRETSHARING_FieldElement plaintext;
172};
173
174
175GNUNET_NETWORK_STRUCT_END
176
177
178/**
179 * A share, with all values in in host byte order.
180 */
181struct GNUNET_SECRETSHARING_Share
182{
183 /**
184 * Threshold for the key this share belongs to.
185 */
186 uint16_t threshold;
187
188 /**
189 * Peers that have the share.
190 */
191 uint16_t num_peers;
192
193 /**
194 * Index of our peer in the list.
195 */
196 uint16_t my_peer;
197
198 /**
199 * Public key. Computed from the
200 * exponentiated coefficients.
201 */
202 struct GNUNET_SECRETSHARING_PublicKey public_key;
203
204 /**
205 * Share of 'my_peer'
206 */
207 struct GNUNET_SECRETSHARING_FieldElement my_share;
208
209 /**
210 * Peer identities (includes 'my_peer')
211 */
212 struct GNUNET_PeerIdentity *peers;
213
214 /*
215 * For each peer, store elgamal_g to the peer's
216 * share.
217 */
218 struct GNUNET_SECRETSHARING_FieldElement *sigmas;
219
220 /*
221 * Original indices of peers from the DKG round.
222 */
223 uint16_t *original_indices;
224};
225
226
227#endif
diff --git a/src/contrib/service/secretsharing/secretsharing_api.c b/src/contrib/service/secretsharing/secretsharing_api.c
new file mode 100644
index 000000000..595af751f
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_api.c
@@ -0,0 +1,492 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/secretsharing_api.c
23 * @brief
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_secretsharing_service.h"
29#include "secretsharing.h"
30#include <gcrypt.h>
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "secretsharing-api", __VA_ARGS__)
34
35/**
36 * Session that will eventually establish a shared secred between
37 * the involved peers and allow encryption and cooperative decryption.
38 */
39struct GNUNET_SECRETSHARING_Session
40{
41 /**
42 * Message queue for @e client.
43 */
44 struct GNUNET_MQ_Handle *mq;
45
46 /**
47 * Called when the secret sharing is done.
48 */
49 GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
50
51 /**
52 * Closure for @e secret_ready_cb.
53 */
54 void *secret_ready_cls;
55};
56
57
58/**
59 * Handle to cancel a cooperative decryption operation.
60 */
61struct GNUNET_SECRETSHARING_DecryptionHandle
62{
63 /**
64 * Message queue for @e client.
65 */
66 struct GNUNET_MQ_Handle *mq;
67
68 /**
69 * Called when the secret sharing is done.
70 */
71 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
72
73 /**
74 * Closure for @e decrypt_cb.
75 */
76 void *decrypt_cls;
77};
78
79
80/**
81 * The ElGamal prime field order as libgcrypt mpi.
82 * Initialized in #init_crypto_constants.
83 */
84static gcry_mpi_t elgamal_q;
85
86/**
87 * Modulus of the prime field used for ElGamal.
88 * Initialized in #init_crypto_constants.
89 */
90static gcry_mpi_t elgamal_p;
91
92/**
93 * Generator for prime field of order 'elgamal_q'.
94 * Initialized in #init_crypto_constants.
95 */
96static gcry_mpi_t elgamal_g;
97
98
99/**
100 * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
101 */
102static void
103ensure_elgamal_initialized (void)
104{
105 if (NULL != elgamal_q)
106 return; /* looks like crypto is already initialized */
107
108 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_q, GCRYMPI_FMT_HEX,
109 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0,
110 NULL));
111 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_p, GCRYMPI_FMT_HEX,
112 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0,
113 NULL));
114 GNUNET_assert (0 == gcry_mpi_scan (&elgamal_g, GCRYMPI_FMT_HEX,
115 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0,
116 NULL));
117}
118
119
120/**
121 * Callback invoked when there is an error communicating with
122 * the service. Notifies the application about the error.
123 *
124 * @param cls the `struct GNUNET_SECRETSHARING_Session`
125 * @param error error code
126 */
127static void
128handle_session_client_error (void *cls,
129 enum GNUNET_MQ_Error error)
130{
131 struct GNUNET_SECRETSHARING_Session *s = cls;
132
133 s->secret_ready_cb (s->secret_ready_cls, NULL, NULL, 0, NULL);
134 GNUNET_SECRETSHARING_session_destroy (s);
135}
136
137
138/**
139 * Callback invoked when there is an error communicating with
140 * the service. Notifies the application about the error.
141 *
142 * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
143 * @param error error code
144 */
145static void
146handle_decrypt_client_error (void *cls,
147 enum GNUNET_MQ_Error error)
148{
149 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
150
151 dh->decrypt_cb (dh->decrypt_cls, NULL);
152 GNUNET_SECRETSHARING_decrypt_cancel (dh);
153}
154
155
156/**
157 * Handler invoked with the final result message from
158 * secret sharing. Decodes the message and passes the
159 * result to the application.
160 *
161 * @param cls the `struct GNUNET_SECRETSHARING_Session`
162 * @param m message with the result
163 */
164static int
165check_secret_ready (void *cls,
166 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
167{
168 /* FIXME: actually check m is well-formed here! */
169 return GNUNET_OK;
170}
171
172
173/**
174 * Handler invoked with the final result message from
175 * secret sharing. Decodes the message and passes the
176 * result to the application.
177 *
178 * @param cls the `struct GNUNET_SECRETSHARING_Session`
179 * @param m message with the result
180 */
181static void
182handle_secret_ready (void *cls,
183 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
184{
185 struct GNUNET_SECRETSHARING_Session *s = cls;
186 struct GNUNET_SECRETSHARING_Share *share;
187 size_t share_size;
188
189 LOG (GNUNET_ERROR_TYPE_DEBUG,
190 "Got secret ready message of size %u\n",
191 ntohs (m->header.size));
192 share_size = ntohs (m->header.size) - sizeof(struct
193 GNUNET_SECRETSHARING_SecretReadyMessage);
194
195 share = GNUNET_SECRETSHARING_share_read (&m[1],
196 share_size,
197 NULL);
198 GNUNET_assert (NULL != share); // FIXME: this can fail!
199 // should have been checked in #check_secret_ready!
200 // FIXME: below we never check &m[1] is valid!
201 // FIXME: do we leak 'share' here?
202 s->secret_ready_cb (s->secret_ready_cls,
203 share, /* FIXME */
204 &share->public_key,
205 share->num_peers,
206 (const struct GNUNET_PeerIdentity *) &m[1]);
207
208 GNUNET_SECRETSHARING_session_destroy (s);
209}
210
211
212/**
213 * Destroy a secret sharing session.
214 * The secret ready callback will not be called.
215 *
216 * @param s session to destroy
217 */
218void
219GNUNET_SECRETSHARING_session_destroy (struct GNUNET_SECRETSHARING_Session *s)
220{
221 GNUNET_MQ_destroy (s->mq);
222 s->mq = NULL;
223 GNUNET_free (s);
224}
225
226
227/**
228 * Create a session that will eventually establish a shared secret
229 * with the other peers.
230 *
231 * @param cfg configuration to use
232 * @param num_peers number of peers in @a peers
233 * @param peers array of peers that we will share secrets with, can optionally contain the local peer
234 * @param session_id unique session id
235 * @param start When should all peers be available for sharing the secret?
236 * Random number generation can take place before the start time.
237 * @param deadline point in time where the session must be established; taken as hint
238 * by underlying consensus sessions
239 * @param threshold minimum number of peers that must cooperate to decrypt a value
240 * @param cb called when the secret has been established
241 * @param cls closure for @a cb
242 */
243struct GNUNET_SECRETSHARING_Session *
244GNUNET_SECRETSHARING_create_session (const struct
245 GNUNET_CONFIGURATION_Handle *cfg,
246 unsigned int num_peers,
247 const struct GNUNET_PeerIdentity *peers,
248 const struct GNUNET_HashCode *session_id,
249 struct GNUNET_TIME_Absolute start,
250 struct GNUNET_TIME_Absolute deadline,
251 unsigned int threshold,
252 GNUNET_SECRETSHARING_SecretReadyCallback cb,
253 void *cls)
254{
255 struct GNUNET_SECRETSHARING_Session *s
256 = GNUNET_new (struct GNUNET_SECRETSHARING_Session);
257 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
258 GNUNET_MQ_hd_var_size (secret_ready,
259 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
260 struct GNUNET_SECRETSHARING_SecretReadyMessage,
261 s),
262 GNUNET_MQ_handler_end ()
263 };
264 struct GNUNET_MQ_Envelope *ev;
265 struct GNUNET_SECRETSHARING_CreateMessage *msg;
266
267 s->mq = GNUNET_CLIENT_connect (cfg,
268 "secretsharing",
269 mq_handlers,
270 &handle_session_client_error,
271 s);
272 if (NULL == s->mq)
273 {
274 /* secretsharing not configured correctly */
275 GNUNET_break (0);
276 GNUNET_free (s);
277 return NULL;
278 }
279 s->secret_ready_cb = cb;
280 s->secret_ready_cls = cls;
281 ev = GNUNET_MQ_msg_extra (msg,
282 num_peers * sizeof(struct GNUNET_PeerIdentity),
283 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
284
285 msg->threshold = htons (threshold);
286 msg->num_peers = htons (num_peers);
287 msg->session_id = *session_id;
288 msg->start = GNUNET_TIME_absolute_hton (start);
289 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
290 GNUNET_memcpy (&msg[1], peers, num_peers * sizeof(struct
291 GNUNET_PeerIdentity));
292
293 GNUNET_MQ_send (s->mq, ev);
294
295 LOG (GNUNET_ERROR_TYPE_DEBUG,
296 "Secretsharing session created with %u peers\n",
297 num_peers);
298 return s;
299}
300
301
302static void
303handle_decrypt_done (void *cls,
304 const struct
305 GNUNET_SECRETSHARING_DecryptResponseMessage *m)
306{
307 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
308 const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
309
310 if (m->success == 0)
311 plaintext = NULL;
312 else
313 plaintext = (void *) &m->plaintext;
314 dh->decrypt_cb (dh->decrypt_cls, plaintext);
315 GNUNET_SECRETSHARING_decrypt_cancel (dh);
316}
317
318
319struct GNUNET_SECRETSHARING_DecryptionHandle *
320GNUNET_SECRETSHARING_decrypt (const struct GNUNET_CONFIGURATION_Handle *cfg,
321 struct GNUNET_SECRETSHARING_Share *share,
322 const struct
323 GNUNET_SECRETSHARING_Ciphertext *ciphertext,
324 struct GNUNET_TIME_Absolute start,
325 struct GNUNET_TIME_Absolute deadline,
326 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
327 void *decrypt_cb_cls)
328{
329 struct GNUNET_SECRETSHARING_DecryptionHandle *s
330 = GNUNET_new (struct GNUNET_SECRETSHARING_DecryptionHandle);
331 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
332 GNUNET_MQ_hd_fixed_size (decrypt_done,
333 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
334 struct GNUNET_SECRETSHARING_DecryptResponseMessage,
335 s),
336 GNUNET_MQ_handler_end ()
337 };
338 struct GNUNET_MQ_Envelope *ev;
339 struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
340 size_t share_size;
341
342 s->decrypt_cb = decrypt_cb;
343 s->decrypt_cls = decrypt_cb_cls;
344 s->mq = GNUNET_CLIENT_connect (cfg,
345 "secretsharing",
346 mq_handlers,
347 &handle_decrypt_client_error,
348 s);
349 if (NULL == s->mq)
350 {
351 GNUNET_free (s);
352 return NULL;
353 }
354 GNUNET_assert (GNUNET_OK ==
355 GNUNET_SECRETSHARING_share_write (share, NULL, 0,
356 &share_size));
357
358 ev = GNUNET_MQ_msg_extra (msg,
359 share_size,
360 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
361
362 GNUNET_assert (GNUNET_OK ==
363 GNUNET_SECRETSHARING_share_write (share,
364 &msg[1],
365 share_size,
366 NULL));
367
368 msg->start = GNUNET_TIME_absolute_hton (start);
369 msg->deadline = GNUNET_TIME_absolute_hton (deadline);
370 msg->ciphertext = *ciphertext;
371
372 GNUNET_MQ_send (s->mq, ev);
373
374 LOG (GNUNET_ERROR_TYPE_DEBUG,
375 "decrypt session created\n");
376 return s;
377}
378
379
380int
381GNUNET_SECRETSHARING_plaintext_generate_i (struct
382 GNUNET_SECRETSHARING_Plaintext *
383 plaintext,
384 int64_t exponent)
385{
386 int negative;
387 gcry_mpi_t x;
388
389 ensure_elgamal_initialized ();
390
391 GNUNET_assert (NULL != (x = gcry_mpi_new (0)));
392
393 negative = GNUNET_NO;
394 if (exponent < 0)
395 {
396 negative = GNUNET_YES;
397 exponent = -exponent;
398 }
399
400 gcry_mpi_set_ui (x, exponent);
401
402 gcry_mpi_powm (x, elgamal_g, x, elgamal_p);
403
404 if (GNUNET_YES == negative)
405 {
406 int res;
407 res = gcry_mpi_invm (x, x, elgamal_p);
408 if (0 == res)
409 return GNUNET_SYSERR;
410 }
411
412 GNUNET_CRYPTO_mpi_print_unsigned (plaintext,
413 sizeof(struct
414 GNUNET_SECRETSHARING_Plaintext),
415 x);
416
417 return GNUNET_OK;
418}
419
420
421int
422GNUNET_SECRETSHARING_encrypt (const struct
423 GNUNET_SECRETSHARING_PublicKey *public_key,
424 const struct
425 GNUNET_SECRETSHARING_Plaintext *plaintext,
426 struct GNUNET_SECRETSHARING_Ciphertext *
427 result_ciphertext)
428{
429 /* pubkey */
430 gcry_mpi_t h;
431 /* nonce */
432 gcry_mpi_t y;
433 /* plaintext message */
434 gcry_mpi_t m;
435 /* temp value */
436 gcry_mpi_t tmp;
437
438 ensure_elgamal_initialized ();
439
440 GNUNET_assert (NULL != (h = gcry_mpi_new (0)));
441 GNUNET_assert (NULL != (y = gcry_mpi_new (0)));
442 GNUNET_assert (NULL != (tmp = gcry_mpi_new (0)));
443
444 GNUNET_CRYPTO_mpi_scan_unsigned (&h, public_key, sizeof *public_key);
445 GNUNET_CRYPTO_mpi_scan_unsigned (&m, plaintext, sizeof *plaintext);
446
447 // Randomize y such that 0 < y < elgamal_q.
448 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
449 do
450 {
451 gcry_mpi_randomize (y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1,
452 GCRY_WEAK_RANDOM);
453 }
454 while ((gcry_mpi_cmp_ui (y, 0) == 0) || (gcry_mpi_cmp (y, elgamal_q) >= 0));
455
456 // tmp <- g^y
457 gcry_mpi_powm (tmp, elgamal_g, y, elgamal_p);
458 // write tmp to c1
459 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c1_bits,
460 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
461
462 // tmp <- h^y
463 gcry_mpi_powm (tmp, h, y, elgamal_p);
464 // tmp <- tmp * m
465 gcry_mpi_mulm (tmp, tmp, m, elgamal_p);
466 // write tmp to c2
467 GNUNET_CRYPTO_mpi_print_unsigned (&result_ciphertext->c2_bits,
468 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
469
470 return GNUNET_OK;
471}
472
473
474/**
475 * Cancel a decryption.
476 *
477 * The decrypt_cb is not called anymore, but the calling
478 * peer may already have irrevocably contributed its share for the decryption of the value.
479 *
480 * @param dh to cancel
481 */
482void
483GNUNET_SECRETSHARING_decrypt_cancel (struct
484 GNUNET_SECRETSHARING_DecryptionHandle *dh)
485{
486 GNUNET_MQ_destroy (dh->mq);
487 dh->mq = NULL;
488 GNUNET_free (dh);
489}
490
491
492/* end of secretsharing_api.c */
diff --git a/src/contrib/service/secretsharing/secretsharing_common.c b/src/contrib/service/secretsharing/secretsharing_common.c
new file mode 100644
index 000000000..44b96b1c8
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_common.c
@@ -0,0 +1,159 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21#include "platform.h"
22#include "secretsharing.h"
23
24/**
25 * Read a share from its binary representation.
26 *
27 * @param data Binary representation of the share.
28 * @param len Length of @a data.
29 * @param[out] readlen Number of bytes read,
30 * ignored if NULL.
31 * @return The share, or NULL on error.
32 */
33struct GNUNET_SECRETSHARING_Share *
34GNUNET_SECRETSHARING_share_read (const void *data,
35 size_t len,
36 size_t *readlen)
37{
38 struct GNUNET_SECRETSHARING_Share *share;
39 const struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh = data;
40 const char *p;
41 size_t n;
42 uint16_t payload_size;
43
44 payload_size = ntohs (sh->num_peers)
45 * (sizeof(uint16_t) + sizeof(struct
46 GNUNET_SECRETSHARING_FieldElement)
47 + sizeof(struct GNUNET_PeerIdentity));
48
49 if (NULL != readlen)
50 *readlen = payload_size + sizeof *sh;
51
52 share = GNUNET_new (struct GNUNET_SECRETSHARING_Share);
53
54 share->threshold = ntohs (sh->threshold);
55 share->num_peers = ntohs (sh->num_peers);
56 share->my_peer = ntohs (sh->my_peer);
57
58 share->my_share = sh->my_share;
59 share->public_key = sh->public_key;
60
61 p = (const char *) &sh[1];
62
63 n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
64 share->peers = GNUNET_new_array (share->num_peers,
65 struct GNUNET_PeerIdentity);
66 GNUNET_memcpy (share->peers, p, n);
67 p += n;
68
69 n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
70 share->sigmas = GNUNET_new_array (share->num_peers,
71 struct GNUNET_SECRETSHARING_FieldElement);
72 GNUNET_memcpy (share->sigmas, p, n);
73 p += n;
74
75 n = share->num_peers * sizeof(uint16_t);
76 share->original_indices = GNUNET_new_array (share->num_peers,
77 uint16_t);
78 GNUNET_memcpy (share->original_indices, p, n);
79
80 return share;
81}
82
83
84/**
85 * Convert a share to its binary representation.
86 * Can be called with a NULL @a buf to get the size of the share.
87 *
88 * @param share Share to write.
89 * @param buf Buffer to write to.
90 * @param buflen Number of writable bytes in @a buf.
91 * @param[out] writelen Pointer to store number of bytes written,
92 * ignored if NULL.
93 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure.
94 */
95int
96GNUNET_SECRETSHARING_share_write (const struct
97 GNUNET_SECRETSHARING_Share *share,
98 void *buf, size_t buflen, size_t *writelen)
99{
100 uint16_t payload_size;
101 struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh;
102 char *p;
103 int n;
104
105 payload_size = share->num_peers
106 * (sizeof(uint16_t) + sizeof(struct
107 GNUNET_SECRETSHARING_FieldElement)
108 + sizeof(struct GNUNET_PeerIdentity));
109
110 if (NULL != writelen)
111 *writelen = payload_size + sizeof(struct
112 GNUNET_SECRETSHARING_ShareHeaderNBO);
113
114 /* just a query for the writelen */
115 if (buf == NULL)
116 return GNUNET_OK;
117
118 /* wrong buffer size */
119 if (buflen < payload_size + sizeof(struct
120 GNUNET_SECRETSHARING_ShareHeaderNBO))
121 return GNUNET_SYSERR;
122
123 sh = buf;
124
125 sh->threshold = htons (share->threshold);
126 sh->num_peers = htons (share->num_peers);
127 sh->my_peer = htons (share->my_peer);
128
129 sh->my_share = share->my_share;
130 sh->public_key = share->public_key;
131
132 p = (void *) &sh[1];
133
134 n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
135 GNUNET_memcpy (p, share->peers, n);
136 p += n;
137
138 n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
139 GNUNET_memcpy (p, share->sigmas, n);
140 p += n;
141
142 n = share->num_peers * sizeof(uint16_t);
143 GNUNET_memcpy (p, share->original_indices, n);
144
145 return GNUNET_OK;
146}
147
148
149void
150GNUNET_SECRETSHARING_share_destroy (struct GNUNET_SECRETSHARING_Share *share)
151{
152 GNUNET_free (share->original_indices);
153 share->original_indices = NULL;
154 GNUNET_free (share->sigmas);
155 share->sigmas = NULL;
156 GNUNET_free (share->peers);
157 share->peers = NULL;
158 GNUNET_free (share);
159}
diff --git a/src/contrib/service/secretsharing/secretsharing_protocol.h b/src/contrib/service/secretsharing/secretsharing_protocol.h
new file mode 100644
index 000000000..e6f3ba286
--- /dev/null
+++ b/src/contrib/service/secretsharing/secretsharing_protocol.h
@@ -0,0 +1,147 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21
22/**
23 * @file secretsharing/secretsharing_protocol.h
24 * @brief p2p message definitions for secretsharing
25 * @author Florian Dold
26 */
27
28#ifndef GNUNET_SECRETSHARING_PROTOCOL_H
29#define GNUNET_SECRETSHARING_PROTOCOL_H
30
31#include "platform.h"
32#include "gnunet_common.h"
33#include "gnunet_protocols.h"
34#include "secretsharing.h"
35
36
37GNUNET_NETWORK_STRUCT_BEGIN
38
39
40/**
41 * Consensus element data used in the first round of key generation.
42 */
43struct GNUNET_SECRETSHARING_KeygenCommitData
44{
45 /**
46 * Signature over the rest of the message.
47 */
48 struct GNUNET_CRYPTO_EddsaSignature signature;
49 /**
50 * Signature purpose for signing the keygen commit data.
51 */
52 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
53 /**
54 * Peer that inserts this element.
55 */
56 struct GNUNET_PeerIdentity peer;
57 /**
58 * Ephemeral paillier public key used by 'peer' for
59 * this session.
60 */
61 struct GNUNET_CRYPTO_PaillierPublicKey pubkey;
62 /**
63 * Commitment of 'peer' to its presecret.
64 */
65 struct GNUNET_HashCode commitment GNUNET_PACKED;
66};
67
68
69struct GNUNET_SECRETSHARING_KeygenRevealData
70{
71 /**
72 * Signature over rest of the message.
73 */
74 struct GNUNET_CRYPTO_EddsaSignature signature;
75 /*
76 * Signature purpose for signing the keygen commit data.
77 */
78 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
79 /**
80 * Peer that inserts this element.
81 */
82 struct GNUNET_PeerIdentity peer;
83
84 /* values follow */
85};
86
87
88/**
89 * Data of then element put in consensus
90 * for decrypting a value.
91 */
92struct GNUNET_SECRETSHARING_DecryptData
93{
94 /*
95 * Signature over rest of the message.
96 */
97 struct GNUNET_CRYPTO_EddsaSignature signature;
98 /*
99 * Signature purpose for signing the keygen commit data.
100 */
101 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
102 /**
103 * Ciphertext we want to decrypt.
104 */
105 struct GNUNET_SECRETSHARING_Ciphertext ciphertext;
106 /**
107 * Peer that inserts this element.
108 */
109 struct GNUNET_PeerIdentity peer;
110 /**
111 * Partial decryption, computed as \f$c_1^{s_i}\f$
112 */
113 struct GNUNET_SECRETSHARING_FieldElement partial_decryption;
114 /**
115 * Commitment for the non-interactive zero knowledge proof.
116 * \f$g^\beta\f$, with \f$\beta < q\f$
117 */
118 struct GNUNET_SECRETSHARING_FieldElement nizk_commit1;
119 /**
120 * Commitment for the non-interactive zero knowledge proof.
121 * \f$c_1^\beta\f$, with \f$\beta < q\f$
122 */
123 struct GNUNET_SECRETSHARING_FieldElement nizk_commit2;
124 /**
125 * Response to the challenge computed from the protocol transcript.
126 * \f$r = \beta + challenge \cdot share_i\f$
127 */
128 struct GNUNET_SECRETSHARING_FieldElement nizk_response;
129};
130
131
132struct GNUNET_SECRETSHARING_FairEncryption
133{
134 struct GNUNET_CRYPTO_PaillierCiphertext c;
135 /**
136 * h = g^x, where x is the fairly encrypted secret.
137 */
138 char h[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
139 char t1[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
140 char t2[GNUNET_CRYPTO_PAILLIER_BITS * 2 / 8];
141 char z[GNUNET_SECRETSHARING_ELGAMAL_BITS / 8];
142 char w[GNUNET_CRYPTO_PAILLIER_BITS / 8];
143};
144
145GNUNET_NETWORK_STRUCT_END
146
147#endif
diff --git a/src/contrib/service/secretsharing/test_secretsharing.conf b/src/contrib/service/secretsharing/test_secretsharing.conf
new file mode 100644
index 000000000..9cbe3ebb1
--- /dev/null
+++ b/src/contrib/service/secretsharing/test_secretsharing.conf
@@ -0,0 +1,40 @@
1[secretsharing]
2START_ON_DEMAND = YES
3#PREFIX = valgrind --leak-check=full
4OPTIONS = -LINFO
5
6[consensus]
7START_ON_DEMAND = YES
8
9[transport]
10OPTIONS = -LERROR
11PLUGINS = unix
12
13[set]
14OPTIONS = -L INFO
15START_ON_DEMAND = YES
16#PREFIX = valgrind --leak-check=full
17
18[testbed]
19OVERLAY_TOPOLOGY = CLIQUE
20
21[hostlist]
22SERVERS =
23
24[nat]
25# Use addresses from the local network interfaces (including loopback, but also others)
26USE_LOCALADDR = YES
27
28# Disable IPv6 support
29DISABLEV6 = NO
30
31# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
32RETURN_LOCAL_ADDRESSES = YES
33
34[nse]
35START_ON_DEMAND = NO
36
37
38[rps]
39START_ON_DEMAND = NO
40IMMEDIATE_START = NO
diff --git a/src/contrib/service/secretsharing/test_secretsharing_api.c b/src/contrib/service/secretsharing/test_secretsharing_api.c
new file mode 100644
index 000000000..470bfddf4
--- /dev/null
+++ b/src/contrib/service/secretsharing/test_secretsharing_api.c
@@ -0,0 +1,103 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file secretsharing/test_secretsharing_api.c
23 * @brief testcase for the secretsharing api
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "gnunet_secretsharing_service.h"
29
30
31static int success;
32
33static struct GNUNET_SECRETSHARING_Session *keygen;
34
35
36static void
37secret_ready_cb (void *cls,
38 struct GNUNET_SECRETSHARING_Share *my_share,
39 struct GNUNET_SECRETSHARING_PublicKey *public_key,
40 unsigned int num_ready_peers,
41 const struct GNUNET_PeerIdentity *ready_peers)
42{
43 keygen = NULL;
44 if (num_ready_peers == 1)
45 success = 1;
46 // FIXME: check that our share is valid, which we can do as there's only
47 // one peer.
48 GNUNET_SECRETSHARING_share_destroy (my_share);
49 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "secret ready, shutting down\n");
50 GNUNET_SCHEDULER_shutdown ();
51}
52
53
54static void
55handle_shutdown (void *cls)
56{
57 if (NULL != keygen)
58 {
59 GNUNET_SECRETSHARING_session_destroy (keygen);
60 keygen = NULL;
61 }
62}
63
64
65static void
66run (void *cls,
67 const struct GNUNET_CONFIGURATION_Handle *cfg,
68 struct GNUNET_TESTING_Peer *peer)
69{
70 struct GNUNET_HashCode session_id;
71 struct GNUNET_TIME_Absolute start;
72 struct GNUNET_TIME_Absolute deadline;
73
74 GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL);
75
76 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "testing secretsharing api\n");
77
78 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &session_id);
79
80 start = GNUNET_TIME_absolute_get ();
81 deadline = GNUNET_TIME_absolute_add (start, GNUNET_TIME_UNIT_SECONDS);
82
83 keygen = GNUNET_SECRETSHARING_create_session (cfg,
84 0, NULL, /* only the local peer */
85 &session_id,
86 start, deadline,
87 1,
88 secret_ready_cb, NULL);
89}
90
91
92int
93main (int argc, char **argv)
94{
95 int ret;
96
97 ret = GNUNET_TESTING_peer_run ("test_secretsharing_api",
98 "test_secretsharing.conf",
99 &run, NULL);
100 if (0 != ret)
101 return ret;
102 return (GNUNET_YES == success) ? 0 : 1;
103}
diff --git a/src/contrib/service/set/.gitignore b/src/contrib/service/set/.gitignore
new file mode 100644
index 000000000..f1c958639
--- /dev/null
+++ b/src/contrib/service/set/.gitignore
@@ -0,0 +1,7 @@
1gnunet-set-profiler
2gnunet-service-set
3gnunet-set-ibf-profiler
4test_set_api
5test_set_intersection_result_full
6test_set_union_copy
7test_set_union_result_symmetric
diff --git a/src/contrib/service/set/Makefile.am b/src/contrib/service/set/Makefile.am
new file mode 100644
index 000000000..d8e19e6d4
--- /dev/null
+++ b/src/contrib/service/set/Makefile.am
@@ -0,0 +1,111 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8plugindir = $(libdir)/gnunet
9
10pkgcfg_DATA = \
11 set.conf
12
13if USE_COVERAGE
14 AM_CFLAGS = -fprofile-arcs -ftest-coverage
15endif
16
17noinst_PROGRAMS = \
18 gnunet-set-ibf-profiler
19
20libexec_PROGRAMS = \
21 gnunet-service-set
22
23lib_LTLIBRARIES = \
24 libgnunetset.la
25
26gnunet_set_ibf_profiler_SOURCES = \
27 gnunet-set-ibf-profiler.c \
28 ibf.c
29gnunet_set_ibf_profiler_LDADD = \
30 $(top_builddir)/src/lib/util/libgnunetutil.la \
31 $(GN_LIBINTL)
32
33gnunet_service_set_SOURCES = \
34 gnunet-service-set.c gnunet-service-set.h \
35 gnunet-service-set_union.c gnunet-service-set_union.h \
36 gnunet-service-set_intersection.c gnunet-service-set_intersection.h \
37 ibf.c ibf.h \
38 gnunet-service-set_union_strata_estimator.c gnunet-service-set_union_strata_estimator.h \
39 gnunet-service-set_protocol.h
40gnunet_service_set_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
43 $(top_builddir)/src/service/core/libgnunetcore.la \
44 $(top_builddir)/src/service/cadet/libgnunetcadet.la \
45 $(top_builddir)/src/lib/block/libgnunetblock.la \
46 libgnunetset.la \
47 $(GN_LIBINTL)
48
49libgnunetset_la_SOURCES = \
50 set_api.c set.h
51libgnunetset_la_LIBADD = \
52 $(top_builddir)/src/lib/util/libgnunetutil.la \
53 $(LTLIBINTL)
54libgnunetset_la_LDFLAGS = \
55 $(GN_LIB_LDFLAGS)
56
57check_PROGRAMS = \
58 # test_set_api \
59 # test_set_union_result_symmetric \
60 # test_set_intersection_result_full \
61 test_set_union_copy
62
63if ENABLE_TEST_RUN
64AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
65TESTS = $(check_PROGRAMS)
66endif
67
68test_set_api_SOURCES = \
69 test_set_api.c
70test_set_api_LDADD = \
71 $(top_builddir)/src/lib/util/libgnunetutil.la \
72 $(top_builddir)/src/service/testing/libgnunettesting.la \
73 libgnunetset.la
74
75test_set_union_result_symmetric_SOURCES = \
76 test_set_union_result_symmetric.c
77test_set_union_result_symmetric_LDADD = \
78 $(top_builddir)/src/lib/util/libgnunetutil.la \
79 $(top_builddir)/src/service/testing/libgnunettesting.la \
80 libgnunetset.la
81
82test_set_intersection_result_full_SOURCES = \
83 test_set_intersection_result_full.c
84test_set_intersection_result_full_LDADD = \
85 $(top_builddir)/src/lib/util/libgnunetutil.la \
86 $(top_builddir)/src/service/testing/libgnunettesting.la \
87 libgnunetset.la
88
89test_set_union_copy_SOURCES = \
90 test_set_union_copy.c
91test_set_union_copy_LDADD = \
92 $(top_builddir)/src/lib/util/libgnunetutil.la \
93 $(top_builddir)/src/service/testing/libgnunettesting.la \
94 libgnunetset.la
95
96plugin_LTLIBRARIES = \
97 libgnunet_plugin_block_set_test.la
98
99libgnunet_plugin_block_set_test_la_SOURCES = \
100 plugin_block_set_test.c
101libgnunet_plugin_block_set_test_la_LIBADD = \
102 $(top_builddir)/src/lib/block/libgnunetblock.la \
103 $(top_builddir)/src/lib/block/libgnunetblockgroup.la \
104 $(top_builddir)/src/lib/util/libgnunetutil.la \
105 $(LTLIBINTL)
106libgnunet_plugin_block_set_test_la_LDFLAGS = \
107 $(GN_PLUGIN_LDFLAGS)
108
109
110EXTRA_DIST = \
111 test_set.conf
diff --git a/src/contrib/service/set/gnunet-service-set.c b/src/contrib/service/set/gnunet-service-set.c
new file mode 100644
index 000000000..7c522ec34
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set.c
@@ -0,0 +1,1983 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set.c
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-service-set.h"
28#include "gnunet-service-set_union.h"
29#include "gnunet-service-set_intersection.h"
30#include "gnunet-service-set_protocol.h"
31#include "gnunet_statistics_service.h"
32
33/**
34 * How long do we hold on to an incoming channel if there is
35 * no local listener before giving up?
36 */
37#define INCOMING_CHANNEL_TIMEOUT GNUNET_TIME_UNIT_MINUTES
38
39
40/**
41 * Lazy copy requests made by a client.
42 */
43struct LazyCopyRequest
44{
45 /**
46 * Kept in a DLL.
47 */
48 struct LazyCopyRequest *prev;
49
50 /**
51 * Kept in a DLL.
52 */
53 struct LazyCopyRequest *next;
54
55 /**
56 * Which set are we supposed to copy?
57 */
58 struct Set *source_set;
59
60 /**
61 * Cookie identifying the request.
62 */
63 uint32_t cookie;
64};
65
66
67/**
68 * A listener is inhabited by a client, and waits for evaluation
69 * requests from remote peers.
70 */
71struct Listener
72{
73 /**
74 * Listeners are held in a doubly linked list.
75 */
76 struct Listener *next;
77
78 /**
79 * Listeners are held in a doubly linked list.
80 */
81 struct Listener *prev;
82
83 /**
84 * Head of DLL of operations this listener is responsible for.
85 * Once the client has accepted/declined the operation, the
86 * operation is moved to the respective set's operation DLLS.
87 */
88 struct Operation *op_head;
89
90 /**
91 * Tail of DLL of operations this listener is responsible for.
92 * Once the client has accepted/declined the operation, the
93 * operation is moved to the respective set's operation DLLS.
94 */
95 struct Operation *op_tail;
96
97 /**
98 * Client that owns the listener.
99 * Only one client may own a listener.
100 */
101 struct ClientState *cs;
102
103 /**
104 * The port we are listening on with CADET.
105 */
106 struct GNUNET_CADET_Port *open_port;
107
108 /**
109 * Application ID for the operation, used to distinguish
110 * multiple operations of the same type with the same peer.
111 */
112 struct GNUNET_HashCode app_id;
113
114 /**
115 * The type of the operation.
116 */
117 enum GNUNET_SET_OperationType operation;
118};
119
120
121/**
122 * Handle to the cadet service, used to listen for and connect to
123 * remote peers.
124 */
125static struct GNUNET_CADET_Handle *cadet;
126
127/**
128 * DLL of lazy copy requests by this client.
129 */
130static struct LazyCopyRequest *lazy_copy_head;
131
132/**
133 * DLL of lazy copy requests by this client.
134 */
135static struct LazyCopyRequest *lazy_copy_tail;
136
137/**
138 * Generator for unique cookie we set per lazy copy request.
139 */
140static uint32_t lazy_copy_cookie;
141
142/**
143 * Statistics handle.
144 */
145struct GNUNET_STATISTICS_Handle *_GSS_statistics;
146
147/**
148 * Listeners are held in a doubly linked list.
149 */
150static struct Listener *listener_head;
151
152/**
153 * Listeners are held in a doubly linked list.
154 */
155static struct Listener *listener_tail;
156
157/**
158 * Number of active clients.
159 */
160static unsigned int num_clients;
161
162/**
163 * Are we in shutdown? if #GNUNET_YES and the number of clients
164 * drops to zero, disconnect from CADET.
165 */
166static int in_shutdown;
167
168/**
169 * Counter for allocating unique IDs for clients, used to identify
170 * incoming operation requests from remote peers, that the client can
171 * choose to accept or refuse. 0 must not be used (reserved for
172 * uninitialized).
173 */
174static uint32_t suggest_id;
175
176
177/**
178 * Get the incoming socket associated with the given id.
179 *
180 * @param id id to look for
181 * @return the incoming socket associated with the id,
182 * or NULL if there is none
183 */
184static struct Operation *
185get_incoming (uint32_t id)
186{
187 for (struct Listener *listener = listener_head; NULL != listener;
188 listener = listener->next)
189 {
190 for (struct Operation *op = listener->op_head; NULL != op; op = op->next)
191 if (op->suggest_id == id)
192 return op;
193 }
194 return NULL;
195}
196
197
198/**
199 * Destroy an incoming request from a remote peer
200 *
201 * @param op remote request to destroy
202 */
203static void
204incoming_destroy (struct Operation *op)
205{
206 struct Listener *listener;
207
208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
209 "Destroying incoming operation %p\n",
210 op);
211 if (NULL != (listener = op->listener))
212 {
213 GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op);
214 op->listener = NULL;
215 }
216 if (NULL != op->timeout_task)
217 {
218 GNUNET_SCHEDULER_cancel (op->timeout_task);
219 op->timeout_task = NULL;
220 }
221 _GSS_operation_destroy2 (op);
222}
223
224
225/**
226 * Context for the #garbage_collect_cb().
227 */
228struct GarbageContext
229{
230 /**
231 * Map for which we are garbage collecting removed elements.
232 */
233 struct GNUNET_CONTAINER_MultiHashMap *map;
234
235 /**
236 * Lowest generation for which an operation is still pending.
237 */
238 unsigned int min_op_generation;
239
240 /**
241 * Largest generation for which an operation is still pending.
242 */
243 unsigned int max_op_generation;
244};
245
246
247/**
248 * Function invoked to check if an element can be removed from
249 * the set's history because it is no longer needed.
250 *
251 * @param cls the `struct GarbageContext *`
252 * @param key key of the element in the map
253 * @param value the `struct ElementEntry *`
254 * @return #GNUNET_OK (continue to iterate)
255 */
256static int
257garbage_collect_cb (void *cls, const struct GNUNET_HashCode *key, void *value)
258{
259 // struct GarbageContext *gc = cls;
260 // struct ElementEntry *ee = value;
261
262 // if (GNUNET_YES != ee->removed)
263 // return GNUNET_OK;
264 // if ( (gc->max_op_generation < ee->generation_added) ||
265 // (ee->generation_removed > gc->min_op_generation) )
266 // {
267 // GNUNET_assert (GNUNET_YES ==
268 // GNUNET_CONTAINER_multihashmap_remove (gc->map,
269 // key,
270 // ee));
271 // GNUNET_free (ee);
272 // }
273 return GNUNET_OK;
274}
275
276
277/**
278 * Collect and destroy elements that are not needed anymore, because
279 * their lifetime (as determined by their generation) does not overlap
280 * with any active set operation.
281 *
282 * @param set set to garbage collect
283 */
284static void
285collect_generation_garbage (struct Set *set)
286{
287 struct GarbageContext gc;
288
289 gc.min_op_generation = UINT_MAX;
290 gc.max_op_generation = 0;
291 for (struct Operation *op = set->ops_head; NULL != op; op = op->next)
292 {
293 gc.min_op_generation =
294 GNUNET_MIN (gc.min_op_generation, op->generation_created);
295 gc.max_op_generation =
296 GNUNET_MAX (gc.max_op_generation, op->generation_created);
297 }
298 gc.map = set->content->elements;
299 GNUNET_CONTAINER_multihashmap_iterate (set->content->elements,
300 &garbage_collect_cb,
301 &gc);
302}
303
304
305/**
306 * Is @a generation in the range of exclusions?
307 *
308 * @param generation generation to query
309 * @param excluded array of generations where the element is excluded
310 * @param excluded_size length of the @a excluded array
311 * @return #GNUNET_YES if @a generation is in any of the ranges
312 */
313static int
314is_excluded_generation (unsigned int generation,
315 struct GenerationRange *excluded,
316 unsigned int excluded_size)
317{
318 for (unsigned int i = 0; i < excluded_size; i++)
319 if ((generation >= excluded[i].start) && (generation < excluded[i].end))
320 return GNUNET_YES;
321 return GNUNET_NO;
322}
323
324
325/**
326 * Is element @a ee part of the set during @a query_generation?
327 *
328 * @param ee element to test
329 * @param query_generation generation to query
330 * @param excluded array of generations where the element is excluded
331 * @param excluded_size length of the @a excluded array
332 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
333 */
334static int
335is_element_of_generation (struct ElementEntry *ee,
336 unsigned int query_generation,
337 struct GenerationRange *excluded,
338 unsigned int excluded_size)
339{
340 struct MutationEvent *mut;
341 int is_present;
342
343 GNUNET_assert (NULL != ee->mutations);
344 if (GNUNET_YES ==
345 is_excluded_generation (query_generation, excluded, excluded_size))
346 {
347 GNUNET_break (0);
348 return GNUNET_NO;
349 }
350
351 is_present = GNUNET_NO;
352
353 /* Could be made faster with binary search, but lists
354 are small, so why bother. */
355 for (unsigned int i = 0; i < ee->mutations_size; i++)
356 {
357 mut = &ee->mutations[i];
358
359 if (mut->generation > query_generation)
360 {
361 /* The mutation doesn't apply to our generation
362 anymore. We can'b break here, since mutations aren't
363 sorted by generation. */
364 continue;
365 }
366
367 if (GNUNET_YES ==
368 is_excluded_generation (mut->generation, excluded, excluded_size))
369 {
370 /* The generation is excluded (because it belongs to another
371 fork via a lazy copy) and thus mutations aren't considered
372 for membership testing. */
373 continue;
374 }
375
376 /* This would be an inconsistency in how we manage mutations. */
377 if ((GNUNET_YES == is_present) && (GNUNET_YES == mut->added))
378 GNUNET_assert (0);
379 /* Likewise. */
380 if ((GNUNET_NO == is_present) && (GNUNET_NO == mut->added))
381 GNUNET_assert (0);
382
383 is_present = mut->added;
384 }
385
386 return is_present;
387}
388
389
390/**
391 * Is element @a ee part of the set used by @a op?
392 *
393 * @param ee element to test
394 * @param op operation the defines the set and its generation
395 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
396 */
397int
398_GSS_is_element_of_operation (struct ElementEntry *ee, struct Operation *op)
399{
400 return is_element_of_generation (ee,
401 op->generation_created,
402 op->set->excluded_generations,
403 op->set->excluded_generations_size);
404}
405
406
407/**
408 * Destroy the given operation. Used for any operation where both
409 * peers were known and that thus actually had a vt and channel. Must
410 * not be used for operations where 'listener' is still set and we do
411 * not know the other peer.
412 *
413 * Call the implementation-specific cancel function of the operation.
414 * Disconnects from the remote peer. Does not disconnect the client,
415 * as there may be multiple operations per set.
416 *
417 * @param op operation to destroy
418 * @param gc #GNUNET_YES to perform garbage collection on the set
419 */
420void
421_GSS_operation_destroy (struct Operation *op, int gc)
422{
423 struct Set *set = op->set;
424 struct GNUNET_CADET_Channel *channel;
425
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying operation %p\n", op);
427 GNUNET_assert (NULL == op->listener);
428 if (NULL != op->state)
429 {
430 set->vt->cancel (op);
431 op->state = NULL;
432 }
433 if (NULL != set)
434 {
435 GNUNET_CONTAINER_DLL_remove (set->ops_head, set->ops_tail, op);
436 op->set = NULL;
437 }
438 if (NULL != op->context_msg)
439 {
440 GNUNET_free (op->context_msg);
441 op->context_msg = NULL;
442 }
443 if (NULL != (channel = op->channel))
444 {
445 /* This will free op; called conditionally as this helper function
446 is also called from within the channel disconnect handler. */
447 op->channel = NULL;
448 GNUNET_CADET_channel_destroy (channel);
449 }
450 if ((NULL != set) && (GNUNET_YES == gc))
451 collect_generation_garbage (set);
452 /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL,
453 * there was a channel end handler that will free 'op' on the call stack. */
454}
455
456
457/**
458 * Callback called when a client connects to the service.
459 *
460 * @param cls closure for the service
461 * @param c the new client that connected to the service
462 * @param mq the message queue used to send messages to the client
463 * @return @a `struct ClientState`
464 */
465static void *
466client_connect_cb (void *cls,
467 struct GNUNET_SERVICE_Client *c,
468 struct GNUNET_MQ_Handle *mq)
469{
470 struct ClientState *cs;
471
472 num_clients++;
473 cs = GNUNET_new (struct ClientState);
474 cs->client = c;
475 cs->mq = mq;
476 return cs;
477}
478
479
480/**
481 * Iterator over hash map entries to free element entries.
482 *
483 * @param cls closure
484 * @param key current key code
485 * @param value a `struct ElementEntry *` to be free'd
486 * @return #GNUNET_YES (continue to iterate)
487 */
488static int
489destroy_elements_iterator (void *cls,
490 const struct GNUNET_HashCode *key,
491 void *value)
492{
493 struct ElementEntry *ee = value;
494
495 GNUNET_free (ee->mutations);
496 GNUNET_free (ee);
497 return GNUNET_YES;
498}
499
500
501/**
502 * Clean up after a client has disconnected
503 *
504 * @param cls closure, unused
505 * @param client the client to clean up after
506 * @param internal_cls the `struct ClientState`
507 */
508static void
509client_disconnect_cb (void *cls,
510 struct GNUNET_SERVICE_Client *client,
511 void *internal_cls)
512{
513 struct ClientState *cs = internal_cls;
514 struct Operation *op;
515 struct Listener *listener;
516 struct Set *set;
517
518 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected, cleaning up\n");
519 if (NULL != (set = cs->set))
520 {
521 struct SetContent *content = set->content;
522 struct PendingMutation *pm;
523 struct PendingMutation *pm_current;
524 struct LazyCopyRequest *lcr;
525
526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's set\n");
527 /* Destroy pending set operations */
528 while (NULL != set->ops_head)
529 _GSS_operation_destroy (set->ops_head, GNUNET_NO);
530
531 /* Destroy operation-specific state */
532 GNUNET_assert (NULL != set->state);
533 set->vt->destroy_set (set->state);
534 set->state = NULL;
535
536 /* Clean up ongoing iterations */
537 if (NULL != set->iter)
538 {
539 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
540 set->iter = NULL;
541 set->iteration_id++;
542 }
543
544 /* discard any pending mutations that reference this set */
545 pm = content->pending_mutations_head;
546 while (NULL != pm)
547 {
548 pm_current = pm;
549 pm = pm->next;
550 if (pm_current->set == set)
551 {
552 GNUNET_CONTAINER_DLL_remove (content->pending_mutations_head,
553 content->pending_mutations_tail,
554 pm_current);
555 GNUNET_free (pm_current);
556 }
557 }
558
559 /* free set content (or at least decrement RC) */
560 set->content = NULL;
561 GNUNET_assert (0 != content->refcount);
562 content->refcount--;
563 if (0 == content->refcount)
564 {
565 GNUNET_assert (NULL != content->elements);
566 GNUNET_CONTAINER_multihashmap_iterate (content->elements,
567 &destroy_elements_iterator,
568 NULL);
569 GNUNET_CONTAINER_multihashmap_destroy (content->elements);
570 content->elements = NULL;
571 GNUNET_free (content);
572 }
573 GNUNET_free (set->excluded_generations);
574 set->excluded_generations = NULL;
575
576 /* remove set from pending copy requests */
577 lcr = lazy_copy_head;
578 while (NULL != lcr)
579 {
580 struct LazyCopyRequest *lcr_current = lcr;
581
582 lcr = lcr->next;
583 if (lcr_current->source_set == set)
584 {
585 GNUNET_CONTAINER_DLL_remove (lazy_copy_head,
586 lazy_copy_tail,
587 lcr_current);
588 GNUNET_free (lcr_current);
589 }
590 }
591 GNUNET_free (set);
592 }
593
594 if (NULL != (listener = cs->listener))
595 {
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's listener\n");
597 GNUNET_CADET_close_port (listener->open_port);
598 listener->open_port = NULL;
599 while (NULL != (op = listener->op_head))
600 {
601 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
602 "Destroying incoming operation `%u' from peer `%s'\n",
603 (unsigned int) op->client_request_id,
604 GNUNET_i2s (&op->peer));
605 incoming_destroy (op);
606 }
607 GNUNET_CONTAINER_DLL_remove (listener_head, listener_tail, listener);
608 GNUNET_free (listener);
609 }
610 GNUNET_free (cs);
611 num_clients--;
612 if ((GNUNET_YES == in_shutdown) && (0 == num_clients))
613 {
614 if (NULL != cadet)
615 {
616 GNUNET_CADET_disconnect (cadet);
617 cadet = NULL;
618 }
619 }
620}
621
622
623/**
624 * Check a request for a set operation from another peer.
625 *
626 * @param cls the operation state
627 * @param msg the received message
628 * @return #GNUNET_OK if the channel should be kept alive,
629 * #GNUNET_SYSERR to destroy the channel
630 */
631static int
632check_incoming_msg (void *cls, const struct OperationRequestMessage *msg)
633{
634 struct Operation *op = cls;
635 struct Listener *listener = op->listener;
636 const struct GNUNET_MessageHeader *nested_context;
637
638 /* double operation request */
639 if (0 != op->suggest_id)
640 {
641 GNUNET_break_op (0);
642 return GNUNET_SYSERR;
643 }
644 /* This should be equivalent to the previous condition, but can't hurt to check twice */
645 if (NULL == op->listener)
646 {
647 GNUNET_break (0);
648 return GNUNET_SYSERR;
649 }
650 if (listener->operation !=
651 (enum GNUNET_SET_OperationType) ntohl (msg->operation))
652 {
653 GNUNET_break_op (0);
654 return GNUNET_SYSERR;
655 }
656 nested_context = GNUNET_MQ_extract_nested_mh (msg);
657 if ((NULL != nested_context) &&
658 (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE))
659 {
660 GNUNET_break_op (0);
661 return GNUNET_SYSERR;
662 }
663 return GNUNET_OK;
664}
665
666
667/**
668 * Handle a request for a set operation from another peer. Checks if we
669 * have a listener waiting for such a request (and in that case initiates
670 * asking the listener about accepting the connection). If no listener
671 * is waiting, we queue the operation request in hope that a listener
672 * shows up soon (before timeout).
673 *
674 * This msg is expected as the first and only msg handled through the
675 * non-operation bound virtual table, acceptance of this operation replaces
676 * our virtual table and subsequent msgs would be routed differently (as
677 * we then know what type of operation this is).
678 *
679 * @param cls the operation state
680 * @param msg the received message
681 * @return #GNUNET_OK if the channel should be kept alive,
682 * #GNUNET_SYSERR to destroy the channel
683 */
684static void
685handle_incoming_msg (void *cls, const struct OperationRequestMessage *msg)
686{
687 struct Operation *op = cls;
688 struct Listener *listener = op->listener;
689 const struct GNUNET_MessageHeader *nested_context;
690 struct GNUNET_MQ_Envelope *env;
691 struct GNUNET_SET_RequestMessage *cmsg;
692
693 nested_context = GNUNET_MQ_extract_nested_mh (msg);
694 /* Make a copy of the nested_context (application-specific context
695 information that is opaque to set) so we can pass it to the
696 listener later on */
697 if (NULL != nested_context)
698 op->context_msg = GNUNET_copy_message (nested_context);
699 op->remote_element_count = ntohl (msg->element_count);
700 GNUNET_log (
701 GNUNET_ERROR_TYPE_DEBUG,
702 "Received P2P operation request (op %u, port %s) for active listener\n",
703 (uint32_t) ntohl (msg->operation),
704 GNUNET_h2s (&op->listener->app_id));
705 GNUNET_assert (0 == op->suggest_id);
706 if (0 == suggest_id)
707 suggest_id++;
708 op->suggest_id = suggest_id++;
709 GNUNET_assert (NULL != op->timeout_task);
710 GNUNET_SCHEDULER_cancel (op->timeout_task);
711 op->timeout_task = NULL;
712 env = GNUNET_MQ_msg_nested_mh (cmsg,
713 GNUNET_MESSAGE_TYPE_SET_REQUEST,
714 op->context_msg);
715 GNUNET_log (
716 GNUNET_ERROR_TYPE_DEBUG,
717 "Suggesting incoming request with accept id %u to listener %p of client %p\n",
718 op->suggest_id,
719 listener,
720 listener->cs);
721 cmsg->accept_id = htonl (op->suggest_id);
722 cmsg->peer_id = op->peer;
723 GNUNET_MQ_send (listener->cs->mq, env);
724 /* NOTE: GNUNET_CADET_receive_done() will be called in
725 #handle_client_accept() */
726}
727
728
729/**
730 * Add an element to @a set as specified by @a msg
731 *
732 * @param set set to manipulate
733 * @param msg message specifying the change
734 */
735static void
736execute_add (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
737{
738 struct GNUNET_SET_Element el;
739 struct ElementEntry *ee;
740 struct GNUNET_HashCode hash;
741
742 GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_ADD == ntohs (msg->header.type));
743 el.size = ntohs (msg->header.size) - sizeof(*msg);
744 el.data = &msg[1];
745 el.element_type = ntohs (msg->element_type);
746 GNUNET_SET_element_hash (&el, &hash);
747 ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash);
748 if (NULL == ee)
749 {
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751 "Client inserts element %s of size %u\n",
752 GNUNET_h2s (&hash),
753 el.size);
754 ee = GNUNET_malloc (el.size + sizeof(*ee));
755 ee->element.size = el.size;
756 GNUNET_memcpy (&ee[1], el.data, el.size);
757 ee->element.data = &ee[1];
758 ee->element.element_type = el.element_type;
759 ee->remote = GNUNET_NO;
760 ee->mutations = NULL;
761 ee->mutations_size = 0;
762 ee->element_hash = hash;
763 GNUNET_break (GNUNET_YES ==
764 GNUNET_CONTAINER_multihashmap_put (
765 set->content->elements,
766 &ee->element_hash,
767 ee,
768 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
769 }
770 else if (GNUNET_YES ==
771 is_element_of_generation (ee,
772 set->current_generation,
773 set->excluded_generations,
774 set->excluded_generations_size))
775 {
776 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
777 "Client inserted element %s of size %u twice (ignored)\n",
778 GNUNET_h2s (&hash),
779 el.size);
780
781 /* same element inserted twice */
782 return;
783 }
784
785 {
786 struct MutationEvent mut = { .generation = set->current_generation,
787 .added = GNUNET_YES };
788 GNUNET_array_append (ee->mutations, ee->mutations_size, mut);
789 }
790 set->vt->add (set->state, ee);
791}
792
793
794/**
795 * Remove an element from @a set as specified by @a msg
796 *
797 * @param set set to manipulate
798 * @param msg message specifying the change
799 */
800static void
801execute_remove (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
802{
803 struct GNUNET_SET_Element el;
804 struct ElementEntry *ee;
805 struct GNUNET_HashCode hash;
806
807 GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_REMOVE == ntohs (msg->header.type));
808 el.size = ntohs (msg->header.size) - sizeof(*msg);
809 el.data = &msg[1];
810 el.element_type = ntohs (msg->element_type);
811 GNUNET_SET_element_hash (&el, &hash);
812 ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash);
813 if (NULL == ee)
814 {
815 /* Client tried to remove non-existing element. */
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Client removes non-existing element of size %u\n",
818 el.size);
819 return;
820 }
821 if (GNUNET_NO == is_element_of_generation (ee,
822 set->current_generation,
823 set->excluded_generations,
824 set->excluded_generations_size))
825 {
826 /* Client tried to remove element twice */
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "Client removed element of size %u twice (ignored)\n",
829 el.size);
830 return;
831 }
832 else
833 {
834 struct MutationEvent mut = { .generation = set->current_generation,
835 .added = GNUNET_NO };
836
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
838 "Client removes element of size %u\n",
839 el.size);
840
841 GNUNET_array_append (ee->mutations, ee->mutations_size, mut);
842 }
843 set->vt->remove (set->state, ee);
844}
845
846
847/**
848 * Perform a mutation on a set as specified by the @a msg
849 *
850 * @param set the set to mutate
851 * @param msg specification of what to change
852 */
853static void
854execute_mutation (struct Set *set, const struct GNUNET_SET_ElementMessage *msg)
855{
856 switch (ntohs (msg->header.type))
857 {
858 case GNUNET_MESSAGE_TYPE_SET_ADD:
859 execute_add (set, msg);
860 break;
861
862 case GNUNET_MESSAGE_TYPE_SET_REMOVE:
863 execute_remove (set, msg);
864 break;
865
866 default:
867 GNUNET_break (0);
868 }
869}
870
871
872/**
873 * Execute mutations that were delayed on a set because of
874 * pending operations.
875 *
876 * @param set the set to execute mutations on
877 */
878static void
879execute_delayed_mutations (struct Set *set)
880{
881 struct PendingMutation *pm;
882
883 if (0 != set->content->iterator_count)
884 return; /* still cannot do this */
885 while (NULL != (pm = set->content->pending_mutations_head))
886 {
887 GNUNET_CONTAINER_DLL_remove (set->content->pending_mutations_head,
888 set->content->pending_mutations_tail,
889 pm);
890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
891 "Executing pending mutation on %p.\n",
892 pm->set);
893 execute_mutation (pm->set, pm->msg);
894 GNUNET_free (pm->msg);
895 GNUNET_free (pm);
896 }
897}
898
899
900/**
901 * Send the next element of a set to the set's client. The next element is given by
902 * the set's current hashmap iterator. The set's iterator will be set to NULL if there
903 * are no more elements in the set. The caller must ensure that the set's iterator is
904 * valid.
905 *
906 * The client will acknowledge each received element with a
907 * #GNUNET_MESSAGE_TYPE_SET_ITER_ACK message. Our
908 * #handle_client_iter_ack() will then trigger the next transmission.
909 * Note that the #GNUNET_MESSAGE_TYPE_SET_ITER_DONE is not acknowledged.
910 *
911 * @param set set that should send its next element to its client
912 */
913static void
914send_client_element (struct Set *set)
915{
916 int ret;
917 struct ElementEntry *ee;
918 struct GNUNET_MQ_Envelope *ev;
919 struct GNUNET_SET_IterResponseMessage *msg;
920
921 GNUNET_assert (NULL != set->iter);
922 do
923 {
924 ret = GNUNET_CONTAINER_multihashmap_iterator_next (set->iter,
925 NULL,
926 (const void **) &ee);
927 if (GNUNET_NO == ret)
928 {
929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration on %p done.\n", set);
930 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_DONE);
931 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
932 set->iter = NULL;
933 set->iteration_id++;
934 GNUNET_assert (set->content->iterator_count > 0);
935 set->content->iterator_count--;
936 execute_delayed_mutations (set);
937 GNUNET_MQ_send (set->cs->mq, ev);
938 return;
939 }
940 GNUNET_assert (NULL != ee);
941 }
942 while (GNUNET_NO ==
943 is_element_of_generation (ee,
944 set->iter_generation,
945 set->excluded_generations,
946 set->excluded_generations_size));
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Sending iteration element on %p.\n",
949 set);
950 ev = GNUNET_MQ_msg_extra (msg,
951 ee->element.size,
952 GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT);
953 GNUNET_memcpy (&msg[1], ee->element.data, ee->element.size);
954 msg->element_type = htons (ee->element.element_type);
955 msg->iteration_id = htons (set->iteration_id);
956 GNUNET_MQ_send (set->cs->mq, ev);
957}
958
959
960/**
961 * Called when a client wants to iterate the elements of a set.
962 * Checks if we have a set associated with the client and if we
963 * can right now start an iteration. If all checks out, starts
964 * sending the elements of the set to the client.
965 *
966 * @param cls client that sent the message
967 * @param m message sent by the client
968 */
969static void
970handle_client_iterate (void *cls, const struct GNUNET_MessageHeader *m)
971{
972 struct ClientState *cs = cls;
973 struct Set *set;
974
975 if (NULL == (set = cs->set))
976 {
977 /* attempt to iterate over a non existing set */
978 GNUNET_break (0);
979 GNUNET_SERVICE_client_drop (cs->client);
980 return;
981 }
982 if (NULL != set->iter)
983 {
984 /* Only one concurrent iterate-action allowed per set */
985 GNUNET_break (0);
986 GNUNET_SERVICE_client_drop (cs->client);
987 return;
988 }
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990 "Iterating set %p in gen %u with %u content elements\n",
991 (void *) set,
992 set->current_generation,
993 GNUNET_CONTAINER_multihashmap_size (set->content->elements));
994 GNUNET_SERVICE_client_continue (cs->client);
995 set->content->iterator_count++;
996 set->iter =
997 GNUNET_CONTAINER_multihashmap_iterator_create (set->content->elements);
998 set->iter_generation = set->current_generation;
999 send_client_element (set);
1000}
1001
1002
1003/**
1004 * Called when a client wants to create a new set. This is typically
1005 * the first request from a client, and includes the type of set
1006 * operation to be performed.
1007 *
1008 * @param cls client that sent the message
1009 * @param msg message sent by the client
1010 */
1011static void
1012handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg)
1013{
1014 struct ClientState *cs = cls;
1015 struct Set *set;
1016
1017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018 "Client created new set (operation %u)\n",
1019 (uint32_t) ntohl (msg->operation));
1020 if (NULL != cs->set)
1021 {
1022 /* There can only be one set per client */
1023 GNUNET_break (0);
1024 GNUNET_SERVICE_client_drop (cs->client);
1025 return;
1026 }
1027 set = GNUNET_new (struct Set);
1028 switch (ntohl (msg->operation))
1029 {
1030 case GNUNET_SET_OPERATION_INTERSECTION:
1031 set->vt = _GSS_intersection_vt ();
1032 break;
1033
1034 case GNUNET_SET_OPERATION_UNION:
1035 set->vt = _GSS_union_vt ();
1036 break;
1037
1038 default:
1039 GNUNET_free (set);
1040 GNUNET_break (0);
1041 GNUNET_SERVICE_client_drop (cs->client);
1042 return;
1043 }
1044 set->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation);
1045 set->state = set->vt->create ();
1046 if (NULL == set->state)
1047 {
1048 /* initialization failed (i.e. out of memory) */
1049 GNUNET_free (set);
1050 GNUNET_SERVICE_client_drop (cs->client);
1051 return;
1052 }
1053 set->content = GNUNET_new (struct SetContent);
1054 set->content->refcount = 1;
1055 set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1056 set->cs = cs;
1057 cs->set = set;
1058 GNUNET_SERVICE_client_continue (cs->client);
1059}
1060
1061
1062/**
1063 * Timeout happens iff:
1064 * - we suggested an operation to our listener,
1065 * but did not receive a response in time
1066 * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST
1067 *
1068 * @param cls channel context
1069 */
1070static void
1071incoming_timeout_cb (void *cls)
1072{
1073 struct Operation *op = cls;
1074
1075 op->timeout_task = NULL;
1076 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1077 "Remote peer's incoming request timed out\n");
1078 incoming_destroy (op);
1079}
1080
1081
1082/**
1083 * Method called whenever another peer has added us to a channel the
1084 * other peer initiated. Only called (once) upon reception of data
1085 * from a channel we listen on.
1086 *
1087 * The channel context represents the operation itself and gets added
1088 * to a DLL, from where it gets looked up when our local listener
1089 * client responds to a proposed/suggested operation or connects and
1090 * associates with this operation.
1091 *
1092 * @param cls closure
1093 * @param channel new handle to the channel
1094 * @param source peer that started the channel
1095 * @return initial channel context for the channel
1096 * returns NULL on error
1097 */
1098static void *
1099channel_new_cb (void *cls,
1100 struct GNUNET_CADET_Channel *channel,
1101 const struct GNUNET_PeerIdentity *source)
1102{
1103 struct Listener *listener = cls;
1104 struct Operation *op;
1105
1106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New incoming channel\n");
1107 op = GNUNET_new (struct Operation);
1108 op->listener = listener;
1109 op->peer = *source;
1110 op->channel = channel;
1111 op->mq = GNUNET_CADET_get_mq (op->channel);
1112 op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1113 op->timeout_task = GNUNET_SCHEDULER_add_delayed (INCOMING_CHANNEL_TIMEOUT,
1114 &incoming_timeout_cb,
1115 op);
1116 GNUNET_CONTAINER_DLL_insert (listener->op_head, listener->op_tail, op);
1117 return op;
1118}
1119
1120
1121/**
1122 * Function called whenever a channel is destroyed. Should clean up
1123 * any associated state. It must NOT call
1124 * GNUNET_CADET_channel_destroy() on the channel.
1125 *
1126 * The peer_disconnect function is part of a a virtual table set initially either
1127 * when a peer creates a new channel with us, or once we create
1128 * a new channel ourselves (evaluate).
1129 *
1130 * Once we know the exact type of operation (union/intersection), the vt is
1131 * replaced with an operation specific instance (_GSS_[op]_vt).
1132 *
1133 * @param channel_ctx place where local state associated
1134 * with the channel is stored
1135 * @param channel connection to the other end (henceforth invalid)
1136 */
1137static void
1138channel_end_cb (void *channel_ctx, const struct GNUNET_CADET_Channel *channel)
1139{
1140 struct Operation *op = channel_ctx;
1141
1142 op->channel = NULL;
1143 _GSS_operation_destroy2 (op);
1144}
1145
1146
1147/**
1148 * This function probably should not exist
1149 * and be replaced by inlining more specific
1150 * logic in the various places where it is called.
1151 */
1152void
1153_GSS_operation_destroy2 (struct Operation *op)
1154{
1155 struct GNUNET_CADET_Channel *channel;
1156
1157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "channel_end_cb called\n");
1158 if (NULL != (channel = op->channel))
1159 {
1160 /* This will free op; called conditionally as this helper function
1161 is also called from within the channel disconnect handler. */
1162 op->channel = NULL;
1163 GNUNET_CADET_channel_destroy (channel);
1164 }
1165 if (NULL != op->listener)
1166 {
1167 incoming_destroy (op);
1168 return;
1169 }
1170 if (NULL != op->set)
1171 op->set->vt->channel_death (op);
1172 else
1173 _GSS_operation_destroy (op, GNUNET_YES);
1174 GNUNET_free (op);
1175}
1176
1177
1178/**
1179 * Function called whenever an MQ-channel's transmission window size changes.
1180 *
1181 * The first callback in an outgoing channel will be with a non-zero value
1182 * and will mean the channel is connected to the destination.
1183 *
1184 * For an incoming channel it will be called immediately after the
1185 * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value.
1186 *
1187 * @param cls Channel closure.
1188 * @param channel Connection to the other end (henceforth invalid).
1189 * @param window_size New window size. If the is more messages than buffer size
1190 * this value will be negative..
1191 */
1192static void
1193channel_window_cb (void *cls,
1194 const struct GNUNET_CADET_Channel *channel,
1195 int window_size)
1196{
1197 /* FIXME: not implemented, we could do flow control here... */
1198}
1199
1200
1201/**
1202 * Called when a client wants to create a new listener.
1203 *
1204 * @param cls client that sent the message
1205 * @param msg message sent by the client
1206 */
1207static void
1208handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg)
1209{
1210 struct ClientState *cs = cls;
1211 struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1212 { GNUNET_MQ_hd_var_size (incoming_msg,
1213 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1214 struct OperationRequestMessage,
1215 NULL),
1216 GNUNET_MQ_hd_var_size (union_p2p_ibf,
1217 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF,
1218 struct IBFMessage,
1219 NULL),
1220 GNUNET_MQ_hd_var_size (union_p2p_elements,
1221 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS,
1222 struct GNUNET_SET_ElementMessage,
1223 NULL),
1224 GNUNET_MQ_hd_var_size (union_p2p_offer,
1225 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER,
1226 struct GNUNET_MessageHeader,
1227 NULL),
1228 GNUNET_MQ_hd_var_size (union_p2p_inquiry,
1229 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY,
1230 struct InquiryMessage,
1231 NULL),
1232 GNUNET_MQ_hd_var_size (union_p2p_demand,
1233 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND,
1234 struct GNUNET_MessageHeader,
1235 NULL),
1236 GNUNET_MQ_hd_fixed_size (union_p2p_done,
1237 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE,
1238 struct GNUNET_MessageHeader,
1239 NULL),
1240 GNUNET_MQ_hd_fixed_size (union_p2p_over,
1241 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER,
1242 struct GNUNET_MessageHeader,
1243 NULL),
1244 GNUNET_MQ_hd_fixed_size (union_p2p_full_done,
1245 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE,
1246 struct GNUNET_MessageHeader,
1247 NULL),
1248 GNUNET_MQ_hd_fixed_size (union_p2p_request_full,
1249 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL,
1250 struct GNUNET_MessageHeader,
1251 NULL),
1252 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1253 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE,
1254 struct StrataEstimatorMessage,
1255 NULL),
1256 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1257 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC,
1258 struct StrataEstimatorMessage,
1259 NULL),
1260 GNUNET_MQ_hd_var_size (union_p2p_full_element,
1261 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT,
1262 struct GNUNET_SET_ElementMessage,
1263 NULL),
1264 GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info,
1265 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO,
1266 struct IntersectionElementInfoMessage,
1267 NULL),
1268 GNUNET_MQ_hd_var_size (intersection_p2p_bf,
1269 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF,
1270 struct BFMessage,
1271 NULL),
1272 GNUNET_MQ_hd_fixed_size (intersection_p2p_done,
1273 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE,
1274 struct IntersectionDoneMessage,
1275 NULL),
1276 GNUNET_MQ_handler_end () };
1277 struct Listener *listener;
1278
1279 if (NULL != cs->listener)
1280 {
1281 /* max. one active listener per client! */
1282 GNUNET_break (0);
1283 GNUNET_SERVICE_client_drop (cs->client);
1284 return;
1285 }
1286 listener = GNUNET_new (struct Listener);
1287 listener->cs = cs;
1288 cs->listener = listener;
1289 listener->app_id = msg->app_id;
1290 listener->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation);
1291 GNUNET_CONTAINER_DLL_insert (listener_head, listener_tail, listener);
1292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1293 "New listener created (op %u, port %s)\n",
1294 listener->operation,
1295 GNUNET_h2s (&listener->app_id));
1296 listener->open_port = GNUNET_CADET_open_port (cadet,
1297 &msg->app_id,
1298 &channel_new_cb,
1299 listener,
1300 &channel_window_cb,
1301 &channel_end_cb,
1302 cadet_handlers);
1303 GNUNET_SERVICE_client_continue (cs->client);
1304}
1305
1306
1307/**
1308 * Called when the listening client rejects an operation
1309 * request by another peer.
1310 *
1311 * @param cls client that sent the message
1312 * @param msg message sent by the client
1313 */
1314static void
1315handle_client_reject (void *cls, const struct GNUNET_SET_RejectMessage *msg)
1316{
1317 struct ClientState *cs = cls;
1318 struct Operation *op;
1319
1320 op = get_incoming (ntohl (msg->accept_reject_id));
1321 if (NULL == op)
1322 {
1323 /* no matching incoming operation for this reject;
1324 could be that the other peer already disconnected... */
1325 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1326 "Client rejected unknown operation %u\n",
1327 (unsigned int) ntohl (msg->accept_reject_id));
1328 GNUNET_SERVICE_client_continue (cs->client);
1329 return;
1330 }
1331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1332 "Peer request (op %u, app %s) rejected by client\n",
1333 op->listener->operation,
1334 GNUNET_h2s (&cs->listener->app_id));
1335 _GSS_operation_destroy2 (op);
1336 GNUNET_SERVICE_client_continue (cs->client);
1337}
1338
1339
1340/**
1341 * Called when a client wants to add or remove an element to a set it inhabits.
1342 *
1343 * @param cls client that sent the message
1344 * @param msg message sent by the client
1345 */
1346static int
1347check_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg)
1348{
1349 /* NOTE: Technically, we should probably check with the
1350 block library whether the element we are given is well-formed */
1351 return GNUNET_OK;
1352}
1353
1354
1355/**
1356 * Called when a client wants to add or remove an element to a set it inhabits.
1357 *
1358 * @param cls client that sent the message
1359 * @param msg message sent by the client
1360 */
1361static void
1362handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg)
1363{
1364 struct ClientState *cs = cls;
1365 struct Set *set;
1366
1367 if (NULL == (set = cs->set))
1368 {
1369 /* client without a set requested an operation */
1370 GNUNET_break (0);
1371 GNUNET_SERVICE_client_drop (cs->client);
1372 return;
1373 }
1374 GNUNET_SERVICE_client_continue (cs->client);
1375
1376 if (0 != set->content->iterator_count)
1377 {
1378 struct PendingMutation *pm;
1379
1380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduling mutation on set\n");
1381 pm = GNUNET_new (struct PendingMutation);
1382 pm->msg =
1383 (struct GNUNET_SET_ElementMessage *) GNUNET_copy_message (&msg->header);
1384 pm->set = set;
1385 GNUNET_CONTAINER_DLL_insert_tail (set->content->pending_mutations_head,
1386 set->content->pending_mutations_tail,
1387 pm);
1388 return;
1389 }
1390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n");
1391 execute_mutation (set, msg);
1392}
1393
1394
1395/**
1396 * Advance the current generation of a set,
1397 * adding exclusion ranges if necessary.
1398 *
1399 * @param set the set where we want to advance the generation
1400 */
1401static void
1402advance_generation (struct Set *set)
1403{
1404 struct GenerationRange r;
1405
1406 if (set->current_generation == set->content->latest_generation)
1407 {
1408 set->content->latest_generation++;
1409 set->current_generation++;
1410 return;
1411 }
1412
1413 GNUNET_assert (set->current_generation < set->content->latest_generation);
1414
1415 r.start = set->current_generation + 1;
1416 r.end = set->content->latest_generation + 1;
1417 set->content->latest_generation = r.end;
1418 set->current_generation = r.end;
1419 GNUNET_array_append (set->excluded_generations,
1420 set->excluded_generations_size,
1421 r);
1422}
1423
1424
1425/**
1426 * Called when a client wants to initiate a set operation with another
1427 * peer. Initiates the CADET connection to the listener and sends the
1428 * request.
1429 *
1430 * @param cls client that sent the message
1431 * @param msg message sent by the client
1432 * @return #GNUNET_OK if the message is well-formed
1433 */
1434static int
1435check_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg)
1436{
1437 /* FIXME: suboptimal, even if the context below could be NULL,
1438 there are malformed messages this does not check for... */
1439 return GNUNET_OK;
1440}
1441
1442
1443/**
1444 * Called when a client wants to initiate a set operation with another
1445 * peer. Initiates the CADET connection to the listener and sends the
1446 * request.
1447 *
1448 * @param cls client that sent the message
1449 * @param msg message sent by the client
1450 */
1451static void
1452handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg)
1453{
1454 struct ClientState *cs = cls;
1455 struct Operation *op = GNUNET_new (struct Operation);
1456 const struct GNUNET_MQ_MessageHandler cadet_handlers[] =
1457 { GNUNET_MQ_hd_var_size (incoming_msg,
1458 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1459 struct OperationRequestMessage,
1460 op),
1461 GNUNET_MQ_hd_var_size (union_p2p_ibf,
1462 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF,
1463 struct IBFMessage,
1464 op),
1465 GNUNET_MQ_hd_var_size (union_p2p_elements,
1466 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS,
1467 struct GNUNET_SET_ElementMessage,
1468 op),
1469 GNUNET_MQ_hd_var_size (union_p2p_offer,
1470 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER,
1471 struct GNUNET_MessageHeader,
1472 op),
1473 GNUNET_MQ_hd_var_size (union_p2p_inquiry,
1474 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY,
1475 struct InquiryMessage,
1476 op),
1477 GNUNET_MQ_hd_var_size (union_p2p_demand,
1478 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND,
1479 struct GNUNET_MessageHeader,
1480 op),
1481 GNUNET_MQ_hd_fixed_size (union_p2p_done,
1482 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE,
1483 struct GNUNET_MessageHeader,
1484 op),
1485 GNUNET_MQ_hd_fixed_size (union_p2p_over,
1486 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER,
1487 struct GNUNET_MessageHeader,
1488 op),
1489 GNUNET_MQ_hd_fixed_size (union_p2p_full_done,
1490 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE,
1491 struct GNUNET_MessageHeader,
1492 op),
1493 GNUNET_MQ_hd_fixed_size (union_p2p_request_full,
1494 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL,
1495 struct GNUNET_MessageHeader,
1496 op),
1497 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1498 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE,
1499 struct StrataEstimatorMessage,
1500 op),
1501 GNUNET_MQ_hd_var_size (union_p2p_strata_estimator,
1502 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC,
1503 struct StrataEstimatorMessage,
1504 op),
1505 GNUNET_MQ_hd_var_size (union_p2p_full_element,
1506 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT,
1507 struct GNUNET_SET_ElementMessage,
1508 op),
1509 GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info,
1510 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO,
1511 struct IntersectionElementInfoMessage,
1512 op),
1513 GNUNET_MQ_hd_var_size (intersection_p2p_bf,
1514 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF,
1515 struct BFMessage,
1516 op),
1517 GNUNET_MQ_hd_fixed_size (intersection_p2p_done,
1518 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE,
1519 struct IntersectionDoneMessage,
1520 op),
1521 GNUNET_MQ_handler_end () };
1522 struct Set *set;
1523 const struct GNUNET_MessageHeader *context;
1524
1525 if (NULL == (set = cs->set))
1526 {
1527 GNUNET_break (0);
1528 GNUNET_free (op);
1529 GNUNET_SERVICE_client_drop (cs->client);
1530 return;
1531 }
1532 op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
1533 op->peer = msg->target_peer;
1534 op->result_mode = ntohl (msg->result_mode);
1535 op->client_request_id = ntohl (msg->request_id);
1536 op->byzantine = msg->byzantine;
1537 op->byzantine_lower_bound = msg->byzantine_lower_bound;
1538 op->force_full = msg->force_full;
1539 op->force_delta = msg->force_delta;
1540 context = GNUNET_MQ_extract_nested_mh (msg);
1541
1542 /* Advance generation values, so that
1543 mutations won't interfer with the running operation. */
1544 op->set = set;
1545 op->generation_created = set->current_generation;
1546 advance_generation (set);
1547 GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op);
1548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1549 "Creating new CADET channel to port %s for set operation type %u\n",
1550 GNUNET_h2s (&msg->app_id),
1551 set->operation);
1552 op->channel = GNUNET_CADET_channel_create (cadet,
1553 op,
1554 &msg->target_peer,
1555 &msg->app_id,
1556 &channel_window_cb,
1557 &channel_end_cb,
1558 cadet_handlers);
1559 op->mq = GNUNET_CADET_get_mq (op->channel);
1560 op->state = set->vt->evaluate (op, context);
1561 if (NULL == op->state)
1562 {
1563 GNUNET_break (0);
1564 GNUNET_SERVICE_client_drop (cs->client);
1565 return;
1566 }
1567 GNUNET_SERVICE_client_continue (cs->client);
1568}
1569
1570
1571/**
1572 * Handle an ack from a client, and send the next element. Note
1573 * that we only expect acks for set elements, not after the
1574 * #GNUNET_MESSAGE_TYPE_SET_ITER_DONE message.
1575 *
1576 * @param cls client the client
1577 * @param ack the message
1578 */
1579static void
1580handle_client_iter_ack (void *cls, const struct GNUNET_SET_IterAckMessage *ack)
1581{
1582 struct ClientState *cs = cls;
1583 struct Set *set;
1584
1585 if (NULL == (set = cs->set))
1586 {
1587 /* client without a set acknowledged receiving a value */
1588 GNUNET_break (0);
1589 GNUNET_SERVICE_client_drop (cs->client);
1590 return;
1591 }
1592 if (NULL == set->iter)
1593 {
1594 /* client sent an ack, but we were not expecting one (as
1595 set iteration has finished) */
1596 GNUNET_break (0);
1597 GNUNET_SERVICE_client_drop (cs->client);
1598 return;
1599 }
1600 GNUNET_SERVICE_client_continue (cs->client);
1601 if (ntohl (ack->send_more))
1602 {
1603 send_client_element (set);
1604 }
1605 else
1606 {
1607 GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter);
1608 set->iter = NULL;
1609 set->iteration_id++;
1610 }
1611}
1612
1613
1614/**
1615 * Handle a request from the client to copy a set.
1616 *
1617 * @param cls the client
1618 * @param mh the message
1619 */
1620static void
1621handle_client_copy_lazy_prepare (void *cls,
1622 const struct GNUNET_MessageHeader *mh)
1623{
1624 struct ClientState *cs = cls;
1625 struct Set *set;
1626 struct LazyCopyRequest *cr;
1627 struct GNUNET_MQ_Envelope *ev;
1628 struct GNUNET_SET_CopyLazyResponseMessage *resp_msg;
1629
1630 if (NULL == (set = cs->set))
1631 {
1632 /* client without a set requested an operation */
1633 GNUNET_break (0);
1634 GNUNET_SERVICE_client_drop (cs->client);
1635 return;
1636 }
1637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1638 "Client requested creation of lazy copy\n");
1639 cr = GNUNET_new (struct LazyCopyRequest);
1640 cr->cookie = ++lazy_copy_cookie;
1641 cr->source_set = set;
1642 GNUNET_CONTAINER_DLL_insert (lazy_copy_head, lazy_copy_tail, cr);
1643 ev = GNUNET_MQ_msg (resp_msg, GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE);
1644 resp_msg->cookie = cr->cookie;
1645 GNUNET_MQ_send (set->cs->mq, ev);
1646 GNUNET_SERVICE_client_continue (cs->client);
1647}
1648
1649
1650/**
1651 * Handle a request from the client to connect to a copy of a set.
1652 *
1653 * @param cls the client
1654 * @param msg the message
1655 */
1656static void
1657handle_client_copy_lazy_connect (
1658 void *cls,
1659 const struct GNUNET_SET_CopyLazyConnectMessage *msg)
1660{
1661 struct ClientState *cs = cls;
1662 struct LazyCopyRequest *cr;
1663 struct Set *set;
1664 int found;
1665
1666 if (NULL != cs->set)
1667 {
1668 /* There can only be one set per client */
1669 GNUNET_break (0);
1670 GNUNET_SERVICE_client_drop (cs->client);
1671 return;
1672 }
1673 found = GNUNET_NO;
1674 for (cr = lazy_copy_head; NULL != cr; cr = cr->next)
1675 {
1676 if (cr->cookie == msg->cookie)
1677 {
1678 found = GNUNET_YES;
1679 break;
1680 }
1681 }
1682 if (GNUNET_NO == found)
1683 {
1684 /* client asked for copy with cookie we don't know */
1685 GNUNET_break (0);
1686 GNUNET_SERVICE_client_drop (cs->client);
1687 return;
1688 }
1689 GNUNET_CONTAINER_DLL_remove (lazy_copy_head, lazy_copy_tail, cr);
1690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1691 "Client %p requested use of lazy copy\n",
1692 cs);
1693 set = GNUNET_new (struct Set);
1694 switch (cr->source_set->operation)
1695 {
1696 case GNUNET_SET_OPERATION_INTERSECTION:
1697 set->vt = _GSS_intersection_vt ();
1698 break;
1699
1700 case GNUNET_SET_OPERATION_UNION:
1701 set->vt = _GSS_union_vt ();
1702 break;
1703
1704 default:
1705 GNUNET_assert (0);
1706 return;
1707 }
1708
1709 if (NULL == set->vt->copy_state)
1710 {
1711 /* Lazy copy not supported for this set operation */
1712 GNUNET_break (0);
1713 GNUNET_free (set);
1714 GNUNET_free (cr);
1715 GNUNET_SERVICE_client_drop (cs->client);
1716 return;
1717 }
1718
1719 set->operation = cr->source_set->operation;
1720 set->state = set->vt->copy_state (cr->source_set->state);
1721 set->content = cr->source_set->content;
1722 set->content->refcount++;
1723
1724 set->current_generation = cr->source_set->current_generation;
1725 set->excluded_generations_size = cr->source_set->excluded_generations_size;
1726 set->excluded_generations =
1727 GNUNET_memdup (cr->source_set->excluded_generations,
1728 set->excluded_generations_size
1729 * sizeof(struct GenerationRange));
1730
1731 /* Advance the generation of the new set, so that mutations to the
1732 of the cloned set and the source set are independent. */
1733 advance_generation (set);
1734 set->cs = cs;
1735 cs->set = set;
1736 GNUNET_free (cr);
1737 GNUNET_SERVICE_client_continue (cs->client);
1738}
1739
1740
1741/**
1742 * Handle a request from the client to cancel a running set operation.
1743 *
1744 * @param cls the client
1745 * @param msg the message
1746 */
1747static void
1748handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg)
1749{
1750 struct ClientState *cs = cls;
1751 struct Set *set;
1752 struct Operation *op;
1753 int found;
1754
1755 if (NULL == (set = cs->set))
1756 {
1757 /* client without a set requested an operation */
1758 GNUNET_break (0);
1759 GNUNET_SERVICE_client_drop (cs->client);
1760 return;
1761 }
1762 found = GNUNET_NO;
1763 for (op = set->ops_head; NULL != op; op = op->next)
1764 {
1765 if (op->client_request_id == ntohl (msg->request_id))
1766 {
1767 found = GNUNET_YES;
1768 break;
1769 }
1770 }
1771 if (GNUNET_NO == found)
1772 {
1773 /* It may happen that the operation was already destroyed due to
1774 * the other peer disconnecting. The client may not know about this
1775 * yet and try to cancel the (just barely non-existent) operation.
1776 * So this is not a hard error.
1777 */GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1778 "Client canceled non-existent op %u\n",
1779 (uint32_t) ntohl (msg->request_id));
1780 }
1781 else
1782 {
1783 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1784 "Client requested cancel for op %u\n",
1785 (uint32_t) ntohl (msg->request_id));
1786 _GSS_operation_destroy (op, GNUNET_YES);
1787 }
1788 GNUNET_SERVICE_client_continue (cs->client);
1789}
1790
1791
1792/**
1793 * Handle a request from the client to accept a set operation that
1794 * came from a remote peer. We forward the accept to the associated
1795 * operation for handling
1796 *
1797 * @param cls the client
1798 * @param msg the message
1799 */
1800static void
1801handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg)
1802{
1803 struct ClientState *cs = cls;
1804 struct Set *set;
1805 struct Operation *op;
1806 struct GNUNET_SET_ResultMessage *result_message;
1807 struct GNUNET_MQ_Envelope *ev;
1808 struct Listener *listener;
1809
1810 if (NULL == (set = cs->set))
1811 {
1812 /* client without a set requested to accept */
1813 GNUNET_break (0);
1814 GNUNET_SERVICE_client_drop (cs->client);
1815 return;
1816 }
1817 op = get_incoming (ntohl (msg->accept_reject_id));
1818 if (NULL == op)
1819 {
1820 /* It is not an error if the set op does not exist -- it may
1821 * have been destroyed when the partner peer disconnected. */
1822 GNUNET_log (
1823 GNUNET_ERROR_TYPE_INFO,
1824 "Client %p accepted request %u of listener %p that is no longer active\n",
1825 cs,
1826 ntohl (msg->accept_reject_id),
1827 cs->listener);
1828 ev = GNUNET_MQ_msg (result_message, GNUNET_MESSAGE_TYPE_SET_RESULT);
1829 result_message->request_id = msg->request_id;
1830 result_message->result_status = htons (GNUNET_SET_STATUS_FAILURE);
1831 GNUNET_MQ_send (set->cs->mq, ev);
1832 GNUNET_SERVICE_client_continue (cs->client);
1833 return;
1834 }
1835 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1836 "Client accepting request %u\n",
1837 (uint32_t) ntohl (msg->accept_reject_id));
1838 listener = op->listener;
1839 op->listener = NULL;
1840 GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op);
1841 op->set = set;
1842 GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op);
1843 op->client_request_id = ntohl (msg->request_id);
1844 op->result_mode = ntohl (msg->result_mode);
1845 op->byzantine = msg->byzantine;
1846 op->byzantine_lower_bound = msg->byzantine_lower_bound;
1847 op->force_full = msg->force_full;
1848 op->force_delta = msg->force_delta;
1849
1850 /* Advance generation values, so that future mutations do not
1851 interfer with the running operation. */
1852 op->generation_created = set->current_generation;
1853 advance_generation (set);
1854 GNUNET_assert (NULL == op->state);
1855 op->state = set->vt->accept (op);
1856 if (NULL == op->state)
1857 {
1858 GNUNET_break (0);
1859 GNUNET_SERVICE_client_drop (cs->client);
1860 return;
1861 }
1862 /* Now allow CADET to continue, as we did not do this in
1863 #handle_incoming_msg (as we wanted to first see if the
1864 local client would accept the request). */
1865 GNUNET_CADET_receive_done (op->channel);
1866 GNUNET_SERVICE_client_continue (cs->client);
1867}
1868
1869
1870/**
1871 * Called to clean up, after a shutdown has been requested.
1872 *
1873 * @param cls closure, NULL
1874 */
1875static void
1876shutdown_task (void *cls)
1877{
1878 /* Delay actual shutdown to allow service to disconnect clients */
1879 in_shutdown = GNUNET_YES;
1880 if (0 == num_clients)
1881 {
1882 if (NULL != cadet)
1883 {
1884 GNUNET_CADET_disconnect (cadet);
1885 cadet = NULL;
1886 }
1887 }
1888 GNUNET_STATISTICS_destroy (_GSS_statistics, GNUNET_YES);
1889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "handled shutdown request\n");
1890}
1891
1892
1893/**
1894 * Function called by the service's run
1895 * method to run service-specific setup code.
1896 *
1897 * @param cls closure
1898 * @param cfg configuration to use
1899 * @param service the initialized service
1900 */
1901static void
1902run (void *cls,
1903 const struct GNUNET_CONFIGURATION_Handle *cfg,
1904 struct GNUNET_SERVICE_Handle *service)
1905{
1906 /* FIXME: need to modify SERVICE (!) API to allow
1907 us to run a shutdown task *after* clients were
1908 forcefully disconnected! */
1909 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1910 _GSS_statistics = GNUNET_STATISTICS_create ("set", cfg);
1911 cadet = GNUNET_CADET_connect (cfg);
1912 if (NULL == cadet)
1913 {
1914 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1915 _ ("Could not connect to CADET service\n"));
1916 GNUNET_SCHEDULER_shutdown ();
1917 return;
1918 }
1919}
1920
1921
1922/**
1923 * Define "main" method using service macro.
1924 */
1925GNUNET_SERVICE_MAIN (
1926 "set",
1927 GNUNET_SERVICE_OPTION_NONE,
1928 &run,
1929 &client_connect_cb,
1930 &client_disconnect_cb,
1931 NULL,
1932 GNUNET_MQ_hd_fixed_size (client_accept,
1933 GNUNET_MESSAGE_TYPE_SET_ACCEPT,
1934 struct GNUNET_SET_AcceptMessage,
1935 NULL),
1936 GNUNET_MQ_hd_fixed_size (client_iter_ack,
1937 GNUNET_MESSAGE_TYPE_SET_ITER_ACK,
1938 struct GNUNET_SET_IterAckMessage,
1939 NULL),
1940 GNUNET_MQ_hd_var_size (client_mutation,
1941 GNUNET_MESSAGE_TYPE_SET_ADD,
1942 struct GNUNET_SET_ElementMessage,
1943 NULL),
1944 GNUNET_MQ_hd_fixed_size (client_create_set,
1945 GNUNET_MESSAGE_TYPE_SET_CREATE,
1946 struct GNUNET_SET_CreateMessage,
1947 NULL),
1948 GNUNET_MQ_hd_fixed_size (client_iterate,
1949 GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST,
1950 struct GNUNET_MessageHeader,
1951 NULL),
1952 GNUNET_MQ_hd_var_size (client_evaluate,
1953 GNUNET_MESSAGE_TYPE_SET_EVALUATE,
1954 struct GNUNET_SET_EvaluateMessage,
1955 NULL),
1956 GNUNET_MQ_hd_fixed_size (client_listen,
1957 GNUNET_MESSAGE_TYPE_SET_LISTEN,
1958 struct GNUNET_SET_ListenMessage,
1959 NULL),
1960 GNUNET_MQ_hd_fixed_size (client_reject,
1961 GNUNET_MESSAGE_TYPE_SET_REJECT,
1962 struct GNUNET_SET_RejectMessage,
1963 NULL),
1964 GNUNET_MQ_hd_var_size (client_mutation,
1965 GNUNET_MESSAGE_TYPE_SET_REMOVE,
1966 struct GNUNET_SET_ElementMessage,
1967 NULL),
1968 GNUNET_MQ_hd_fixed_size (client_cancel,
1969 GNUNET_MESSAGE_TYPE_SET_CANCEL,
1970 struct GNUNET_SET_CancelMessage,
1971 NULL),
1972 GNUNET_MQ_hd_fixed_size (client_copy_lazy_prepare,
1973 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE,
1974 struct GNUNET_MessageHeader,
1975 NULL),
1976 GNUNET_MQ_hd_fixed_size (client_copy_lazy_connect,
1977 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT,
1978 struct GNUNET_SET_CopyLazyConnectMessage,
1979 NULL),
1980 GNUNET_MQ_handler_end ());
1981
1982
1983/* end of gnunet-service-set.c */
diff --git a/src/contrib/service/set/gnunet-service-set.h b/src/contrib/service/set/gnunet-service-set.h
new file mode 100644
index 000000000..abdec7f08
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set.h
@@ -0,0 +1,665 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set.h
22 * @brief common components for the implementation the different set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_H_PRIVATE
27#define GNUNET_SERVICE_SET_H_PRIVATE
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_protocols.h"
32#include "gnunet_applications.h"
33#include "gnunet_core_service.h"
34#include "gnunet_cadet_service.h"
35#include "gnunet_set_service.h"
36#include "set.h"
37
38
39/**
40 * Implementation-specific set state. Used as opaque pointer, and
41 * specified further in the respective implementation.
42 */
43struct SetState;
44
45/**
46 * Implementation-specific set operation. Used as opaque pointer, and
47 * specified further in the respective implementation.
48 */
49struct OperationState;
50
51/**
52 * A set that supports a specific operation with other peers.
53 */
54struct Set;
55
56/**
57 * Information about an element element in the set. All elements are
58 * stored in a hash-table from their hash-code to their 'struct
59 * Element', so that the remove and add operations are reasonably
60 * fast.
61 */
62struct ElementEntry;
63
64/**
65 * Operation context used to execute a set operation.
66 */
67struct Operation;
68
69
70/**
71 * Signature of functions that create the implementation-specific
72 * state for a set supporting a specific operation.
73 *
74 * @return a set state specific to the supported operation, NULL on error
75 */
76typedef struct SetState *
77(*SetCreateImpl) (void);
78
79
80/**
81 * Signature of functions that implement the add/remove functionality
82 * for a set supporting a specific operation.
83 *
84 * @param set implementation-specific set state
85 * @param ee element message from the client
86 */
87typedef void
88(*SetAddRemoveImpl) (struct SetState *state,
89 struct ElementEntry *ee);
90
91
92/**
93 * Make a copy of a set's internal state.
94 *
95 * @param state set state to copy
96 * @return copy of the internal state
97 */
98typedef struct SetState *
99(*SetCopyStateImpl) (struct SetState *state);
100
101
102/**
103 * Signature of functions that implement the destruction of the
104 * implementation-specific set state.
105 *
106 * @param state the set state, contains implementation-specific data
107 */
108typedef void
109(*SetDestroyImpl) (struct SetState *state);
110
111
112/**
113 * Signature of functions that implement accepting a set operation.
114 *
115 * @param op operation that is created by accepting the operation,
116 * should be initialized by the implementation
117 * @return operation-specific state to keep in @a op
118 */
119typedef struct OperationState *
120(*OpAcceptImpl) (struct Operation *op);
121
122
123/**
124 * Signature of functions that implement starting the evaluation of
125 * set operations.
126 *
127 * @param op operation that is created, should be initialized to
128 * begin the evaluation
129 * @param opaque_context message to be transmitted to the listener
130 * to convince it to accept, may be NULL
131 * @return operation-specific state to keep in @a op
132 */
133typedef struct OperationState *
134(*OpEvaluateImpl) (struct Operation *op,
135 const struct GNUNET_MessageHeader *opaque_context);
136
137/**
138 * Signature of functions that implement operation cancellation.
139 * This includes notifying the client about the operation's final
140 * state.
141 *
142 * @param op operation state
143 */
144typedef void
145(*OpCancelImpl) (struct Operation *op);
146
147
148/**
149 * Signature of functions called when the CADET channel died.
150 *
151 * @param op operation state
152 */
153typedef void
154(*OpChannelDeathImpl) (struct Operation *op);
155
156
157/**
158 * Dispatch table for a specific set operation. Every set operation
159 * has to implement the callback in this struct.
160 */
161struct SetVT
162{
163 /**
164 * Callback for the set creation.
165 */
166 SetCreateImpl create;
167
168 /**
169 * Callback for element insertion
170 */
171 SetAddRemoveImpl add;
172
173 /**
174 * Callback for element removal.
175 */
176 SetAddRemoveImpl remove;
177
178 /**
179 * Callback for making a copy of a set's internal state.
180 */
181 SetCopyStateImpl copy_state;
182
183 /**
184 * Callback for destruction of the set state.
185 */
186 SetDestroyImpl destroy_set;
187
188 /**
189 * Callback for accepting a set operation request
190 */
191 OpAcceptImpl accept;
192
193 /**
194 * Callback for starting evaluation with a remote peer.
195 */
196 OpEvaluateImpl evaluate;
197
198 /**
199 * Callback for canceling an operation.
200 */
201 OpCancelImpl cancel;
202
203 /**
204 * Callback called in case the CADET channel died.
205 */
206 OpChannelDeathImpl channel_death;
207};
208
209
210/**
211 * MutationEvent gives information about changes
212 * to an element (removal / addition) in a set content.
213 */
214struct MutationEvent
215{
216 /**
217 * First generation affected by this mutation event.
218 *
219 * If @a generation is 0, this mutation event is a list
220 * sentinel element.
221 */
222 unsigned int generation;
223
224 /**
225 * If @a added is #GNUNET_YES, then this is a
226 * `remove` event, otherwise it is an `add` event.
227 */
228 int added;
229};
230
231
232/**
233 * Information about an element element in the set. All elements are
234 * stored in a hash-table from their hash-code to their `struct
235 * Element`, so that the remove and add operations are reasonably
236 * fast.
237 */
238struct ElementEntry
239{
240 /**
241 * The actual element. The data for the element
242 * should be allocated at the end of this struct.
243 */
244 struct GNUNET_SET_Element element;
245
246 /**
247 * Hash of the element. For set union: Will be used to derive the
248 * different IBF keys for different salts.
249 */
250 struct GNUNET_HashCode element_hash;
251
252 /**
253 * If @a mutations is not NULL, it contains
254 * a list of mutations, ordered by increasing generation.
255 * The list is terminated by a sentinel event with `generation`
256 * set to 0.
257 *
258 * If @a mutations is NULL, then this element exists in all generations
259 * of the respective set content this element belongs to.
260 */
261 struct MutationEvent *mutations;
262
263 /**
264 * Number of elements in the array @a mutations.
265 */
266 unsigned int mutations_size;
267
268 /**
269 * #GNUNET_YES if the element is a remote element, and does not belong
270 * to the operation's set.
271 */
272 int remote;
273};
274
275
276/**
277 * A listener is inhabited by a client, and waits for evaluation
278 * requests from remote peers.
279 */
280struct Listener;
281
282
283/**
284 * State we keep per client.
285 */
286struct ClientState
287{
288 /**
289 * Set, if associated with the client, otherwise NULL.
290 */
291 struct Set *set;
292
293 /**
294 * Listener, if associated with the client, otherwise NULL.
295 */
296 struct Listener *listener;
297
298 /**
299 * Client handle.
300 */
301 struct GNUNET_SERVICE_Client *client;
302
303 /**
304 * Message queue.
305 */
306 struct GNUNET_MQ_Handle *mq;
307};
308
309
310/**
311 * Operation context used to execute a set operation.
312 */
313struct Operation
314{
315 /**
316 * Kept in a DLL of the listener, if @e listener is non-NULL.
317 */
318 struct Operation *next;
319
320 /**
321 * Kept in a DLL of the listener, if @e listener is non-NULL.
322 */
323 struct Operation *prev;
324
325 /**
326 * Channel to the peer.
327 */
328 struct GNUNET_CADET_Channel *channel;
329
330 /**
331 * Port this operation runs on.
332 */
333 struct Listener *listener;
334
335 /**
336 * Message queue for the channel.
337 */
338 struct GNUNET_MQ_Handle *mq;
339
340 /**
341 * Context message, may be NULL.
342 */
343 struct GNUNET_MessageHeader *context_msg;
344
345 /**
346 * Set associated with the operation, NULL until the spec has been
347 * associated with a set.
348 */
349 struct Set *set;
350
351 /**
352 * Operation-specific operation state. Note that the exact
353 * type depends on this being a union or intersection operation
354 * (and thus on @e vt).
355 */
356 struct OperationState *state;
357
358 /**
359 * The identity of the requesting peer. Needs to
360 * be stored here as the op spec might not have been created yet.
361 */
362 struct GNUNET_PeerIdentity peer;
363
364 /**
365 * Timeout task, if the incoming peer has not been accepted
366 * after the timeout, it will be disconnected.
367 */
368 struct GNUNET_SCHEDULER_Task *timeout_task;
369
370 /**
371 * Salt to use for the operation.
372 */
373 uint32_t salt;
374
375 /**
376 * Remote peers element count
377 */
378 uint32_t remote_element_count;
379
380 /**
381 * ID used to identify an operation between service and client
382 */
383 uint32_t client_request_id;
384
385 /**
386 * When are elements sent to the client, and which elements are sent?
387 */
388 enum GNUNET_SET_ResultMode result_mode;
389
390 /**
391 * Always use delta operation instead of sending full sets,
392 * even it it's less efficient.
393 */
394 int force_delta;
395
396 /**
397 * Always send full sets, even if delta operations would
398 * be more efficient.
399 */
400 int force_full;
401
402 /**
403 * #GNUNET_YES to fail operations where Byzantine faults
404 * are suspected
405 */
406 int byzantine;
407
408 /**
409 * Lower bound for the set size, used only when
410 * byzantine mode is enabled.
411 */
412 int byzantine_lower_bound;
413
414 /**
415 * Unique request id for the request from a remote peer, sent to the
416 * client, which will accept or reject the request. Set to '0' iff
417 * the request has not been suggested yet.
418 */
419 uint32_t suggest_id;
420
421 /**
422 * Generation in which the operation handle
423 * was created.
424 */
425 unsigned int generation_created;
426};
427
428
429/**
430 * SetContent stores the actual set elements, which may be shared by
431 * multiple generations derived from one set.
432 */
433struct SetContent
434{
435 /**
436 * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`.
437 */
438 struct GNUNET_CONTAINER_MultiHashMap *elements;
439
440 /**
441 * Mutations requested by the client that we're
442 * unable to execute right now because we're iterating
443 * over the underlying hash map of elements.
444 */
445 struct PendingMutation *pending_mutations_head;
446
447 /**
448 * Mutations requested by the client that we're
449 * unable to execute right now because we're iterating
450 * over the underlying hash map of elements.
451 */
452 struct PendingMutation *pending_mutations_tail;
453
454 /**
455 * Number of references to the content.
456 */
457 unsigned int refcount;
458
459 /**
460 * FIXME: document!
461 */
462 unsigned int latest_generation;
463
464 /**
465 * Number of concurrently active iterators.
466 */
467 int iterator_count;
468};
469
470
471struct GenerationRange
472{
473 /**
474 * First generation that is excluded.
475 */
476 unsigned int start;
477
478 /**
479 * Generation after the last excluded generation.
480 */
481 unsigned int end;
482};
483
484
485/**
486 * Information about a mutation to apply to a set.
487 */
488struct PendingMutation
489{
490 /**
491 * Mutations are kept in a DLL.
492 */
493 struct PendingMutation *prev;
494
495 /**
496 * Mutations are kept in a DLL.
497 */
498 struct PendingMutation *next;
499
500 /**
501 * Set this mutation is about.
502 */
503 struct Set *set;
504
505 /**
506 * Message that describes the desired mutation.
507 * May only be a #GNUNET_MESSAGE_TYPE_SET_ADD or
508 * #GNUNET_MESSAGE_TYPE_SET_REMOVE.
509 */
510 struct GNUNET_SET_ElementMessage *msg;
511};
512
513
514/**
515 * A set that supports a specific operation with other peers.
516 */
517struct Set
518{
519 /**
520 * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`).
521 */
522 struct Set *next;
523
524 /**
525 * Sets are held in a doubly linked list.
526 */
527 struct Set *prev;
528
529 /**
530 * Client that owns the set. Only one client may own a set,
531 * and there can only be one set per client.
532 */
533 struct ClientState *cs;
534
535 /**
536 * Content, possibly shared by multiple sets,
537 * and thus reference counted.
538 */
539 struct SetContent *content;
540
541 /**
542 * Virtual table for this set. Determined by the operation type of
543 * this set.
544 *
545 * Used only for Add/remove of elements and when receiving an incoming
546 * operation from a remote peer.
547 */
548 const struct SetVT *vt;
549
550 /**
551 * Implementation-specific state.
552 */
553 struct SetState *state;
554
555 /**
556 * Current state of iterating elements for the client.
557 * NULL if we are not currently iterating.
558 */
559 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
560
561 /**
562 * Evaluate operations are held in a linked list.
563 */
564 struct Operation *ops_head;
565
566 /**
567 * Evaluate operations are held in a linked list.
568 */
569 struct Operation *ops_tail;
570
571 /**
572 * List of generations we have to exclude, due to lazy copies.
573 */
574 struct GenerationRange *excluded_generations;
575
576 /**
577 * Current generation, that is, number of previously executed
578 * operations and lazy copies on the underlying set content.
579 */
580 unsigned int current_generation;
581
582 /**
583 * Number of elements in array @a excluded_generations.
584 */
585 unsigned int excluded_generations_size;
586
587 /**
588 * Type of operation supported for this set
589 */
590 enum GNUNET_SET_OperationType operation;
591
592 /**
593 * Generation we're currently iteration over.
594 */
595 unsigned int iter_generation;
596
597 /**
598 * Each @e iter is assigned a unique number, so that the client
599 * can distinguish iterations.
600 */
601 uint16_t iteration_id;
602};
603
604
605extern struct GNUNET_STATISTICS_Handle *_GSS_statistics;
606
607
608/**
609 * Destroy the given operation. Used for any operation where both
610 * peers were known and that thus actually had a vt and channel. Must
611 * not be used for operations where 'listener' is still set and we do
612 * not know the other peer.
613 *
614 * Call the implementation-specific cancel function of the operation.
615 * Disconnects from the remote peer. Does not disconnect the client,
616 * as there may be multiple operations per set.
617 *
618 * @param op operation to destroy
619 * @param gc #GNUNET_YES to perform garbage collection on the set
620 */
621void
622_GSS_operation_destroy (struct Operation *op,
623 int gc);
624
625
626/**
627 * This function probably should not exist
628 * and be replaced by inlining more specific
629 * logic in the various places where it is called.
630 */
631void
632_GSS_operation_destroy2 (struct Operation *op);
633
634
635/**
636 * Get the table with implementing functions for set union.
637 *
638 * @return the operation specific VTable
639 */
640const struct SetVT *
641_GSS_union_vt (void);
642
643
644/**
645 * Get the table with implementing functions for set intersection.
646 *
647 * @return the operation specific VTable
648 */
649const struct SetVT *
650_GSS_intersection_vt (void);
651
652
653/**
654 * Is element @a ee part of the set used by @a op?
655 *
656 * @param ee element to test
657 * @param op operation the defines the set and its generation
658 * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not
659 */
660int
661_GSS_is_element_of_operation (struct ElementEntry *ee,
662 struct Operation *op);
663
664
665#endif
diff --git a/src/contrib/service/set/gnunet-service-set_intersection.c b/src/contrib/service/set/gnunet-service-set_intersection.c
new file mode 100644
index 000000000..51a8d0dbc
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_intersection.c
@@ -0,0 +1,1324 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_intersection.c
22 * @brief two-peer set intersection
23 * @author Christian Fuchs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "gnunet-service-set.h"
30#include "gnunet_block_lib.h"
31#include "gnunet-service-set_protocol.h"
32#include "gnunet-service-set_intersection.h"
33#include <gcrypt.h>
34
35
36/**
37 * Current phase we are in for a intersection operation.
38 */
39enum IntersectionOperationPhase
40{
41 /**
42 * We are just starting.
43 */
44 PHASE_INITIAL,
45
46 /**
47 * We have send the number of our elements to the other
48 * peer, but did not setup our element set yet.
49 */
50 PHASE_COUNT_SENT,
51
52 /**
53 * We have initialized our set and are now reducing it by exchanging
54 * Bloom filters until one party notices the their element hashes
55 * are equal.
56 */
57 PHASE_BF_EXCHANGE,
58
59 /**
60 * We must next send the P2P DONE message (after finishing mostly
61 * with the local client). Then we will wait for the channel to close.
62 */
63 PHASE_MUST_SEND_DONE,
64
65 /**
66 * We have received the P2P DONE message, and must finish with the
67 * local client before terminating the channel.
68 */
69 PHASE_DONE_RECEIVED,
70
71 /**
72 * The protocol is over. Results may still have to be sent to the
73 * client.
74 */
75 PHASE_FINISHED
76};
77
78
79/**
80 * State of an evaluate operation with another peer.
81 */
82struct OperationState
83{
84 /**
85 * The bf we currently receive
86 */
87 struct GNUNET_CONTAINER_BloomFilter *remote_bf;
88
89 /**
90 * BF of the set's element.
91 */
92 struct GNUNET_CONTAINER_BloomFilter *local_bf;
93
94 /**
95 * Remaining elements in the intersection operation.
96 * Maps element-id-hashes to 'elements in our set'.
97 */
98 struct GNUNET_CONTAINER_MultiHashMap *my_elements;
99
100 /**
101 * Iterator for sending the final set of @e my_elements to the client.
102 */
103 struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter;
104
105 /**
106 * Evaluate operations are held in a linked list.
107 */
108 struct OperationState *next;
109
110 /**
111 * Evaluate operations are held in a linked list.
112 */
113 struct OperationState *prev;
114
115 /**
116 * For multipart BF transmissions, we have to store the
117 * bloomfilter-data until we fully received it.
118 */
119 char *bf_data;
120
121 /**
122 * XOR of the keys of all of the elements (remaining) in my set.
123 * Always updated when elements are added or removed to
124 * @e my_elements.
125 */
126 struct GNUNET_HashCode my_xor;
127
128 /**
129 * XOR of the keys of all of the elements (remaining) in
130 * the other peer's set. Updated when we receive the
131 * other peer's Bloom filter.
132 */
133 struct GNUNET_HashCode other_xor;
134
135 /**
136 * How many bytes of @e bf_data are valid?
137 */
138 uint32_t bf_data_offset;
139
140 /**
141 * Current element count contained within @e my_elements.
142 * (May differ briefly during initialization.)
143 */
144 uint32_t my_element_count;
145
146 /**
147 * size of the bloomfilter in @e bf_data.
148 */
149 uint32_t bf_data_size;
150
151 /**
152 * size of the bloomfilter
153 */
154 uint32_t bf_bits_per_element;
155
156 /**
157 * Salt currently used for BF construction (by us or the other peer,
158 * depending on where we are in the code).
159 */
160 uint32_t salt;
161
162 /**
163 * Current state of the operation.
164 */
165 enum IntersectionOperationPhase phase;
166
167 /**
168 * Generation in which the operation handle
169 * was created.
170 */
171 unsigned int generation_created;
172
173 /**
174 * Did we send the client that we are done?
175 */
176 int client_done_sent;
177
178 /**
179 * Set whenever we reach the state where the death of the
180 * channel is perfectly find and should NOT result in the
181 * operation being cancelled.
182 */
183 int channel_death_expected;
184};
185
186
187/**
188 * Extra state required for efficient set intersection.
189 * Merely tracks the total number of elements.
190 */
191struct SetState
192{
193 /**
194 * Number of currently valid elements in the set which have not been
195 * removed.
196 */
197 uint32_t current_set_element_count;
198};
199
200
201/**
202 * If applicable in the current operation mode, send a result message
203 * to the client indicating we removed an element.
204 *
205 * @param op intersection operation
206 * @param element element to send
207 */
208static void
209send_client_removed_element (struct Operation *op,
210 struct GNUNET_SET_Element *element)
211{
212 struct GNUNET_MQ_Envelope *ev;
213 struct GNUNET_SET_ResultMessage *rm;
214
215 if (GNUNET_SET_RESULT_REMOVED != op->result_mode)
216 return; /* Wrong mode for transmitting removed elements */
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218 "Sending removed element (size %u) to client\n",
219 element->size);
220 GNUNET_STATISTICS_update (_GSS_statistics,
221 "# Element removed messages sent",
222 1,
223 GNUNET_NO);
224 GNUNET_assert (0 != op->client_request_id);
225 ev = GNUNET_MQ_msg_extra (rm,
226 element->size,
227 GNUNET_MESSAGE_TYPE_SET_RESULT);
228 if (NULL == ev)
229 {
230 GNUNET_break (0);
231 return;
232 }
233 rm->result_status = htons (GNUNET_SET_STATUS_OK);
234 rm->request_id = htonl (op->client_request_id);
235 rm->element_type = element->element_type;
236 GNUNET_memcpy (&rm[1],
237 element->data,
238 element->size);
239 GNUNET_MQ_send (op->set->cs->mq,
240 ev);
241}
242
243
244/**
245 * Fills the "my_elements" hashmap with all relevant elements.
246 *
247 * @param cls the `struct Operation *` we are performing
248 * @param key current key code
249 * @param value the `struct ElementEntry *` from the hash map
250 * @return #GNUNET_YES (we should continue to iterate)
251 */
252static int
253filtered_map_initialization (void *cls,
254 const struct GNUNET_HashCode *key,
255 void *value)
256{
257 struct Operation *op = cls;
258 struct ElementEntry *ee = value;
259 struct GNUNET_HashCode mutated_hash;
260
261
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263 "FIMA called for %s:%u\n",
264 GNUNET_h2s (&ee->element_hash),
265 ee->element.size);
266
267 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
270 "Reduced initialization, not starting with %s:%u (wrong generation)\n",
271 GNUNET_h2s (&ee->element_hash),
272 ee->element.size);
273 return GNUNET_YES; /* element not valid in our operation's generation */
274 }
275
276 /* Test if element is in other peer's bloomfilter */
277 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
278 op->state->salt,
279 &mutated_hash);
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Testing mingled hash %s with salt %u\n",
282 GNUNET_h2s (&mutated_hash),
283 op->state->salt);
284 if (GNUNET_NO ==
285 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
286 &mutated_hash))
287 {
288 /* remove this element */
289 send_client_removed_element (op,
290 &ee->element);
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "Reduced initialization, not starting with %s:%u\n",
293 GNUNET_h2s (&ee->element_hash),
294 ee->element.size);
295 return GNUNET_YES;
296 }
297 op->state->my_element_count++;
298 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
299 &ee->element_hash,
300 &op->state->my_xor);
301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302 "Filtered initialization of my_elements, adding %s:%u\n",
303 GNUNET_h2s (&ee->element_hash),
304 ee->element.size);
305 GNUNET_break (GNUNET_YES ==
306 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
307 &ee->element_hash,
308 ee,
309 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
310
311 return GNUNET_YES;
312}
313
314
315/**
316 * Removes elements from our hashmap if they are not contained within the
317 * provided remote bloomfilter.
318 *
319 * @param cls closure with the `struct Operation *`
320 * @param key current key code
321 * @param value value in the hash map
322 * @return #GNUNET_YES (we should continue to iterate)
323 */
324static int
325iterator_bf_reduce (void *cls,
326 const struct GNUNET_HashCode *key,
327 void *value)
328{
329 struct Operation *op = cls;
330 struct ElementEntry *ee = value;
331 struct GNUNET_HashCode mutated_hash;
332
333 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
334 op->state->salt,
335 &mutated_hash);
336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337 "Testing mingled hash %s with salt %u\n",
338 GNUNET_h2s (&mutated_hash),
339 op->state->salt);
340 if (GNUNET_NO ==
341 GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf,
342 &mutated_hash))
343 {
344 GNUNET_break (0 < op->state->my_element_count);
345 op->state->my_element_count--;
346 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
347 &ee->element_hash,
348 &op->state->my_xor);
349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350 "Bloom filter reduction of my_elements, removing %s:%u\n",
351 GNUNET_h2s (&ee->element_hash),
352 ee->element.size);
353 GNUNET_assert (GNUNET_YES ==
354 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
355 &ee->element_hash,
356 ee));
357 send_client_removed_element (op,
358 &ee->element);
359 }
360 else
361 {
362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363 "Bloom filter reduction of my_elements, keeping %s:%u\n",
364 GNUNET_h2s (&ee->element_hash),
365 ee->element.size);
366 }
367 return GNUNET_YES;
368}
369
370
371/**
372 * Create initial bloomfilter based on all the elements given.
373 *
374 * @param cls the `struct Operation *`
375 * @param key current key code
376 * @param value the `struct ElementEntry` to process
377 * @return #GNUNET_YES (we should continue to iterate)
378 */
379static int
380iterator_bf_create (void *cls,
381 const struct GNUNET_HashCode *key,
382 void *value)
383{
384 struct Operation *op = cls;
385 struct ElementEntry *ee = value;
386 struct GNUNET_HashCode mutated_hash;
387
388 GNUNET_BLOCK_mingle_hash (&ee->element_hash,
389 op->state->salt,
390 &mutated_hash);
391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392 "Initializing BF with hash %s with salt %u\n",
393 GNUNET_h2s (&mutated_hash),
394 op->state->salt);
395 GNUNET_CONTAINER_bloomfilter_add (op->state->local_bf,
396 &mutated_hash);
397 return GNUNET_YES;
398}
399
400
401/**
402 * Inform the client that the intersection operation has failed,
403 * and proceed to destroy the evaluate operation.
404 *
405 * @param op the intersection operation to fail
406 */
407static void
408fail_intersection_operation (struct Operation *op)
409{
410 struct GNUNET_MQ_Envelope *ev;
411 struct GNUNET_SET_ResultMessage *msg;
412
413 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
414 "Intersection operation failed\n");
415 GNUNET_STATISTICS_update (_GSS_statistics,
416 "# Intersection operations failed",
417 1,
418 GNUNET_NO);
419 if (NULL != op->state->my_elements)
420 {
421 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
422 op->state->my_elements = NULL;
423 }
424 ev = GNUNET_MQ_msg (msg,
425 GNUNET_MESSAGE_TYPE_SET_RESULT);
426 msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
427 msg->request_id = htonl (op->client_request_id);
428 msg->element_type = htons (0);
429 GNUNET_MQ_send (op->set->cs->mq,
430 ev);
431 _GSS_operation_destroy (op,
432 GNUNET_YES);
433}
434
435
436/**
437 * Send a bloomfilter to our peer. After the result done message has
438 * been sent to the client, destroy the evaluate operation.
439 *
440 * @param op intersection operation
441 */
442static void
443send_bloomfilter (struct Operation *op)
444{
445 struct GNUNET_MQ_Envelope *ev;
446 struct BFMessage *msg;
447 uint32_t bf_size;
448 uint32_t bf_elementbits;
449 uint32_t chunk_size;
450 char *bf_data;
451 uint32_t offset;
452
453 /* We consider the ratio of the set sizes to determine
454 the number of bits per element, as the smaller set
455 should use more bits to maximize its set reduction
456 potential and minimize overall bandwidth consumption. */
457 bf_elementbits = 2 + ceil (log2 ((double)
458 (op->remote_element_count
459 / (double) op->state->my_element_count)));
460 if (bf_elementbits < 1)
461 bf_elementbits = 1; /* make sure k is not 0 */
462 /* optimize BF-size to ~50% of bits set */
463 bf_size = ceil ((double) (op->state->my_element_count
464 * bf_elementbits / log (2)));
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Sending Bloom filter (%u) of size %u bytes\n",
467 (unsigned int) bf_elementbits,
468 (unsigned int) bf_size);
469 op->state->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL,
470 bf_size,
471 bf_elementbits);
472 op->state->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
473 UINT32_MAX);
474 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
475 &iterator_bf_create,
476 op);
477
478 /* send our Bloom filter */
479 GNUNET_STATISTICS_update (_GSS_statistics,
480 "# Intersection Bloom filters sent",
481 1,
482 GNUNET_NO);
483 chunk_size = 60 * 1024 - sizeof(struct BFMessage);
484 if (bf_size <= chunk_size)
485 {
486 /* singlepart */
487 chunk_size = bf_size;
488 ev = GNUNET_MQ_msg_extra (msg,
489 chunk_size,
490 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
491 GNUNET_assert (GNUNET_SYSERR !=
492 GNUNET_CONTAINER_bloomfilter_get_raw_data (
493 op->state->local_bf,
494 (char *) &msg[1],
495 bf_size));
496 msg->sender_element_count = htonl (op->state->my_element_count);
497 msg->bloomfilter_total_length = htonl (bf_size);
498 msg->bits_per_element = htonl (bf_elementbits);
499 msg->sender_mutator = htonl (op->state->salt);
500 msg->element_xor_hash = op->state->my_xor;
501 GNUNET_MQ_send (op->mq, ev);
502 }
503 else
504 {
505 /* multipart */
506 bf_data = GNUNET_malloc (bf_size);
507 GNUNET_assert (GNUNET_SYSERR !=
508 GNUNET_CONTAINER_bloomfilter_get_raw_data (
509 op->state->local_bf,
510 bf_data,
511 bf_size));
512 offset = 0;
513 while (offset < bf_size)
514 {
515 if (bf_size - chunk_size < offset)
516 chunk_size = bf_size - offset;
517 ev = GNUNET_MQ_msg_extra (msg,
518 chunk_size,
519 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF);
520 GNUNET_memcpy (&msg[1],
521 &bf_data[offset],
522 chunk_size);
523 offset += chunk_size;
524 msg->sender_element_count = htonl (op->state->my_element_count);
525 msg->bloomfilter_total_length = htonl (bf_size);
526 msg->bits_per_element = htonl (bf_elementbits);
527 msg->sender_mutator = htonl (op->state->salt);
528 msg->element_xor_hash = op->state->my_xor;
529 GNUNET_MQ_send (op->mq, ev);
530 }
531 GNUNET_free (bf_data);
532 }
533 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
534 op->state->local_bf = NULL;
535}
536
537
538/**
539 * Signal to the client that the operation has finished and
540 * destroy the operation.
541 *
542 * @param cls operation to destroy
543 */
544static void
545send_client_done_and_destroy (void *cls)
546{
547 struct Operation *op = cls;
548 struct GNUNET_MQ_Envelope *ev;
549 struct GNUNET_SET_ResultMessage *rm;
550
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552 "Intersection succeeded, sending DONE to local client\n");
553 GNUNET_STATISTICS_update (_GSS_statistics,
554 "# Intersection operations succeeded",
555 1,
556 GNUNET_NO);
557 ev = GNUNET_MQ_msg (rm,
558 GNUNET_MESSAGE_TYPE_SET_RESULT);
559 rm->request_id = htonl (op->client_request_id);
560 rm->result_status = htons (GNUNET_SET_STATUS_DONE);
561 rm->element_type = htons (0);
562 GNUNET_MQ_send (op->set->cs->mq,
563 ev);
564 _GSS_operation_destroy (op,
565 GNUNET_YES);
566}
567
568
569/**
570 * Remember that we are done dealing with the local client
571 * AND have sent the other peer our message that we are done,
572 * so we are not just waiting for the channel to die before
573 * telling the local client that we are done as our last act.
574 *
575 * @param cls the `struct Operation`.
576 */
577static void
578finished_local_operations (void *cls)
579{
580 struct Operation *op = cls;
581
582 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583 "DONE sent to other peer, now waiting for other end to close the channel\n");
584 op->state->phase = PHASE_FINISHED;
585 op->state->channel_death_expected = GNUNET_YES;
586}
587
588
589/**
590 * Notify the other peer that we are done. Once this message
591 * is out, we still need to notify the local client that we
592 * are done.
593 *
594 * @param op operation to notify for.
595 */
596static void
597send_p2p_done (struct Operation *op)
598{
599 struct GNUNET_MQ_Envelope *ev;
600 struct IntersectionDoneMessage *idm;
601
602 GNUNET_assert (PHASE_MUST_SEND_DONE == op->state->phase);
603 GNUNET_assert (GNUNET_NO == op->state->channel_death_expected);
604 ev = GNUNET_MQ_msg (idm,
605 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE);
606 idm->final_element_count = htonl (op->state->my_element_count);
607 idm->element_xor_hash = op->state->my_xor;
608 GNUNET_MQ_notify_sent (ev,
609 &finished_local_operations,
610 op);
611 GNUNET_MQ_send (op->mq,
612 ev);
613}
614
615
616/**
617 * Send all elements in the full result iterator.
618 *
619 * @param cls the `struct Operation *`
620 */
621static void
622send_remaining_elements (void *cls)
623{
624 struct Operation *op = cls;
625 const void *nxt;
626 const struct ElementEntry *ee;
627 struct GNUNET_MQ_Envelope *ev;
628 struct GNUNET_SET_ResultMessage *rm;
629 const struct GNUNET_SET_Element *element;
630 int res;
631
632 res = GNUNET_CONTAINER_multihashmap_iterator_next (
633 op->state->full_result_iter,
634 NULL,
635 &nxt);
636 if (GNUNET_NO == res)
637 {
638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639 "Sending done and destroy because iterator ran out\n");
640 GNUNET_CONTAINER_multihashmap_iterator_destroy (
641 op->state->full_result_iter);
642 op->state->full_result_iter = NULL;
643 if (PHASE_DONE_RECEIVED == op->state->phase)
644 {
645 op->state->phase = PHASE_FINISHED;
646 send_client_done_and_destroy (op);
647 }
648 else if (PHASE_MUST_SEND_DONE == op->state->phase)
649 {
650 send_p2p_done (op);
651 }
652 else
653 {
654 GNUNET_assert (0);
655 }
656 return;
657 }
658 ee = nxt;
659 element = &ee->element;
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Sending element %s:%u to client (full set)\n",
662 GNUNET_h2s (&ee->element_hash),
663 element->size);
664 GNUNET_assert (0 != op->client_request_id);
665 ev = GNUNET_MQ_msg_extra (rm,
666 element->size,
667 GNUNET_MESSAGE_TYPE_SET_RESULT);
668 GNUNET_assert (NULL != ev);
669 rm->result_status = htons (GNUNET_SET_STATUS_OK);
670 rm->request_id = htonl (op->client_request_id);
671 rm->element_type = element->element_type;
672 GNUNET_memcpy (&rm[1],
673 element->data,
674 element->size);
675 GNUNET_MQ_notify_sent (ev,
676 &send_remaining_elements,
677 op);
678 GNUNET_MQ_send (op->set->cs->mq,
679 ev);
680}
681
682
683/**
684 * Fills the "my_elements" hashmap with the initial set of
685 * (non-deleted) elements from the set of the specification.
686 *
687 * @param cls closure with the `struct Operation *`
688 * @param key current key code for the element
689 * @param value value in the hash map with the `struct ElementEntry *`
690 * @return #GNUNET_YES (we should continue to iterate)
691 */
692static int
693initialize_map_unfiltered (void *cls,
694 const struct GNUNET_HashCode *key,
695 void *value)
696{
697 struct ElementEntry *ee = value;
698 struct Operation *op = cls;
699
700 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
701 return GNUNET_YES; /* element not live in operation's generation */
702 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
703 &ee->element_hash,
704 &op->state->my_xor);
705 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706 "Initial full initialization of my_elements, adding %s:%u\n",
707 GNUNET_h2s (&ee->element_hash),
708 ee->element.size);
709 GNUNET_break (GNUNET_YES ==
710 GNUNET_CONTAINER_multihashmap_put (op->state->my_elements,
711 &ee->element_hash,
712 ee,
713 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
714 return GNUNET_YES;
715}
716
717
718/**
719 * Send our element count to the peer, in case our element count is
720 * lower than theirs.
721 *
722 * @param op intersection operation
723 */
724static void
725send_element_count (struct Operation *op)
726{
727 struct GNUNET_MQ_Envelope *ev;
728 struct IntersectionElementInfoMessage *msg;
729
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
731 "Sending our element count (%u)\n",
732 op->state->my_element_count);
733 ev = GNUNET_MQ_msg (msg,
734 GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO);
735 msg->sender_element_count = htonl (op->state->my_element_count);
736 GNUNET_MQ_send (op->mq, ev);
737}
738
739
740/**
741 * We go first, initialize our map with all elements and
742 * send the first Bloom filter.
743 *
744 * @param op operation to start exchange for
745 */
746static void
747begin_bf_exchange (struct Operation *op)
748{
749 op->state->phase = PHASE_BF_EXCHANGE;
750 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
751 &initialize_map_unfiltered,
752 op);
753 send_bloomfilter (op);
754}
755
756
757void
758handle_intersection_p2p_element_info (void *cls,
759 const struct
760 IntersectionElementInfoMessage *msg)
761{
762 struct Operation *op = cls;
763
764 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
765 {
766 GNUNET_break_op (0);
767 fail_intersection_operation (op);
768 return;
769 }
770 op->remote_element_count = ntohl (msg->sender_element_count);
771 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
772 "Received remote element count (%u), I have %u\n",
773 op->remote_element_count,
774 op->state->my_element_count);
775 if (((PHASE_INITIAL != op->state->phase) &&
776 (PHASE_COUNT_SENT != op->state->phase)) ||
777 (op->state->my_element_count > op->remote_element_count) ||
778 (0 == op->state->my_element_count) ||
779 (0 == op->remote_element_count))
780 {
781 GNUNET_break_op (0);
782 fail_intersection_operation (op);
783 return;
784 }
785 GNUNET_break (NULL == op->state->remote_bf);
786 begin_bf_exchange (op);
787 GNUNET_CADET_receive_done (op->channel);
788}
789
790
791/**
792 * Process a Bloomfilter once we got all the chunks.
793 *
794 * @param op the intersection operation
795 */
796static void
797process_bf (struct Operation *op)
798{
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Received BF in phase %u, foreign count is %u, my element count is %u/%u\n",
801 op->state->phase,
802 op->remote_element_count,
803 op->state->my_element_count,
804 GNUNET_CONTAINER_multihashmap_size (op->set->content->elements));
805 switch (op->state->phase)
806 {
807 case PHASE_INITIAL:
808 GNUNET_break_op (0);
809 fail_intersection_operation (op);
810 return;
811
812 case PHASE_COUNT_SENT:
813 /* This is the first BF being sent, build our initial map with
814 filtering in place */
815 op->state->my_element_count = 0;
816 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
817 &filtered_map_initialization,
818 op);
819 break;
820
821 case PHASE_BF_EXCHANGE:
822 /* Update our set by reduction */
823 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
824 &iterator_bf_reduce,
825 op);
826 break;
827
828 case PHASE_MUST_SEND_DONE:
829 GNUNET_break_op (0);
830 fail_intersection_operation (op);
831 return;
832
833 case PHASE_DONE_RECEIVED:
834 GNUNET_break_op (0);
835 fail_intersection_operation (op);
836 return;
837
838 case PHASE_FINISHED:
839 GNUNET_break_op (0);
840 fail_intersection_operation (op);
841 return;
842 }
843 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
844 op->state->remote_bf = NULL;
845
846 if ((0 == op->state->my_element_count) || /* fully disjoint */
847 ((op->state->my_element_count == op->remote_element_count) &&
848 (0 == GNUNET_memcmp (&op->state->my_xor,
849 &op->state->other_xor))))
850 {
851 /* we are done */
852 op->state->phase = PHASE_MUST_SEND_DONE;
853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854 "Intersection succeeded, sending DONE to other peer\n");
855 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
856 op->state->local_bf = NULL;
857 if (GNUNET_SET_RESULT_FULL == op->result_mode)
858 {
859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
860 "Sending full result set (%u elements)\n",
861 GNUNET_CONTAINER_multihashmap_size (op->state->my_elements));
862 op->state->full_result_iter
863 = GNUNET_CONTAINER_multihashmap_iterator_create (
864 op->state->my_elements);
865 send_remaining_elements (op);
866 return;
867 }
868 send_p2p_done (op);
869 return;
870 }
871 op->state->phase = PHASE_BF_EXCHANGE;
872 send_bloomfilter (op);
873}
874
875
876/**
877 * Check an BF message from a remote peer.
878 *
879 * @param cls the intersection operation
880 * @param msg the header of the message
881 * @return #GNUNET_OK if @a msg is well-formed
882 */
883int
884check_intersection_p2p_bf (void *cls,
885 const struct BFMessage *msg)
886{
887 struct Operation *op = cls;
888
889 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
890 {
891 GNUNET_break_op (0);
892 return GNUNET_SYSERR;
893 }
894 return GNUNET_OK;
895}
896
897
898/**
899 * Handle an BF message from a remote peer.
900 *
901 * @param cls the intersection operation
902 * @param msg the header of the message
903 */
904void
905handle_intersection_p2p_bf (void *cls,
906 const struct BFMessage *msg)
907{
908 struct Operation *op = cls;
909 uint32_t bf_size;
910 uint32_t chunk_size;
911 uint32_t bf_bits_per_element;
912
913 switch (op->state->phase)
914 {
915 case PHASE_INITIAL:
916 GNUNET_break_op (0);
917 fail_intersection_operation (op);
918 return;
919
920 case PHASE_COUNT_SENT:
921 case PHASE_BF_EXCHANGE:
922 bf_size = ntohl (msg->bloomfilter_total_length);
923 bf_bits_per_element = ntohl (msg->bits_per_element);
924 chunk_size = htons (msg->header.size) - sizeof(struct BFMessage);
925 op->state->other_xor = msg->element_xor_hash;
926 if (bf_size == chunk_size)
927 {
928 if (NULL != op->state->bf_data)
929 {
930 GNUNET_break_op (0);
931 fail_intersection_operation (op);
932 return;
933 }
934 /* single part, done here immediately */
935 op->state->remote_bf
936 = GNUNET_CONTAINER_bloomfilter_init ((const char *) &msg[1],
937 bf_size,
938 bf_bits_per_element);
939 op->state->salt = ntohl (msg->sender_mutator);
940 op->remote_element_count = ntohl (msg->sender_element_count);
941 process_bf (op);
942 break;
943 }
944 /* multipart chunk */
945 if (NULL == op->state->bf_data)
946 {
947 /* first chunk, initialize */
948 op->state->bf_data = GNUNET_malloc (bf_size);
949 op->state->bf_data_size = bf_size;
950 op->state->bf_bits_per_element = bf_bits_per_element;
951 op->state->bf_data_offset = 0;
952 op->state->salt = ntohl (msg->sender_mutator);
953 op->remote_element_count = ntohl (msg->sender_element_count);
954 }
955 else
956 {
957 /* increment */
958 if ((op->state->bf_data_size != bf_size) ||
959 (op->state->bf_bits_per_element != bf_bits_per_element) ||
960 (op->state->bf_data_offset + chunk_size > bf_size) ||
961 (op->state->salt != ntohl (msg->sender_mutator)) ||
962 (op->remote_element_count != ntohl (msg->sender_element_count)))
963 {
964 GNUNET_break_op (0);
965 fail_intersection_operation (op);
966 return;
967 }
968 }
969 GNUNET_memcpy (&op->state->bf_data[op->state->bf_data_offset],
970 (const char *) &msg[1],
971 chunk_size);
972 op->state->bf_data_offset += chunk_size;
973 if (op->state->bf_data_offset == bf_size)
974 {
975 /* last chunk, run! */
976 op->state->remote_bf
977 = GNUNET_CONTAINER_bloomfilter_init (op->state->bf_data,
978 bf_size,
979 bf_bits_per_element);
980 GNUNET_free (op->state->bf_data);
981 op->state->bf_data = NULL;
982 op->state->bf_data_size = 0;
983 process_bf (op);
984 }
985 break;
986
987 default:
988 GNUNET_break_op (0);
989 fail_intersection_operation (op);
990 return;
991 }
992 GNUNET_CADET_receive_done (op->channel);
993}
994
995
996/**
997 * Remove all elements from our hashmap.
998 *
999 * @param cls closure with the `struct Operation *`
1000 * @param key current key code
1001 * @param value value in the hash map
1002 * @return #GNUNET_YES (we should continue to iterate)
1003 */
1004static int
1005filter_all (void *cls,
1006 const struct GNUNET_HashCode *key,
1007 void *value)
1008{
1009 struct Operation *op = cls;
1010 struct ElementEntry *ee = value;
1011
1012 GNUNET_break (0 < op->state->my_element_count);
1013 op->state->my_element_count--;
1014 GNUNET_CRYPTO_hash_xor (&op->state->my_xor,
1015 &ee->element_hash,
1016 &op->state->my_xor);
1017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018 "Final reduction of my_elements, removing %s:%u\n",
1019 GNUNET_h2s (&ee->element_hash),
1020 ee->element.size);
1021 GNUNET_assert (GNUNET_YES ==
1022 GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements,
1023 &ee->element_hash,
1024 ee));
1025 send_client_removed_element (op,
1026 &ee->element);
1027 return GNUNET_YES;
1028}
1029
1030
1031/**
1032 * Handle a done message from a remote peer
1033 *
1034 * @param cls the intersection operation
1035 * @param idm the message
1036 */
1037void
1038handle_intersection_p2p_done (void *cls,
1039 const struct IntersectionDoneMessage *idm)
1040{
1041 struct Operation *op = cls;
1042
1043 if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation)
1044 {
1045 GNUNET_break_op (0);
1046 fail_intersection_operation (op);
1047 return;
1048 }
1049 if (PHASE_BF_EXCHANGE != op->state->phase)
1050 {
1051 /* wrong phase to conclude? FIXME: Or should we allow this
1052 if the other peer has _initially_ already an empty set? */
1053 GNUNET_break_op (0);
1054 fail_intersection_operation (op);
1055 return;
1056 }
1057 if (0 == ntohl (idm->final_element_count))
1058 {
1059 /* other peer determined empty set is the intersection,
1060 remove all elements */
1061 GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements,
1062 &filter_all,
1063 op);
1064 }
1065 if ((op->state->my_element_count != ntohl (idm->final_element_count)) ||
1066 (0 != GNUNET_memcmp (&op->state->my_xor,
1067 &idm->element_xor_hash)))
1068 {
1069 /* Other peer thinks we are done, but we disagree on the result! */
1070 GNUNET_break_op (0);
1071 fail_intersection_operation (op);
1072 return;
1073 }
1074 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1075 "Got IntersectionDoneMessage, have %u elements in intersection\n",
1076 op->state->my_element_count);
1077 op->state->phase = PHASE_DONE_RECEIVED;
1078 GNUNET_CADET_receive_done (op->channel);
1079
1080 GNUNET_assert (GNUNET_NO == op->state->client_done_sent);
1081 if (GNUNET_SET_RESULT_FULL == op->result_mode)
1082 {
1083 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084 "Sending full result set to client (%u elements)\n",
1085 GNUNET_CONTAINER_multihashmap_size (op->state->my_elements));
1086 op->state->full_result_iter
1087 = GNUNET_CONTAINER_multihashmap_iterator_create (op->state->my_elements);
1088 send_remaining_elements (op);
1089 return;
1090 }
1091 op->state->phase = PHASE_FINISHED;
1092 send_client_done_and_destroy (op);
1093}
1094
1095
1096/**
1097 * Initiate a set intersection operation with a remote peer.
1098 *
1099 * @param op operation that is created, should be initialized to
1100 * begin the evaluation
1101 * @param opaque_context message to be transmitted to the listener
1102 * to convince it to accept, may be NULL
1103 * @return operation-specific state to keep in @a op
1104 */
1105static struct OperationState *
1106intersection_evaluate (struct Operation *op,
1107 const struct GNUNET_MessageHeader *opaque_context)
1108{
1109 struct OperationState *state;
1110 struct GNUNET_MQ_Envelope *ev;
1111 struct OperationRequestMessage *msg;
1112
1113 ev = GNUNET_MQ_msg_nested_mh (msg,
1114 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
1115 opaque_context);
1116 if (NULL == ev)
1117 {
1118 /* the context message is too large!? */
1119 GNUNET_break (0);
1120 return NULL;
1121 }
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Initiating intersection operation evaluation\n");
1124 state = GNUNET_new (struct OperationState);
1125 /* we started the operation, thus we have to send the operation request */
1126 state->phase = PHASE_INITIAL;
1127 state->my_element_count = op->set->state->current_set_element_count;
1128 state->my_elements
1129 = GNUNET_CONTAINER_multihashmap_create (state->my_element_count,
1130 GNUNET_YES);
1131
1132 msg->operation = htonl (GNUNET_SET_OPERATION_INTERSECTION);
1133 msg->element_count = htonl (state->my_element_count);
1134 GNUNET_MQ_send (op->mq,
1135 ev);
1136 state->phase = PHASE_COUNT_SENT;
1137 if (NULL != opaque_context)
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "Sent op request with context message\n");
1140 else
1141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142 "Sent op request without context message\n");
1143 return state;
1144}
1145
1146
1147/**
1148 * Accept an intersection operation request from a remote peer. Only
1149 * initializes the private operation state.
1150 *
1151 * @param op operation that will be accepted as an intersection operation
1152 */
1153static struct OperationState *
1154intersection_accept (struct Operation *op)
1155{
1156 struct OperationState *state;
1157
1158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1159 "Accepting set intersection operation\n");
1160 state = GNUNET_new (struct OperationState);
1161 state->phase = PHASE_INITIAL;
1162 state->my_element_count
1163 = op->set->state->current_set_element_count;
1164 state->my_elements
1165 = GNUNET_CONTAINER_multihashmap_create (GNUNET_MIN (state->my_element_count,
1166 op->remote_element_count),
1167 GNUNET_YES);
1168 op->state = state;
1169 if (op->remote_element_count < state->my_element_count)
1170 {
1171 /* If the other peer (Alice) has fewer elements than us (Bob),
1172 we just send the count as Alice should send the first BF */
1173 send_element_count (op);
1174 state->phase = PHASE_COUNT_SENT;
1175 return state;
1176 }
1177 /* We have fewer elements, so we start with the BF */
1178 begin_bf_exchange (op);
1179 return state;
1180}
1181
1182
1183/**
1184 * Destroy the intersection operation. Only things specific to the
1185 * intersection operation are destroyed.
1186 *
1187 * @param op intersection operation to destroy
1188 */
1189static void
1190intersection_op_cancel (struct Operation *op)
1191{
1192 /* check if the op was canceled twice */
1193 GNUNET_assert (NULL != op->state);
1194 if (NULL != op->state->remote_bf)
1195 {
1196 GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf);
1197 op->state->remote_bf = NULL;
1198 }
1199 if (NULL != op->state->local_bf)
1200 {
1201 GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf);
1202 op->state->local_bf = NULL;
1203 }
1204 if (NULL != op->state->my_elements)
1205 {
1206 GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements);
1207 op->state->my_elements = NULL;
1208 }
1209 if (NULL != op->state->full_result_iter)
1210 {
1211 GNUNET_CONTAINER_multihashmap_iterator_destroy (
1212 op->state->full_result_iter);
1213 op->state->full_result_iter = NULL;
1214 }
1215 GNUNET_free (op->state);
1216 op->state = NULL;
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1218 "Destroying intersection op state done\n");
1219}
1220
1221
1222/**
1223 * Create a new set supporting the intersection operation.
1224 *
1225 * @return the newly created set
1226 */
1227static struct SetState *
1228intersection_set_create ()
1229{
1230 struct SetState *set_state;
1231
1232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1233 "Intersection set created\n");
1234 set_state = GNUNET_new (struct SetState);
1235 set_state->current_set_element_count = 0;
1236
1237 return set_state;
1238}
1239
1240
1241/**
1242 * Add the element from the given element message to the set.
1243 *
1244 * @param set_state state of the set want to add to
1245 * @param ee the element to add to the set
1246 */
1247static void
1248intersection_add (struct SetState *set_state,
1249 struct ElementEntry *ee)
1250{
1251 set_state->current_set_element_count++;
1252}
1253
1254
1255/**
1256 * Destroy a set that supports the intersection operation
1257 *
1258 * @param set_state the set to destroy
1259 */
1260static void
1261intersection_set_destroy (struct SetState *set_state)
1262{
1263 GNUNET_free (set_state);
1264}
1265
1266
1267/**
1268 * Remove the element given in the element message from the set.
1269 *
1270 * @param set_state state of the set to remove from
1271 * @param element set element to remove
1272 */
1273static void
1274intersection_remove (struct SetState *set_state,
1275 struct ElementEntry *element)
1276{
1277 GNUNET_assert (0 < set_state->current_set_element_count);
1278 set_state->current_set_element_count--;
1279}
1280
1281
1282/**
1283 * Callback for channel death for the intersection operation.
1284 *
1285 * @param op operation that lost the channel
1286 */
1287static void
1288intersection_channel_death (struct Operation *op)
1289{
1290 if (GNUNET_YES == op->state->channel_death_expected)
1291 {
1292 /* oh goodie, we are done! */
1293 send_client_done_and_destroy (op);
1294 }
1295 else
1296 {
1297 /* sorry, channel went down early, too bad. */
1298 _GSS_operation_destroy (op,
1299 GNUNET_YES);
1300 }
1301}
1302
1303
1304/**
1305 * Get the table with implementing functions for set intersection.
1306 *
1307 * @return the operation specific VTable
1308 */
1309const struct SetVT *
1310_GSS_intersection_vt ()
1311{
1312 static const struct SetVT intersection_vt = {
1313 .create = &intersection_set_create,
1314 .add = &intersection_add,
1315 .remove = &intersection_remove,
1316 .destroy_set = &intersection_set_destroy,
1317 .evaluate = &intersection_evaluate,
1318 .accept = &intersection_accept,
1319 .cancel = &intersection_op_cancel,
1320 .channel_death = &intersection_channel_death,
1321 };
1322
1323 return &intersection_vt;
1324}
diff --git a/src/contrib/service/set/gnunet-service-set_intersection.h b/src/contrib/service/set/gnunet-service-set_intersection.h
new file mode 100644
index 000000000..d8884d0a7
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_intersection.h
@@ -0,0 +1,79 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_intersection.h
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_INTERSECTION_H
27#define GNUNET_SERVICE_SET_INTERSECTION_H
28
29#include "gnunet-service-set.h"
30
31
32/**
33 * Check an BF message from a remote peer.
34 *
35 * @param cls the intersection operation
36 * @param msg the header of the message
37 * @return #GNUNET_OK if @a msg is well-formed
38 */
39int
40check_intersection_p2p_bf (void *cls,
41 const struct BFMessage *msg);
42
43
44/**
45 * Handle an BF message from a remote peer.
46 *
47 * @param cls the intersection operation
48 * @param msg the header of the message
49 */
50void
51handle_intersection_p2p_bf (void *cls,
52 const struct BFMessage *msg);
53
54
55/**
56 * Handle the initial `struct IntersectionElementInfoMessage` from a
57 * remote peer.
58 *
59 * @param cls the intersection operation
60 * @param msg the header of the message
61 */
62void
63handle_intersection_p2p_element_info (void *cls,
64 const struct
65 IntersectionElementInfoMessage *msg);
66
67
68/**
69 * Handle a done message from a remote peer
70 *
71 * @param cls the intersection operation
72 * @param mh the message
73 */
74void
75handle_intersection_p2p_done (void *cls,
76 const struct IntersectionDoneMessage *idm);
77
78
79#endif
diff --git a/src/contrib/service/set/gnunet-service-set_protocol.h b/src/contrib/service/set/gnunet-service-set_protocol.h
new file mode 100644
index 000000000..a2803ee47
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_protocol.h
@@ -0,0 +1,226 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Florian Dold
22 * @author Christian Grothoff
23 * @file set/gnunet-service-set_protocol.h
24 * @brief Peer-to-Peer messages for gnunet set
25 */
26#ifndef SET_PROTOCOL_H
27#define SET_PROTOCOL_H
28
29#include "platform.h"
30#include "gnunet_common.h"
31
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35struct OperationRequestMessage
36{
37 /**
38 * Type: #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST
39 */
40 struct GNUNET_MessageHeader header;
41
42 /**
43 * Operation to request, values from `enum GNUNET_SET_OperationType`
44 */
45 uint32_t operation GNUNET_PACKED;
46
47 /**
48 * For Intersection: my element count
49 */
50 uint32_t element_count GNUNET_PACKED;
51
52 /**
53 * Application-specific identifier of the request.
54 */
55 struct GNUNET_HashCode app_idX;
56
57 /* rest: optional message */
58};
59
60
61/**
62 * Message containing buckets of an invertible bloom filter.
63 *
64 * If an IBF has too many buckets for an IBF message,
65 * it is split into multiple messages.
66 */
67struct IBFMessage
68{
69 /**
70 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF
71 */
72 struct GNUNET_MessageHeader header;
73
74 /**
75 * Order of the whole ibf, where
76 * num_buckets = 2^order
77 */
78 uint8_t order;
79
80 /**
81 * Padding, must be 0.
82 */
83 uint8_t reserved1;
84
85 /**
86 * Padding, must be 0.
87 */
88 uint16_t reserved2 GNUNET_PACKED;
89
90 /**
91 * Offset of the strata in the rest of the message
92 */
93 uint32_t offset GNUNET_PACKED;
94
95 /**
96 * Salt used when hashing elements for this IBF.
97 */
98 uint32_t salt GNUNET_PACKED;
99
100 /* rest: buckets */
101};
102
103
104struct InquiryMessage
105{
106 /**
107 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF
108 */
109 struct GNUNET_MessageHeader header;
110
111 /**
112 * Salt used when hashing elements for this inquiry.
113 */
114 uint32_t salt GNUNET_PACKED;
115
116 /**
117 * Reserved, set to 0.
118 */
119 uint32_t reserved GNUNET_PACKED;
120
121 /* rest: inquiry IBF keys */
122};
123
124
125/**
126 * During intersection, the first (and possibly second) message
127 * send it the number of elements in the set, to allow the peers
128 * to decide who should start with the Bloom filter.
129 */
130struct IntersectionElementInfoMessage
131{
132 /**
133 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO
134 */
135 struct GNUNET_MessageHeader header;
136
137 /**
138 * mutator used with this bloomfilter.
139 */
140 uint32_t sender_element_count GNUNET_PACKED;
141};
142
143
144/**
145 * Bloom filter messages exchanged for set intersection calculation.
146 */
147struct BFMessage
148{
149 /**
150 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF
151 */
152 struct GNUNET_MessageHeader header;
153
154 /**
155 * Number of elements the sender still has in the set.
156 */
157 uint32_t sender_element_count GNUNET_PACKED;
158
159 /**
160 * XOR of all hashes over all elements remaining in the set.
161 * Used to determine termination.
162 */
163 struct GNUNET_HashCode element_xor_hash;
164
165 /**
166 * Mutator used with this bloomfilter.
167 */
168 uint32_t sender_mutator GNUNET_PACKED;
169
170 /**
171 * Total length of the bloomfilter data.
172 */
173 uint32_t bloomfilter_total_length GNUNET_PACKED;
174
175 /**
176 * Number of bits (k-value) used in encoding the bloomfilter.
177 */
178 uint32_t bits_per_element GNUNET_PACKED;
179
180 /**
181 * rest: the sender's bloomfilter
182 */
183};
184
185
186/**
187 * Last message, send to confirm the final set. Contains the element
188 * count as it is possible that the peer determined that we were done
189 * by getting the empty set, which in that case also needs to be
190 * communicated.
191 */
192struct IntersectionDoneMessage
193{
194 /**
195 * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE
196 */
197 struct GNUNET_MessageHeader header;
198
199 /**
200 * Final number of elements in intersection.
201 */
202 uint32_t final_element_count GNUNET_PACKED;
203
204 /**
205 * XOR of all hashes over all elements remaining in the set.
206 */
207 struct GNUNET_HashCode element_xor_hash;
208};
209
210
211/**
212 * Strata estimator together with the peer's overall set size.
213 */
214struct StrataEstimatorMessage
215{
216 /**
217 * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE(C)
218 */
219 struct GNUNET_MessageHeader header;
220
221 uint64_t set_size;
222};
223
224GNUNET_NETWORK_STRUCT_END
225
226#endif
diff --git a/src/contrib/service/set/gnunet-service-set_union.c b/src/contrib/service/set/gnunet-service-set_union.c
new file mode 100644
index 000000000..3a2bc8bd7
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union.c
@@ -0,0 +1,2463 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union.c
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_statistics_service.h"
29#include "gnunet-service-set.h"
30#include "ibf.h"
31#include "gnunet-service-set_union.h"
32#include "gnunet-service-set_union_strata_estimator.h"
33#include "gnunet-service-set_protocol.h"
34#include <gcrypt.h>
35
36
37#define LOG(kind, ...) GNUNET_log_from (kind, "set-union", __VA_ARGS__)
38
39
40/**
41 * Number of IBFs in a strata estimator.
42 */
43#define SE_STRATA_COUNT 32
44
45/**
46 * Size of the IBFs in the strata estimator.
47 */
48#define SE_IBF_SIZE 80
49
50/**
51 * The hash num parameter for the difference digests and strata estimators.
52 */
53#define SE_IBF_HASH_NUM 4
54
55/**
56 * Number of buckets that can be transmitted in one message.
57 */
58#define MAX_BUCKETS_PER_MESSAGE ((1 << 15) / IBF_BUCKET_SIZE)
59
60/**
61 * The maximum size of an ibf we use is 2^(MAX_IBF_ORDER).
62 * Choose this value so that computing the IBF is still cheaper
63 * than transmitting all values.
64 */
65#define MAX_IBF_ORDER (20)
66
67/**
68 * Number of buckets used in the ibf per estimated
69 * difference.
70 */
71#define IBF_ALPHA 4
72
73
74/**
75 * Current phase we are in for a union operation.
76 */
77enum UnionOperationPhase
78{
79 /**
80 * We sent the request message, and expect a strata estimator.
81 */
82 PHASE_EXPECT_SE,
83
84 /**
85 * We sent the strata estimator, and expect an IBF. This phase is entered once
86 * upon initialization and later via #PHASE_EXPECT_ELEMENTS_AND_REQUESTS.
87 *
88 * XXX: could use better wording.
89 * XXX: repurposed to also expect a "request full set" message, should be renamed
90 *
91 * After receiving the complete IBF, we enter #PHASE_EXPECT_ELEMENTS
92 */
93 PHASE_EXPECT_IBF,
94
95 /**
96 * Continuation for multi part IBFs.
97 */
98 PHASE_EXPECT_IBF_CONT,
99
100 /**
101 * We are decoding an IBF.
102 */
103 PHASE_INVENTORY_ACTIVE,
104
105 /**
106 * The other peer is decoding the IBF we just sent.
107 */
108 PHASE_INVENTORY_PASSIVE,
109
110 /**
111 * The protocol is almost finished, but we still have to flush our message
112 * queue and/or expect some elements.
113 */
114 PHASE_FINISH_CLOSING,
115
116 /**
117 * In the penultimate phase,
118 * we wait until all our demands
119 * are satisfied. Then we send a done
120 * message, and wait for another done message.
121 */
122 PHASE_FINISH_WAITING,
123
124 /**
125 * In the ultimate phase, we wait until
126 * our demands are satisfied and then
127 * quit (sending another DONE message).
128 */
129 PHASE_DONE,
130
131 /**
132 * After sending the full set, wait for responses with the elements
133 * that the local peer is missing.
134 */
135 PHASE_FULL_SENDING,
136};
137
138
139/**
140 * State of an evaluate operation with another peer.
141 */
142struct OperationState
143{
144 /**
145 * Copy of the set's strata estimator at the time of
146 * creation of this operation.
147 */
148 struct StrataEstimator *se;
149
150 /**
151 * The IBF we currently receive.
152 */
153 struct InvertibleBloomFilter *remote_ibf;
154
155 /**
156 * The IBF with the local set's element.
157 */
158 struct InvertibleBloomFilter *local_ibf;
159
160 /**
161 * Maps unsalted IBF-Keys to elements.
162 * Used as a multihashmap, the keys being the lower 32bit of the IBF-Key.
163 * Colliding IBF-Keys are linked.
164 */
165 struct GNUNET_CONTAINER_MultiHashMap32 *key_to_element;
166
167 /**
168 * Current state of the operation.
169 */
170 enum UnionOperationPhase phase;
171
172 /**
173 * Did we send the client that we are done?
174 */
175 int client_done_sent;
176
177 /**
178 * Number of ibf buckets already received into the @a remote_ibf.
179 */
180 unsigned int ibf_buckets_received;
181
182 /**
183 * Hashes for elements that we have demanded from the other peer.
184 */
185 struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes;
186
187 /**
188 * Salt that we're using for sending IBFs
189 */
190 uint32_t salt_send;
191
192 /**
193 * Salt for the IBF we've received and that we're currently decoding.
194 */
195 uint32_t salt_receive;
196
197 /**
198 * Number of elements we received from the other peer
199 * that were not in the local set yet.
200 */
201 uint32_t received_fresh;
202
203 /**
204 * Total number of elements received from the other peer.
205 */
206 uint32_t received_total;
207
208 /**
209 * Initial size of our set, just before
210 * the operation started.
211 */
212 uint64_t initial_size;
213};
214
215
216/**
217 * The key entry is used to associate an ibf key with an element.
218 */
219struct KeyEntry
220{
221 /**
222 * IBF key for the entry, derived from the current salt.
223 */
224 struct IBF_Key ibf_key;
225
226 /**
227 * The actual element associated with the key.
228 *
229 * Only owned by the union operation if element->operation
230 * is #GNUNET_YES.
231 */
232 struct ElementEntry *element;
233
234 /**
235 * Did we receive this element?
236 * Even if element->is_foreign is false, we might
237 * have received the element, so this indicates that
238 * the other peer has it.
239 */
240 int received;
241};
242
243
244/**
245 * Used as a closure for sending elements
246 * with a specific IBF key.
247 */
248struct SendElementClosure
249{
250 /**
251 * The IBF key whose matching elements should be
252 * sent.
253 */
254 struct IBF_Key ibf_key;
255
256 /**
257 * Operation for which the elements
258 * should be sent.
259 */
260 struct Operation *op;
261};
262
263
264/**
265 * Extra state required for efficient set union.
266 */
267struct SetState
268{
269 /**
270 * The strata estimator is only generated once for
271 * each set.
272 * The IBF keys are derived from the element hashes with
273 * salt=0.
274 */
275 struct StrataEstimator *se;
276};
277
278
279/**
280 * Iterator over hash map entries, called to
281 * destroy the linked list of colliding ibf key entries.
282 *
283 * @param cls closure
284 * @param key current key code
285 * @param value value in the hash map
286 * @return #GNUNET_YES if we should continue to iterate,
287 * #GNUNET_NO if not.
288 */
289static int
290destroy_key_to_element_iter (void *cls,
291 uint32_t key,
292 void *value)
293{
294 struct KeyEntry *k = value;
295
296 GNUNET_assert (NULL != k);
297 if (GNUNET_YES == k->element->remote)
298 {
299 GNUNET_free (k->element);
300 k->element = NULL;
301 }
302 GNUNET_free (k);
303 return GNUNET_YES;
304}
305
306
307/**
308 * Destroy the union operation. Only things specific to the union
309 * operation are destroyed.
310 *
311 * @param op union operation to destroy
312 */
313static void
314union_op_cancel (struct Operation *op)
315{
316 LOG (GNUNET_ERROR_TYPE_DEBUG,
317 "destroying union op\n");
318 /* check if the op was canceled twice */
319 GNUNET_assert (NULL != op->state);
320 if (NULL != op->state->remote_ibf)
321 {
322 ibf_destroy (op->state->remote_ibf);
323 op->state->remote_ibf = NULL;
324 }
325 if (NULL != op->state->demanded_hashes)
326 {
327 GNUNET_CONTAINER_multihashmap_destroy (op->state->demanded_hashes);
328 op->state->demanded_hashes = NULL;
329 }
330 if (NULL != op->state->local_ibf)
331 {
332 ibf_destroy (op->state->local_ibf);
333 op->state->local_ibf = NULL;
334 }
335 if (NULL != op->state->se)
336 {
337 strata_estimator_destroy (op->state->se);
338 op->state->se = NULL;
339 }
340 if (NULL != op->state->key_to_element)
341 {
342 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
343 &destroy_key_to_element_iter,
344 NULL);
345 GNUNET_CONTAINER_multihashmap32_destroy (op->state->key_to_element);
346 op->state->key_to_element = NULL;
347 }
348 GNUNET_free (op->state);
349 op->state = NULL;
350 LOG (GNUNET_ERROR_TYPE_DEBUG,
351 "destroying union op done\n");
352}
353
354
355/**
356 * Inform the client that the union operation has failed,
357 * and proceed to destroy the evaluate operation.
358 *
359 * @param op the union operation to fail
360 */
361static void
362fail_union_operation (struct Operation *op)
363{
364 struct GNUNET_MQ_Envelope *ev;
365 struct GNUNET_SET_ResultMessage *msg;
366
367 LOG (GNUNET_ERROR_TYPE_WARNING,
368 "union operation failed\n");
369 ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT);
370 msg->result_status = htons (GNUNET_SET_STATUS_FAILURE);
371 msg->request_id = htonl (op->client_request_id);
372 msg->element_type = htons (0);
373 GNUNET_MQ_send (op->set->cs->mq,
374 ev);
375 _GSS_operation_destroy (op, GNUNET_YES);
376}
377
378
379/**
380 * Derive the IBF key from a hash code and
381 * a salt.
382 *
383 * @param src the hash code
384 * @return the derived IBF key
385 */
386static struct IBF_Key
387get_ibf_key (const struct GNUNET_HashCode *src)
388{
389 struct IBF_Key key;
390 uint16_t salt = 0;
391
392 GNUNET_assert (GNUNET_OK ==
393 GNUNET_CRYPTO_kdf (&key, sizeof(key),
394 src, sizeof *src,
395 &salt, sizeof(salt),
396 NULL, 0));
397 return key;
398}
399
400
401/**
402 * Context for #op_get_element_iterator
403 */
404struct GetElementContext
405{
406 /**
407 * FIXME.
408 */
409 struct GNUNET_HashCode hash;
410
411 /**
412 * FIXME.
413 */
414 struct KeyEntry *k;
415};
416
417
418/**
419 * Iterator over the mapping from IBF keys to element entries. Checks if we
420 * have an element with a given GNUNET_HashCode.
421 *
422 * @param cls closure
423 * @param key current key code
424 * @param value value in the hash map
425 * @return #GNUNET_YES if we should search further,
426 * #GNUNET_NO if we've found the element.
427 */
428static int
429op_get_element_iterator (void *cls,
430 uint32_t key,
431 void *value)
432{
433 struct GetElementContext *ctx = cls;
434 struct KeyEntry *k = value;
435
436 GNUNET_assert (NULL != k);
437 if (0 == GNUNET_CRYPTO_hash_cmp (&k->element->element_hash,
438 &ctx->hash))
439 {
440 ctx->k = k;
441 return GNUNET_NO;
442 }
443 return GNUNET_YES;
444}
445
446
447/**
448 * Determine whether the given element is already in the operation's element
449 * set.
450 *
451 * @param op operation that should be tested for 'element_hash'
452 * @param element_hash hash of the element to look for
453 * @return #GNUNET_YES if the element has been found, #GNUNET_NO otherwise
454 */
455static struct KeyEntry *
456op_get_element (struct Operation *op,
457 const struct GNUNET_HashCode *element_hash)
458{
459 int ret;
460 struct IBF_Key ibf_key;
461 struct GetElementContext ctx = { { { 0 } }, 0 };
462
463 ctx.hash = *element_hash;
464
465 ibf_key = get_ibf_key (element_hash);
466 ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element,
467 (uint32_t) ibf_key.key_val,
468 op_get_element_iterator,
469 &ctx);
470
471 /* was the iteration aborted because we found the element? */
472 if (GNUNET_SYSERR == ret)
473 {
474 GNUNET_assert (NULL != ctx.k);
475 return ctx.k;
476 }
477 return NULL;
478}
479
480
481/**
482 * Insert an element into the union operation's
483 * key-to-element mapping. Takes ownership of 'ee'.
484 * Note that this does not insert the element in the set,
485 * only in the operation's key-element mapping.
486 * This is done to speed up re-tried operations, if some elements
487 * were transmitted, and then the IBF fails to decode.
488 *
489 * XXX: clarify ownership, doesn't sound right.
490 *
491 * @param op the union operation
492 * @param ee the element entry
493 * @param received was this element received from the remote peer?
494 */
495static void
496op_register_element (struct Operation *op,
497 struct ElementEntry *ee,
498 int received)
499{
500 struct IBF_Key ibf_key;
501 struct KeyEntry *k;
502
503 ibf_key = get_ibf_key (&ee->element_hash);
504 k = GNUNET_new (struct KeyEntry);
505 k->element = ee;
506 k->ibf_key = ibf_key;
507 k->received = received;
508 GNUNET_assert (GNUNET_OK ==
509 GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element,
510 (uint32_t) ibf_key.key_val,
511 k,
512 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
513}
514
515
516/**
517 * FIXME.
518 */
519static void
520salt_key (const struct IBF_Key *k_in,
521 uint32_t salt,
522 struct IBF_Key *k_out)
523{
524 int s = salt % 64;
525 uint64_t x = k_in->key_val;
526
527 /* rotate ibf key */
528 x = (x >> s) | (x << (64 - s));
529 k_out->key_val = x;
530}
531
532
533/**
534 * FIXME.
535 */
536static void
537unsalt_key (const struct IBF_Key *k_in,
538 uint32_t salt,
539 struct IBF_Key *k_out)
540{
541 int s = salt % 64;
542 uint64_t x = k_in->key_val;
543
544 x = (x << s) | (x >> (64 - s));
545 k_out->key_val = x;
546}
547
548
549/**
550 * Insert a key into an ibf.
551 *
552 * @param cls the ibf
553 * @param key unused
554 * @param value the key entry to get the key from
555 */
556static int
557prepare_ibf_iterator (void *cls,
558 uint32_t key,
559 void *value)
560{
561 struct Operation *op = cls;
562 struct KeyEntry *ke = value;
563 struct IBF_Key salted_key;
564
565 LOG (GNUNET_ERROR_TYPE_DEBUG,
566 "[OP %p] inserting %lx (hash %s) into ibf\n",
567 op,
568 (unsigned long) ke->ibf_key.key_val,
569 GNUNET_h2s (&ke->element->element_hash));
570 salt_key (&ke->ibf_key,
571 op->state->salt_send,
572 &salted_key);
573 ibf_insert (op->state->local_ibf, salted_key);
574 return GNUNET_YES;
575}
576
577
578/**
579 * Iterator for initializing the
580 * key-to-element mapping of a union operation
581 *
582 * @param cls the union operation `struct Operation *`
583 * @param key unused
584 * @param value the `struct ElementEntry *` to insert
585 * into the key-to-element mapping
586 * @return #GNUNET_YES (to continue iterating)
587 */
588static int
589init_key_to_element_iterator (void *cls,
590 const struct GNUNET_HashCode *key,
591 void *value)
592{
593 struct Operation *op = cls;
594 struct ElementEntry *ee = value;
595
596 /* make sure that the element belongs to the set at the time
597 * of creating the operation */
598 if (GNUNET_NO ==
599 _GSS_is_element_of_operation (ee,
600 op))
601 return GNUNET_YES;
602 GNUNET_assert (GNUNET_NO == ee->remote);
603 op_register_element (op,
604 ee,
605 GNUNET_NO);
606 return GNUNET_YES;
607}
608
609
610/**
611 * Initialize the IBF key to element mapping local to this set
612 * operation.
613 *
614 * @param op the set union operation
615 */
616static void
617initialize_key_to_element (struct Operation *op)
618{
619 unsigned int len;
620
621 GNUNET_assert (NULL == op->state->key_to_element);
622 len = GNUNET_CONTAINER_multihashmap_size (op->set->content->elements);
623 op->state->key_to_element = GNUNET_CONTAINER_multihashmap32_create (len + 1);
624 GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
625 &init_key_to_element_iterator,
626 op);
627}
628
629
630/**
631 * Create an ibf with the operation's elements
632 * of the specified size
633 *
634 * @param op the union operation
635 * @param size size of the ibf to create
636 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
637 */
638static int
639prepare_ibf (struct Operation *op,
640 uint32_t size)
641{
642 GNUNET_assert (NULL != op->state->key_to_element);
643
644 if (NULL != op->state->local_ibf)
645 ibf_destroy (op->state->local_ibf);
646 op->state->local_ibf = ibf_create (size, SE_IBF_HASH_NUM);
647 if (NULL == op->state->local_ibf)
648 {
649 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650 "Failed to allocate local IBF\n");
651 return GNUNET_SYSERR;
652 }
653 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
654 &prepare_ibf_iterator,
655 op);
656 return GNUNET_OK;
657}
658
659
660/**
661 * Send an ibf of appropriate size.
662 *
663 * Fragments the IBF into multiple messages if necessary.
664 *
665 * @param op the union operation
666 * @param ibf_order order of the ibf to send, size=2^order
667 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
668 */
669static int
670send_ibf (struct Operation *op,
671 uint16_t ibf_order)
672{
673 unsigned int buckets_sent = 0;
674 struct InvertibleBloomFilter *ibf;
675
676 if (GNUNET_OK !=
677 prepare_ibf (op, 1 << ibf_order))
678 {
679 /* allocation failed */
680 return GNUNET_SYSERR;
681 }
682
683 LOG (GNUNET_ERROR_TYPE_DEBUG,
684 "sending ibf of size %u\n",
685 1 << ibf_order);
686
687 {
688 char name[64] = { 0 };
689 snprintf (name, sizeof(name), "# sent IBF (order %u)", ibf_order);
690 GNUNET_STATISTICS_update (_GSS_statistics, name, 1, GNUNET_NO);
691 }
692
693 ibf = op->state->local_ibf;
694
695 while (buckets_sent < (1 << ibf_order))
696 {
697 unsigned int buckets_in_message;
698 struct GNUNET_MQ_Envelope *ev;
699 struct IBFMessage *msg;
700
701 buckets_in_message = (1 << ibf_order) - buckets_sent;
702 /* limit to maximum */
703 if (buckets_in_message > MAX_BUCKETS_PER_MESSAGE)
704 buckets_in_message = MAX_BUCKETS_PER_MESSAGE;
705
706 ev = GNUNET_MQ_msg_extra (msg,
707 buckets_in_message * IBF_BUCKET_SIZE,
708 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF);
709 msg->reserved1 = 0;
710 msg->reserved2 = 0;
711 msg->order = ibf_order;
712 msg->offset = htonl (buckets_sent);
713 msg->salt = htonl (op->state->salt_send);
714 ibf_write_slice (ibf, buckets_sent,
715 buckets_in_message, &msg[1]);
716 buckets_sent += buckets_in_message;
717 LOG (GNUNET_ERROR_TYPE_DEBUG,
718 "ibf chunk size %u, %u/%u sent\n",
719 buckets_in_message,
720 buckets_sent,
721 1 << ibf_order);
722 GNUNET_MQ_send (op->mq, ev);
723 }
724
725 /* The other peer must decode the IBF, so
726 * we're passive. */
727 op->state->phase = PHASE_INVENTORY_PASSIVE;
728 return GNUNET_OK;
729}
730
731
732/**
733 * Compute the necessary order of an ibf
734 * from the size of the symmetric set difference.
735 *
736 * @param diff the difference
737 * @return the required size of the ibf
738 */
739static unsigned int
740get_order_from_difference (unsigned int diff)
741{
742 unsigned int ibf_order;
743
744 ibf_order = 2;
745 while (((1 << ibf_order) < (IBF_ALPHA * diff) ||
746 ((1 << ibf_order) < SE_IBF_HASH_NUM)) &&
747 (ibf_order < MAX_IBF_ORDER))
748 ibf_order++;
749 // add one for correction
750 return ibf_order + 1;
751}
752
753
754/**
755 * Send a set element.
756 *
757 * @param cls the union operation `struct Operation *`
758 * @param key unused
759 * @param value the `struct ElementEntry *` to insert
760 * into the key-to-element mapping
761 * @return #GNUNET_YES (to continue iterating)
762 */
763static int
764send_full_element_iterator (void *cls,
765 const struct GNUNET_HashCode *key,
766 void *value)
767{
768 struct Operation *op = cls;
769 struct GNUNET_SET_ElementMessage *emsg;
770 struct ElementEntry *ee = value;
771 struct GNUNET_SET_Element *el = &ee->element;
772 struct GNUNET_MQ_Envelope *ev;
773
774 LOG (GNUNET_ERROR_TYPE_DEBUG,
775 "Sending element %s\n",
776 GNUNET_h2s (key));
777 ev = GNUNET_MQ_msg_extra (emsg,
778 el->size,
779 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT);
780 emsg->element_type = htons (el->element_type);
781 GNUNET_memcpy (&emsg[1],
782 el->data,
783 el->size);
784 GNUNET_MQ_send (op->mq,
785 ev);
786 return GNUNET_YES;
787}
788
789
790/**
791 * Switch to full set transmission for @a op.
792 *
793 * @param op operation to switch to full set transmission.
794 */
795static void
796send_full_set (struct Operation *op)
797{
798 struct GNUNET_MQ_Envelope *ev;
799
800 op->state->phase = PHASE_FULL_SENDING;
801 LOG (GNUNET_ERROR_TYPE_DEBUG,
802 "Dedicing to transmit the full set\n");
803 /* FIXME: use a more memory-friendly way of doing this with an
804 iterator, just as we do in the non-full case! */
805 (void) GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements,
806 &send_full_element_iterator,
807 op);
808 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE);
809 GNUNET_MQ_send (op->mq,
810 ev);
811}
812
813
814/**
815 * Handle a strata estimator from a remote peer
816 *
817 * @param cls the union operation
818 * @param msg the message
819 */
820int
821check_union_p2p_strata_estimator (void *cls,
822 const struct StrataEstimatorMessage *msg)
823{
824 struct Operation *op = cls;
825 int is_compressed;
826 size_t len;
827
828 if (op->state->phase != PHASE_EXPECT_SE)
829 {
830 GNUNET_break (0);
831 return GNUNET_SYSERR;
832 }
833 is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (
834 msg->header.type));
835 len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage);
836 if ((GNUNET_NO == is_compressed) &&
837 (len != SE_STRATA_COUNT * SE_IBF_SIZE * IBF_BUCKET_SIZE))
838 {
839 GNUNET_break (0);
840 return GNUNET_SYSERR;
841 }
842 return GNUNET_OK;
843}
844
845
846/**
847 * Handle a strata estimator from a remote peer
848 *
849 * @param cls the union operation
850 * @param msg the message
851 */
852void
853handle_union_p2p_strata_estimator (void *cls,
854 const struct StrataEstimatorMessage *msg)
855{
856 struct Operation *op = cls;
857 struct StrataEstimator *remote_se;
858 unsigned int diff;
859 uint64_t other_size;
860 size_t len;
861 int is_compressed;
862
863 is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (
864 msg->header.type));
865 GNUNET_STATISTICS_update (_GSS_statistics,
866 "# bytes of SE received",
867 ntohs (msg->header.size),
868 GNUNET_NO);
869 len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage);
870 other_size = GNUNET_ntohll (msg->set_size);
871 remote_se = strata_estimator_create (SE_STRATA_COUNT,
872 SE_IBF_SIZE,
873 SE_IBF_HASH_NUM);
874 if (NULL == remote_se)
875 {
876 /* insufficient resources, fail */
877 fail_union_operation (op);
878 return;
879 }
880 if (GNUNET_OK !=
881 strata_estimator_read (&msg[1],
882 len,
883 is_compressed,
884 remote_se))
885 {
886 /* decompression failed */
887 strata_estimator_destroy (remote_se);
888 fail_union_operation (op);
889 return;
890 }
891 GNUNET_assert (NULL != op->state->se);
892 diff = strata_estimator_difference (remote_se,
893 op->state->se);
894
895 if (diff > 200)
896 diff = diff * 3 / 2;
897
898 strata_estimator_destroy (remote_se);
899 strata_estimator_destroy (op->state->se);
900 op->state->se = NULL;
901 LOG (GNUNET_ERROR_TYPE_DEBUG,
902 "got se diff=%d, using ibf size %d\n",
903 diff,
904 1U << get_order_from_difference (diff));
905
906 {
907 char *set_debug;
908
909 set_debug = getenv ("GNUNET_SET_BENCHMARK");
910 if ((NULL != set_debug) &&
911 (0 == strcmp (set_debug, "1")))
912 {
913 FILE *f = fopen ("set.log", "a");
914 fprintf (f, "%llu\n", (unsigned long long) diff);
915 fclose (f);
916 }
917 }
918
919 if ((GNUNET_YES == op->byzantine) &&
920 (other_size < op->byzantine_lower_bound))
921 {
922 GNUNET_break (0);
923 fail_union_operation (op);
924 return;
925 }
926
927 if ((GNUNET_YES == op->force_full) ||
928 (diff > op->state->initial_size / 4) ||
929 (0 == other_size))
930 {
931 LOG (GNUNET_ERROR_TYPE_DEBUG,
932 "Deciding to go for full set transmission (diff=%d, own set=%llu)\n",
933 diff,
934 (unsigned long long) op->state->initial_size);
935 GNUNET_STATISTICS_update (_GSS_statistics,
936 "# of full sends",
937 1,
938 GNUNET_NO);
939 if ((op->state->initial_size <= other_size) ||
940 (0 == other_size))
941 {
942 send_full_set (op);
943 }
944 else
945 {
946 struct GNUNET_MQ_Envelope *ev;
947
948 LOG (GNUNET_ERROR_TYPE_DEBUG,
949 "Telling other peer that we expect its full set\n");
950 op->state->phase = PHASE_EXPECT_IBF;
951 ev = GNUNET_MQ_msg_header (
952 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL);
953 GNUNET_MQ_send (op->mq,
954 ev);
955 }
956 }
957 else
958 {
959 GNUNET_STATISTICS_update (_GSS_statistics,
960 "# of ibf sends",
961 1,
962 GNUNET_NO);
963 if (GNUNET_OK !=
964 send_ibf (op,
965 get_order_from_difference (diff)))
966 {
967 /* Internal error, best we can do is shut the connection */
968 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
969 "Failed to send IBF, closing connection\n");
970 fail_union_operation (op);
971 return;
972 }
973 }
974 GNUNET_CADET_receive_done (op->channel);
975}
976
977
978/**
979 * Iterator to send elements to a remote peer
980 *
981 * @param cls closure with the element key and the union operation
982 * @param key ignored
983 * @param value the key entry
984 */
985static int
986send_offers_iterator (void *cls,
987 uint32_t key,
988 void *value)
989{
990 struct SendElementClosure *sec = cls;
991 struct Operation *op = sec->op;
992 struct KeyEntry *ke = value;
993 struct GNUNET_MQ_Envelope *ev;
994 struct GNUNET_MessageHeader *mh;
995
996 /* Detect 32-bit key collision for the 64-bit IBF keys. */
997 if (ke->ibf_key.key_val != sec->ibf_key.key_val)
998 return GNUNET_YES;
999
1000 ev = GNUNET_MQ_msg_header_extra (mh,
1001 sizeof(struct GNUNET_HashCode),
1002 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER);
1003
1004 GNUNET_assert (NULL != ev);
1005 *(struct GNUNET_HashCode *) &mh[1] = ke->element->element_hash;
1006 LOG (GNUNET_ERROR_TYPE_DEBUG,
1007 "[OP %p] sending element offer (%s) to peer\n",
1008 op,
1009 GNUNET_h2s (&ke->element->element_hash));
1010 GNUNET_MQ_send (op->mq, ev);
1011 return GNUNET_YES;
1012}
1013
1014
1015/**
1016 * Send offers (in the form of GNUNET_Hash-es) to the remote peer for the given IBF key.
1017 *
1018 * @param op union operation
1019 * @param ibf_key IBF key of interest
1020 */
1021static void
1022send_offers_for_key (struct Operation *op,
1023 struct IBF_Key ibf_key)
1024{
1025 struct SendElementClosure send_cls;
1026
1027 send_cls.ibf_key = ibf_key;
1028 send_cls.op = op;
1029 (void) GNUNET_CONTAINER_multihashmap32_get_multiple (
1030 op->state->key_to_element,
1031 (uint32_t) ibf_key.
1032 key_val,
1033 &send_offers_iterator,
1034 &send_cls);
1035}
1036
1037
1038/**
1039 * Decode which elements are missing on each side, and
1040 * send the appropriate offers and inquiries.
1041 *
1042 * @param op union operation
1043 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1044 */
1045static int
1046decode_and_send (struct Operation *op)
1047{
1048 struct IBF_Key key;
1049 struct IBF_Key last_key;
1050 int side;
1051 unsigned int num_decoded;
1052 struct InvertibleBloomFilter *diff_ibf;
1053
1054 GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->state->phase);
1055
1056 if (GNUNET_OK !=
1057 prepare_ibf (op,
1058 op->state->remote_ibf->size))
1059 {
1060 GNUNET_break (0);
1061 /* allocation failed */
1062 return GNUNET_SYSERR;
1063 }
1064 diff_ibf = ibf_dup (op->state->local_ibf);
1065 ibf_subtract (diff_ibf,
1066 op->state->remote_ibf);
1067
1068 ibf_destroy (op->state->remote_ibf);
1069 op->state->remote_ibf = NULL;
1070
1071 LOG (GNUNET_ERROR_TYPE_DEBUG,
1072 "decoding IBF (size=%u)\n",
1073 diff_ibf->size);
1074
1075 num_decoded = 0;
1076 key.key_val = 0; /* just to avoid compiler thinking we use undef'ed variable */
1077
1078 while (1)
1079 {
1080 int res;
1081 int cycle_detected = GNUNET_NO;
1082
1083 last_key = key;
1084
1085 res = ibf_decode (diff_ibf, &side, &key);
1086 if (res == GNUNET_OK)
1087 {
1088 LOG (GNUNET_ERROR_TYPE_DEBUG,
1089 "decoded ibf key %lx\n",
1090 (unsigned long) key.key_val);
1091 num_decoded += 1;
1092 if ((num_decoded > diff_ibf->size) ||
1093 ((num_decoded > 1) &&
1094 (last_key.key_val == key.key_val)))
1095 {
1096 LOG (GNUNET_ERROR_TYPE_DEBUG,
1097 "detected cyclic ibf (decoded %u/%u)\n",
1098 num_decoded,
1099 diff_ibf->size);
1100 cycle_detected = GNUNET_YES;
1101 }
1102 }
1103 if ((GNUNET_SYSERR == res) ||
1104 (GNUNET_YES == cycle_detected))
1105 {
1106 int next_order;
1107 next_order = 0;
1108 while (1 << next_order < diff_ibf->size)
1109 next_order++;
1110 next_order++;
1111 if (next_order <= MAX_IBF_ORDER)
1112 {
1113 LOG (GNUNET_ERROR_TYPE_DEBUG,
1114 "decoding failed, sending larger ibf (size %u)\n",
1115 1 << next_order);
1116 GNUNET_STATISTICS_update (_GSS_statistics,
1117 "# of IBF retries",
1118 1,
1119 GNUNET_NO);
1120 op->state->salt_send++;
1121 if (GNUNET_OK !=
1122 send_ibf (op, next_order))
1123 {
1124 /* Internal error, best we can do is shut the connection */
1125 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1126 "Failed to send IBF, closing connection\n");
1127 fail_union_operation (op);
1128 ibf_destroy (diff_ibf);
1129 return GNUNET_SYSERR;
1130 }
1131 }
1132 else
1133 {
1134 GNUNET_STATISTICS_update (_GSS_statistics,
1135 "# of failed union operations (too large)",
1136 1,
1137 GNUNET_NO);
1138 // XXX: Send the whole set, element-by-element
1139 LOG (GNUNET_ERROR_TYPE_ERROR,
1140 "set union failed: reached ibf limit\n");
1141 fail_union_operation (op);
1142 ibf_destroy (diff_ibf);
1143 return GNUNET_SYSERR;
1144 }
1145 break;
1146 }
1147 if (GNUNET_NO == res)
1148 {
1149 struct GNUNET_MQ_Envelope *ev;
1150
1151 LOG (GNUNET_ERROR_TYPE_DEBUG,
1152 "transmitted all values, sending DONE\n");
1153 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE);
1154 GNUNET_MQ_send (op->mq, ev);
1155 /* We now wait until we get a DONE message back
1156 * and then wait for our MQ to be flushed and all our
1157 * demands be delivered. */
1158 break;
1159 }
1160 if (1 == side)
1161 {
1162 struct IBF_Key unsalted_key;
1163
1164 unsalt_key (&key,
1165 op->state->salt_receive,
1166 &unsalted_key);
1167 send_offers_for_key (op,
1168 unsalted_key);
1169 }
1170 else if (-1 == side)
1171 {
1172 struct GNUNET_MQ_Envelope *ev;
1173 struct InquiryMessage *msg;
1174
1175 /* It may be nice to merge multiple requests, but with CADET's corking it is not worth
1176 * the effort additional complexity. */
1177 ev = GNUNET_MQ_msg_extra (msg,
1178 sizeof(struct IBF_Key),
1179 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY);
1180 msg->salt = htonl (op->state->salt_receive);
1181 GNUNET_memcpy (&msg[1],
1182 &key,
1183 sizeof(struct IBF_Key));
1184 LOG (GNUNET_ERROR_TYPE_DEBUG,
1185 "sending element inquiry for IBF key %lx\n",
1186 (unsigned long) key.key_val);
1187 GNUNET_MQ_send (op->mq, ev);
1188 }
1189 else
1190 {
1191 GNUNET_assert (0);
1192 }
1193 }
1194 ibf_destroy (diff_ibf);
1195 return GNUNET_OK;
1196}
1197
1198
1199/**
1200 * Check an IBF message from a remote peer.
1201 *
1202 * Reassemble the IBF from multiple pieces, and
1203 * process the whole IBF once possible.
1204 *
1205 * @param cls the union operation
1206 * @param msg the header of the message
1207 * @return #GNUNET_OK if @a msg is well-formed
1208 */
1209int
1210check_union_p2p_ibf (void *cls,
1211 const struct IBFMessage *msg)
1212{
1213 struct Operation *op = cls;
1214 unsigned int buckets_in_message;
1215
1216 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1217 {
1218 GNUNET_break_op (0);
1219 return GNUNET_SYSERR;
1220 }
1221 buckets_in_message = (ntohs (msg->header.size) - sizeof *msg)
1222 / IBF_BUCKET_SIZE;
1223 if (0 == buckets_in_message)
1224 {
1225 GNUNET_break_op (0);
1226 return GNUNET_SYSERR;
1227 }
1228 if ((ntohs (msg->header.size) - sizeof *msg) != buckets_in_message
1229 * IBF_BUCKET_SIZE)
1230 {
1231 GNUNET_break_op (0);
1232 return GNUNET_SYSERR;
1233 }
1234 if (op->state->phase == PHASE_EXPECT_IBF_CONT)
1235 {
1236 if (ntohl (msg->offset) != op->state->ibf_buckets_received)
1237 {
1238 GNUNET_break_op (0);
1239 return GNUNET_SYSERR;
1240 }
1241 if (1 << msg->order != op->state->remote_ibf->size)
1242 {
1243 GNUNET_break_op (0);
1244 return GNUNET_SYSERR;
1245 }
1246 if (ntohl (msg->salt) != op->state->salt_receive)
1247 {
1248 GNUNET_break_op (0);
1249 return GNUNET_SYSERR;
1250 }
1251 }
1252 else if ((op->state->phase != PHASE_INVENTORY_PASSIVE) &&
1253 (op->state->phase != PHASE_EXPECT_IBF))
1254 {
1255 GNUNET_break_op (0);
1256 return GNUNET_SYSERR;
1257 }
1258
1259 return GNUNET_OK;
1260}
1261
1262
1263/**
1264 * Handle an IBF message from a remote peer.
1265 *
1266 * Reassemble the IBF from multiple pieces, and
1267 * process the whole IBF once possible.
1268 *
1269 * @param cls the union operation
1270 * @param msg the header of the message
1271 */
1272void
1273handle_union_p2p_ibf (void *cls,
1274 const struct IBFMessage *msg)
1275{
1276 struct Operation *op = cls;
1277 unsigned int buckets_in_message;
1278
1279 buckets_in_message = (ntohs (msg->header.size) - sizeof *msg)
1280 / IBF_BUCKET_SIZE;
1281 if ((op->state->phase == PHASE_INVENTORY_PASSIVE) ||
1282 (op->state->phase == PHASE_EXPECT_IBF))
1283 {
1284 op->state->phase = PHASE_EXPECT_IBF_CONT;
1285 GNUNET_assert (NULL == op->state->remote_ibf);
1286 LOG (GNUNET_ERROR_TYPE_DEBUG,
1287 "Creating new ibf of size %u\n",
1288 1 << msg->order);
1289 op->state->remote_ibf = ibf_create (1 << msg->order, SE_IBF_HASH_NUM);
1290 op->state->salt_receive = ntohl (msg->salt);
1291 LOG (GNUNET_ERROR_TYPE_DEBUG,
1292 "Receiving new IBF with salt %u\n",
1293 op->state->salt_receive);
1294 if (NULL == op->state->remote_ibf)
1295 {
1296 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1297 "Failed to parse remote IBF, closing connection\n");
1298 fail_union_operation (op);
1299 return;
1300 }
1301 op->state->ibf_buckets_received = 0;
1302 if (0 != ntohl (msg->offset))
1303 {
1304 GNUNET_break_op (0);
1305 fail_union_operation (op);
1306 return;
1307 }
1308 }
1309 else
1310 {
1311 GNUNET_assert (op->state->phase == PHASE_EXPECT_IBF_CONT);
1312 LOG (GNUNET_ERROR_TYPE_DEBUG,
1313 "Received more of IBF\n");
1314 }
1315 GNUNET_assert (NULL != op->state->remote_ibf);
1316
1317 ibf_read_slice (&msg[1],
1318 op->state->ibf_buckets_received,
1319 buckets_in_message,
1320 op->state->remote_ibf);
1321 op->state->ibf_buckets_received += buckets_in_message;
1322
1323 if (op->state->ibf_buckets_received == op->state->remote_ibf->size)
1324 {
1325 LOG (GNUNET_ERROR_TYPE_DEBUG,
1326 "received full ibf\n");
1327 op->state->phase = PHASE_INVENTORY_ACTIVE;
1328 if (GNUNET_OK !=
1329 decode_and_send (op))
1330 {
1331 /* Internal error, best we can do is shut down */
1332 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1333 "Failed to decode IBF, closing connection\n");
1334 fail_union_operation (op);
1335 return;
1336 }
1337 }
1338 GNUNET_CADET_receive_done (op->channel);
1339}
1340
1341
1342/**
1343 * Send a result message to the client indicating
1344 * that there is a new element.
1345 *
1346 * @param op union operation
1347 * @param element element to send
1348 * @param status status to send with the new element
1349 */
1350static void
1351send_client_element (struct Operation *op,
1352 struct GNUNET_SET_Element *element,
1353 int status)
1354{
1355 struct GNUNET_MQ_Envelope *ev;
1356 struct GNUNET_SET_ResultMessage *rm;
1357
1358 LOG (GNUNET_ERROR_TYPE_DEBUG,
1359 "sending element (size %u) to client\n",
1360 element->size);
1361 GNUNET_assert (0 != op->client_request_id);
1362 ev = GNUNET_MQ_msg_extra (rm, element->size, GNUNET_MESSAGE_TYPE_SET_RESULT);
1363 if (NULL == ev)
1364 {
1365 GNUNET_MQ_discard (ev);
1366 GNUNET_break (0);
1367 return;
1368 }
1369 rm->result_status = htons (status);
1370 rm->request_id = htonl (op->client_request_id);
1371 rm->element_type = htons (element->element_type);
1372 rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size (
1373 op->state->key_to_element));
1374 GNUNET_memcpy (&rm[1],
1375 element->data,
1376 element->size);
1377 GNUNET_MQ_send (op->set->cs->mq,
1378 ev);
1379}
1380
1381
1382/**
1383 * Signal to the client that the operation has finished and
1384 * destroy the operation.
1385 *
1386 * @param cls operation to destroy
1387 */
1388static void
1389send_client_done (void *cls)
1390{
1391 struct Operation *op = cls;
1392 struct GNUNET_MQ_Envelope *ev;
1393 struct GNUNET_SET_ResultMessage *rm;
1394
1395 if (GNUNET_YES == op->state->client_done_sent)
1396 {
1397 return;
1398 }
1399
1400 if (PHASE_DONE != op->state->phase)
1401 {
1402 LOG (GNUNET_ERROR_TYPE_WARNING,
1403 "Union operation failed\n");
1404 GNUNET_STATISTICS_update (_GSS_statistics,
1405 "# Union operations failed",
1406 1,
1407 GNUNET_NO);
1408 ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT);
1409 rm->result_status = htons (GNUNET_SET_STATUS_FAILURE);
1410 rm->request_id = htonl (op->client_request_id);
1411 rm->element_type = htons (0);
1412 GNUNET_MQ_send (op->set->cs->mq,
1413 ev);
1414 return;
1415 }
1416
1417 op->state->client_done_sent = GNUNET_YES;
1418
1419 GNUNET_STATISTICS_update (_GSS_statistics,
1420 "# Union operations succeeded",
1421 1,
1422 GNUNET_NO);
1423 LOG (GNUNET_ERROR_TYPE_INFO,
1424 "Signalling client that union operation is done\n");
1425 ev = GNUNET_MQ_msg (rm,
1426 GNUNET_MESSAGE_TYPE_SET_RESULT);
1427 rm->request_id = htonl (op->client_request_id);
1428 rm->result_status = htons (GNUNET_SET_STATUS_DONE);
1429 rm->element_type = htons (0);
1430 rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size (
1431 op->state->key_to_element));
1432 GNUNET_MQ_send (op->set->cs->mq,
1433 ev);
1434}
1435
1436
1437/**
1438 * Tests if the operation is finished, and if so notify.
1439 *
1440 * @param op operation to check
1441 */
1442static void
1443maybe_finish (struct Operation *op)
1444{
1445 unsigned int num_demanded;
1446
1447 num_demanded = GNUNET_CONTAINER_multihashmap_size (
1448 op->state->demanded_hashes);
1449
1450 if (PHASE_FINISH_WAITING == op->state->phase)
1451 {
1452 LOG (GNUNET_ERROR_TYPE_DEBUG,
1453 "In PHASE_FINISH_WAITING, pending %u demands\n",
1454 num_demanded);
1455 if (0 == num_demanded)
1456 {
1457 struct GNUNET_MQ_Envelope *ev;
1458
1459 op->state->phase = PHASE_DONE;
1460 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE);
1461 GNUNET_MQ_send (op->mq,
1462 ev);
1463 /* We now wait until the other peer sends P2P_OVER
1464 * after it got all elements from us. */
1465 }
1466 }
1467 if (PHASE_FINISH_CLOSING == op->state->phase)
1468 {
1469 LOG (GNUNET_ERROR_TYPE_DEBUG,
1470 "In PHASE_FINISH_CLOSING, pending %u demands\n",
1471 num_demanded);
1472 if (0 == num_demanded)
1473 {
1474 op->state->phase = PHASE_DONE;
1475 send_client_done (op);
1476 _GSS_operation_destroy2 (op);
1477 }
1478 }
1479}
1480
1481
1482/**
1483 * Check an element message from a remote peer.
1484 *
1485 * @param cls the union operation
1486 * @param emsg the message
1487 */
1488int
1489check_union_p2p_elements (void *cls,
1490 const struct GNUNET_SET_ElementMessage *emsg)
1491{
1492 struct Operation *op = cls;
1493
1494 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1495 {
1496 GNUNET_break_op (0);
1497 return GNUNET_SYSERR;
1498 }
1499 if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes))
1500 {
1501 GNUNET_break_op (0);
1502 return GNUNET_SYSERR;
1503 }
1504 return GNUNET_OK;
1505}
1506
1507
1508/**
1509 * Handle an element message from a remote peer.
1510 * Sent by the other peer either because we decoded an IBF and placed a demand,
1511 * or because the other peer switched to full set transmission.
1512 *
1513 * @param cls the union operation
1514 * @param emsg the message
1515 */
1516void
1517handle_union_p2p_elements (void *cls,
1518 const struct GNUNET_SET_ElementMessage *emsg)
1519{
1520 struct Operation *op = cls;
1521 struct ElementEntry *ee;
1522 struct KeyEntry *ke;
1523 uint16_t element_size;
1524
1525 element_size = ntohs (emsg->header.size) - sizeof(struct
1526 GNUNET_SET_ElementMessage);
1527 ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size);
1528 GNUNET_memcpy (&ee[1],
1529 &emsg[1],
1530 element_size);
1531 ee->element.size = element_size;
1532 ee->element.data = &ee[1];
1533 ee->element.element_type = ntohs (emsg->element_type);
1534 ee->remote = GNUNET_YES;
1535 GNUNET_SET_element_hash (&ee->element,
1536 &ee->element_hash);
1537 if (GNUNET_NO ==
1538 GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes,
1539 &ee->element_hash,
1540 NULL))
1541 {
1542 /* We got something we didn't demand, since it's not in our map. */
1543 GNUNET_break_op (0);
1544 fail_union_operation (op);
1545 return;
1546 }
1547
1548 LOG (GNUNET_ERROR_TYPE_DEBUG,
1549 "Got element (size %u, hash %s) from peer\n",
1550 (unsigned int) element_size,
1551 GNUNET_h2s (&ee->element_hash));
1552
1553 GNUNET_STATISTICS_update (_GSS_statistics,
1554 "# received elements",
1555 1,
1556 GNUNET_NO);
1557 GNUNET_STATISTICS_update (_GSS_statistics,
1558 "# exchanged elements",
1559 1,
1560 GNUNET_NO);
1561
1562 op->state->received_total++;
1563
1564 ke = op_get_element (op, &ee->element_hash);
1565 if (NULL != ke)
1566 {
1567 /* Got repeated element. Should not happen since
1568 * we track demands. */
1569 GNUNET_STATISTICS_update (_GSS_statistics,
1570 "# repeated elements",
1571 1,
1572 GNUNET_NO);
1573 ke->received = GNUNET_YES;
1574 GNUNET_free (ee);
1575 }
1576 else
1577 {
1578 LOG (GNUNET_ERROR_TYPE_DEBUG,
1579 "Registering new element from remote peer\n");
1580 op->state->received_fresh++;
1581 op_register_element (op, ee, GNUNET_YES);
1582 /* only send results immediately if the client wants it */
1583 switch (op->result_mode)
1584 {
1585 case GNUNET_SET_RESULT_ADDED:
1586 send_client_element (op, &ee->element, GNUNET_SET_STATUS_OK);
1587 break;
1588
1589 case GNUNET_SET_RESULT_SYMMETRIC:
1590 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_LOCAL);
1591 break;
1592
1593 default:
1594 /* Result mode not supported, should have been caught earlier. */
1595 GNUNET_break (0);
1596 break;
1597 }
1598 }
1599
1600 if ((op->state->received_total > 8) &&
1601 (op->state->received_fresh < op->state->received_total / 3))
1602 {
1603 /* The other peer gave us lots of old elements, there's something wrong. */
1604 GNUNET_break_op (0);
1605 fail_union_operation (op);
1606 return;
1607 }
1608 GNUNET_CADET_receive_done (op->channel);
1609 maybe_finish (op);
1610}
1611
1612
1613/**
1614 * Check a full element message from a remote peer.
1615 *
1616 * @param cls the union operation
1617 * @param emsg the message
1618 */
1619int
1620check_union_p2p_full_element (void *cls,
1621 const struct GNUNET_SET_ElementMessage *emsg)
1622{
1623 struct Operation *op = cls;
1624
1625 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1626 {
1627 GNUNET_break_op (0);
1628 return GNUNET_SYSERR;
1629 }
1630 // FIXME: check that we expect full elements here?
1631 return GNUNET_OK;
1632}
1633
1634
1635/**
1636 * Handle an element message from a remote peer.
1637 *
1638 * @param cls the union operation
1639 * @param emsg the message
1640 */
1641void
1642handle_union_p2p_full_element (void *cls,
1643 const struct GNUNET_SET_ElementMessage *emsg)
1644{
1645 struct Operation *op = cls;
1646 struct ElementEntry *ee;
1647 struct KeyEntry *ke;
1648 uint16_t element_size;
1649
1650 element_size = ntohs (emsg->header.size) - sizeof(struct
1651 GNUNET_SET_ElementMessage);
1652 ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size);
1653 GNUNET_memcpy (&ee[1], &emsg[1], element_size);
1654 ee->element.size = element_size;
1655 ee->element.data = &ee[1];
1656 ee->element.element_type = ntohs (emsg->element_type);
1657 ee->remote = GNUNET_YES;
1658 GNUNET_SET_element_hash (&ee->element, &ee->element_hash);
1659
1660 LOG (GNUNET_ERROR_TYPE_DEBUG,
1661 "Got element (full diff, size %u, hash %s) from peer\n",
1662 (unsigned int) element_size,
1663 GNUNET_h2s (&ee->element_hash));
1664
1665 GNUNET_STATISTICS_update (_GSS_statistics,
1666 "# received elements",
1667 1,
1668 GNUNET_NO);
1669 GNUNET_STATISTICS_update (_GSS_statistics,
1670 "# exchanged elements",
1671 1,
1672 GNUNET_NO);
1673
1674 op->state->received_total++;
1675
1676 ke = op_get_element (op, &ee->element_hash);
1677 if (NULL != ke)
1678 {
1679 /* Got repeated element. Should not happen since
1680 * we track demands. */
1681 GNUNET_STATISTICS_update (_GSS_statistics,
1682 "# repeated elements",
1683 1,
1684 GNUNET_NO);
1685 ke->received = GNUNET_YES;
1686 GNUNET_free (ee);
1687 }
1688 else
1689 {
1690 LOG (GNUNET_ERROR_TYPE_DEBUG,
1691 "Registering new element from remote peer\n");
1692 op->state->received_fresh++;
1693 op_register_element (op, ee, GNUNET_YES);
1694 /* only send results immediately if the client wants it */
1695 switch (op->result_mode)
1696 {
1697 case GNUNET_SET_RESULT_ADDED:
1698 send_client_element (op, &ee->element, GNUNET_SET_STATUS_OK);
1699 break;
1700
1701 case GNUNET_SET_RESULT_SYMMETRIC:
1702 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_LOCAL);
1703 break;
1704
1705 default:
1706 /* Result mode not supported, should have been caught earlier. */
1707 GNUNET_break (0);
1708 break;
1709 }
1710 }
1711
1712 if ((GNUNET_YES == op->byzantine) &&
1713 (op->state->received_total > 384 + op->state->received_fresh * 4) &&
1714 (op->state->received_fresh < op->state->received_total / 6))
1715 {
1716 /* The other peer gave us lots of old elements, there's something wrong. */
1717 LOG (GNUNET_ERROR_TYPE_ERROR,
1718 "Other peer sent only %llu/%llu fresh elements, failing operation\n",
1719 (unsigned long long) op->state->received_fresh,
1720 (unsigned long long) op->state->received_total);
1721 GNUNET_break_op (0);
1722 fail_union_operation (op);
1723 return;
1724 }
1725 GNUNET_CADET_receive_done (op->channel);
1726}
1727
1728
1729/**
1730 * Send offers (for GNUNET_Hash-es) in response
1731 * to inquiries (for IBF_Key-s).
1732 *
1733 * @param cls the union operation
1734 * @param msg the message
1735 */
1736int
1737check_union_p2p_inquiry (void *cls,
1738 const struct InquiryMessage *msg)
1739{
1740 struct Operation *op = cls;
1741 unsigned int num_keys;
1742
1743 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1744 {
1745 GNUNET_break_op (0);
1746 return GNUNET_SYSERR;
1747 }
1748 if (op->state->phase != PHASE_INVENTORY_PASSIVE)
1749 {
1750 GNUNET_break_op (0);
1751 return GNUNET_SYSERR;
1752 }
1753 num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1754 / sizeof(struct IBF_Key);
1755 if ((ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1756 != num_keys * sizeof(struct IBF_Key))
1757 {
1758 GNUNET_break_op (0);
1759 return GNUNET_SYSERR;
1760 }
1761 return GNUNET_OK;
1762}
1763
1764
1765/**
1766 * Send offers (for GNUNET_Hash-es) in response
1767 * to inquiries (for IBF_Key-s).
1768 *
1769 * @param cls the union operation
1770 * @param msg the message
1771 */
1772void
1773handle_union_p2p_inquiry (void *cls,
1774 const struct InquiryMessage *msg)
1775{
1776 struct Operation *op = cls;
1777 const struct IBF_Key *ibf_key;
1778 unsigned int num_keys;
1779
1780 LOG (GNUNET_ERROR_TYPE_DEBUG,
1781 "Received union inquiry\n");
1782 num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage))
1783 / sizeof(struct IBF_Key);
1784 ibf_key = (const struct IBF_Key *) &msg[1];
1785 while (0 != num_keys--)
1786 {
1787 struct IBF_Key unsalted_key;
1788
1789 unsalt_key (ibf_key,
1790 ntohl (msg->salt),
1791 &unsalted_key);
1792 send_offers_for_key (op,
1793 unsalted_key);
1794 ibf_key++;
1795 }
1796 GNUNET_CADET_receive_done (op->channel);
1797}
1798
1799
1800/**
1801 * Iterator over hash map entries, called to
1802 * destroy the linked list of colliding ibf key entries.
1803 *
1804 * @param cls closure
1805 * @param key current key code
1806 * @param value value in the hash map
1807 * @return #GNUNET_YES if we should continue to iterate,
1808 * #GNUNET_NO if not.
1809 */
1810static int
1811send_missing_full_elements_iter (void *cls,
1812 uint32_t key,
1813 void *value)
1814{
1815 struct Operation *op = cls;
1816 struct KeyEntry *ke = value;
1817 struct GNUNET_MQ_Envelope *ev;
1818 struct GNUNET_SET_ElementMessage *emsg;
1819 struct ElementEntry *ee = ke->element;
1820
1821 if (GNUNET_YES == ke->received)
1822 return GNUNET_YES;
1823 ev = GNUNET_MQ_msg_extra (emsg,
1824 ee->element.size,
1825 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT);
1826 GNUNET_memcpy (&emsg[1],
1827 ee->element.data,
1828 ee->element.size);
1829 emsg->element_type = htons (ee->element.element_type);
1830 GNUNET_MQ_send (op->mq,
1831 ev);
1832 return GNUNET_YES;
1833}
1834
1835
1836/**
1837 * Handle a request for full set transmission.
1838 *
1839 * @param cls closure, a set union operation
1840 * @param mh the demand message
1841 */
1842void
1843handle_union_p2p_request_full (void *cls,
1844 const struct GNUNET_MessageHeader *mh)
1845{
1846 struct Operation *op = cls;
1847
1848 LOG (GNUNET_ERROR_TYPE_DEBUG,
1849 "Received request for full set transmission\n");
1850 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1851 {
1852 GNUNET_break_op (0);
1853 fail_union_operation (op);
1854 return;
1855 }
1856 if (PHASE_EXPECT_IBF != op->state->phase)
1857 {
1858 GNUNET_break_op (0);
1859 fail_union_operation (op);
1860 return;
1861 }
1862
1863 // FIXME: we need to check that our set is larger than the
1864 // byzantine_lower_bound by some threshold
1865 send_full_set (op);
1866 GNUNET_CADET_receive_done (op->channel);
1867}
1868
1869
1870/**
1871 * Handle a "full done" message.
1872 *
1873 * @param cls closure, a set union operation
1874 * @param mh the demand message
1875 */
1876void
1877handle_union_p2p_full_done (void *cls,
1878 const struct GNUNET_MessageHeader *mh)
1879{
1880 struct Operation *op = cls;
1881
1882 switch (op->state->phase)
1883 {
1884 case PHASE_EXPECT_IBF:
1885 {
1886 struct GNUNET_MQ_Envelope *ev;
1887
1888 LOG (GNUNET_ERROR_TYPE_DEBUG,
1889 "got FULL DONE, sending elements that other peer is missing\n");
1890
1891 /* send all the elements that did not come from the remote peer */
1892 GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element,
1893 &send_missing_full_elements_iter,
1894 op);
1895
1896 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE);
1897 GNUNET_MQ_send (op->mq,
1898 ev);
1899 op->state->phase = PHASE_DONE;
1900 /* we now wait until the other peer sends us the OVER message*/
1901 }
1902 break;
1903
1904 case PHASE_FULL_SENDING:
1905 {
1906 LOG (GNUNET_ERROR_TYPE_DEBUG,
1907 "got FULL DONE, finishing\n");
1908 /* We sent the full set, and got the response for that. We're done. */
1909 op->state->phase = PHASE_DONE;
1910 GNUNET_CADET_receive_done (op->channel);
1911 send_client_done (op);
1912 _GSS_operation_destroy2 (op);
1913 return;
1914 }
1915 break;
1916
1917 default:
1918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1919 "Handle full done phase is %u\n",
1920 (unsigned) op->state->phase);
1921 GNUNET_break_op (0);
1922 fail_union_operation (op);
1923 return;
1924 }
1925 GNUNET_CADET_receive_done (op->channel);
1926}
1927
1928
1929/**
1930 * Check a demand by the other peer for elements based on a list
1931 * of `struct GNUNET_HashCode`s.
1932 *
1933 * @param cls closure, a set union operation
1934 * @param mh the demand message
1935 * @return #GNUNET_OK if @a mh is well-formed
1936 */
1937int
1938check_union_p2p_demand (void *cls,
1939 const struct GNUNET_MessageHeader *mh)
1940{
1941 struct Operation *op = cls;
1942 unsigned int num_hashes;
1943
1944 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
1945 {
1946 GNUNET_break_op (0);
1947 return GNUNET_SYSERR;
1948 }
1949 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1950 / sizeof(struct GNUNET_HashCode);
1951 if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1952 != num_hashes * sizeof(struct GNUNET_HashCode))
1953 {
1954 GNUNET_break_op (0);
1955 return GNUNET_SYSERR;
1956 }
1957 return GNUNET_OK;
1958}
1959
1960
1961/**
1962 * Handle a demand by the other peer for elements based on a list
1963 * of `struct GNUNET_HashCode`s.
1964 *
1965 * @param cls closure, a set union operation
1966 * @param mh the demand message
1967 */
1968void
1969handle_union_p2p_demand (void *cls,
1970 const struct GNUNET_MessageHeader *mh)
1971{
1972 struct Operation *op = cls;
1973 struct ElementEntry *ee;
1974 struct GNUNET_SET_ElementMessage *emsg;
1975 const struct GNUNET_HashCode *hash;
1976 unsigned int num_hashes;
1977 struct GNUNET_MQ_Envelope *ev;
1978
1979 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
1980 / sizeof(struct GNUNET_HashCode);
1981 for (hash = (const struct GNUNET_HashCode *) &mh[1];
1982 num_hashes > 0;
1983 hash++, num_hashes--)
1984 {
1985 ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements,
1986 hash);
1987 if (NULL == ee)
1988 {
1989 /* Demand for non-existing element. */
1990 GNUNET_break_op (0);
1991 fail_union_operation (op);
1992 return;
1993 }
1994 if (GNUNET_NO == _GSS_is_element_of_operation (ee, op))
1995 {
1996 /* Probably confused lazily copied sets. */
1997 GNUNET_break_op (0);
1998 fail_union_operation (op);
1999 return;
2000 }
2001 ev = GNUNET_MQ_msg_extra (emsg, ee->element.size,
2002 GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS);
2003 GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size);
2004 emsg->reserved = htons (0);
2005 emsg->element_type = htons (ee->element.element_type);
2006 LOG (GNUNET_ERROR_TYPE_DEBUG,
2007 "[OP %p] Sending demanded element (size %u, hash %s) to peer\n",
2008 op,
2009 (unsigned int) ee->element.size,
2010 GNUNET_h2s (&ee->element_hash));
2011 GNUNET_MQ_send (op->mq, ev);
2012 GNUNET_STATISTICS_update (_GSS_statistics,
2013 "# exchanged elements",
2014 1,
2015 GNUNET_NO);
2016
2017 switch (op->result_mode)
2018 {
2019 case GNUNET_SET_RESULT_ADDED:
2020 /* Nothing to do. */
2021 break;
2022
2023 case GNUNET_SET_RESULT_SYMMETRIC:
2024 send_client_element (op, &ee->element, GNUNET_SET_STATUS_ADD_REMOTE);
2025 break;
2026
2027 default:
2028 /* Result mode not supported, should have been caught earlier. */
2029 GNUNET_break (0);
2030 break;
2031 }
2032 }
2033 GNUNET_CADET_receive_done (op->channel);
2034}
2035
2036
2037/**
2038 * Check offer (of `struct GNUNET_HashCode`s).
2039 *
2040 * @param cls the union operation
2041 * @param mh the message
2042 * @return #GNUNET_OK if @a mh is well-formed
2043 */
2044int
2045check_union_p2p_offer (void *cls,
2046 const struct GNUNET_MessageHeader *mh)
2047{
2048 struct Operation *op = cls;
2049 unsigned int num_hashes;
2050
2051 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
2052 {
2053 GNUNET_break_op (0);
2054 return GNUNET_SYSERR;
2055 }
2056 /* look up elements and send them */
2057 if ((op->state->phase != PHASE_INVENTORY_PASSIVE) &&
2058 (op->state->phase != PHASE_INVENTORY_ACTIVE))
2059 {
2060 GNUNET_break_op (0);
2061 return GNUNET_SYSERR;
2062 }
2063 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
2064 / sizeof(struct GNUNET_HashCode);
2065 if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) !=
2066 num_hashes * sizeof(struct GNUNET_HashCode))
2067 {
2068 GNUNET_break_op (0);
2069 return GNUNET_SYSERR;
2070 }
2071 return GNUNET_OK;
2072}
2073
2074
2075/**
2076 * Handle offers (of `struct GNUNET_HashCode`s) and
2077 * respond with demands (of `struct GNUNET_HashCode`s).
2078 *
2079 * @param cls the union operation
2080 * @param mh the message
2081 */
2082void
2083handle_union_p2p_offer (void *cls,
2084 const struct GNUNET_MessageHeader *mh)
2085{
2086 struct Operation *op = cls;
2087 const struct GNUNET_HashCode *hash;
2088 unsigned int num_hashes;
2089
2090 num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader))
2091 / sizeof(struct GNUNET_HashCode);
2092 for (hash = (const struct GNUNET_HashCode *) &mh[1];
2093 num_hashes > 0;
2094 hash++, num_hashes--)
2095 {
2096 struct ElementEntry *ee;
2097 struct GNUNET_MessageHeader *demands;
2098 struct GNUNET_MQ_Envelope *ev;
2099
2100 ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements,
2101 hash);
2102 if (NULL != ee)
2103 if (GNUNET_YES == _GSS_is_element_of_operation (ee, op))
2104 continue;
2105
2106 if (GNUNET_YES ==
2107 GNUNET_CONTAINER_multihashmap_contains (op->state->demanded_hashes,
2108 hash))
2109 {
2110 LOG (GNUNET_ERROR_TYPE_DEBUG,
2111 "Skipped sending duplicate demand\n");
2112 continue;
2113 }
2114
2115 GNUNET_assert (GNUNET_OK ==
2116 GNUNET_CONTAINER_multihashmap_put (
2117 op->state->demanded_hashes,
2118 hash,
2119 NULL,
2120 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
2121
2122 LOG (GNUNET_ERROR_TYPE_DEBUG,
2123 "[OP %p] Requesting element (hash %s)\n",
2124 op, GNUNET_h2s (hash));
2125 ev = GNUNET_MQ_msg_header_extra (demands,
2126 sizeof(struct GNUNET_HashCode),
2127 GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND);
2128 GNUNET_memcpy (&demands[1],
2129 hash,
2130 sizeof(struct GNUNET_HashCode));
2131 GNUNET_MQ_send (op->mq, ev);
2132 }
2133 GNUNET_CADET_receive_done (op->channel);
2134}
2135
2136
2137/**
2138 * Handle a done message from a remote peer
2139 *
2140 * @param cls the union operation
2141 * @param mh the message
2142 */
2143void
2144handle_union_p2p_done (void *cls,
2145 const struct GNUNET_MessageHeader *mh)
2146{
2147 struct Operation *op = cls;
2148
2149 if (GNUNET_SET_OPERATION_UNION != op->set->operation)
2150 {
2151 GNUNET_break_op (0);
2152 fail_union_operation (op);
2153 return;
2154 }
2155 switch (op->state->phase)
2156 {
2157 case PHASE_INVENTORY_PASSIVE:
2158 /* We got all requests, but still have to send our elements in response. */
2159 op->state->phase = PHASE_FINISH_WAITING;
2160
2161 LOG (GNUNET_ERROR_TYPE_DEBUG,
2162 "got DONE (as passive partner), waiting for our demands to be satisfied\n");
2163 /* The active peer is done sending offers
2164 * and inquiries. This means that all
2165 * our responses to that (demands and offers)
2166 * must be in flight (queued or in mesh).
2167 *
2168 * We should notify the active peer once
2169 * all our demands are satisfied, so that the active
2170 * peer can quit if we gave it everything.
2171 */GNUNET_CADET_receive_done (op->channel);
2172 maybe_finish (op);
2173 return;
2174
2175 case PHASE_INVENTORY_ACTIVE:
2176 LOG (GNUNET_ERROR_TYPE_DEBUG,
2177 "got DONE (as active partner), waiting to finish\n");
2178 /* All demands of the other peer are satisfied,
2179 * and we processed all offers, thus we know
2180 * exactly what our demands must be.
2181 *
2182 * We'll close the channel
2183 * to the other peer once our demands are met.
2184 */op->state->phase = PHASE_FINISH_CLOSING;
2185 GNUNET_CADET_receive_done (op->channel);
2186 maybe_finish (op);
2187 return;
2188
2189 default:
2190 GNUNET_break_op (0);
2191 fail_union_operation (op);
2192 return;
2193 }
2194}
2195
2196
2197void
2198handle_union_p2p_over (void *cls,
2199 const struct GNUNET_MessageHeader *mh)
2200{
2201 send_client_done (cls);
2202}
2203
2204
2205/**
2206 * Initiate operation to evaluate a set union with a remote peer.
2207 *
2208 * @param op operation to perform (to be initialized)
2209 * @param opaque_context message to be transmitted to the listener
2210 * to convince it to accept, may be NULL
2211 */
2212static struct OperationState *
2213union_evaluate (struct Operation *op,
2214 const struct GNUNET_MessageHeader *opaque_context)
2215{
2216 struct OperationState *state;
2217 struct GNUNET_MQ_Envelope *ev;
2218 struct OperationRequestMessage *msg;
2219
2220 ev = GNUNET_MQ_msg_nested_mh (msg,
2221 GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST,
2222 opaque_context);
2223 if (NULL == ev)
2224 {
2225 /* the context message is too large */
2226 GNUNET_break (0);
2227 return NULL;
2228 }
2229 state = GNUNET_new (struct OperationState);
2230 state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32,
2231 GNUNET_NO);
2232 /* copy the current generation's strata estimator for this operation */
2233 state->se = strata_estimator_dup (op->set->state->se);
2234 /* we started the operation, thus we have to send the operation request */
2235 state->phase = PHASE_EXPECT_SE;
2236 state->salt_receive = state->salt_send = 42; // FIXME?????
2237 LOG (GNUNET_ERROR_TYPE_DEBUG,
2238 "Initiating union operation evaluation\n");
2239 GNUNET_STATISTICS_update (_GSS_statistics,
2240 "# of total union operations",
2241 1,
2242 GNUNET_NO);
2243 GNUNET_STATISTICS_update (_GSS_statistics,
2244 "# of initiated union operations",
2245 1,
2246 GNUNET_NO);
2247 msg->operation = htonl (GNUNET_SET_OPERATION_UNION);
2248 GNUNET_MQ_send (op->mq,
2249 ev);
2250
2251 if (NULL != opaque_context)
2252 LOG (GNUNET_ERROR_TYPE_DEBUG,
2253 "sent op request with context message\n");
2254 else
2255 LOG (GNUNET_ERROR_TYPE_DEBUG,
2256 "sent op request without context message\n");
2257
2258 op->state = state;
2259 initialize_key_to_element (op);
2260 state->initial_size = GNUNET_CONTAINER_multihashmap32_size (
2261 state->key_to_element);
2262 return state;
2263}
2264
2265
2266/**
2267 * Accept an union operation request from a remote peer.
2268 * Only initializes the private operation state.
2269 *
2270 * @param op operation that will be accepted as a union operation
2271 */
2272static struct OperationState *
2273union_accept (struct Operation *op)
2274{
2275 struct OperationState *state;
2276 const struct StrataEstimator *se;
2277 struct GNUNET_MQ_Envelope *ev;
2278 struct StrataEstimatorMessage *strata_msg;
2279 char *buf;
2280 size_t len;
2281 uint16_t type;
2282
2283 LOG (GNUNET_ERROR_TYPE_DEBUG,
2284 "accepting set union operation\n");
2285 GNUNET_STATISTICS_update (_GSS_statistics,
2286 "# of accepted union operations",
2287 1,
2288 GNUNET_NO);
2289 GNUNET_STATISTICS_update (_GSS_statistics,
2290 "# of total union operations",
2291 1,
2292 GNUNET_NO);
2293
2294 state = GNUNET_new (struct OperationState);
2295 state->se = strata_estimator_dup (op->set->state->se);
2296 state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32,
2297 GNUNET_NO);
2298 state->salt_receive = state->salt_send = 42; // FIXME?????
2299 op->state = state;
2300 initialize_key_to_element (op);
2301 state->initial_size = GNUNET_CONTAINER_multihashmap32_size (
2302 state->key_to_element);
2303
2304 /* kick off the operation */
2305 se = state->se;
2306 buf = GNUNET_malloc (se->strata_count * IBF_BUCKET_SIZE * se->ibf_size);
2307 len = strata_estimator_write (se,
2308 buf);
2309 if (len < se->strata_count * IBF_BUCKET_SIZE * se->ibf_size)
2310 type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC;
2311 else
2312 type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE;
2313 ev = GNUNET_MQ_msg_extra (strata_msg,
2314 len,
2315 type);
2316 GNUNET_memcpy (&strata_msg[1],
2317 buf,
2318 len);
2319 GNUNET_free (buf);
2320 strata_msg->set_size
2321 = GNUNET_htonll (GNUNET_CONTAINER_multihashmap_size (
2322 op->set->content->elements));
2323 GNUNET_MQ_send (op->mq,
2324 ev);
2325 state->phase = PHASE_EXPECT_IBF;
2326 return state;
2327}
2328
2329
2330/**
2331 * Create a new set supporting the union operation
2332 *
2333 * We maintain one strata estimator per set and then manipulate it over the
2334 * lifetime of the set, as recreating a strata estimator would be expensive.
2335 *
2336 * @return the newly created set, NULL on error
2337 */
2338static struct SetState *
2339union_set_create (void)
2340{
2341 struct SetState *set_state;
2342
2343 LOG (GNUNET_ERROR_TYPE_DEBUG,
2344 "union set created\n");
2345 set_state = GNUNET_new (struct SetState);
2346 set_state->se = strata_estimator_create (SE_STRATA_COUNT,
2347 SE_IBF_SIZE, SE_IBF_HASH_NUM);
2348 if (NULL == set_state->se)
2349 {
2350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2351 "Failed to allocate strata estimator\n");
2352 GNUNET_free (set_state);
2353 return NULL;
2354 }
2355 return set_state;
2356}
2357
2358
2359/**
2360 * Add the element from the given element message to the set.
2361 *
2362 * @param set_state state of the set want to add to
2363 * @param ee the element to add to the set
2364 */
2365static void
2366union_add (struct SetState *set_state,
2367 struct ElementEntry *ee)
2368{
2369 strata_estimator_insert (set_state->se,
2370 get_ibf_key (&ee->element_hash));
2371}
2372
2373
2374/**
2375 * Remove the element given in the element message from the set.
2376 * Only marks the element as removed, so that older set operations can still exchange it.
2377 *
2378 * @param set_state state of the set to remove from
2379 * @param ee set element to remove
2380 */
2381static void
2382union_remove (struct SetState *set_state,
2383 struct ElementEntry *ee)
2384{
2385 strata_estimator_remove (set_state->se,
2386 get_ibf_key (&ee->element_hash));
2387}
2388
2389
2390/**
2391 * Destroy a set that supports the union operation.
2392 *
2393 * @param set_state the set to destroy
2394 */
2395static void
2396union_set_destroy (struct SetState *set_state)
2397{
2398 if (NULL != set_state->se)
2399 {
2400 strata_estimator_destroy (set_state->se);
2401 set_state->se = NULL;
2402 }
2403 GNUNET_free (set_state);
2404}
2405
2406
2407/**
2408 * Copy union-specific set state.
2409 *
2410 * @param state source state for copying the union state
2411 * @return a copy of the union-specific set state
2412 */
2413static struct SetState *
2414union_copy_state (struct SetState *state)
2415{
2416 struct SetState *new_state;
2417
2418 GNUNET_assert ((NULL != state) &&
2419 (NULL != state->se));
2420 new_state = GNUNET_new (struct SetState);
2421 new_state->se = strata_estimator_dup (state->se);
2422
2423 return new_state;
2424}
2425
2426
2427/**
2428 * Handle case where channel went down for an operation.
2429 *
2430 * @param op operation that lost the channel
2431 */
2432static void
2433union_channel_death (struct Operation *op)
2434{
2435 send_client_done (op);
2436 _GSS_operation_destroy (op,
2437 GNUNET_YES);
2438}
2439
2440
2441/**
2442 * Get the table with implementing functions for
2443 * set union.
2444 *
2445 * @return the operation specific VTable
2446 */
2447const struct SetVT *
2448_GSS_union_vt ()
2449{
2450 static const struct SetVT union_vt = {
2451 .create = &union_set_create,
2452 .add = &union_add,
2453 .remove = &union_remove,
2454 .destroy_set = &union_set_destroy,
2455 .evaluate = &union_evaluate,
2456 .accept = &union_accept,
2457 .cancel = &union_op_cancel,
2458 .copy_state = &union_copy_state,
2459 .channel_death = &union_channel_death
2460 };
2461
2462 return &union_vt;
2463}
diff --git a/src/contrib/service/set/gnunet-service-set_union.h b/src/contrib/service/set/gnunet-service-set_union.h
new file mode 100644
index 000000000..68301c96b
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union.h
@@ -0,0 +1,246 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013-2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union.h
22 * @brief two-peer set operations
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef GNUNET_SERVICE_SET_UNION_H
27#define GNUNET_SERVICE_SET_UNION_H
28
29#include "gnunet-service-set.h"
30#include "gnunet-service-set_protocol.h"
31
32
33/**
34 * Handle a strata estimator from a remote peer
35 *
36 * @param cls the union operation
37 * @param msg the message
38 */
39int
40check_union_p2p_strata_estimator (void *cls,
41 const struct StrataEstimatorMessage *msg);
42
43
44/**
45 * Handle a strata estimator from a remote peer
46 *
47 * @param cls the union operation
48 * @param msg the message
49 */
50void
51handle_union_p2p_strata_estimator (void *cls,
52 const struct StrataEstimatorMessage *msg);
53
54
55/**
56 * Check an IBF message from a remote peer.
57 *
58 * Reassemble the IBF from multiple pieces, and
59 * process the whole IBF once possible.
60 *
61 * @param cls the union operation
62 * @param msg the header of the message
63 * @return #GNUNET_OK if @a msg is well-formed
64 */
65int
66check_union_p2p_ibf (void *cls,
67 const struct IBFMessage *msg);
68
69
70/**
71 * Handle an IBF message from a remote peer.
72 *
73 * Reassemble the IBF from multiple pieces, and
74 * process the whole IBF once possible.
75 *
76 * @param cls the union operation
77 * @param msg the header of the message
78 */
79void
80handle_union_p2p_ibf (void *cls,
81 const struct IBFMessage *msg);
82
83
84/**
85 * Check an element message from a remote peer.
86 *
87 * @param cls the union operation
88 * @param emsg the message
89 */
90int
91check_union_p2p_elements (void *cls,
92 const struct GNUNET_SET_ElementMessage *emsg);
93
94
95/**
96 * Handle an element message from a remote peer.
97 * Sent by the other peer either because we decoded an IBF and placed a demand,
98 * or because the other peer switched to full set transmission.
99 *
100 * @param cls the union operation
101 * @param emsg the message
102 */
103void
104handle_union_p2p_elements (void *cls,
105 const struct GNUNET_SET_ElementMessage *emsg);
106
107
108/**
109 * Check a full element message from a remote peer.
110 *
111 * @param cls the union operation
112 * @param emsg the message
113 */
114int
115check_union_p2p_full_element (void *cls,
116 const struct GNUNET_SET_ElementMessage *emsg);
117
118
119/**
120 * Handle an element message from a remote peer.
121 *
122 * @param cls the union operation
123 * @param emsg the message
124 */
125void
126handle_union_p2p_full_element (void *cls,
127 const struct GNUNET_SET_ElementMessage *emsg);
128
129
130/**
131 * Send offers (for GNUNET_Hash-es) in response
132 * to inquiries (for IBF_Key-s).
133 *
134 * @param cls the union operation
135 * @param msg the message
136 */
137int
138check_union_p2p_inquiry (void *cls,
139 const struct InquiryMessage *msg);
140
141
142/**
143 * Send offers (for GNUNET_Hash-es) in response
144 * to inquiries (for IBF_Key-s).
145 *
146 * @param cls the union operation
147 * @param msg the message
148 */
149void
150handle_union_p2p_inquiry (void *cls,
151 const struct InquiryMessage *msg);
152
153
154/**
155 * Handle a request for full set transmission.
156 *
157 * @param cls closure, a set union operation
158 * @param mh the demand message
159 */
160void
161handle_union_p2p_request_full (void *cls,
162 const struct GNUNET_MessageHeader *mh);
163
164
165/**
166 * Handle a "full done" message.
167 *
168 * @param cls closure, a set union operation
169 * @param mh the demand message
170 */
171void
172handle_union_p2p_full_done (void *cls,
173 const struct GNUNET_MessageHeader *mh);
174
175
176/**
177 * Check a demand by the other peer for elements based on a list
178 * of `struct GNUNET_HashCode`s.
179 *
180 * @param cls closure, a set union operation
181 * @param mh the demand message
182 * @return #GNUNET_OK if @a mh is well-formed
183 */
184int
185check_union_p2p_demand (void *cls,
186 const struct GNUNET_MessageHeader *mh);
187
188
189/**
190 * Handle a demand by the other peer for elements based on a list
191 * of `struct GNUNET_HashCode`s.
192 *
193 * @param cls closure, a set union operation
194 * @param mh the demand message
195 */
196void
197handle_union_p2p_demand (void *cls,
198 const struct GNUNET_MessageHeader *mh);
199
200
201/**
202 * Check offer (of `struct GNUNET_HashCode`s).
203 *
204 * @param cls the union operation
205 * @param mh the message
206 * @return #GNUNET_OK if @a mh is well-formed
207 */
208int
209check_union_p2p_offer (void *cls,
210 const struct GNUNET_MessageHeader *mh);
211
212
213/**
214 * Handle offers (of `struct GNUNET_HashCode`s) and
215 * respond with demands (of `struct GNUNET_HashCode`s).
216 *
217 * @param cls the union operation
218 * @param mh the message
219 */
220void
221handle_union_p2p_offer (void *cls,
222 const struct GNUNET_MessageHeader *mh);
223
224
225/**
226 * Handle a done message from a remote peer
227 *
228 * @param cls the union operation
229 * @param mh the message
230 */
231void
232handle_union_p2p_done (void *cls,
233 const struct GNUNET_MessageHeader *mh);
234
235/**
236 * Handle an over message from a remote peer
237 *
238 * @param cls the union operation
239 * @param mh the message
240 */
241void
242handle_union_p2p_over (void *cls,
243 const struct GNUNET_MessageHeader *mh);
244
245
246#endif
diff --git a/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c
new file mode 100644
index 000000000..6de9fb5eb
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.c
@@ -0,0 +1,297 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/gnunet-service-set_union_strata_estimator.c
22 * @brief invertible bloom filter
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "ibf.h"
29#include "gnunet-service-set_union_strata_estimator.h"
30
31
32/**
33 * Should we try compressing the strata estimator? This will
34 * break compatibility with the 0.10.1-network.
35 */
36#define FAIL_10_1_COMPATIBILTIY 1
37
38
39/**
40 * Write the given strata estimator to the buffer.
41 *
42 * @param se strata estimator to serialize
43 * @param[out] buf buffer to write to, must be of appropriate size
44 * @return number of bytes written to @a buf
45 */
46size_t
47strata_estimator_write (const struct StrataEstimator *se,
48 void *buf)
49{
50 char *sbuf = buf;
51 unsigned int i;
52 size_t osize;
53
54 GNUNET_assert (NULL != se);
55 for (i = 0; i < se->strata_count; i++)
56 {
57 ibf_write_slice (se->strata[i],
58 0,
59 se->ibf_size,
60 &sbuf[se->ibf_size * IBF_BUCKET_SIZE * i]);
61 }
62 osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count;
63#if FAIL_10_1_COMPATIBILTIY
64 {
65 char *cbuf;
66 size_t nsize;
67
68 if (GNUNET_YES ==
69 GNUNET_try_compression (buf,
70 osize,
71 &cbuf,
72 &nsize))
73 {
74 GNUNET_memcpy (buf, cbuf, nsize);
75 osize = nsize;
76 GNUNET_free (cbuf);
77 }
78 }
79#endif
80 return osize;
81}
82
83
84/**
85 * Read strata from the buffer into the given strata
86 * estimator. The strata estimator must already be allocated.
87 *
88 * @param buf buffer to read from
89 * @param buf_len number of bytes in @a buf
90 * @param is_compressed is the data compressed?
91 * @param[out] se strata estimator to write to
92 * @return #GNUNET_OK on success
93 */
94int
95strata_estimator_read (const void *buf,
96 size_t buf_len,
97 int is_compressed,
98 struct StrataEstimator *se)
99{
100 unsigned int i;
101 size_t osize;
102 char *dbuf;
103
104 dbuf = NULL;
105 if (GNUNET_YES == is_compressed)
106 {
107 osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count;
108 dbuf = GNUNET_decompress (buf,
109 buf_len,
110 osize);
111 if (NULL == dbuf)
112 {
113 GNUNET_break_op (0); /* bad compressed input data */
114 return GNUNET_SYSERR;
115 }
116 buf = dbuf;
117 buf_len = osize;
118 }
119
120 if (buf_len != se->strata_count * se->ibf_size * IBF_BUCKET_SIZE)
121 {
122 GNUNET_break (0); /* very odd error */
123 GNUNET_free (dbuf);
124 return GNUNET_SYSERR;
125 }
126
127 for (i = 0; i < se->strata_count; i++)
128 {
129 ibf_read_slice (buf, 0, se->ibf_size, se->strata[i]);
130 buf += se->ibf_size * IBF_BUCKET_SIZE;
131 }
132 GNUNET_free (dbuf);
133 return GNUNET_OK;
134}
135
136
137/**
138 * Add a key to the strata estimator.
139 *
140 * @param se strata estimator to add the key to
141 * @param key key to add
142 */
143void
144strata_estimator_insert (struct StrataEstimator *se,
145 struct IBF_Key key)
146{
147 uint64_t v;
148 unsigned int i;
149
150 v = key.key_val;
151 /* count trailing '1'-bits of v */
152 for (i = 0; v & 1; v >>= 1, i++)
153 /* empty */;
154 ibf_insert (se->strata[i], key);
155}
156
157
158void
159strata_estimator_remove (struct StrataEstimator *se,
160 struct IBF_Key key)
161{
162 uint64_t v;
163 unsigned int i;
164
165 v = key.key_val;
166 /* count trailing '1'-bits of v */
167 for (i = 0; v & 1; v >>= 1, i++)
168 /* empty */;
169 ibf_remove (se->strata[i], key);
170}
171
172
173/**
174 * Create a new strata estimator with the given parameters.
175 *
176 * @param strata_count number of stratas, that is, number of ibfs in the estimator
177 * @param ibf_size size of each ibf stratum
178 * @param ibf_hashnum hashnum parameter of each ibf
179 * @return a freshly allocated, empty strata estimator, NULL on error
180 */
181struct StrataEstimator *
182strata_estimator_create (unsigned int strata_count,
183 uint32_t ibf_size,
184 uint8_t ibf_hashnum)
185{
186 struct StrataEstimator *se;
187 unsigned int i;
188 unsigned int j;
189
190 se = GNUNET_new (struct StrataEstimator);
191 se->strata_count = strata_count;
192 se->ibf_size = ibf_size;
193 se->strata = GNUNET_new_array (strata_count,
194 struct InvertibleBloomFilter *);
195 for (i = 0; i < strata_count; i++)
196 {
197 se->strata[i] = ibf_create (ibf_size, ibf_hashnum);
198 if (NULL == se->strata[i])
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Failed to allocate memory for strata estimator\n");
202 for (j = 0; j < i; j++)
203 ibf_destroy (se->strata[i]);
204 GNUNET_free (se);
205 return NULL;
206 }
207 }
208 return se;
209}
210
211
212/**
213 * Estimate set difference with two strata estimators,
214 * i.e. arrays of IBFs.
215 * Does not not modify its arguments.
216 *
217 * @param se1 first strata estimator
218 * @param se2 second strata estimator
219 * @return the estimated difference
220 */
221unsigned int
222strata_estimator_difference (const struct StrataEstimator *se1,
223 const struct StrataEstimator *se2)
224{
225 unsigned int count;
226
227 GNUNET_assert (se1->strata_count == se2->strata_count);
228 count = 0;
229 for (int i = se1->strata_count - 1; i >= 0; i--)
230 {
231 struct InvertibleBloomFilter *diff;
232 /* number of keys decoded from the ibf */
233
234 /* FIXME: implement this without always allocating new IBFs */
235 diff = ibf_dup (se1->strata[i]);
236 ibf_subtract (diff, se2->strata[i]);
237 for (int ibf_count = 0; GNUNET_YES; ibf_count++)
238 {
239 int more;
240
241 more = ibf_decode (diff, NULL, NULL);
242 if (GNUNET_NO == more)
243 {
244 count += ibf_count;
245 break;
246 }
247 /* Estimate if decoding fails or would not terminate */
248 if ((GNUNET_SYSERR == more) || (ibf_count > diff->size))
249 {
250 ibf_destroy (diff);
251 return count * (1 << (i + 1));
252 }
253 }
254 ibf_destroy (diff);
255 }
256 return count;
257}
258
259
260/**
261 * Make a copy of a strata estimator.
262 *
263 * @param se the strata estimator to copy
264 * @return the copy
265 */
266struct StrataEstimator *
267strata_estimator_dup (struct StrataEstimator *se)
268{
269 struct StrataEstimator *c;
270 unsigned int i;
271
272 c = GNUNET_new (struct StrataEstimator);
273 c->strata_count = se->strata_count;
274 c->ibf_size = se->ibf_size;
275 c->strata = GNUNET_new_array (se->strata_count,
276 struct InvertibleBloomFilter *);
277 for (i = 0; i < se->strata_count; i++)
278 c->strata[i] = ibf_dup (se->strata[i]);
279 return c;
280}
281
282
283/**
284 * Destroy a strata estimator, free all of its resources.
285 *
286 * @param se strata estimator to destroy.
287 */
288void
289strata_estimator_destroy (struct StrataEstimator *se)
290{
291 unsigned int i;
292
293 for (i = 0; i < se->strata_count; i++)
294 ibf_destroy (se->strata[i]);
295 GNUNET_free (se->strata);
296 GNUNET_free (se);
297}
diff --git a/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h
new file mode 100644
index 000000000..94267a2dc
--- /dev/null
+++ b/src/contrib/service/set/gnunet-service-set_union_strata_estimator.h
@@ -0,0 +1,169 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/gnunet-service-set_union_strata_estimator.h
23 * @brief estimator of set difference
24 * @author Florian Dold
25 */
26
27#ifndef GNUNET_CONSENSUS_STRATA_ESTIMATOR_H
28#define GNUNET_CONSENSUS_STRATA_ESTIMATOR_H
29
30#include "platform.h"
31#include "gnunet_common.h"
32#include "gnunet_util_lib.h"
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42
43/**
44 * A handle to a strata estimator.
45 */
46struct StrataEstimator
47{
48 /**
49 * The IBFs of this strata estimator.
50 */
51 struct InvertibleBloomFilter **strata;
52
53 /**
54 * Size of the IBF array in @e strata
55 */
56 unsigned int strata_count;
57
58 /**
59 * Size of each IBF stratum (in bytes)
60 */
61 unsigned int ibf_size;
62};
63
64
65/**
66 * Write the given strata estimator to the buffer.
67 *
68 * @param se strata estimator to serialize
69 * @param[out] buf buffer to write to, must be of appropriate size
70 * @return number of bytes written to @a buf
71 */
72size_t
73strata_estimator_write (const struct StrataEstimator *se,
74 void *buf);
75
76
77/**
78 * Read strata from the buffer into the given strata
79 * estimator. The strata estimator must already be allocated.
80 *
81 * @param buf buffer to read from
82 * @param buf_len number of bytes in @a buf
83 * @param is_compressed is the data compressed?
84 * @param[out] se strata estimator to write to
85 * @return #GNUNET_OK on success
86 */
87int
88strata_estimator_read (const void *buf,
89 size_t buf_len,
90 int is_compressed,
91 struct StrataEstimator *se);
92
93
94/**
95 * Create a new strata estimator with the given parameters.
96 *
97 * @param strata_count number of stratas, that is, number of ibfs in the estimator
98 * @param ibf_size size of each ibf stratum
99 * @param ibf_hashnum hashnum parameter of each ibf
100 * @return a freshly allocated, empty strata estimator, NULL on error
101 */
102struct StrataEstimator *
103strata_estimator_create (unsigned int strata_count,
104 uint32_t ibf_size,
105 uint8_t ibf_hashnum);
106
107
108/**
109 * Get an estimation of the symmetric difference of the elements
110 * contained in both strata estimators.
111 *
112 * @param se1 first strata estimator
113 * @param se2 second strata estimator
114 * @return abs(|se1| - |se2|)
115 */
116unsigned int
117strata_estimator_difference (const struct StrataEstimator *se1,
118 const struct StrataEstimator *se2);
119
120
121/**
122 * Add a key to the strata estimator.
123 *
124 * @param se strata estimator to add the key to
125 * @param key key to add
126 */
127void
128strata_estimator_insert (struct StrataEstimator *se,
129 struct IBF_Key key);
130
131
132/**
133 * Remove a key from the strata estimator.
134 *
135 * @param se strata estimator to remove the key from
136 * @param key key to remove
137 */
138void
139strata_estimator_remove (struct StrataEstimator *se,
140 struct IBF_Key key);
141
142
143/**
144 * Destroy a strata estimator, free all of its resources.
145 *
146 * @param se strata estimator to destroy.
147 */
148void
149strata_estimator_destroy (struct StrataEstimator *se);
150
151
152/**
153 * Make a copy of a strata estimator.
154 *
155 * @param se the strata estimator to copy
156 * @return the copy
157 */
158struct StrataEstimator *
159strata_estimator_dup (struct StrataEstimator *se);
160
161
162#if 0 /* keep Emacsens' auto-indent happy */
163{
164#endif
165#ifdef __cplusplus
166}
167#endif
168
169#endif
diff --git a/src/contrib/service/set/gnunet-set-ibf-profiler.c b/src/contrib/service/set/gnunet-set-ibf-profiler.c
new file mode 100644
index 000000000..6465b15b8
--- /dev/null
+++ b/src/contrib/service/set/gnunet-set-ibf-profiler.c
@@ -0,0 +1,308 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/gnunet-set-ibf-profiler.c
23 * @brief tool for profiling the invertible bloom filter implementation
24 * @author Florian Dold
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#include "ibf.h"
31
32static unsigned int asize = 10;
33static unsigned int bsize = 10;
34static unsigned int csize = 10;
35static unsigned int hash_num = 4;
36static unsigned int ibf_size = 80;
37
38/* FIXME: add parameter for this */
39static enum GNUNET_CRYPTO_Quality random_quality = GNUNET_CRYPTO_QUALITY_WEAK;
40
41static struct GNUNET_CONTAINER_MultiHashMap *set_a;
42static struct GNUNET_CONTAINER_MultiHashMap *set_b;
43/* common elements in a and b */
44static struct GNUNET_CONTAINER_MultiHashMap *set_c;
45
46static struct GNUNET_CONTAINER_MultiHashMap *key_to_hashcode;
47
48static struct InvertibleBloomFilter *ibf_a;
49static struct InvertibleBloomFilter *ibf_b;
50
51
52static void
53register_hashcode (struct GNUNET_HashCode *hash)
54{
55 struct GNUNET_HashCode replicated;
56 struct IBF_Key key;
57
58 key = ibf_key_from_hashcode (hash);
59 ibf_hashcode_from_key (key, &replicated);
60 (void) GNUNET_CONTAINER_multihashmap_put (
61 key_to_hashcode,
62 &replicated,
63 GNUNET_memdup (hash, sizeof *hash),
64 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
65}
66
67
68static void
69iter_hashcodes (struct IBF_Key key,
70 GNUNET_CONTAINER_MultiHashMapIteratorCallback iter,
71 void *cls)
72{
73 struct GNUNET_HashCode replicated;
74
75 ibf_hashcode_from_key (key, &replicated);
76 GNUNET_CONTAINER_multihashmap_get_multiple (key_to_hashcode,
77 &replicated,
78 iter,
79 cls);
80}
81
82
83static int
84insert_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
85{
86 struct InvertibleBloomFilter *ibf = cls;
87
88 ibf_insert (ibf, ibf_key_from_hashcode (key));
89 return GNUNET_YES;
90}
91
92
93static int
94remove_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
95{
96 struct GNUNET_CONTAINER_MultiHashMap *hashmap = cls;
97
98 /* if remove fails, there just was a collision with another key */
99 (void) GNUNET_CONTAINER_multihashmap_remove (hashmap, value, NULL);
100 return GNUNET_YES;
101}
102
103
104static void
105run (void *cls,
106 char *const *args,
107 const char *cfgfile,
108 const struct GNUNET_CONFIGURATION_Handle *cfg)
109{
110 struct GNUNET_HashCode id;
111 struct IBF_Key ibf_key;
112 int i;
113 int side;
114 int res;
115 struct GNUNET_TIME_Absolute start_time;
116 struct GNUNET_TIME_Relative delta_time;
117
118 set_a =
119 GNUNET_CONTAINER_multihashmap_create (((asize == 0) ? 1 : (asize + csize)),
120 GNUNET_NO);
121 set_b =
122 GNUNET_CONTAINER_multihashmap_create (((bsize == 0) ? 1 : (bsize + csize)),
123 GNUNET_NO);
124 set_c = GNUNET_CONTAINER_multihashmap_create (((csize == 0) ? 1 : csize),
125 GNUNET_NO);
126
127 key_to_hashcode =
128 GNUNET_CONTAINER_multihashmap_create (((asize + bsize + csize == 0)
129 ? 1
130 : (asize + bsize + csize)),
131 GNUNET_NO);
132
133 printf ("hash-num=%u, size=%u, #(A-B)=%u, #(B-A)=%u, #(A&B)=%u\n",
134 hash_num,
135 ibf_size,
136 asize,
137 bsize,
138 csize);
139
140 i = 0;
141 while (i < asize)
142 {
143 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
144 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
145 continue;
146 GNUNET_break (GNUNET_OK ==
147 GNUNET_CONTAINER_multihashmap_put (
148 set_a,
149 &id,
150 NULL,
151 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
152 register_hashcode (&id);
153 i++;
154 }
155 i = 0;
156 while (i < bsize)
157 {
158 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
159 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
160 continue;
161 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id))
162 continue;
163 GNUNET_break (GNUNET_OK ==
164 GNUNET_CONTAINER_multihashmap_put (
165 set_b,
166 &id,
167 NULL,
168 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
169 register_hashcode (&id);
170 i++;
171 }
172 i = 0;
173 while (i < csize)
174 {
175 GNUNET_CRYPTO_hash_create_random (random_quality, &id);
176 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id))
177 continue;
178 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id))
179 continue;
180 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_c, &id))
181 continue;
182 GNUNET_break (GNUNET_OK ==
183 GNUNET_CONTAINER_multihashmap_put (
184 set_c,
185 &id,
186 NULL,
187 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
188 register_hashcode (&id);
189 i++;
190 }
191
192 ibf_a = ibf_create (ibf_size, hash_num);
193 ibf_b = ibf_create (ibf_size, hash_num);
194 if ((NULL == ibf_a) || (NULL == ibf_b))
195 {
196 /* insufficient memory */
197 GNUNET_break (0);
198 GNUNET_SCHEDULER_shutdown ();
199 return;
200 }
201
202
203 printf ("generated sets\n");
204
205 start_time = GNUNET_TIME_absolute_get ();
206
207 GNUNET_CONTAINER_multihashmap_iterate (set_a, &insert_iterator, ibf_a);
208 GNUNET_CONTAINER_multihashmap_iterate (set_b, &insert_iterator, ibf_b);
209 GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_a);
210 GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_b);
211
212 delta_time = GNUNET_TIME_absolute_get_duration (start_time);
213
214 printf ("encoded in: %s\n",
215 GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO));
216
217 ibf_subtract (ibf_a, ibf_b);
218
219
220 start_time = GNUNET_TIME_absolute_get ();
221
222 for (i = 0; i <= asize + bsize; i++)
223 {
224 res = ibf_decode (ibf_a, &side, &ibf_key);
225 if (GNUNET_SYSERR == res)
226 {
227 printf ("decode failed, %u/%u elements left\n",
228 GNUNET_CONTAINER_multihashmap_size (set_a)
229 + GNUNET_CONTAINER_multihashmap_size (set_b),
230 asize + bsize);
231 return;
232 }
233 if (GNUNET_NO == res)
234 {
235 if ((0 == GNUNET_CONTAINER_multihashmap_size (set_b)) &&
236 (0 == GNUNET_CONTAINER_multihashmap_size (set_a)))
237 {
238 delta_time = GNUNET_TIME_absolute_get_duration (start_time);
239 printf ("decoded successfully in: %s\n",
240 GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO));
241 }
242 else
243 {
244 printf ("decode missed elements (should never happen)\n");
245 }
246 return;
247 }
248
249 if (side == 1)
250 iter_hashcodes (ibf_key, remove_iterator, set_a);
251 if (side == -1)
252 iter_hashcodes (ibf_key, remove_iterator, set_b);
253 }
254 printf ("cyclic IBF, %u/%u elements left\n",
255 GNUNET_CONTAINER_multihashmap_size (set_a)
256 + GNUNET_CONTAINER_multihashmap_size (set_b),
257 asize + bsize);
258}
259
260
261int
262main (int argc, char **argv)
263{
264 struct GNUNET_GETOPT_CommandLineOption options[] = {
265 GNUNET_GETOPT_option_uint ('A',
266 "asize",
267 NULL,
268 gettext_noop ("number of element in set A-B"),
269 &asize),
270
271 GNUNET_GETOPT_option_uint ('B',
272 "bsize",
273 NULL,
274 gettext_noop ("number of element in set B-A"),
275 &bsize),
276
277 GNUNET_GETOPT_option_uint ('C',
278 "csize",
279 NULL,
280 gettext_noop (
281 "number of common elements in A and B"),
282 &csize),
283
284 GNUNET_GETOPT_option_uint ('k',
285 "hash-num",
286 NULL,
287 gettext_noop ("hash num"),
288 &hash_num),
289
290 GNUNET_GETOPT_option_uint ('s',
291 "ibf-size",
292 NULL,
293 gettext_noop ("ibf size"),
294 &ibf_size),
295
296 GNUNET_GETOPT_OPTION_END
297 };
298
299 GNUNET_PROGRAM_run2 (argc,
300 argv,
301 "gnunet-consensus-ibf",
302 "help",
303 options,
304 &run,
305 NULL,
306 GNUNET_YES);
307 return 0;
308}
diff --git a/src/contrib/service/set/ibf.c b/src/contrib/service/set/ibf.c
new file mode 100644
index 000000000..b6fb52b6b
--- /dev/null
+++ b/src/contrib/service/set/ibf.c
@@ -0,0 +1,410 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf.c
23 * @brief implementation of the invertible bloom filter
24 * @author Florian Dold
25 */
26
27#include "platform.h"
28#include "ibf.h"
29
30/**
31 * Compute the key's hash from the key.
32 * Redefine to use a different hash function.
33 */
34#define IBF_KEY_HASH_VAL(k) (GNUNET_CRYPTO_crc32_n (&(k), sizeof(struct \
35 IBF_KeyHash)))
36
37/**
38 * Create a key from a hashcode.
39 *
40 * @param hash the hashcode
41 * @return a key
42 */
43struct IBF_Key
44ibf_key_from_hashcode (const struct GNUNET_HashCode *hash)
45{
46 return *(struct IBF_Key *) hash;
47}
48
49
50/**
51 * Create a hashcode from a key, by replicating the key
52 * until the hascode is filled
53 *
54 * @param key the key
55 * @param dst hashcode to store the result in
56 */
57void
58ibf_hashcode_from_key (struct IBF_Key key,
59 struct GNUNET_HashCode *dst)
60{
61 struct IBF_Key *p;
62 unsigned int i;
63 const unsigned int keys_per_hashcode = sizeof(struct GNUNET_HashCode)
64 / sizeof(struct IBF_Key);
65
66 p = (struct IBF_Key *) dst;
67 for (i = 0; i < keys_per_hashcode; i++)
68 *p++ = key;
69}
70
71
72/**
73 * Create an invertible bloom filter.
74 *
75 * @param size number of IBF buckets
76 * @param hash_num number of buckets one element is hashed in
77 * @return the newly created invertible bloom filter, NULL on error
78 */
79struct InvertibleBloomFilter *
80ibf_create (uint32_t size, uint8_t hash_num)
81{
82 struct InvertibleBloomFilter *ibf;
83
84 GNUNET_assert (0 != size);
85
86 ibf = GNUNET_new (struct InvertibleBloomFilter);
87 ibf->count = GNUNET_malloc_large (size * sizeof(uint8_t));
88 if (NULL == ibf->count)
89 {
90 GNUNET_free (ibf);
91 return NULL;
92 }
93 ibf->key_sum = GNUNET_malloc_large (size * sizeof(struct IBF_Key));
94 if (NULL == ibf->key_sum)
95 {
96 GNUNET_free (ibf->count);
97 GNUNET_free (ibf);
98 return NULL;
99 }
100 ibf->key_hash_sum = GNUNET_malloc_large (size * sizeof(struct IBF_KeyHash));
101 if (NULL == ibf->key_hash_sum)
102 {
103 GNUNET_free (ibf->key_sum);
104 GNUNET_free (ibf->count);
105 GNUNET_free (ibf);
106 return NULL;
107 }
108 ibf->size = size;
109 ibf->hash_num = hash_num;
110
111 return ibf;
112}
113
114
115/**
116 * Store unique bucket indices for the specified key in dst.
117 */
118static void
119ibf_get_indices (const struct InvertibleBloomFilter *ibf,
120 struct IBF_Key key,
121 int *dst)
122{
123 uint32_t filled;
124 uint32_t i;
125 uint32_t bucket;
126
127 bucket = GNUNET_CRYPTO_crc32_n (&key, sizeof key);
128 for (i = 0, filled = 0; filled < ibf->hash_num; i++)
129 {
130 unsigned int j;
131 uint64_t x;
132 for (j = 0; j < filled; j++)
133 if (dst[j] == bucket)
134 goto try_next;
135 dst[filled++] = bucket % ibf->size;
136try_next:;
137 x = ((uint64_t) bucket << 32) | i;
138 bucket = GNUNET_CRYPTO_crc32_n (&x, sizeof x);
139 }
140}
141
142
143static void
144ibf_insert_into (struct InvertibleBloomFilter *ibf,
145 struct IBF_Key key,
146 const int *buckets, int side)
147{
148 int i;
149
150 for (i = 0; i < ibf->hash_num; i++)
151 {
152 const int bucket = buckets[i];
153 ibf->count[bucket].count_val += side;
154 ibf->key_sum[bucket].key_val ^= key.key_val;
155 ibf->key_hash_sum[bucket].key_hash_val
156 ^= IBF_KEY_HASH_VAL (key);
157 }
158}
159
160
161/**
162 * Insert a key into an IBF.
163 *
164 * @param ibf the IBF
165 * @param key the element's hash code
166 */
167void
168ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key)
169{
170 int buckets[ibf->hash_num];
171
172 GNUNET_assert (ibf->hash_num <= ibf->size);
173 ibf_get_indices (ibf, key, buckets);
174 ibf_insert_into (ibf, key, buckets, 1);
175}
176
177
178/**
179 * Remove a key from an IBF.
180 *
181 * @param ibf the IBF
182 * @param key the element's hash code
183 */
184void
185ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key)
186{
187 int buckets[ibf->hash_num];
188
189 GNUNET_assert (ibf->hash_num <= ibf->size);
190 ibf_get_indices (ibf, key, buckets);
191 ibf_insert_into (ibf, key, buckets, -1);
192}
193
194
195/**
196 * Test is the IBF is empty, i.e. all counts, keys and key hashes are zero.
197 */
198static int
199ibf_is_empty (struct InvertibleBloomFilter *ibf)
200{
201 int i;
202
203 for (i = 0; i < ibf->size; i++)
204 {
205 if (0 != ibf->count[i].count_val)
206 return GNUNET_NO;
207 if (0 != ibf->key_hash_sum[i].key_hash_val)
208 return GNUNET_NO;
209 if (0 != ibf->key_sum[i].key_val)
210 return GNUNET_NO;
211 }
212 return GNUNET_YES;
213}
214
215
216/**
217 * Decode and remove an element from the IBF, if possible.
218 *
219 * @param ibf the invertible bloom filter to decode
220 * @param ret_side sign of the cell's count where the decoded element came from.
221 * A negative sign indicates that the element was recovered
222 * resides in an IBF that was previously subtracted from.
223 * @param ret_id receives the hash code of the decoded element, if successful
224 * @return GNUNET_YES if decoding an element was successful,
225 * GNUNET_NO if the IBF is empty,
226 * GNUNET_SYSERR if the decoding has failed
227 */
228int
229ibf_decode (struct InvertibleBloomFilter *ibf,
230 int *ret_side, struct IBF_Key *ret_id)
231{
232 struct IBF_KeyHash hash;
233 int i;
234 int buckets[ibf->hash_num];
235
236 GNUNET_assert (NULL != ibf);
237
238 for (i = 0; i < ibf->size; i++)
239 {
240 int j;
241 int hit;
242
243 /* we can only decode from pure buckets */
244 if ((1 != ibf->count[i].count_val) && (-1 != ibf->count[i].count_val))
245 continue;
246
247 hash.key_hash_val = IBF_KEY_HASH_VAL (ibf->key_sum[i]);
248
249 /* test if the hash matches the key */
250 if (hash.key_hash_val != ibf->key_hash_sum[i].key_hash_val)
251 continue;
252
253 /* test if key in bucket hits its own location,
254 * if not, the key hash was subject to collision */
255 hit = GNUNET_NO;
256 ibf_get_indices (ibf, ibf->key_sum[i], buckets);
257 for (j = 0; j < ibf->hash_num; j++)
258 if (buckets[j] == i)
259 hit = GNUNET_YES;
260
261 if (GNUNET_NO == hit)
262 continue;
263
264 if (NULL != ret_side)
265 *ret_side = ibf->count[i].count_val;
266 if (NULL != ret_id)
267 *ret_id = ibf->key_sum[i];
268
269 /* insert on the opposite side, effectively removing the element */
270 ibf_insert_into (ibf, ibf->key_sum[i], buckets, -ibf->count[i].count_val);
271
272 return GNUNET_YES;
273 }
274
275 if (GNUNET_YES == ibf_is_empty (ibf))
276 return GNUNET_NO;
277 return GNUNET_SYSERR;
278}
279
280
281/**
282 * Write buckets from an ibf to a buffer.
283 * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf.
284 *
285 * @param ibf the ibf to write
286 * @param start with which bucket to start
287 * @param count how many buckets to write
288 * @param buf buffer to write the data to
289 */
290void
291ibf_write_slice (const struct InvertibleBloomFilter *ibf, uint32_t start,
292 uint32_t count, void *buf)
293{
294 struct IBF_Key *key_dst;
295 struct IBF_KeyHash *key_hash_dst;
296 struct IBF_Count *count_dst;
297
298 GNUNET_assert (start + count <= ibf->size);
299
300 /* copy keys */
301 key_dst = (struct IBF_Key *) buf;
302 GNUNET_memcpy (key_dst, ibf->key_sum + start, count * sizeof *key_dst);
303 key_dst += count;
304 /* copy key hashes */
305 key_hash_dst = (struct IBF_KeyHash *) key_dst;
306 GNUNET_memcpy (key_hash_dst, ibf->key_hash_sum + start, count
307 * sizeof *key_hash_dst);
308 key_hash_dst += count;
309 /* copy counts */
310 count_dst = (struct IBF_Count *) key_hash_dst;
311 GNUNET_memcpy (count_dst, ibf->count + start, count * sizeof *count_dst);
312}
313
314
315/**
316 * Read buckets from a buffer into an ibf.
317 *
318 * @param buf pointer to the buffer to read from
319 * @param start which bucket to start at
320 * @param count how many buckets to read
321 * @param ibf the ibf to read from
322 */
323void
324ibf_read_slice (const void *buf, uint32_t start, uint32_t count, struct
325 InvertibleBloomFilter *ibf)
326{
327 struct IBF_Key *key_src;
328 struct IBF_KeyHash *key_hash_src;
329 struct IBF_Count *count_src;
330
331 GNUNET_assert (count > 0);
332 GNUNET_assert (start + count <= ibf->size);
333
334 /* copy keys */
335 key_src = (struct IBF_Key *) buf;
336 GNUNET_memcpy (ibf->key_sum + start, key_src, count * sizeof *key_src);
337 key_src += count;
338 /* copy key hashes */
339 key_hash_src = (struct IBF_KeyHash *) key_src;
340 GNUNET_memcpy (ibf->key_hash_sum + start, key_hash_src, count
341 * sizeof *key_hash_src);
342 key_hash_src += count;
343 /* copy counts */
344 count_src = (struct IBF_Count *) key_hash_src;
345 GNUNET_memcpy (ibf->count + start, count_src, count * sizeof *count_src);
346}
347
348
349/**
350 * Subtract ibf2 from ibf1, storing the result in ibf1.
351 * The two IBF's must have the same parameters size and hash_num.
352 *
353 * @param ibf1 IBF that is subtracted from
354 * @param ibf2 IBF that will be subtracted from ibf1
355 */
356void
357ibf_subtract (struct InvertibleBloomFilter *ibf1, const struct
358 InvertibleBloomFilter *ibf2)
359{
360 int i;
361
362 GNUNET_assert (ibf1->size == ibf2->size);
363 GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
364
365 for (i = 0; i < ibf1->size; i++)
366 {
367 ibf1->count[i].count_val -= ibf2->count[i].count_val;
368 ibf1->key_hash_sum[i].key_hash_val ^= ibf2->key_hash_sum[i].key_hash_val;
369 ibf1->key_sum[i].key_val ^= ibf2->key_sum[i].key_val;
370 }
371}
372
373
374/**
375 * Create a copy of an IBF, the copy has to be destroyed properly.
376 *
377 * @param ibf the IBF to copy
378 */
379struct InvertibleBloomFilter *
380ibf_dup (const struct InvertibleBloomFilter *ibf)
381{
382 struct InvertibleBloomFilter *copy;
383
384 copy = GNUNET_malloc (sizeof *copy);
385 copy->hash_num = ibf->hash_num;
386 copy->size = ibf->size;
387 copy->key_hash_sum = GNUNET_memdup (ibf->key_hash_sum, ibf->size
388 * sizeof(struct IBF_KeyHash));
389 copy->key_sum = GNUNET_memdup (ibf->key_sum, ibf->size * sizeof(struct
390 IBF_Key));
391 copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof(struct
392 IBF_Count));
393 return copy;
394}
395
396
397/**
398 * Destroy all resources associated with the invertible bloom filter.
399 * No more ibf_*-functions may be called on ibf after calling destroy.
400 *
401 * @param ibf the intertible bloom filter to destroy
402 */
403void
404ibf_destroy (struct InvertibleBloomFilter *ibf)
405{
406 GNUNET_free (ibf->key_sum);
407 GNUNET_free (ibf->key_hash_sum);
408 GNUNET_free (ibf->count);
409 GNUNET_free (ibf);
410}
diff --git a/src/contrib/service/set/ibf.h b/src/contrib/service/set/ibf.h
new file mode 100644
index 000000000..334a797ef
--- /dev/null
+++ b/src/contrib/service/set/ibf.h
@@ -0,0 +1,256 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf.h
23 * @brief invertible bloom filter
24 * @author Florian Dold
25 */
26
27#ifndef GNUNET_CONSENSUS_IBF_H
28#define GNUNET_CONSENSUS_IBF_H
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32
33#ifdef __cplusplus
34extern "C"
35{
36#if 0 /* keep Emacsens' auto-indent happy */
37}
38#endif
39#endif
40
41
42/**
43 * Keys that can be inserted into and removed from an IBF.
44 */
45struct IBF_Key
46{
47 uint64_t key_val;
48};
49
50
51/**
52 * Hash of an IBF key.
53 */
54struct IBF_KeyHash
55{
56 uint32_t key_hash_val;
57};
58
59
60/**
61 * Type of the count field of IBF buckets.
62 */
63struct IBF_Count
64{
65 int8_t count_val;
66};
67
68
69/**
70 * Size of one ibf bucket in bytes
71 */
72#define IBF_BUCKET_SIZE (sizeof(struct IBF_Count) + sizeof(struct IBF_Key) \
73 + sizeof(struct IBF_KeyHash))
74
75
76/**
77 * Invertible bloom filter (IBF).
78 *
79 * An IBF is a counting bloom filter that has the ability to restore
80 * the hashes of its stored elements with high probability.
81 */
82struct InvertibleBloomFilter
83{
84 /**
85 * How many cells does this IBF have?
86 */
87 uint32_t size;
88
89 /**
90 * In how many cells do we hash one element?
91 * Usually 4 or 3.
92 */
93 uint8_t hash_num;
94
95 /**
96 * Xor sums of the elements' keys, used to identify the elements.
97 * Array of 'size' elements.
98 */
99 struct IBF_Key *key_sum;
100
101 /**
102 * Xor sums of the hashes of the keys of inserted elements.
103 * Array of 'size' elements.
104 */
105 struct IBF_KeyHash *key_hash_sum;
106
107 /**
108 * How many times has a bucket been hit?
109 * Can be negative, as a result of IBF subtraction.
110 * Array of 'size' elements.
111 */
112 struct IBF_Count *count;
113};
114
115
116/**
117 * Write buckets from an ibf to a buffer.
118 * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf.
119 *
120 * @param ibf the ibf to write
121 * @param start with which bucket to start
122 * @param count how many buckets to write
123 * @param buf buffer to write the data to
124 */
125void
126ibf_write_slice (const struct InvertibleBloomFilter *ibf,
127 uint32_t start,
128 uint32_t count,
129 void *buf);
130
131
132/**
133 * Read buckets from a buffer into an ibf.
134 *
135 * @param buf pointer to the buffer to read from
136 * @param start which bucket to start at
137 * @param count how many buckets to read
138 * @param ibf the ibf to write to
139 */
140void
141ibf_read_slice (const void *buf,
142 uint32_t start,
143 uint32_t count,
144 struct InvertibleBloomFilter *ibf);
145
146
147/**
148 * Create a key from a hashcode.
149 *
150 * @param hash the hashcode
151 * @return a key
152 */
153struct IBF_Key
154ibf_key_from_hashcode (const struct GNUNET_HashCode *hash);
155
156
157/**
158 * Create a hashcode from a key, by replicating the key
159 * until the hascode is filled
160 *
161 * @param key the key
162 * @param dst hashcode to store the result in
163 */
164void
165ibf_hashcode_from_key (struct IBF_Key key, struct GNUNET_HashCode *dst);
166
167
168/**
169 * Create an invertible bloom filter.
170 *
171 * @param size number of IBF buckets
172 * @param hash_num number of buckets one element is hashed in, usually 3 or 4
173 * @return the newly created invertible bloom filter, NULL on error
174 */
175struct InvertibleBloomFilter *
176ibf_create (uint32_t size, uint8_t hash_num);
177
178
179/**
180 * Insert a key into an IBF.
181 *
182 * @param ibf the IBF
183 * @param key the element's hash code
184 */
185void
186ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key);
187
188
189/**
190 * Remove a key from an IBF.
191 *
192 * @param ibf the IBF
193 * @param key the element's hash code
194 */
195void
196ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key);
197
198
199/**
200 * Subtract ibf2 from ibf1, storing the result in ibf1.
201 * The two IBF's must have the same parameters size and hash_num.
202 *
203 * @param ibf1 IBF that is subtracted from
204 * @param ibf2 IBF that will be subtracted from ibf1
205 */
206void
207ibf_subtract (struct InvertibleBloomFilter *ibf1,
208 const struct InvertibleBloomFilter *ibf2);
209
210
211/**
212 * Decode and remove an element from the IBF, if possible.
213 *
214 * @param ibf the invertible bloom filter to decode
215 * @param ret_side sign of the cell's count where the decoded element came from.
216 * A negative sign indicates that the element was recovered
217 * resides in an IBF that was previously subtracted from.
218 * @param ret_id receives the hash code of the decoded element, if successful
219 * @return #GNUNET_YES if decoding an element was successful,
220 * #GNUNET_NO if the IBF is empty,
221 * #GNUNET_SYSERR if the decoding has failed
222 */
223int
224ibf_decode (struct InvertibleBloomFilter *ibf,
225 int *ret_side,
226 struct IBF_Key *ret_id);
227
228
229/**
230 * Create a copy of an IBF, the copy has to be destroyed properly.
231 *
232 * @param ibf the IBF to copy
233 */
234struct InvertibleBloomFilter *
235ibf_dup (const struct InvertibleBloomFilter *ibf);
236
237
238/**
239 * Destroy all resources associated with the invertible bloom filter.
240 * No more ibf_*-functions may be called on ibf after calling destroy.
241 *
242 * @param ibf the intertible bloom filter to destroy
243 */
244void
245ibf_destroy (struct InvertibleBloomFilter *ibf);
246
247
248
249#if 0 /* keep Emacsens' auto-indent happy */
250{
251#endif
252#ifdef __cplusplus
253}
254#endif
255
256#endif
diff --git a/src/contrib/service/set/ibf_sim.c b/src/contrib/service/set/ibf_sim.c
new file mode 100644
index 000000000..563ed0fb8
--- /dev/null
+++ b/src/contrib/service/set/ibf_sim.c
@@ -0,0 +1,143 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/ibf_sim.c
23 * @brief implementation of simulation for invertible bloom filter
24 * @author Florian Dold
25 *
26 * This code was used for some internal experiments, it is not
27 * build or shipped as part of the GNUnet system.
28 */
29#include "platform.h"
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33
34#define MAX_IBF_DECODE 16
35
36/* report average over how many rounds? */
37#define ROUNDS 100000
38
39/* enable one of the three below */
40// simple fix
41#define FIX1 0
42// possibly slightly better fix for large IBF_DECODE values
43#define FIX2 1
44
45// SIGCOMM algorithm
46#define STRATA 0
47
48// print each value?
49#define VERBOSE 0
50// avoid assembly? (ASM is about 50% faster)
51#define SLOW 0
52
53int
54main (int argc, char **argv)
55{
56 unsigned int round;
57 unsigned int buckets[31]; // max is 2^31 as 'random' returns only between 0 and 2^31
58 unsigned int i;
59 int j;
60 unsigned int r;
61 unsigned int ret;
62 unsigned long long total;
63 unsigned int want;
64 double predict;
65
66 srandom (time (NULL));
67 total = 0;
68 want = atoi (argv[1]);
69 for (round = 0; round < ROUNDS; round++)
70 {
71 memset (buckets, 0, sizeof(buckets));
72 for (i = 0; i < want; i++)
73 {
74 /* FIXME: might want to use 'better' PRNG to avoid
75 PRNG-induced biases */
76 r = random ();
77 if (0 == r)
78 continue;
79#if SLOW
80 for (j = 0; (j < 31) && (0 == (r & (1 << j))); j++)
81 ;
82#else
83 /* use assembly / gcc */
84 j = __builtin_ffs (r) - 1;
85#endif
86 buckets[j]++;
87 }
88 ret = 0;
89 predict = 0.0;
90 for (j = 31; j >= 0; j--)
91 {
92#if FIX1
93 /* improved algorithm, for 1000 elements with IBF-DECODE 8, I
94 get 990/1000 elements on average over 1 million runs; key
95 idea being to stop short of the 'last' possible IBF as
96 otherwise a "lowball" per-chance would unduely influence the
97 result */if ((j > 0) &&
98 (buckets[j - 1] > MAX_IBF_DECODE))
99 {
100 ret *= (1 << (j + 1));
101 break;
102 }
103#endif
104#if FIX2
105 /* another improvement: don't just always cut off the last one,
106 but rather try to predict based on all previous values where
107 that "last" one is; additional prediction can only really
108 work if MAX_IBF_DECODE is sufficiently high */
109 if ((j > 0) &&
110 ((buckets[j - 1] > MAX_IBF_DECODE) ||
111 (predict > MAX_IBF_DECODE)))
112 {
113 ret *= (1 << (j + 1));
114 break;
115 }
116#endif
117#if STRATA
118 /* original algorithm, for 1000 elements with IBF-DECODE 8,
119 I get 920/1000 elements on average over 1 million runs */
120 if (buckets[j] > MAX_IBF_DECODE)
121 {
122 ret *= (1 << (j + 1));
123 break;
124 }
125#endif
126 ret += buckets[j];
127 predict = (buckets[j] + 2.0 * predict) / 2.0;
128 }
129#if VERBOSE
130 fprintf (stderr, "%u ", ret);
131#endif
132 total += ret;
133 }
134 fprintf (stderr, "\n");
135 fprintf (stdout, "average %llu\n", total / ROUNDS);
136 return 0;
137}
138
139
140/* TODO: should calculate stddev of the results to also be able to
141 say something about the stability of the results, outside of
142 large-scale averages -- gaining 8% precision at the expense of
143 50% additional variance might not be worth it... */
diff --git a/src/contrib/service/set/meson.build b/src/contrib/service/set/meson.build
new file mode 100644
index 000000000..153c16ac5
--- /dev/null
+++ b/src/contrib/service/set/meson.build
@@ -0,0 +1,45 @@
1libgnunetset_src = ['set_api.c']
2
3gnunetserviceset_src = ['gnunet-service-set.c',
4 'gnunet-service-set_union.c',
5 'gnunet-service-set_intersection.c',
6 'gnunet-service-set_union_strata_estimator.c',
7 'ibf.c']
8
9configure_file(input : 'set.conf.in',
10 output : 'set.conf',
11 configuration : cdata,
12 install: true,
13 install_dir: pkgcfgdir)
14
15
16libgnunetset = library('gnunetset',
17 libgnunetset_src,
18 soversion: '0',
19 version: '0.0.0',
20 dependencies: libgnunetutil_dep,
21 include_directories: [incdir, configuration_inc],
22 install: true,
23 install_dir: get_option('libdir'))
24pkg.generate(libgnunetset, url: 'https://www.gnunet.org',
25 description : 'Provides API for accessing the set service')
26libgnunetset_dep = declare_dependency(link_with : libgnunetset)
27shared_module('gnunet_plugin_block_set_test',
28 ['plugin_block_set_test.c'],
29 dependencies: libgnunetutil_dep,
30 include_directories: [incdir, configuration_inc],
31 install:true,
32 install_dir: get_option('libdir')/'gnunet')
33executable ('gnunet-service-set',
34 gnunetserviceset_src,
35 dependencies: [libgnunetset_dep,
36 libgnunetutil_dep,
37 m_dep,
38 libgnunetstatistics_dep,
39 libgnunetcore_dep,
40 libgnunetcadet_dep,
41 libgnunetblock_dep],
42 include_directories: [incdir, configuration_inc],
43 install: true,
44 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
45
diff --git a/src/contrib/service/set/plugin_block_set_test.c b/src/contrib/service/set/plugin_block_set_test.c
new file mode 100644
index 000000000..cb5cef5ad
--- /dev/null
+++ b/src/contrib/service/set/plugin_block_set_test.c
@@ -0,0 +1,167 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/plugin_block_set_test.c
23 * @brief set test block, recognizes elements with non-zero first byte as invalid
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_block_plugin.h"
29#include "gnunet_block_group_lib.h"
30
31
32/**
33 * Function called to validate a query.
34 *
35 * @param cls closure
36 * @param type block type
37 * @param query original query (hash)
38 * @param xquery extrended query data (can be NULL, depending on type)
39 * @param xquery_size number of bytes in @a xquery
40 * @return #GNUNET_OK if the query is fine, #GNUNET_NO if not
41 */
42static enum GNUNET_GenericReturnValue
43block_plugin_set_test_check_query (void *cls,
44 enum GNUNET_BLOCK_Type type,
45 const struct GNUNET_HashCode *query,
46 const void *xquery,
47 size_t xquery_size)
48{
49 return GNUNET_OK;
50}
51
52
53/**
54 * Function called to validate a block for storage.
55 *
56 * @param cls closure
57 * @param type block type
58 * @param block block data to validate
59 * @param block_size number of bytes in @a block
60 * @return #GNUNET_OK if the block is fine, #GNUNET_NO if not
61 */
62static enum GNUNET_GenericReturnValue
63block_plugin_set_test_check_block (void *cls,
64 enum GNUNET_BLOCK_Type type,
65 const void *block,
66 size_t block_size)
67{
68 if ((NULL == block) ||
69 (0 == block_size) ||
70 (0 != ((char *) block)[0]))
71 return GNUNET_SYSERR;
72 return GNUNET_OK;
73}
74
75
76/**
77 * Function called to validate a reply to a request. Note that it is assumed
78 * that the reply has already been matched to the key (and signatures checked)
79 * as it would be done with the GetKeyFunction and the
80 * BlockEvaluationFunction.
81 *
82 * @param cls closure
83 * @param type block type
84 * @param group which block group to use for evaluation
85 * @param query original query (hash)
86 * @param xquery extrended query data (can be NULL, depending on type)
87 * @param xquery_size number of bytes in @a xquery
88 * @param reply_block response to validate
89 * @param reply_block_size number of bytes in @a reply_block
90 * @return characterization of result
91 */
92static enum GNUNET_BLOCK_ReplyEvaluationResult
93block_plugin_set_test_check_reply (void *cls,
94 enum GNUNET_BLOCK_Type type,
95 struct GNUNET_BLOCK_Group *group,
96 const struct GNUNET_HashCode *query,
97 const void *xquery,
98 size_t xquery_size,
99 const void *reply_block,
100 size_t reply_block_size)
101{
102 if ((NULL == reply_block) ||
103 (0 == reply_block_size) ||
104 (0 != ((char *) reply_block)[0]))
105 GNUNET_assert (0);
106 return GNUNET_BLOCK_REPLY_OK_MORE;
107}
108
109
110/**
111 * Function called to obtain the key for a block.
112 *
113 * @param cls closure
114 * @param type block type
115 * @param block block to get the key for
116 * @param block_size number of bytes in block
117 * @param key set to the key (query) for the given block
118 * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
119 * (or if extracting a key from a block of this type does not work)
120 */
121static enum GNUNET_GenericReturnValue
122block_plugin_set_test_get_key (void *cls,
123 enum GNUNET_BLOCK_Type type,
124 const void *block,
125 size_t block_size,
126 struct GNUNET_HashCode *key)
127{
128 return GNUNET_NO;
129}
130
131
132/**
133 * Entry point for the plugin.
134 */
135void *
136libgnunet_plugin_block_set_test_init (void *cls)
137{
138 static const enum GNUNET_BLOCK_Type types[] = {
139 GNUNET_BLOCK_TYPE_SET_TEST,
140 GNUNET_BLOCK_TYPE_ANY /* end of list */
141 };
142 struct GNUNET_BLOCK_PluginFunctions *api;
143
144 api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
145 api->get_key = &block_plugin_set_test_get_key;
146 api->check_query = &block_plugin_set_test_check_query;
147 api->check_block = &block_plugin_set_test_check_block;
148 api->check_reply = &block_plugin_set_test_check_reply;
149 api->types = types;
150 return api;
151}
152
153
154/**
155 * Exit point from the plugin.
156 */
157void *
158libgnunet_plugin_block_set_test_done (void *cls)
159{
160 struct GNUNET_BLOCK_PluginFunctions *api = cls;
161
162 GNUNET_free (api);
163 return NULL;
164}
165
166
167/* end of plugin_block_set_test.c */
diff --git a/src/contrib/service/set/set.conf.in b/src/contrib/service/set/set.conf.in
new file mode 100644
index 000000000..66bcfa169
--- /dev/null
+++ b/src/contrib/service/set/set.conf.in
@@ -0,0 +1,12 @@
1[set]
2START_ON_DEMAND = @START_ON_DEMAND@
3@UNIXONLY@PORT = 2106
4HOSTNAME = localhost
5BINARY = gnunet-service-set
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-set.sock
9UNIX_MATCH_UID = YES
10UNIX_MATCH_GID = YES
11
12#PREFIX = valgrind
diff --git a/src/contrib/service/set/set.h b/src/contrib/service/set/set.h
new file mode 100644
index 000000000..e9d10ea22
--- /dev/null
+++ b/src/contrib/service/set/set.h
@@ -0,0 +1,400 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/set.h
22 * @brief messages used for the set api
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#ifndef SET_H
27#define SET_H
28
29#include "platform.h"
30#include "gnunet_common.h"
31#include "gnunet_set_service.h"
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35/**
36 * Message sent by the client to the service to ask starting
37 * a new set to perform operations with. Includes the desired
38 * set operation type.
39 */
40struct GNUNET_SET_CreateMessage
41{
42 /**
43 * Type: #GNUNET_MESSAGE_TYPE_SET_CREATE
44 */
45 struct GNUNET_MessageHeader header;
46
47 /**
48 * Operation type, values of `enum GNUNET_SET_OperationType`
49 */
50 uint32_t operation GNUNET_PACKED;
51};
52
53
54/**
55 * Message sent by the client to the service to start listening for
56 * incoming requests to perform a certain type of set operation for a
57 * certain type of application.
58 */
59struct GNUNET_SET_ListenMessage
60{
61 /**
62 * Type: #GNUNET_MESSAGE_TYPE_SET_LISTEN
63 */
64 struct GNUNET_MessageHeader header;
65
66 /**
67 * Operation type, values of `enum GNUNET_SET_OperationType`
68 */
69 uint32_t operation GNUNET_PACKED;
70
71 /**
72 * application id
73 */
74 struct GNUNET_HashCode app_id;
75};
76
77
78/**
79 * Message sent by a listening client to the service to accept
80 * performing the operation with the other peer.
81 */
82struct GNUNET_SET_AcceptMessage
83{
84 /**
85 * Type: #GNUNET_MESSAGE_TYPE_SET_ACCEPT
86 */
87 struct GNUNET_MessageHeader header;
88
89 /**
90 * ID of the incoming request we want to accept.
91 */
92 uint32_t accept_reject_id GNUNET_PACKED;
93
94 /**
95 * Request ID to identify responses.
96 */
97 uint32_t request_id GNUNET_PACKED;
98
99 /**
100 * How should results be sent to us?
101 * See `enum GNUNET_SET_ResultMode`.
102 */
103 uint32_t result_mode GNUNET_PACKED;
104
105 /**
106 * Always use delta operation instead of sending full sets,
107 * even it it's less efficient.
108 */
109 uint8_t force_delta;
110
111 /**
112 * Always send full sets, even if delta operations would
113 * be more efficient.
114 */
115 uint8_t force_full;
116
117 /**
118 * #GNUNET_YES to fail operations where Byzantine faults
119 * are suspected
120 */
121 uint8_t byzantine;
122
123 /**
124 * Lower bound for the set size, used only when
125 * byzantine mode is enabled.
126 */
127 uint8_t byzantine_lower_bound;
128};
129
130
131/**
132 * Message sent by a listening client to the service to reject
133 * performing the operation with the other peer.
134 */
135struct GNUNET_SET_RejectMessage
136{
137 /**
138 * Type: #GNUNET_MESSAGE_TYPE_SET_REJECT
139 */
140 struct GNUNET_MessageHeader header;
141
142 /**
143 * ID of the incoming request we want to reject.
144 */
145 uint32_t accept_reject_id GNUNET_PACKED;
146};
147
148
149/**
150 * A request for an operation with another client.
151 */
152struct GNUNET_SET_RequestMessage
153{
154 /**
155 * Type: #GNUNET_MESSAGE_TYPE_SET_REQUEST.
156 */
157 struct GNUNET_MessageHeader header;
158
159 /**
160 * ID of the to identify the request when accepting or
161 * rejecting it.
162 */
163 uint32_t accept_id GNUNET_PACKED;
164
165 /**
166 * Identity of the requesting peer.
167 */
168 struct GNUNET_PeerIdentity peer_id;
169
170 /* rest: context message, that is, application-specific
171 message to convince listener to pick up */
172};
173
174
175/**
176 * Message sent by client to service to initiate a set operation as a
177 * client (not as listener). A set (which determines the operation
178 * type) must already exist in association with this client.
179 */
180struct GNUNET_SET_EvaluateMessage
181{
182 /**
183 * Type: #GNUNET_MESSAGE_TYPE_SET_EVALUATE
184 */
185 struct GNUNET_MessageHeader header;
186
187 /**
188 * How should results be sent to us?
189 * See `enum GNUNET_SET_ResultMode`.
190 */
191 uint32_t result_mode GNUNET_PACKED;
192
193 /**
194 * Peer to evaluate the operation with
195 */
196 struct GNUNET_PeerIdentity target_peer;
197
198 /**
199 * Application id
200 */
201 struct GNUNET_HashCode app_id;
202
203 /**
204 * Id of our set to evaluate, chosen implicitly by the client when it
205 * calls #GNUNET_SET_commit().
206 */
207 uint32_t request_id GNUNET_PACKED;
208
209 /**
210 * Always use delta operation instead of sending full sets,
211 * even it it's less efficient.
212 */
213 uint8_t force_delta;
214
215 /**
216 * Always send full sets, even if delta operations would
217 * be more efficient.
218 */
219 uint8_t force_full;
220
221 /**
222 * #GNUNET_YES to fail operations where Byzantine faults
223 * are suspected
224 */
225 uint8_t byzantine;
226
227 /**
228 * Lower bound for the set size, used only when
229 * byzantine mode is enabled.
230 */
231 uint8_t byzantine_lower_bound;
232
233 /* rest: context message, that is, application-specific
234 message to convince listener to pick up */
235};
236
237
238/**
239 * Message sent by the service to the client to indicate an
240 * element that is removed (set intersection) or added
241 * (set union) or part of the final result, depending on
242 * options specified for the operation.
243 */
244struct GNUNET_SET_ResultMessage
245{
246 /**
247 * Type: #GNUNET_MESSAGE_TYPE_SET_RESULT
248 */
249 struct GNUNET_MessageHeader header;
250
251 /**
252 * Current set size.
253 */
254 uint64_t current_size;
255
256 /**
257 * id the result belongs to
258 */
259 uint32_t request_id GNUNET_PACKED;
260
261 /**
262 * Was the evaluation successful? Contains
263 * an `enum GNUNET_SET_Status` in NBO.
264 */
265 uint16_t result_status GNUNET_PACKED;
266
267 /**
268 * Type of the element attached to the message, if any.
269 */
270 uint16_t element_type GNUNET_PACKED;
271
272 /* rest: the actual element */
273};
274
275
276/**
277 * Message sent by client to the service to add or remove
278 * an element to/from the set.
279 */
280struct GNUNET_SET_ElementMessage
281{
282 /**
283 * Type: #GNUNET_MESSAGE_TYPE_SET_ADD or
284 * #GNUNET_MESSAGE_TYPE_SET_REMOVE
285 */
286 struct GNUNET_MessageHeader header;
287
288 /**
289 * Type of the element to add or remove.
290 */
291 uint16_t element_type GNUNET_PACKED;
292
293 /**
294 * For alignment, always zero.
295 */
296 uint16_t reserved GNUNET_PACKED;
297
298 /* rest: the actual element */
299};
300
301
302/**
303 * Sent to the service by the client
304 * in order to cancel a set operation.
305 */
306struct GNUNET_SET_CancelMessage
307{
308 /**
309 * Type: #GNUNET_MESSAGE_TYPE_SET_CANCEL
310 */
311 struct GNUNET_MessageHeader header;
312
313 /**
314 * ID of the request we want to cancel.
315 */
316 uint32_t request_id GNUNET_PACKED;
317};
318
319
320/**
321 * Set element transmitted by service to client in response to a set
322 * iteration request.
323 */
324struct GNUNET_SET_IterResponseMessage
325{
326 /**
327 * Type: #GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT
328 */
329 struct GNUNET_MessageHeader header;
330
331 /**
332 * To which set iteration does this response belong to? First
333 * iteration (per client) has counter zero. Wraps around.
334 */
335 uint16_t iteration_id GNUNET_PACKED;
336
337 /**
338 * Type of the element attached to the message,
339 * if any.
340 */
341 uint16_t element_type GNUNET_PACKED;
342
343 /* rest: element */
344};
345
346
347/**
348 * Client acknowledges receiving element in iteration.
349 */
350struct GNUNET_SET_IterAckMessage
351{
352 /**
353 * Type: #GNUNET_MESSAGE_TYPE_SET_ITER_ACK
354 */
355 struct GNUNET_MessageHeader header;
356
357 /**
358 * Non-zero if the service should continue sending elements.
359 */
360 uint32_t send_more;
361};
362
363
364/**
365 * Server responds to a lazy copy request.
366 */
367struct GNUNET_SET_CopyLazyResponseMessage
368{
369 /**
370 * Type: #GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE
371 */
372 struct GNUNET_MessageHeader header;
373
374 /**
375 * Temporary name for the copied set.
376 */
377 uint32_t cookie;
378};
379
380
381/**
382 * Client connects to a lazily copied set.
383 */
384struct GNUNET_SET_CopyLazyConnectMessage
385{
386 /**
387 * Type: #GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT
388 */
389 struct GNUNET_MessageHeader header;
390
391 /**
392 * Temporary name for the copied set.
393 */
394 uint32_t cookie;
395};
396
397
398GNUNET_NETWORK_STRUCT_END
399
400#endif
diff --git a/src/contrib/service/set/set_api.c b/src/contrib/service/set/set_api.c
new file mode 100644
index 000000000..4f73ff06c
--- /dev/null
+++ b/src/contrib/service/set/set_api.c
@@ -0,0 +1,1199 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/set_api.c
22 * @brief api for the set service
23 * @author Florian Dold
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_protocols.h"
29#include "gnunet_set_service.h"
30#include "set.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "set-api", __VA_ARGS__)
34
35struct SetCopyRequest
36{
37 struct SetCopyRequest *next;
38
39 struct SetCopyRequest *prev;
40
41 void *cls;
42
43 GNUNET_SET_CopyReadyCallback cb;
44};
45
46/**
47 * Opaque handle to a set.
48 */
49struct GNUNET_SET_Handle
50{
51 /**
52 * Message queue for @e client.
53 */
54 struct GNUNET_MQ_Handle *mq;
55
56 /**
57 * Linked list of operations on the set.
58 */
59 struct GNUNET_SET_OperationHandle *ops_head;
60
61 /**
62 * Linked list of operations on the set.
63 */
64 struct GNUNET_SET_OperationHandle *ops_tail;
65
66 /**
67 * Callback for the current iteration over the set,
68 * NULL if no iterator is active.
69 */
70 GNUNET_SET_ElementIterator iterator;
71
72 /**
73 * Closure for @e iterator
74 */
75 void *iterator_cls;
76
77 /**
78 * Should the set be destroyed once all operations are gone?
79 * #GNUNET_SYSERR if #GNUNET_SET_destroy() must raise this flag,
80 * #GNUNET_YES if #GNUNET_SET_destroy() did raise this flag.
81 */
82 int destroy_requested;
83
84 /**
85 * Has the set become invalid (e.g. service died)?
86 */
87 int invalid;
88
89 /**
90 * Both client and service count the number of iterators
91 * created so far to match replies with iterators.
92 */
93 uint16_t iteration_id;
94
95 /**
96 * Configuration, needed when creating (lazy) copies.
97 */
98 const struct GNUNET_CONFIGURATION_Handle *cfg;
99
100 /**
101 * Doubly linked list of copy requests.
102 */
103 struct SetCopyRequest *copy_req_head;
104
105 /**
106 * Doubly linked list of copy requests.
107 */
108 struct SetCopyRequest *copy_req_tail;
109};
110
111
112/**
113 * Handle for a set operation request from another peer.
114 */
115struct GNUNET_SET_Request
116{
117 /**
118 * Id of the request, used to identify the request when
119 * accepting/rejecting it.
120 */
121 uint32_t accept_id;
122
123 /**
124 * Has the request been accepted already?
125 * #GNUNET_YES/#GNUNET_NO
126 */
127 int accepted;
128};
129
130
131/**
132 * Handle to an operation. Only known to the service after committing
133 * the handle with a set.
134 */
135struct GNUNET_SET_OperationHandle
136{
137 /**
138 * Function to be called when we have a result,
139 * or an error.
140 */
141 GNUNET_SET_ResultIterator result_cb;
142
143 /**
144 * Closure for @e result_cb.
145 */
146 void *result_cls;
147
148 /**
149 * Local set used for the operation,
150 * NULL if no set has been provided by conclude yet.
151 */
152 struct GNUNET_SET_Handle *set;
153
154 /**
155 * Message sent to the server on calling conclude,
156 * NULL if conclude has been called.
157 */
158 struct GNUNET_MQ_Envelope *conclude_mqm;
159
160 /**
161 * Address of the request if in the conclude message,
162 * used to patch the request id into the message when the set is known.
163 */
164 uint32_t *request_id_addr;
165
166 /**
167 * Handles are kept in a linked list.
168 */
169 struct GNUNET_SET_OperationHandle *prev;
170
171 /**
172 * Handles are kept in a linked list.
173 */
174 struct GNUNET_SET_OperationHandle *next;
175
176 /**
177 * Request ID to identify the operation within the set.
178 */
179 uint32_t request_id;
180};
181
182
183/**
184 * Opaque handle to a listen operation.
185 */
186struct GNUNET_SET_ListenHandle
187{
188 /**
189 * Message queue for the client.
190 */
191 struct GNUNET_MQ_Handle*mq;
192
193 /**
194 * Configuration handle for the listener, stored
195 * here to be able to reconnect transparently on
196 * connection failure.
197 */
198 const struct GNUNET_CONFIGURATION_Handle *cfg;
199
200 /**
201 * Function to call on a new incoming request,
202 * or on error.
203 */
204 GNUNET_SET_ListenCallback listen_cb;
205
206 /**
207 * Closure for @e listen_cb.
208 */
209 void *listen_cls;
210
211 /**
212 * Application ID we listen for.
213 */
214 struct GNUNET_HashCode app_id;
215
216 /**
217 * Time to wait until we try to reconnect on failure.
218 */
219 struct GNUNET_TIME_Relative reconnect_backoff;
220
221 /**
222 * Task for reconnecting when the listener fails.
223 */
224 struct GNUNET_SCHEDULER_Task *reconnect_task;
225
226 /**
227 * Operation we listen for.
228 */
229 enum GNUNET_SET_OperationType operation;
230};
231
232
233/* mutual recursion with handle_copy_lazy */
234static struct GNUNET_SET_Handle *
235create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
236 enum GNUNET_SET_OperationType op,
237 const uint32_t *cookie);
238
239
240/**
241 * Handle element for iteration over the set. Notifies the
242 * iterator and sends an acknowledgement to the service.
243 *
244 * @param cls the `struct GNUNET_SET_Handle *`
245 * @param msg the message
246 */
247static void
248handle_copy_lazy (void *cls,
249 const struct GNUNET_SET_CopyLazyResponseMessage *msg)
250{
251 struct GNUNET_SET_Handle *set = cls;
252 struct SetCopyRequest *req;
253 struct GNUNET_SET_Handle *new_set;
254
255 req = set->copy_req_head;
256 if (NULL == req)
257 {
258 /* Service sent us unsolicited lazy copy response */
259 GNUNET_break (0);
260 return;
261 }
262
263 LOG (GNUNET_ERROR_TYPE_DEBUG,
264 "Handling response to lazy copy\n");
265 GNUNET_CONTAINER_DLL_remove (set->copy_req_head,
266 set->copy_req_tail,
267 req);
268 // We pass none as operation here, since it doesn't matter when
269 // cloning.
270 new_set = create_internal (set->cfg,
271 GNUNET_SET_OPERATION_NONE,
272 &msg->cookie);
273 req->cb (req->cls, new_set);
274 GNUNET_free (req);
275}
276
277
278/**
279 * Check that the given @a msg is well-formed.
280 *
281 * @param cls closure
282 * @param msg message to check
283 * @return #GNUNET_OK if message is well-formed
284 */
285static int
286check_iter_element (void *cls,
287 const struct GNUNET_SET_IterResponseMessage *msg)
288{
289 /* minimum size was already checked, everything else is OK! */
290 return GNUNET_OK;
291}
292
293
294/**
295 * Handle element for iteration over the set. Notifies the
296 * iterator and sends an acknowledgement to the service.
297 *
298 * @param cls the `struct GNUNET_SET_Handle *`
299 * @param msg the message
300 */
301static void
302handle_iter_element (void *cls,
303 const struct GNUNET_SET_IterResponseMessage *msg)
304{
305 struct GNUNET_SET_Handle *set = cls;
306 GNUNET_SET_ElementIterator iter = set->iterator;
307 struct GNUNET_SET_Element element;
308 struct GNUNET_SET_IterAckMessage *ack_msg;
309 struct GNUNET_MQ_Envelope *ev;
310 uint16_t msize;
311
312 LOG (GNUNET_ERROR_TYPE_DEBUG,
313 "Received element in set iteration\n");
314 msize = ntohs (msg->header.size);
315 if (set->iteration_id != ntohs (msg->iteration_id))
316 {
317 /* element from a previous iteration, skip! */
318 iter = NULL;
319 }
320 if (NULL != iter)
321 {
322 element.size = msize - sizeof(struct GNUNET_SET_IterResponseMessage);
323 element.element_type = ntohs (msg->element_type);
324 element.data = &msg[1];
325 iter (set->iterator_cls,
326 &element);
327 }
328 ev = GNUNET_MQ_msg (ack_msg,
329 GNUNET_MESSAGE_TYPE_SET_ITER_ACK);
330 ack_msg->send_more = htonl ((NULL != iter));
331 GNUNET_MQ_send (set->mq, ev);
332}
333
334
335/**
336 * Handle message signalling conclusion of iteration over the set.
337 * Notifies the iterator that we are done.
338 *
339 * @param cls the set
340 * @param mh the message
341 */
342static void
343handle_iter_done (void *cls,
344 const struct GNUNET_MessageHeader *mh)
345{
346 struct GNUNET_SET_Handle *set = cls;
347 GNUNET_SET_ElementIterator iter = set->iterator;
348
349 if (NULL == iter)
350 {
351 /* FIXME: if this is true, could cancel+start a fresh one
352 cause elements to go to the wrong iteration? */
353 LOG (GNUNET_ERROR_TYPE_INFO,
354 "Service completed set iteration that was already cancelled\n");
355 return;
356 }
357 LOG (GNUNET_ERROR_TYPE_DEBUG,
358 "Set iteration completed\n");
359 set->destroy_requested = GNUNET_SYSERR;
360 set->iterator = NULL;
361 set->iteration_id++;
362 iter (set->iterator_cls,
363 NULL);
364 if (GNUNET_SYSERR == set->destroy_requested)
365 set->destroy_requested = GNUNET_NO;
366 if (GNUNET_YES == set->destroy_requested)
367 GNUNET_SET_destroy (set);
368}
369
370
371/**
372 * Check that the given @a msg is well-formed.
373 *
374 * @param cls closure
375 * @param msg message to check
376 * @return #GNUNET_OK if message is well-formed
377 */
378static int
379check_result (void *cls,
380 const struct GNUNET_SET_ResultMessage *msg)
381{
382 /* minimum size was already checked, everything else is OK! */
383 return GNUNET_OK;
384}
385
386
387/**
388 * Handle result message for a set operation.
389 *
390 * @param cls the set
391 * @param mh the message
392 */
393static void
394handle_result (void *cls,
395 const struct GNUNET_SET_ResultMessage *msg)
396{
397 struct GNUNET_SET_Handle *set = cls;
398 struct GNUNET_SET_OperationHandle *oh;
399 struct GNUNET_SET_Element e;
400 enum GNUNET_SET_Status result_status;
401 int destroy_set;
402
403 GNUNET_assert (NULL != set->mq);
404 result_status = (enum GNUNET_SET_Status) ntohs (msg->result_status);
405 LOG (GNUNET_ERROR_TYPE_DEBUG,
406 "Got result message with status %d\n",
407 result_status);
408
409 oh = GNUNET_MQ_assoc_get (set->mq,
410 ntohl (msg->request_id));
411 if (NULL == oh)
412 {
413 /* 'oh' can be NULL if we canceled the operation, but the service
414 did not get the cancel message yet. */
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 "Ignoring result from canceled operation\n");
417 return;
418 }
419
420 switch (result_status)
421 {
422 case GNUNET_SET_STATUS_OK:
423 case GNUNET_SET_STATUS_ADD_LOCAL:
424 case GNUNET_SET_STATUS_ADD_REMOTE:
425 goto do_element;
426
427 case GNUNET_SET_STATUS_FAILURE:
428 case GNUNET_SET_STATUS_DONE:
429 goto do_final;
430
431 case GNUNET_SET_STATUS_HALF_DONE:
432 /* not used anymore */
433 GNUNET_assert (0);
434 }
435
436do_final:
437 LOG (GNUNET_ERROR_TYPE_DEBUG,
438 "Treating result as final status\n");
439 GNUNET_MQ_assoc_remove (set->mq,
440 ntohl (msg->request_id));
441 GNUNET_CONTAINER_DLL_remove (set->ops_head,
442 set->ops_tail,
443 oh);
444 /* Need to do this calculation _before_ the result callback,
445 as IF the application still has a valid set handle, it
446 may trigger destruction of the set during the callback. */
447 destroy_set = (GNUNET_YES == set->destroy_requested) &&
448 (NULL == set->ops_head);
449 if (NULL != oh->result_cb)
450 {
451 oh->result_cb (oh->result_cls,
452 NULL,
453 GNUNET_ntohll (msg->current_size),
454 result_status);
455 }
456 else
457 {
458 LOG (GNUNET_ERROR_TYPE_DEBUG,
459 "No callback for final status\n");
460 }
461 if (destroy_set)
462 GNUNET_SET_destroy (set);
463 GNUNET_free (oh);
464 return;
465
466do_element:
467 LOG (GNUNET_ERROR_TYPE_DEBUG,
468 "Treating result as element\n");
469 e.data = &msg[1];
470 e.size = ntohs (msg->header.size) - sizeof(struct GNUNET_SET_ResultMessage);
471 e.element_type = ntohs (msg->element_type);
472 if (NULL != oh->result_cb)
473 oh->result_cb (oh->result_cls,
474 &e,
475 GNUNET_ntohll (msg->current_size),
476 result_status);
477}
478
479
480/**
481 * Destroy the given set operation.
482 *
483 * @param oh set operation to destroy
484 */
485static void
486set_operation_destroy (struct GNUNET_SET_OperationHandle *oh)
487{
488 struct GNUNET_SET_Handle *set = oh->set;
489 struct GNUNET_SET_OperationHandle *h_assoc;
490
491 if (NULL != oh->conclude_mqm)
492 GNUNET_MQ_discard (oh->conclude_mqm);
493 /* is the operation already committed? */
494 if (NULL != set)
495 {
496 GNUNET_CONTAINER_DLL_remove (set->ops_head,
497 set->ops_tail,
498 oh);
499 h_assoc = GNUNET_MQ_assoc_remove (set->mq,
500 oh->request_id);
501 GNUNET_assert ((NULL == h_assoc) ||
502 (h_assoc == oh));
503 }
504 GNUNET_free (oh);
505}
506
507
508/**
509 * Cancel the given set operation. We need to send an explicit cancel
510 * message, as all operations one one set communicate using one
511 * handle.
512 *
513 * @param oh set operation to cancel
514 */
515void
516GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
517{
518 struct GNUNET_SET_Handle *set = oh->set;
519 struct GNUNET_SET_CancelMessage *m;
520 struct GNUNET_MQ_Envelope *mqm;
521
522 LOG (GNUNET_ERROR_TYPE_DEBUG,
523 "Cancelling SET operation\n");
524 if (NULL != set)
525 {
526 mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SET_CANCEL);
527 m->request_id = htonl (oh->request_id);
528 GNUNET_MQ_send (set->mq, mqm);
529 }
530 set_operation_destroy (oh);
531 if ((NULL != set) &&
532 (GNUNET_YES == set->destroy_requested) &&
533 (NULL == set->ops_head))
534 {
535 LOG (GNUNET_ERROR_TYPE_DEBUG,
536 "Destroying set after operation cancel\n");
537 GNUNET_SET_destroy (set);
538 }
539}
540
541
542/**
543 * We encountered an error communicating with the set service while
544 * performing a set operation. Report to the application.
545 *
546 * @param cls the `struct GNUNET_SET_Handle`
547 * @param error error code
548 */
549static void
550handle_client_set_error (void *cls,
551 enum GNUNET_MQ_Error error)
552{
553 struct GNUNET_SET_Handle *set = cls;
554 GNUNET_SET_ElementIterator iter = set->iterator;
555
556 LOG (GNUNET_ERROR_TYPE_ERROR,
557 "Handling client set error %d\n",
558 error);
559 while (NULL != set->ops_head)
560 {
561 if ((NULL != set->ops_head->result_cb) &&
562 (GNUNET_NO == set->destroy_requested))
563 set->ops_head->result_cb (set->ops_head->result_cls,
564 NULL,
565 0,
566 GNUNET_SET_STATUS_FAILURE);
567 set_operation_destroy (set->ops_head);
568 }
569 set->iterator = NULL;
570 set->iteration_id++;
571 set->invalid = GNUNET_YES;
572 if (NULL != iter)
573 iter (set->iterator_cls,
574 NULL);
575}
576
577
578/**
579 * FIXME.
580 */
581static struct GNUNET_SET_Handle *
582create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
583 enum GNUNET_SET_OperationType op,
584 const uint32_t *cookie)
585{
586 struct GNUNET_SET_Handle *set = GNUNET_new (struct GNUNET_SET_Handle);
587 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
588 GNUNET_MQ_hd_var_size (result,
589 GNUNET_MESSAGE_TYPE_SET_RESULT,
590 struct GNUNET_SET_ResultMessage,
591 set),
592 GNUNET_MQ_hd_var_size (iter_element,
593 GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT,
594 struct GNUNET_SET_IterResponseMessage,
595 set),
596 GNUNET_MQ_hd_fixed_size (iter_done,
597 GNUNET_MESSAGE_TYPE_SET_ITER_DONE,
598 struct GNUNET_MessageHeader,
599 set),
600 GNUNET_MQ_hd_fixed_size (copy_lazy,
601 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE,
602 struct GNUNET_SET_CopyLazyResponseMessage,
603 set),
604 GNUNET_MQ_handler_end ()
605 };
606 struct GNUNET_MQ_Envelope *mqm;
607 struct GNUNET_SET_CreateMessage *create_msg;
608 struct GNUNET_SET_CopyLazyConnectMessage *copy_msg;
609
610 set->cfg = cfg;
611 set->mq = GNUNET_CLIENT_connect (cfg,
612 "set",
613 mq_handlers,
614 &handle_client_set_error,
615 set);
616 if (NULL == set->mq)
617 {
618 GNUNET_free (set);
619 return NULL;
620 }
621 if (NULL == cookie)
622 {
623 LOG (GNUNET_ERROR_TYPE_DEBUG,
624 "Creating new set (operation %u)\n",
625 op);
626 mqm = GNUNET_MQ_msg (create_msg,
627 GNUNET_MESSAGE_TYPE_SET_CREATE);
628 create_msg->operation = htonl (op);
629 }
630 else
631 {
632 LOG (GNUNET_ERROR_TYPE_DEBUG,
633 "Creating new set (lazy copy)\n");
634 mqm = GNUNET_MQ_msg (copy_msg,
635 GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT);
636 copy_msg->cookie = *cookie;
637 }
638 GNUNET_MQ_send (set->mq,
639 mqm);
640 return set;
641}
642
643
644/**
645 * Create an empty set, supporting the specified operation.
646 *
647 * @param cfg configuration to use for connecting to the
648 * set service
649 * @param op operation supported by the set
650 * Note that the operation has to be specified
651 * beforehand, as certain set operations need to maintain
652 * data structures specific to the operation
653 * @return a handle to the set
654 */
655struct GNUNET_SET_Handle *
656GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
657 enum GNUNET_SET_OperationType op)
658{
659 struct GNUNET_SET_Handle *set;
660
661 set = create_internal (cfg,
662 op,
663 NULL);
664 LOG (GNUNET_ERROR_TYPE_DEBUG,
665 "Creating set %p for operation %d\n",
666 set,
667 op);
668 return set;
669}
670
671
672int
673GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
674 const struct GNUNET_SET_Element *element,
675 GNUNET_SET_Continuation cont,
676 void *cont_cls)
677{
678 struct GNUNET_MQ_Envelope *mqm;
679 struct GNUNET_SET_ElementMessage *msg;
680
681 LOG (GNUNET_ERROR_TYPE_DEBUG,
682 "adding element of type %u to set %p\n",
683 (unsigned int) element->element_type,
684 set);
685 GNUNET_assert (NULL != set);
686 if (GNUNET_YES == set->invalid)
687 {
688 if (NULL != cont)
689 cont (cont_cls);
690 return GNUNET_SYSERR;
691 }
692 mqm = GNUNET_MQ_msg_extra (msg,
693 element->size,
694 GNUNET_MESSAGE_TYPE_SET_ADD);
695 msg->element_type = htons (element->element_type);
696 GNUNET_memcpy (&msg[1],
697 element->data,
698 element->size);
699 GNUNET_MQ_notify_sent (mqm,
700 cont, cont_cls);
701 GNUNET_MQ_send (set->mq, mqm);
702 return GNUNET_OK;
703}
704
705
706int
707GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
708 const struct GNUNET_SET_Element *element,
709 GNUNET_SET_Continuation cont,
710 void *cont_cls)
711{
712 struct GNUNET_MQ_Envelope *mqm;
713 struct GNUNET_SET_ElementMessage *msg;
714
715 LOG (GNUNET_ERROR_TYPE_DEBUG,
716 "Removing element from set %p\n",
717 set);
718 if (GNUNET_YES == set->invalid)
719 {
720 if (NULL != cont)
721 cont (cont_cls);
722 return GNUNET_SYSERR;
723 }
724 mqm = GNUNET_MQ_msg_extra (msg,
725 element->size,
726 GNUNET_MESSAGE_TYPE_SET_REMOVE);
727 msg->element_type = htons (element->element_type);
728 GNUNET_memcpy (&msg[1],
729 element->data,
730 element->size);
731 GNUNET_MQ_notify_sent (mqm,
732 cont, cont_cls);
733 GNUNET_MQ_send (set->mq, mqm);
734 return GNUNET_OK;
735}
736
737
738/**
739 * Destroy the set handle if no operations are left, mark the set
740 * for destruction otherwise.
741 *
742 * @param set set handle to destroy
743 */
744void
745GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
746{
747 /* destroying set while iterator is active is currently
748 not supported; we should expand the API to allow
749 clients to explicitly cancel the iteration! */
750 GNUNET_assert (NULL != set);
751 if ((NULL != set->ops_head) ||
752 (NULL != set->iterator) ||
753 (GNUNET_SYSERR == set->destroy_requested))
754 {
755 LOG (GNUNET_ERROR_TYPE_DEBUG,
756 "Set operations are pending, delaying set destruction\n");
757 set->destroy_requested = GNUNET_YES;
758 return;
759 }
760 LOG (GNUNET_ERROR_TYPE_DEBUG,
761 "Really destroying set\n");
762 if (NULL != set->mq)
763 {
764 GNUNET_MQ_destroy (set->mq);
765 set->mq = NULL;
766 }
767 GNUNET_free (set);
768}
769
770
771struct GNUNET_SET_OperationHandle *
772GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
773 const struct GNUNET_HashCode *app_id,
774 const struct GNUNET_MessageHeader *context_msg,
775 enum GNUNET_SET_ResultMode result_mode,
776 struct GNUNET_SET_Option options[],
777 GNUNET_SET_ResultIterator result_cb,
778 void *result_cls)
779{
780 struct GNUNET_MQ_Envelope *mqm;
781 struct GNUNET_SET_OperationHandle *oh;
782 struct GNUNET_SET_EvaluateMessage *msg;
783 struct GNUNET_SET_Option *opt;
784
785 LOG (GNUNET_ERROR_TYPE_DEBUG,
786 "Client prepares set operation (%d)\n",
787 result_mode);
788 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
789 oh->result_cb = result_cb;
790 oh->result_cls = result_cls;
791 mqm = GNUNET_MQ_msg_nested_mh (msg,
792 GNUNET_MESSAGE_TYPE_SET_EVALUATE,
793 context_msg);
794 msg->app_id = *app_id;
795 msg->result_mode = htonl (result_mode);
796 msg->target_peer = *other_peer;
797 for (opt = options; opt->type != 0; opt++)
798 {
799 switch (opt->type)
800 {
801 case GNUNET_SET_OPTION_BYZANTINE:
802 msg->byzantine = GNUNET_YES;
803 msg->byzantine_lower_bound = opt->v.num;
804 break;
805
806 case GNUNET_SET_OPTION_FORCE_FULL:
807 msg->force_full = GNUNET_YES;
808 break;
809
810 case GNUNET_SET_OPTION_FORCE_DELTA:
811 msg->force_delta = GNUNET_YES;
812 break;
813
814 default:
815 LOG (GNUNET_ERROR_TYPE_ERROR,
816 "Option with type %d not recognized\n", (int) opt->type);
817 }
818 }
819 oh->conclude_mqm = mqm;
820 oh->request_id_addr = &msg->request_id;
821
822 return oh;
823}
824
825
826/**
827 * Connect to the set service in order to listen for requests.
828 *
829 * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
830 */
831static void
832listen_connect (void *cls);
833
834
835/**
836 * Check validity of request message for a listen operation
837 *
838 * @param cls the listen handle
839 * @param msg the message
840 * @return #GNUNET_OK if the message is well-formed
841 */
842static int
843check_request (void *cls,
844 const struct GNUNET_SET_RequestMessage *msg)
845{
846 const struct GNUNET_MessageHeader *context_msg;
847
848 if (ntohs (msg->header.size) == sizeof(*msg))
849 return GNUNET_OK; /* no context message is OK */
850 context_msg = GNUNET_MQ_extract_nested_mh (msg);
851 if (NULL == context_msg)
852 {
853 /* malformed context message is NOT ok */
854 GNUNET_break_op (0);
855 return GNUNET_SYSERR;
856 }
857 return GNUNET_OK;
858}
859
860
861/**
862 * Handle request message for a listen operation
863 *
864 * @param cls the listen handle
865 * @param msg the message
866 */
867static void
868handle_request (void *cls,
869 const struct GNUNET_SET_RequestMessage *msg)
870{
871 struct GNUNET_SET_ListenHandle *lh = cls;
872 struct GNUNET_SET_Request req;
873 const struct GNUNET_MessageHeader *context_msg;
874 struct GNUNET_MQ_Envelope *mqm;
875 struct GNUNET_SET_RejectMessage *rmsg;
876
877 LOG (GNUNET_ERROR_TYPE_DEBUG,
878 "Processing incoming operation request with id %u\n",
879 ntohl (msg->accept_id));
880 /* we got another valid request => reset the backoff */
881 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
882 req.accept_id = ntohl (msg->accept_id);
883 req.accepted = GNUNET_NO;
884 context_msg = GNUNET_MQ_extract_nested_mh (msg);
885 /* calling #GNUNET_SET_accept() in the listen cb will set req->accepted */
886 lh->listen_cb (lh->listen_cls,
887 &msg->peer_id,
888 context_msg,
889 &req);
890 if (GNUNET_YES == req.accepted)
891 return; /* the accept-case is handled in #GNUNET_SET_accept() */
892 LOG (GNUNET_ERROR_TYPE_DEBUG,
893 "Rejected request %u\n",
894 ntohl (msg->accept_id));
895 mqm = GNUNET_MQ_msg (rmsg,
896 GNUNET_MESSAGE_TYPE_SET_REJECT);
897 rmsg->accept_reject_id = msg->accept_id;
898 GNUNET_MQ_send (lh->mq, mqm);
899}
900
901
902/**
903 * Our connection with the set service encountered an error,
904 * re-initialize with exponential back-off.
905 *
906 * @param cls the `struct GNUNET_SET_ListenHandle *`
907 * @param error reason for the disconnect
908 */
909static void
910handle_client_listener_error (void *cls,
911 enum GNUNET_MQ_Error error)
912{
913 struct GNUNET_SET_ListenHandle *lh = cls;
914
915 LOG (GNUNET_ERROR_TYPE_DEBUG,
916 "Listener broke down (%d), re-connecting\n",
917 (int) error);
918 GNUNET_MQ_destroy (lh->mq);
919 lh->mq = NULL;
920 lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
921 &listen_connect,
922 lh);
923 lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
924}
925
926
927/**
928 * Connect to the set service in order to listen for requests.
929 *
930 * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
931 */
932static void
933listen_connect (void *cls)
934{
935 struct GNUNET_SET_ListenHandle *lh = cls;
936 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
937 GNUNET_MQ_hd_var_size (request,
938 GNUNET_MESSAGE_TYPE_SET_REQUEST,
939 struct GNUNET_SET_RequestMessage,
940 lh),
941 GNUNET_MQ_handler_end ()
942 };
943 struct GNUNET_MQ_Envelope *mqm;
944 struct GNUNET_SET_ListenMessage *msg;
945
946 lh->reconnect_task = NULL;
947 GNUNET_assert (NULL == lh->mq);
948 lh->mq = GNUNET_CLIENT_connect (lh->cfg,
949 "set",
950 mq_handlers,
951 &handle_client_listener_error,
952 lh);
953 if (NULL == lh->mq)
954 return;
955 mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
956 msg->operation = htonl (lh->operation);
957 msg->app_id = lh->app_id;
958 GNUNET_MQ_send (lh->mq,
959 mqm);
960}
961
962
963/**
964 * Wait for set operation requests for the given application id
965 *
966 * @param cfg configuration to use for connecting to
967 * the set service, needs to be valid for the lifetime of the listen handle
968 * @param operation operation we want to listen for
969 * @param app_id id of the application that handles set operation requests
970 * @param listen_cb called for each incoming request matching the operation
971 * and application id
972 * @param listen_cls handle for @a listen_cb
973 * @return a handle that can be used to cancel the listen operation
974 */
975struct GNUNET_SET_ListenHandle *
976GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
977 enum GNUNET_SET_OperationType operation,
978 const struct GNUNET_HashCode *app_id,
979 GNUNET_SET_ListenCallback listen_cb,
980 void *listen_cls)
981{
982 struct GNUNET_SET_ListenHandle *lh;
983
984 LOG (GNUNET_ERROR_TYPE_DEBUG,
985 "Starting listener for app %s\n",
986 GNUNET_h2s (app_id));
987 lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
988 lh->listen_cb = listen_cb;
989 lh->listen_cls = listen_cls;
990 lh->cfg = cfg;
991 lh->operation = operation;
992 lh->app_id = *app_id;
993 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
994 listen_connect (lh);
995 if (NULL == lh->mq)
996 {
997 GNUNET_free (lh);
998 return NULL;
999 }
1000 return lh;
1001}
1002
1003
1004/**
1005 * Cancel the given listen operation.
1006 *
1007 * @param lh handle for the listen operation
1008 */
1009void
1010GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
1011{
1012 LOG (GNUNET_ERROR_TYPE_DEBUG,
1013 "Canceling listener %s\n",
1014 GNUNET_h2s (&lh->app_id));
1015 if (NULL != lh->mq)
1016 {
1017 GNUNET_MQ_destroy (lh->mq);
1018 lh->mq = NULL;
1019 }
1020 if (NULL != lh->reconnect_task)
1021 {
1022 GNUNET_SCHEDULER_cancel (lh->reconnect_task);
1023 lh->reconnect_task = NULL;
1024 }
1025 GNUNET_free (lh);
1026}
1027
1028
1029struct GNUNET_SET_OperationHandle *
1030GNUNET_SET_accept (struct GNUNET_SET_Request *request,
1031 enum GNUNET_SET_ResultMode result_mode,
1032 struct GNUNET_SET_Option options[],
1033 GNUNET_SET_ResultIterator result_cb,
1034 void *result_cls)
1035{
1036 struct GNUNET_MQ_Envelope *mqm;
1037 struct GNUNET_SET_OperationHandle *oh;
1038 struct GNUNET_SET_AcceptMessage *msg;
1039
1040 GNUNET_assert (GNUNET_NO == request->accepted);
1041 LOG (GNUNET_ERROR_TYPE_DEBUG,
1042 "Client accepts set operation (%d) with id %u\n",
1043 result_mode,
1044 request->accept_id);
1045 request->accepted = GNUNET_YES;
1046 mqm = GNUNET_MQ_msg (msg,
1047 GNUNET_MESSAGE_TYPE_SET_ACCEPT);
1048 msg->accept_reject_id = htonl (request->accept_id);
1049 msg->result_mode = htonl (result_mode);
1050 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
1051 oh->result_cb = result_cb;
1052 oh->result_cls = result_cls;
1053 oh->conclude_mqm = mqm;
1054 oh->request_id_addr = &msg->request_id;
1055 return oh;
1056}
1057
1058
1059/**
1060 * Commit a set to be used with a set operation.
1061 * This function is called once we have fully constructed
1062 * the set that we want to use for the operation. At this
1063 * time, the P2P protocol can then begin to exchange the
1064 * set information and call the result callback with the
1065 * result information.
1066 *
1067 * @param oh handle to the set operation
1068 * @param set the set to use for the operation
1069 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
1070 * set is invalid (e.g. the set service crashed)
1071 */
1072int
1073GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
1074 struct GNUNET_SET_Handle *set)
1075{
1076 if (NULL != oh->set)
1077 {
1078 /* Some other set was already committed for this
1079 * operation, there is a logic bug in the client of this API */
1080 GNUNET_break (0);
1081 return GNUNET_OK;
1082 }
1083 GNUNET_assert (NULL != set);
1084 if (GNUNET_YES == set->invalid)
1085 return GNUNET_SYSERR;
1086 LOG (GNUNET_ERROR_TYPE_DEBUG,
1087 "Client commits to SET\n");
1088 GNUNET_assert (NULL != oh->conclude_mqm);
1089 oh->set = set;
1090 GNUNET_CONTAINER_DLL_insert (set->ops_head,
1091 set->ops_tail,
1092 oh);
1093 oh->request_id = GNUNET_MQ_assoc_add (set->mq,
1094 oh);
1095 *oh->request_id_addr = htonl (oh->request_id);
1096 GNUNET_MQ_send (set->mq,
1097 oh->conclude_mqm);
1098 oh->conclude_mqm = NULL;
1099 oh->request_id_addr = NULL;
1100 return GNUNET_OK;
1101}
1102
1103
1104/**
1105 * Iterate over all elements in the given set. Note that this
1106 * operation involves transferring every element of the set from the
1107 * service to the client, and is thus costly.
1108 *
1109 * @param set the set to iterate over
1110 * @param iter the iterator to call for each element
1111 * @param iter_cls closure for @a iter
1112 * @return #GNUNET_YES if the iteration started successfully,
1113 * #GNUNET_NO if another iteration is active
1114 * #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
1115 */
1116int
1117GNUNET_SET_iterate (struct GNUNET_SET_Handle *set,
1118 GNUNET_SET_ElementIterator iter,
1119 void *iter_cls)
1120{
1121 struct GNUNET_MQ_Envelope *ev;
1122
1123 GNUNET_assert (NULL != iter);
1124 if (GNUNET_YES == set->invalid)
1125 return GNUNET_SYSERR;
1126 if (NULL != set->iterator)
1127 return GNUNET_NO;
1128 LOG (GNUNET_ERROR_TYPE_DEBUG,
1129 "Iterating over set\n");
1130 set->iterator = iter;
1131 set->iterator_cls = iter_cls;
1132 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
1133 GNUNET_MQ_send (set->mq, ev);
1134 return GNUNET_YES;
1135}
1136
1137
1138void
1139GNUNET_SET_copy_lazy (struct GNUNET_SET_Handle *set,
1140 GNUNET_SET_CopyReadyCallback cb,
1141 void *cls)
1142{
1143 struct GNUNET_MQ_Envelope *ev;
1144 struct SetCopyRequest *req;
1145
1146 LOG (GNUNET_ERROR_TYPE_DEBUG,
1147 "Creating lazy copy of set\n");
1148 ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE);
1149 GNUNET_MQ_send (set->mq, ev);
1150
1151 req = GNUNET_new (struct SetCopyRequest);
1152 req->cb = cb;
1153 req->cls = cls;
1154 GNUNET_CONTAINER_DLL_insert (set->copy_req_head,
1155 set->copy_req_tail,
1156 req);
1157}
1158
1159
1160/**
1161 * Create a copy of an element. The copy
1162 * must be GNUNET_free-d by the caller.
1163 *
1164 * @param element the element to copy
1165 * @return the copied element
1166 */
1167struct GNUNET_SET_Element *
1168GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element)
1169{
1170 struct GNUNET_SET_Element *copy;
1171
1172 copy = GNUNET_malloc (element->size + sizeof(struct GNUNET_SET_Element));
1173 copy->size = element->size;
1174 copy->element_type = element->element_type;
1175 copy->data = &copy[1];
1176 GNUNET_memcpy (&copy[1],
1177 element->data,
1178 copy->size);
1179 return copy;
1180}
1181
1182
1183void
1184GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element,
1185 struct GNUNET_HashCode *ret_hash)
1186{
1187 struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
1188
1189 /* It's not guaranteed that the element data is always after the element header,
1190 so we need to hash the chunks separately. */
1191 GNUNET_CRYPTO_hash_context_read (ctx, &element->size, sizeof(uint16_t));
1192 GNUNET_CRYPTO_hash_context_read (ctx, &element->element_type,
1193 sizeof(uint16_t));
1194 GNUNET_CRYPTO_hash_context_read (ctx, element->data, element->size);
1195 GNUNET_CRYPTO_hash_context_finish (ctx, ret_hash);
1196}
1197
1198
1199/* end of set_api.c */
diff --git a/src/contrib/service/set/test_set.conf b/src/contrib/service/set/test_set.conf
new file mode 100644
index 000000000..67d6f1bb7
--- /dev/null
+++ b/src/contrib/service/set/test_set.conf
@@ -0,0 +1,33 @@
1@INLINE@ ../../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-set/
5
6[set]
7START_ON_DEMAND = YES
8#PREFIX = valgrind --leak-check=full
9#PREFIX = gdbserver :1234
10OPTIONS = -L INFO
11
12[transport]
13PLUGINS = unix
14OPTIONS = -LERROR
15
16[nat]
17RETURN_LOCAL_ADDRESSES = YES
18DISABLEV6 = YES
19USE_LOCALADDR = YES
20
21[peerinfo]
22NO_IO = YES
23
24[nat]
25# Use addresses from the local network interfaces (including loopback, but also others)
26USE_LOCALADDR = YES
27
28# Disable IPv6 support
29DISABLEV6 = NO
30
31# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8)
32RETURN_LOCAL_ADDRESSES = YES
33
diff --git a/src/contrib/service/set/test_set_api.c b/src/contrib/service/set/test_set_api.c
new file mode 100644
index 000000000..d1afdd354
--- /dev/null
+++ b/src/contrib/service/set/test_set_api.c
@@ -0,0 +1,403 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_api.c
23 * @brief testcase for set_api.c
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29#include "gnunet_set_service.h"
30
31
32static struct GNUNET_PeerIdentity local_id;
33
34static struct GNUNET_HashCode app_id;
35
36static struct GNUNET_SET_Handle *set1;
37
38static struct GNUNET_SET_Handle *set2;
39
40static struct GNUNET_SET_ListenHandle *listen_handle;
41
42static struct GNUNET_SET_OperationHandle *oh1;
43
44static struct GNUNET_SET_OperationHandle *oh2;
45
46static const struct GNUNET_CONFIGURATION_Handle *config;
47
48static unsigned int iter_count;
49
50static int ret;
51
52static struct GNUNET_SCHEDULER_Task *tt;
53
54
55static void
56result_cb_set1 (void *cls,
57 const struct GNUNET_SET_Element *element,
58 uint64_t size,
59 enum GNUNET_SET_Status status)
60{
61 switch (status)
62 {
63 case GNUNET_SET_STATUS_OK:
64 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: got element\n");
65 break;
66
67 case GNUNET_SET_STATUS_FAILURE:
68 GNUNET_break (0);
69 oh1 = NULL;
70 fprintf (stderr, "set 1: received failure status!\n");
71 ret = 1;
72 if (NULL != tt)
73 {
74 GNUNET_SCHEDULER_cancel (tt);
75 tt = NULL;
76 }
77 GNUNET_SCHEDULER_shutdown ();
78 break;
79
80 case GNUNET_SET_STATUS_DONE:
81 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: done\n");
82 oh1 = NULL;
83 if (NULL != set1)
84 {
85 GNUNET_SET_destroy (set1);
86 set1 = NULL;
87 }
88 if (NULL == set2)
89 {
90 GNUNET_SCHEDULER_cancel (tt);
91 tt = NULL;
92 GNUNET_SCHEDULER_shutdown ();
93 }
94 break;
95
96 default:
97 GNUNET_assert (0);
98 }
99}
100
101
102static void
103result_cb_set2 (void *cls,
104 const struct GNUNET_SET_Element *element,
105 uint64_t size,
106 enum GNUNET_SET_Status status)
107{
108 switch (status)
109 {
110 case GNUNET_SET_STATUS_OK:
111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: got element\n");
112 break;
113
114 case GNUNET_SET_STATUS_FAILURE:
115 GNUNET_break (0);
116 oh2 = NULL;
117 fprintf (stderr, "set 2: received failure status\n");
118 GNUNET_SCHEDULER_shutdown ();
119 ret = 1;
120 break;
121
122 case GNUNET_SET_STATUS_DONE:
123 oh2 = NULL;
124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: done\n");
125 GNUNET_SET_destroy (set2);
126 set2 = NULL;
127 if (NULL == set1)
128 {
129 GNUNET_SCHEDULER_cancel (tt);
130 tt = NULL;
131 GNUNET_SCHEDULER_shutdown ();
132 }
133 break;
134
135 default:
136 GNUNET_assert (0);
137 }
138}
139
140
141static void
142listen_cb (void *cls,
143 const struct GNUNET_PeerIdentity *other_peer,
144 const struct GNUNET_MessageHeader *context_msg,
145 struct GNUNET_SET_Request *request)
146{
147 GNUNET_assert (NULL != context_msg);
148 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "listen cb called\n");
150 oh2 = GNUNET_SET_accept (request,
151 GNUNET_SET_RESULT_ADDED,
152 (struct GNUNET_SET_Option[]){ 0 },
153 &result_cb_set2,
154 NULL);
155 GNUNET_SET_commit (oh2, set2);
156}
157
158
159/**
160 * Start the set operation.
161 *
162 * @param cls closure, unused
163 */
164static void
165start (void *cls)
166{
167 struct GNUNET_MessageHeader context_msg;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting reconciliation\n");
170 context_msg.size = htons (sizeof context_msg);
171 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
172 listen_handle = GNUNET_SET_listen (config,
173 GNUNET_SET_OPERATION_UNION,
174 &app_id,
175 &listen_cb,
176 NULL);
177 oh1 = GNUNET_SET_prepare (&local_id,
178 &app_id,
179 &context_msg,
180 GNUNET_SET_RESULT_ADDED,
181 (struct GNUNET_SET_Option[]){ 0 },
182 &result_cb_set1,
183 NULL);
184 GNUNET_SET_commit (oh1, set1);
185}
186
187
188/**
189 * Initialize the second set, continue
190 *
191 * @param cls closure, unused
192 */
193static void
194init_set2 (void *cls)
195{
196 struct GNUNET_SET_Element element;
197
198 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing set 2\n");
199
200 element.element_type = 0;
201 element.data = "hello";
202 element.size = strlen (element.data);
203 GNUNET_SET_add_element (set2, &element, NULL, NULL);
204 element.data = "quux";
205 element.size = strlen (element.data);
206 GNUNET_SET_add_element (set2, &element, NULL, NULL);
207 element.data = "baz";
208 element.size = strlen (element.data);
209 GNUNET_SET_add_element (set2, &element, &start, NULL);
210}
211
212
213/**
214 * Initialize the first set, continue.
215 */
216static void
217init_set1 (void)
218{
219 struct GNUNET_SET_Element element;
220
221 element.element_type = 0;
222 element.data = "hello";
223 element.size = strlen (element.data);
224 GNUNET_SET_add_element (set1, &element, NULL, NULL);
225 element.data = "bar";
226 element.size = strlen (element.data);
227 GNUNET_SET_add_element (set1, &element, &init_set2, NULL);
228 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initialized set 1\n");
229}
230
231
232static int
233iter_cb (void *cls, const struct GNUNET_SET_Element *element)
234{
235 struct GNUNET_SET_Handle *set = cls;
236
237 if (NULL == element)
238 {
239 GNUNET_assert (3 == iter_count);
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241 "Iteration finished, destroying set %p\n",
242 set);
243 GNUNET_SET_destroy (set);
244 return GNUNET_YES;
245 }
246 iter_count++;
247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "iter: got element %u\n", iter_count);
248 return GNUNET_YES;
249}
250
251
252static void
253test_iter ()
254{
255 struct GNUNET_SET_Element element;
256 struct GNUNET_SET_Handle *iter_set;
257
258 iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260 "Testing iteration over 3 elements on set %p\n",
261 iter_set);
262 element.element_type = 0;
263
264 element.data = "hello";
265 element.size = strlen (element.data);
266 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
267 element.data = "bar";
268 element.size = strlen (element.data);
269 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
270 element.data = "quux";
271 element.size = strlen (element.data);
272 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
273 GNUNET_SET_iterate (iter_set, &iter_cb, iter_set);
274}
275
276
277/**
278 * Function run on timeout.
279 *
280 * @param cls closure
281 */
282static void
283timeout_fail (void *cls)
284{
285 tt = NULL;
286 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Testcase failed with timeout\n");
287 GNUNET_SCHEDULER_shutdown ();
288 ret = 1;
289}
290
291
292/**
293 * Function run on shutdown.
294 *
295 * @param cls closure
296 */
297static void
298do_shutdown (void *cls)
299{
300 if (NULL != tt)
301 {
302 GNUNET_SCHEDULER_cancel (tt);
303 tt = NULL;
304 }
305 if (NULL != oh1)
306 {
307 GNUNET_SET_operation_cancel (oh1);
308 oh1 = NULL;
309 }
310 if (NULL != oh2)
311 {
312 GNUNET_SET_operation_cancel (oh2);
313 oh2 = NULL;
314 }
315 if (NULL != set1)
316 {
317 GNUNET_SET_destroy (set1);
318 set1 = NULL;
319 }
320 if (NULL != set2)
321 {
322 GNUNET_SET_destroy (set2);
323 set2 = NULL;
324 }
325 if (NULL != listen_handle)
326 {
327 GNUNET_SET_listen_cancel (listen_handle);
328 listen_handle = NULL;
329 }
330}
331
332
333/**
334 * Signature of the 'main' function for a (single-peer) testcase that
335 * is run using 'GNUNET_TESTING_peer_run'.
336 *
337 * @param cls closure
338 * @param cfg configuration of the peer that was started
339 * @param peer identity of the peer that was created
340 */
341static void
342run (void *cls,
343 const struct GNUNET_CONFIGURATION_Handle *cfg,
344 struct GNUNET_TESTING_Peer *peer)
345{
346 struct GNUNET_SET_OperationHandle *my_oh;
347
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running preparatory tests\n");
349 tt = GNUNET_SCHEDULER_add_delayed (
350 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5),
351 &timeout_fail,
352 NULL);
353 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
354
355 config = cfg;
356 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_get_peer_identity (cfg, &local_id));
357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
358 "my id (from CRYPTO): %s\n",
359 GNUNET_i2s (&local_id));
360 GNUNET_TESTING_peer_get_identity (peer, &local_id);
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362 "my id (from TESTING): %s\n",
363 GNUNET_i2s (&local_id));
364 test_iter ();
365
366 set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
367 set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "Created sets %p and %p for union operation\n",
370 set1,
371 set2);
372 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id);
373
374 /* test if canceling an uncommitted request works! */
375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376 "Launching and instantly stopping set operation\n");
377 my_oh = GNUNET_SET_prepare (&local_id,
378 &app_id,
379 NULL,
380 GNUNET_SET_RESULT_ADDED,
381 (struct GNUNET_SET_Option[]){ 0 },
382 NULL,
383 NULL);
384 GNUNET_SET_operation_cancel (my_oh);
385
386 /* test the real set reconciliation */
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running real set-reconciliation\n");
388 init_set1 ();
389}
390
391
392int
393main (int argc, char **argv)
394{
395 GNUNET_log_setup ("test_set_api", "WARNING", NULL);
396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Launching peer\n");
397 if (0 !=
398 GNUNET_TESTING_peer_run ("test_set_api", "test_set.conf", &run, NULL))
399 {
400 return 1;
401 }
402 return ret;
403}
diff --git a/src/contrib/service/set/test_set_intersection_result_full.c b/src/contrib/service/set/test_set_intersection_result_full.c
new file mode 100644
index 000000000..42dedb846
--- /dev/null
+++ b/src/contrib/service/set/test_set_intersection_result_full.c
@@ -0,0 +1,393 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_intersection_result_full.c
23 * @brief testcase for full result mode of the intersection set operation
24 * @author Christian Fuchs
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33static int ret;
34
35static struct GNUNET_PeerIdentity local_id;
36
37static struct GNUNET_HashCode app_id;
38
39static struct GNUNET_SET_Handle *set1;
40
41static struct GNUNET_SET_Handle *set2;
42
43static struct GNUNET_SET_ListenHandle *listen_handle;
44
45static const struct GNUNET_CONFIGURATION_Handle *config;
46
47static int iter_count;
48
49static struct GNUNET_SCHEDULER_Task *tt;
50
51static struct GNUNET_SET_OperationHandle *oh1;
52
53static struct GNUNET_SET_OperationHandle *oh2;
54
55
56static void
57result_cb_set1 (void *cls,
58 const struct GNUNET_SET_Element *element,
59 uint64_t current_size,
60 enum GNUNET_SET_Status status)
61{
62 static int count;
63
64 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
65 "Processing result set 1 (%d)\n",
66 status);
67 switch (status)
68 {
69 case GNUNET_SET_STATUS_OK:
70 count++;
71 break;
72
73 case GNUNET_SET_STATUS_FAILURE:
74 oh1 = NULL;
75 ret = 1;
76 break;
77
78 case GNUNET_SET_STATUS_DONE:
79 oh1 = NULL;
80 GNUNET_assert (1 == count);
81 GNUNET_SET_destroy (set1);
82 set1 = NULL;
83 if (NULL == set2)
84 GNUNET_SCHEDULER_shutdown ();
85 break;
86
87 default:
88 GNUNET_assert (0);
89 }
90}
91
92
93static void
94result_cb_set2 (void *cls,
95 const struct GNUNET_SET_Element *element,
96 uint64_t current_size,
97 enum GNUNET_SET_Status status)
98{
99 static int count;
100
101 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
102 "Processing result set 2 (%d)\n",
103 status);
104 switch (status)
105 {
106 case GNUNET_SET_STATUS_OK:
107 count++;
108 break;
109
110 case GNUNET_SET_STATUS_FAILURE:
111 oh2 = NULL;
112 ret = 1;
113 break;
114
115 case GNUNET_SET_STATUS_DONE:
116 oh2 = NULL;
117 GNUNET_assert (1 == count);
118 GNUNET_SET_destroy (set2);
119 set2 = NULL;
120 if (NULL == set1)
121 GNUNET_SCHEDULER_shutdown ();
122 break;
123
124 default:
125 GNUNET_assert (0);
126 }
127}
128
129
130static void
131listen_cb (void *cls,
132 const struct GNUNET_PeerIdentity *other_peer,
133 const struct GNUNET_MessageHeader *context_msg,
134 struct GNUNET_SET_Request *request)
135{
136 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
137 "starting intersection by accepting and committing\n");
138 GNUNET_assert (NULL != context_msg);
139 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
140 oh2 = GNUNET_SET_accept (request,
141 GNUNET_SET_RESULT_FULL,
142 (struct GNUNET_SET_Option[]) { 0 },
143 &result_cb_set2,
144 NULL);
145 GNUNET_SET_commit (oh2,
146 set2);
147}
148
149
150/**
151 * Start the set operation.
152 *
153 * @param cls closure, unused
154 */
155static void
156start (void *cls)
157{
158 struct GNUNET_MessageHeader context_msg;
159
160 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
161 "starting listener\n");
162 context_msg.size = htons (sizeof context_msg);
163 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
164 listen_handle = GNUNET_SET_listen (config,
165 GNUNET_SET_OPERATION_INTERSECTION,
166 &app_id,
167 &listen_cb,
168 NULL);
169 oh1 = GNUNET_SET_prepare (&local_id,
170 &app_id,
171 &context_msg,
172 GNUNET_SET_RESULT_FULL,
173 (struct GNUNET_SET_Option[]) { 0 },
174 &result_cb_set1,
175 NULL);
176 GNUNET_SET_commit (oh1,
177 set1);
178}
179
180
181/**
182 * Initialize the second set, continue
183 *
184 * @param cls closure, unused
185 */
186static void
187init_set2 (void *cls)
188{
189 struct GNUNET_SET_Element element;
190
191 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
192 "initializing set 2\n");
193 element.element_type = 0;
194 element.data = "hello";
195 element.size = strlen (element.data);
196 GNUNET_SET_add_element (set2,
197 &element,
198 NULL,
199 NULL);
200 element.data = "quux";
201 element.size = strlen (element.data);
202 GNUNET_SET_add_element (set2,
203 &element,
204 NULL,
205 NULL);
206 element.data = "baz";
207 element.size = strlen (element.data);
208 GNUNET_SET_add_element (set2,
209 &element,
210 &start,
211 NULL);
212}
213
214
215/**
216 * Initialize the first set, continue.
217 */
218static void
219init_set1 (void)
220{
221 struct GNUNET_SET_Element element;
222
223 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
224 "initializing set 1\n");
225 element.element_type = 0;
226 element.data = "hello";
227 element.size = strlen (element.data);
228 GNUNET_SET_add_element (set1,
229 &element,
230 NULL,
231 NULL);
232 element.data = "bar";
233 element.size = strlen (element.data);
234 GNUNET_SET_add_element (set1,
235 &element,
236 &init_set2,
237 NULL);
238}
239
240
241static int
242iter_cb (void *cls,
243 const struct GNUNET_SET_Element *element)
244{
245 if (NULL == element)
246 {
247 GNUNET_assert (iter_count == 3);
248 GNUNET_SET_destroy (cls);
249 return GNUNET_YES;
250 }
251 iter_count++;
252 return GNUNET_YES;
253}
254
255
256static void
257test_iter ()
258{
259 struct GNUNET_SET_Element element;
260 struct GNUNET_SET_Handle *iter_set;
261
262 iter_set = GNUNET_SET_create (config,
263 GNUNET_SET_OPERATION_INTERSECTION);
264 element.element_type = 0;
265 element.data = "hello";
266 element.size = strlen (element.data);
267 GNUNET_SET_add_element (iter_set,
268 &element,
269 NULL,
270 NULL);
271 element.data = "bar";
272 element.size = strlen (element.data);
273 GNUNET_SET_add_element (iter_set,
274 &element,
275 NULL,
276 NULL);
277 element.data = "quux";
278 element.size = strlen (element.data);
279 GNUNET_SET_add_element (iter_set,
280 &element,
281 NULL,
282 NULL);
283 GNUNET_SET_iterate (iter_set,
284 &iter_cb,
285 iter_set);
286}
287
288
289/**
290 * Function run on shutdown.
291 *
292 * @param cls closure
293 */
294static void
295do_shutdown (void *cls)
296{
297 if (NULL != tt)
298 {
299 GNUNET_SCHEDULER_cancel (tt);
300 tt = NULL;
301 }
302 if (NULL != oh1)
303 {
304 GNUNET_SET_operation_cancel (oh1);
305 oh1 = NULL;
306 }
307 if (NULL != oh2)
308 {
309 GNUNET_SET_operation_cancel (oh2);
310 oh2 = NULL;
311 }
312 if (NULL != set1)
313 {
314 GNUNET_SET_destroy (set1);
315 set1 = NULL;
316 }
317 if (NULL != set2)
318 {
319 GNUNET_SET_destroy (set2);
320 set2 = NULL;
321 }
322 if (NULL != listen_handle)
323 {
324 GNUNET_SET_listen_cancel (listen_handle);
325 listen_handle = NULL;
326 }
327}
328
329
330/**
331 * Function run on timeout.
332 *
333 * @param cls closure
334 */
335static void
336timeout_fail (void *cls)
337{
338 tt = NULL;
339 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
340 "Testcase failed with timeout\n");
341 GNUNET_SCHEDULER_shutdown ();
342 ret = 1;
343}
344
345
346/**
347 * Signature of the 'main' function for a (single-peer) testcase that
348 * is run using 'GNUNET_TESTING_peer_run'.
349 *
350 * @param cls closure
351 * @param cfg configuration of the peer that was started
352 * @param peer identity of the peer that was created
353 */
354static void
355run (void *cls,
356 const struct GNUNET_CONFIGURATION_Handle *cfg,
357 struct GNUNET_TESTING_Peer *peer)
358{
359 config = cfg;
360 GNUNET_TESTING_peer_get_identity (peer,
361 &local_id);
362 if (0)
363 test_iter ();
364
365 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
366 GNUNET_TIME_UNIT_SECONDS, 5),
367 &timeout_fail,
368 NULL);
369 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
370 NULL);
371
372 set1 = GNUNET_SET_create (cfg,
373 GNUNET_SET_OPERATION_INTERSECTION);
374 set2 = GNUNET_SET_create (cfg,
375 GNUNET_SET_OPERATION_INTERSECTION);
376 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
377 &app_id);
378
379 /* test the real set reconciliation */
380 init_set1 ();
381}
382
383
384int
385main (int argc,
386 char **argv)
387{
388 if (0 != GNUNET_TESTING_peer_run ("test_set_intersection_result_full",
389 "test_set.conf",
390 &run, NULL))
391 return 1;
392 return ret;
393}
diff --git a/src/contrib/service/set/test_set_union_copy.c b/src/contrib/service/set/test_set_union_copy.c
new file mode 100644
index 000000000..908527017
--- /dev/null
+++ b/src/contrib/service/set/test_set_union_copy.c
@@ -0,0 +1,311 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2015, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_union_copy.c
23 * @brief testcase for lazy copying of union sets
24 * @author Florian Dold
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_common.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33/**
34 * Value to return from #main().
35 */
36static int ret;
37
38static struct GNUNET_PeerIdentity local_id;
39
40static struct GNUNET_SET_Handle *set1;
41
42static struct GNUNET_SET_Handle *set2;
43
44static const struct GNUNET_CONFIGURATION_Handle *config;
45
46static struct GNUNET_SCHEDULER_Task *tt;
47
48
49static void
50add_element_str (struct GNUNET_SET_Handle *set,
51 char *str)
52{
53 struct GNUNET_SET_Element element;
54
55 element.element_type = 0;
56 element.data = str;
57 element.size = strlen (str);
58 GNUNET_SET_add_element (set,
59 &element,
60 NULL,
61 NULL);
62}
63
64
65static void
66remove_element_str (struct GNUNET_SET_Handle *set,
67 char *str)
68{
69 struct GNUNET_SET_Element element;
70
71 element.element_type = 0;
72 element.data = str;
73 element.size = strlen (str);
74 GNUNET_SET_remove_element (set,
75 &element,
76 NULL,
77 NULL);
78}
79
80
81/**
82 * Signature of the main function of a task.
83 *
84 * @param cls closure
85 */
86static void
87timeout_fail (void *cls)
88{
89 tt = NULL;
90 GNUNET_SCHEDULER_shutdown ();
91 ret = 1;
92}
93
94
95struct CountIterClosure
96{
97 unsigned int expected_count;
98 unsigned int ongoing_count;
99 GNUNET_SCHEDULER_TaskCallback cont;
100 void *cont_cls;
101 char *what;
102};
103
104
105static int
106check_count_iter (void *cls,
107 const struct GNUNET_SET_Element *element)
108{
109 struct CountIterClosure *ci_cls = cls;
110
111 if (NULL == element)
112 {
113 if (ci_cls->expected_count != ci_cls->ongoing_count)
114 {
115 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116 "Expected count (what: %s) to be %u, but it's actually %u\n",
117 ci_cls->what,
118 ci_cls->expected_count,
119 ci_cls->ongoing_count);
120 ret = 1;
121 GNUNET_SCHEDULER_shutdown ();
122 return GNUNET_NO;
123 }
124 ci_cls->cont (ci_cls->cont_cls);
125 GNUNET_free (ci_cls);
126 return GNUNET_NO;
127 }
128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
129 "Set `%s' has element %.*s\n",
130 ci_cls->what,
131 (int) element->size,
132 (const char *) element->data);
133
134 ci_cls->ongoing_count++;
135 return GNUNET_YES;
136}
137
138
139static void
140check_count (struct GNUNET_SET_Handle *set,
141 char *what,
142 unsigned int expected_count,
143 GNUNET_SCHEDULER_TaskCallback cont,
144 void *cont_cls)
145{
146 struct CountIterClosure *ci_cls = GNUNET_new (struct CountIterClosure);
147
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Checking count of %s\n",
150 what);
151
152 ci_cls->expected_count = expected_count;
153 ci_cls->ongoing_count = 0;
154 ci_cls->cont = cont;
155 ci_cls->cont_cls = cont_cls;
156 ci_cls->what = what;
157
158 GNUNET_assert (GNUNET_YES ==
159 GNUNET_SET_iterate (set,
160 &check_count_iter,
161 ci_cls));
162}
163
164
165static void
166test_done (void *cls)
167{
168 GNUNET_SCHEDULER_shutdown ();
169}
170
171
172static void
173check_new_set_count (void *cls)
174{
175 check_count (set2,
176 "new set",
177 3,
178 &test_done,
179 NULL);
180}
181
182
183static void
184copy_done (void *cls,
185 struct GNUNET_SET_Handle *new_set)
186{
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "copy done\n");
189 set2 = new_set;
190 remove_element_str (set2,
191 "k5555");
192 add_element_str (set2,
193 "n66666");
194 add_element_str (set2,
195 "new2butremoved");
196 remove_element_str (set2,
197 "new2butremoved");
198 remove_element_str (set2,
199 "new3justremoved");
200 // Check that set1 didn't change.
201 check_count (set1,
202 "old set",
203 3,
204 &check_new_set_count,
205 NULL);
206}
207
208
209static void
210test_copy (void *cls)
211{
212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213 "about to copy\n");
214 GNUNET_SET_copy_lazy (set1,
215 &copy_done,
216 NULL);
217}
218
219
220/**
221 * Function run on shutdown.
222 *
223 * @param cls closure
224 */
225static void
226do_shutdown (void *cls)
227{
228 if (NULL != tt)
229 {
230 GNUNET_SCHEDULER_cancel (tt);
231 tt = NULL;
232 }
233 if (NULL != set1)
234 {
235 GNUNET_SET_destroy (set1);
236 set1 = NULL;
237 }
238 if (NULL != set2)
239 {
240 GNUNET_SET_destroy (set2);
241 set2 = NULL;
242 }
243}
244
245
246/**
247 * Signature of the 'main' function for a (single-peer) testcase that
248 * is run using #GNUNET_TESTING_peer_run().
249 *
250 * @param cls closure
251 * @param cfg configuration of the peer that was started
252 * @param peer identity of the peer that was created
253 */
254static void
255run (void *cls,
256 const struct GNUNET_CONFIGURATION_Handle *cfg,
257 struct GNUNET_TESTING_Peer *peer)
258{
259 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
260 GNUNET_TIME_UNIT_SECONDS, 5),
261 &timeout_fail,
262 NULL);
263 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
264 NULL);
265 config = cfg;
266 GNUNET_TESTING_peer_get_identity (peer,
267 &local_id);
268
269 set1 = GNUNET_SET_create (cfg,
270 GNUNET_SET_OPERATION_UNION);
271 add_element_str (set1,
272 "333");
273 add_element_str (set1,
274 "k444");
275 /* duplicate -- ignored */
276 add_element_str (set1,
277 "k444");
278 remove_element_str (set1,
279 "333");
280 /* non-existent -- ignored */
281 remove_element_str (set1,
282 "999999999");
283 add_element_str (set1,
284 "k5555");
285 /* duplicate -- ignored */
286 remove_element_str (set1,
287 "333");
288 add_element_str (set1,
289 "k2");
290
291 check_count (set1,
292 "initial test",
293 3,
294 &test_copy,
295 NULL);
296}
297
298
299int
300main (int argc, char **argv)
301{
302 if (0 != GNUNET_TESTING_peer_run ("test_set_union_copy",
303 "test_set.conf",
304 &run, NULL))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 "failed to start testing peer\n");
308 return 1;
309 }
310 return ret;
311}
diff --git a/src/contrib/service/set/test_set_union_result_symmetric.c b/src/contrib/service/set/test_set_union_result_symmetric.c
new file mode 100644
index 000000000..b6c7a82f6
--- /dev/null
+++ b/src/contrib/service/set/test_set_union_result_symmetric.c
@@ -0,0 +1,455 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file set/test_set_union_result_smmetric
23 * @brief testcase for symmetric result mode of the union set operation
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testing_lib.h"
30#include "gnunet_set_service.h"
31
32
33/**
34 * Value to return from #main().
35 */
36static int ret;
37
38static struct GNUNET_PeerIdentity local_id;
39
40static struct GNUNET_HashCode app_id;
41
42static struct GNUNET_SET_Handle *set1;
43
44static struct GNUNET_SET_Handle *set2;
45
46static struct GNUNET_SET_ListenHandle *listen_handle;
47
48static const struct GNUNET_CONFIGURATION_Handle *config;
49
50static struct GNUNET_SET_OperationHandle *oh1;
51
52static struct GNUNET_SET_OperationHandle *oh2;
53
54static int iter_count;
55
56/**
57 * Are we testing correctness for the empty set union?
58 */
59static int empty;
60
61/**
62 * Number of elements found in set 1
63 */
64static unsigned int count_set1;
65
66/**
67 * Number of elements found in set 2
68 */
69static unsigned int count_set2;
70
71/**
72 * Task that is run when the test times out.
73 */
74static struct GNUNET_SCHEDULER_Task *timeout_task;
75
76
77static void
78result_cb_set1 (void *cls,
79 const struct GNUNET_SET_Element *element,
80 uint64_t current_size,
81 enum GNUNET_SET_Status status)
82{
83 switch (status)
84 {
85 case GNUNET_SET_STATUS_ADD_LOCAL:
86 count_set1++;
87 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88 "set 1: got element\n");
89 break;
90
91 case GNUNET_SET_STATUS_FAILURE:
92 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
93 "set 1: failure\n");
94 oh1 = NULL;
95 ret = 1;
96 if (NULL != timeout_task)
97 {
98 GNUNET_SCHEDULER_cancel (timeout_task);
99 timeout_task = NULL;
100 }
101 GNUNET_SCHEDULER_shutdown ();
102 break;
103
104 case GNUNET_SET_STATUS_DONE:
105 oh1 = NULL;
106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
107 "set 1: done\n");
108 GNUNET_SET_destroy (set1);
109 set1 = NULL;
110 if (NULL == set2)
111 {
112 if (NULL != timeout_task)
113 {
114 GNUNET_SCHEDULER_cancel (timeout_task);
115 timeout_task = NULL;
116 }
117 GNUNET_SCHEDULER_shutdown ();
118 }
119 break;
120
121 case GNUNET_SET_STATUS_ADD_REMOTE:
122 break;
123
124 default:
125 GNUNET_assert (0);
126 }
127}
128
129
130static void
131result_cb_set2 (void *cls,
132 const struct GNUNET_SET_Element *element,
133 uint64_t current_size,
134 enum GNUNET_SET_Status status)
135{
136 switch (status)
137 {
138 case GNUNET_SET_STATUS_ADD_LOCAL:
139 count_set2++;
140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
141 "set 2: got element\n");
142 break;
143
144 case GNUNET_SET_STATUS_FAILURE:
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
146 "set 2: failure\n");
147 oh2 = NULL;
148 ret = 1;
149 if (NULL != timeout_task)
150 {
151 GNUNET_SCHEDULER_cancel (timeout_task);
152 timeout_task = NULL;
153 }
154 GNUNET_SCHEDULER_shutdown ();
155 break;
156
157 case GNUNET_SET_STATUS_DONE:
158 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
159 "set 2: done\n");
160 oh2 = NULL;
161 GNUNET_SET_destroy (set2);
162 set2 = NULL;
163 if (NULL == set1)
164 {
165 if (NULL != timeout_task)
166 {
167 GNUNET_SCHEDULER_cancel (timeout_task);
168 timeout_task = NULL;
169 }
170 GNUNET_SCHEDULER_shutdown ();
171 }
172 break;
173
174 case GNUNET_SET_STATUS_ADD_REMOTE:
175 break;
176
177 default:
178 GNUNET_assert (0);
179 }
180}
181
182
183static void
184listen_cb (void *cls,
185 const struct GNUNET_PeerIdentity *other_peer,
186 const struct GNUNET_MessageHeader *context_msg,
187 struct GNUNET_SET_Request *request)
188{
189 GNUNET_assert (NULL != context_msg);
190 GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY);
191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192 "listen cb called\n");
193 oh2 = GNUNET_SET_accept (request,
194 GNUNET_SET_RESULT_SYMMETRIC,
195 (struct GNUNET_SET_Option[]) { 0 },
196 &result_cb_set2,
197 NULL);
198 GNUNET_SET_commit (oh2,
199 set2);
200}
201
202
203/**
204 * Start the set operation.
205 *
206 * @param cls closure, unused
207 */
208static void
209start (void *cls)
210{
211 struct GNUNET_MessageHeader context_msg;
212
213 context_msg.size = htons (sizeof context_msg);
214 context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY);
215
216 listen_handle = GNUNET_SET_listen (config,
217 GNUNET_SET_OPERATION_UNION,
218 &app_id,
219 &listen_cb, NULL);
220 oh1 = GNUNET_SET_prepare (&local_id,
221 &app_id,
222 &context_msg,
223 GNUNET_SET_RESULT_SYMMETRIC,
224 (struct GNUNET_SET_Option[]) { 0 },
225 &result_cb_set1, NULL);
226 GNUNET_SET_commit (oh1, set1);
227}
228
229
230/**
231 * Initialize the second set, continue
232 *
233 * @param cls closure, unused
234 */
235static void
236init_set2 (void *cls)
237{
238 struct GNUNET_SET_Element element;
239
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241 "initializing set 2\n");
242 if (empty)
243 {
244 start (NULL);
245 return;
246 }
247 element.element_type = 0;
248 element.data = "hello";
249 element.size = strlen (element.data);
250 GNUNET_SET_add_element (set2,
251 &element,
252 NULL,
253 NULL);
254 element.data = "quux";
255 element.size = strlen (element.data);
256 GNUNET_SET_add_element (set2,
257 &element,
258 NULL,
259 NULL);
260 element.data = "baz";
261 element.size = strlen (element.data);
262 GNUNET_SET_add_element (set2,
263 &element,
264 &start, NULL);
265}
266
267
268/**
269 * Initialize the first set, continue.
270 */
271static void
272init_set1 (void)
273{
274 struct GNUNET_SET_Element element;
275
276 if (empty)
277 {
278 init_set2 (NULL);
279 return;
280 }
281 element.element_type = 0;
282 element.data = "hello";
283 element.size = strlen (element.data);
284 GNUNET_SET_add_element (set1,
285 &element,
286 NULL,
287 NULL);
288 element.data = "bar";
289 element.size = strlen (element.data);
290 GNUNET_SET_add_element (set1,
291 &element,
292 &init_set2,
293 NULL);
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "initialized set 1\n");
296}
297
298
299static int
300iter_cb (void *cls,
301 const struct GNUNET_SET_Element *element)
302{
303 if (NULL == element)
304 {
305 GNUNET_assert (iter_count == 3);
306 GNUNET_SET_destroy (cls);
307 return GNUNET_YES;
308 }
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310 "iter: got element\n");
311 iter_count++;
312 return GNUNET_YES;
313}
314
315
316static void
317test_iter ()
318{
319 struct GNUNET_SET_Element element;
320 struct GNUNET_SET_Handle *iter_set;
321
322 iter_count = 0;
323 iter_set = GNUNET_SET_create (config, GNUNET_SET_OPERATION_UNION);
324 element.element_type = 0;
325 element.data = "hello";
326 element.size = strlen (element.data);
327 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
328 element.data = "bar";
329 element.size = strlen (element.data);
330 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
331 element.data = "quux";
332 element.size = strlen (element.data);
333 GNUNET_SET_add_element (iter_set, &element, NULL, NULL);
334
335 GNUNET_SET_iterate (iter_set,
336 &iter_cb,
337 iter_set);
338}
339
340
341/**
342 * Signature of the main function of a task.
343 *
344 * @param cls closure
345 */
346static void
347timeout_fail (void *cls)
348{
349 timeout_task = NULL;
350 GNUNET_SCHEDULER_shutdown ();
351 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
352 "test timed out\n");
353 ret = 1;
354}
355
356
357/**
358 * Function run on shutdown.
359 *
360 * @param cls closure
361 */
362static void
363do_shutdown (void *cls)
364{
365 if (NULL != timeout_task)
366 {
367 GNUNET_SCHEDULER_cancel (timeout_task);
368 timeout_task = NULL;
369 }
370 if (NULL != oh1)
371 {
372 GNUNET_SET_operation_cancel (oh1);
373 oh1 = NULL;
374 }
375 if (NULL != oh2)
376 {
377 GNUNET_SET_operation_cancel (oh2);
378 oh2 = NULL;
379 }
380 if (NULL != set1)
381 {
382 GNUNET_SET_destroy (set1);
383 set1 = NULL;
384 }
385 if (NULL != set2)
386 {
387 GNUNET_SET_destroy (set2);
388 set2 = NULL;
389 }
390 if (NULL != listen_handle)
391 {
392 GNUNET_SET_listen_cancel (listen_handle);
393 listen_handle = NULL;
394 }
395}
396
397
398/**
399 * Signature of the 'main' function for a (single-peer) testcase that
400 * is run using 'GNUNET_TESTING_peer_run'.
401 *
402 * @param cls closure
403 * @param cfg configuration of the peer that was started
404 * @param peer identity of the peer that was created
405 */
406static void
407run (void *cls,
408 const struct GNUNET_CONFIGURATION_Handle *cfg,
409 struct GNUNET_TESTING_Peer *peer)
410{
411 timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
412 GNUNET_TIME_UNIT_SECONDS, 5),
413 &timeout_fail,
414 NULL);
415 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
416 NULL);
417 config = cfg;
418 GNUNET_TESTING_peer_get_identity (peer,
419 &local_id);
420
421 if (0)
422 test_iter ();
423
424 set1 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
425 set2 = GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_UNION);
426 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id);
427
428 /* test the real set reconciliation */
429 init_set1 ();
430}
431
432
433int
434main (int argc, char **argv)
435{
436 empty = 1;
437 if (0 != GNUNET_TESTING_peer_run ("test_set_api",
438 "test_set.conf",
439 &run, NULL))
440 {
441 return 1;
442 }
443 GNUNET_assert (0 == count_set1);
444 GNUNET_assert (0 == count_set2);
445 empty = 0;
446 if (0 != GNUNET_TESTING_peer_run ("test_set_api",
447 "test_set.conf",
448 &run, NULL))
449 {
450 return 1;
451 }
452 GNUNET_break (2 == count_set1);
453 GNUNET_break (1 == count_set2);
454 return ret;
455}
diff --git a/src/contrib/service/template/.gitignore b/src/contrib/service/template/.gitignore
new file mode 100644
index 000000000..8ac4bacb5
--- /dev/null
+++ b/src/contrib/service/template/.gitignore
@@ -0,0 +1,3 @@
1gnunet-template
2gnunet-service-template
3test_template_api
diff --git a/src/contrib/service/template/Makefile.am b/src/contrib/service/template/Makefile.am
new file mode 100644
index 000000000..4661266d7
--- /dev/null
+++ b/src/contrib/service/template/Makefile.am
@@ -0,0 +1,41 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8dist_pkgcfg_DATA = \
9 template.conf
10
11if USE_COVERAGE
12 AM_CFLAGS = -fprofile-arcs -ftest-coverage
13endif
14
15# Note: In a real installation,
16# bin_PROGRAMS should be used for gnunet-template
17# libexec_PROGRAMS should be used for gnunet-service-template
18
19noinst_PROGRAMS = \
20 gnunet-service-template
21
22
23gnunet_service_template_SOURCES = \
24 gnunet-service-template.c
25gnunet_service_template_LDADD = \
26 $(top_builddir)/src/lib/util/libgnunetutil.la \
27 $(GN_LIBINTL)
28
29
30check_PROGRAMS = \
31 test_template_api
32
33if ENABLE_TEST_RUN
34AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
35TESTS = $(check_PROGRAMS)
36endif
37
38test_template_api_SOURCES = \
39 test_template_api.c
40test_template_api_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la
diff --git a/src/contrib/service/template/gnunet-service-template.c b/src/contrib/service/template/gnunet-service-template.c
new file mode 100644
index 000000000..e04d2f61d
--- /dev/null
+++ b/src/contrib/service/template/gnunet-service-template.c
@@ -0,0 +1,104 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file template/gnunet-service-template.c
23 * @brief program that does template
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29
30/**
31 * Task run during shutdown.
32 *
33 * @param cls unused
34 */
35static void
36cleanup_task (void *cls)
37{
38 /* FIXME: do clean up here */
39}
40
41
42/**
43 * Callback called when a client connects to the service.
44 *
45 * @param cls closure for the service
46 * @param c the new client that connected to the service
47 * @param mq the message queue used to send messages to the client
48 * @return @a c
49 */
50static void *
51client_connect_cb (void *cls,
52 struct GNUNET_SERVICE_Client *c,
53 struct GNUNET_MQ_Handle *mq)
54{
55 return c;
56}
57
58
59/**
60 * Callback called when a client disconnected from the service
61 *
62 * @param cls closure for the service
63 * @param c the client that disconnected
64 * @param internal_cls should be equal to @a c
65 */
66static void
67client_disconnect_cb (void *cls,
68 struct GNUNET_SERVICE_Client *c,
69 void *internal_cls)
70{
71 GNUNET_assert (c == internal_cls);
72}
73
74
75/**
76 * Process template requests.
77 *
78 * @param cls closure
79 * @param cfg configuration to use
80 * @param service the initialized service
81 */
82static void
83run (void *cls,
84 const struct GNUNET_CONFIGURATION_Handle *cfg,
85 struct GNUNET_SERVICE_Handle *service)
86{
87 /* FIXME: do setup here */
88 GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL);
89}
90
91
92/**
93 * Define "main" method using service macro.
94 */
95GNUNET_SERVICE_MAIN ("template",
96 GNUNET_SERVICE_OPTION_NONE,
97 &run,
98 &client_connect_cb,
99 &client_disconnect_cb,
100 NULL,
101 GNUNET_MQ_handler_end ());
102
103
104/* end of gnunet-service-template.c */
diff --git a/src/contrib/service/template/meson.build b/src/contrib/service/template/meson.build
new file mode 100644
index 000000000..414abc880
--- /dev/null
+++ b/src/contrib/service/template/meson.build
@@ -0,0 +1,19 @@
1gnunetservicetemplate_src = ['gnunet-service-template.c']
2
3configure_file(input : 'template.conf',
4 output : 'template.conf',
5 configuration : cdata,
6 install: false)
7
8executable ('gnunet-service-template',
9 gnunetservicetemplate_src,
10 dependencies: [libgnunetutil_dep],
11 include_directories: [incdir, configuration_inc],
12 install: false)
13testtemplateapi = executable ('test_template_api',
14 ['test_template_api.c'],
15 dependencies: [libgnunetutil_dep],
16 include_directories: [incdir, configuration_inc],
17 install: false)
18test('test_template_api', testtemplateapi, workdir: meson.current_source_dir(),
19 suite: ['template', 'contrib'])
diff --git a/src/contrib/service/template/template.conf b/src/contrib/service/template/template.conf
new file mode 100644
index 000000000..24b123390
--- /dev/null
+++ b/src/contrib/service/template/template.conf
@@ -0,0 +1,23 @@
1[template]
2START_ON_DEMAND = NO
3# for tests please come up with numbers that are
4# unlikely to be in use by anyone. we typically
5# use five digit numbers < 65536 with no "beauty"
6# (no repetitions, no sequences, no popularity).
7PORT = 99999
8HOSTNAME = localhost
9BINARY = gnunet-service-template
10ACCEPT_FROM = 127.0.0.1;
11ACCEPT_FROM6 = ::1;
12UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-template.sock
13UNIX_MATCH_UID = YES
14UNIX_MATCH_GID = YES
15# DISABLE_SOCKET_FORWARDING = NO
16# USERNAME =
17# MAXBUF =
18# TIMEOUT =
19# DISABLEV6 =
20# BINDTO =
21# REJECT_FROM =
22# REJECT_FROM6 =
23# PREFIX =
diff --git a/src/contrib/service/template/test_template_api.c b/src/contrib/service/template/test_template_api.c
new file mode 100644
index 000000000..196edec2d
--- /dev/null
+++ b/src/contrib/service/template/test_template_api.c
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file template/test_template_api.c
22 * @brief testcase for template.c
23 */
24#include "platform.h"
25
26static int
27check ()
28{
29 return 0;
30}
31
32
33int
34main (int argc, char *argv[])
35{
36 int ret;
37
38 ret = check ();
39
40 return ret;
41}
42
43
44/* end of test_template_api.c */