aboutsummaryrefslogtreecommitdiff
path: root/src/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/service')
-rw-r--r--src/service/Makefile.am2
-rw-r--r--src/service/hostlist/.gitignore4
-rw-r--r--src/service/hostlist/Makefile.am82
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist.c401
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist.h43
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist_client.c1790
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist_client.h70
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist_server.c890
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist_server.h61
-rw-r--r--src/service/hostlist/hostlist.conf45
-rw-r--r--src/service/hostlist/hostlists_learn_peer2.filebin0 -> 72 bytes
-rw-r--r--src/service/hostlist/learning_data.conf8
-rw-r--r--src/service/hostlist/meson.build26
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist.c264
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist_data.conf28
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist_learning.c593
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist_peer1.conf44
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist_peer2.conf44
-rw-r--r--src/service/hostlist/test_gnunet_daemon_hostlist_reconnect.c263
-rw-r--r--src/service/hostlist/test_hostlist_defaults.conf18
-rw-r--r--src/service/hostlist/test_learning_adv_peer.conf45
-rw-r--r--src/service/hostlist/test_learning_learn_peer.conf44
-rw-r--r--src/service/hostlist/test_learning_learn_peer2.conf40
-rw-r--r--src/service/topology/.gitignore2
-rw-r--r--src/service/topology/Makefile.am47
-rw-r--r--src/service/topology/gnunet-daemon-topology.c1033
-rw-r--r--src/service/topology/meson.build43
-rw-r--r--src/service/topology/test_gnunet_daemon_topology.c300
-rw-r--r--src/service/topology/test_gnunet_daemon_topology_data.conf34
-rw-r--r--src/service/topology/topology.conf8
30 files changed, 6272 insertions, 0 deletions
diff --git a/src/service/Makefile.am b/src/service/Makefile.am
index f25006a37..6b4fa3264 100644
--- a/src/service/Makefile.am
+++ b/src/service/Makefile.am
@@ -9,6 +9,8 @@ SUBDIRS = \
9 transport \ 9 transport \
10 core \ 10 core \
11 nse \ 11 nse \
12 hostlist \
13 topology \
12 identity \ 14 identity \
13 rest \ 15 rest \
14 datacache \ 16 datacache \
diff --git a/src/service/hostlist/.gitignore b/src/service/hostlist/.gitignore
new file mode 100644
index 000000000..b16e3444a
--- /dev/null
+++ b/src/service/hostlist/.gitignore
@@ -0,0 +1,4 @@
1gnunet-daemon-hostlist
2test_gnunet_daemon_hostlist
3test_gnunet_daemon_hostlist_learning
4test_gnunet_daemon_hostlist_reconnect
diff --git a/src/service/hostlist/Makefile.am b/src/service/hostlist/Makefile.am
new file mode 100644
index 000000000..fc9952aa6
--- /dev/null
+++ b/src/service/hostlist/Makefile.am
@@ -0,0 +1,82 @@
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
9if USE_COVERAGE
10 AM_CFLAGS = --coverage -O0
11endif
12
13 HOSTLIST_SERVER_SOURCES = \
14 gnunet-daemon-hostlist_server.c gnunet-daemon-hostlist_server.h
15 GN_LIBMHD = $(MHD_LIBS)
16 GN_CPPMHD = $(MHD_CFLAGS)
17
18libexec_PROGRAMS = \
19 gnunet-daemon-hostlist
20dist_pkgcfg_DATA = \
21 hostlist.conf
22
23gnunet_daemon_hostlist_SOURCES = \
24 gnunet-daemon-hostlist.c gnunet-daemon-hostlist.h \
25 gnunet-daemon-hostlist_client.c gnunet-daemon-hostlist_client.h \
26 $(HOSTLIST_SERVER_SOURCES)
27
28gnunet_daemon_hostlist_LDADD = \
29 $(top_builddir)/src/service/core/libgnunetcore.la \
30 $(top_builddir)/src/lib/hello/libgnunethello.la \
31 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
32 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
33 $(top_builddir)/src/lib/util/libgnunetutil.la \
34 $(GN_LIBMHD) \
35 @LIBCURL@ \
36 $(GN_LIBINTL)
37
38gnunet_daemon_hostlist_CFLAGS = \
39 $(GN_CPPMHD) \
40 @LIBCURL_CPPFLAGS@ \
41 $(AM_CFLAGS)
42
43#check_PROGRAMS = \
44# test_gnunet_daemon_hostlist \
45# test_gnunet_daemon_hostlist_reconnect \
46# test_gnunet_daemon_hostlist_learning
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 = \
51 $(check_PROGRAMS)
52endif
53
54#test_gnunet_daemon_hostlist_SOURCES = \
55# test_gnunet_daemon_hostlist.c
56#test_gnunet_daemon_hostlist_LDADD = \
57# $(top_builddir)/src/service/transport/libgnunettransport.la \
58# $(top_builddir)/src/lib/util/libgnunetutil.la
59#
60#test_gnunet_daemon_hostlist_reconnect_SOURCES = \
61# test_gnunet_daemon_hostlist_reconnect.c
62#test_gnunet_daemon_hostlist_reconnect_LDADD = \
63# $(top_builddir)/src/service/transport/libgnunettransport.la \
64# $(top_builddir)/src/lib/util/libgnunetutil.la
65#
66#test_gnunet_daemon_hostlist_learning_SOURCES = \
67# test_gnunet_daemon_hostlist_learning.c
68#test_gnunet_daemon_hostlist_learning_LDADD = \
69# $(top_builddir)/src/service/transport/libgnunettransport.la \
70# $(top_builddir)/src/service/core/libgnunetcore.la \
71# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
72# $(top_builddir)/src/lib/util/libgnunetutil.la
73
74EXTRA_DIST = \
75 test_hostlist_defaults.conf \
76 test_gnunet_daemon_hostlist_data.conf \
77 test_gnunet_daemon_hostlist_peer1.conf \
78 test_gnunet_daemon_hostlist_peer2.conf \
79 test_learning_adv_peer.conf \
80 test_learning_learn_peer.conf \
81 test_learning_learn_peer2.conf \
82 learning_data.conf
diff --git a/src/service/hostlist/gnunet-daemon-hostlist.c b/src/service/hostlist/gnunet-daemon-hostlist.c
new file mode 100644
index 000000000..54e070f89
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist.c
@@ -0,0 +1,401 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007, 2008, 2009, 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 hostlist/gnunet-daemon-hostlist.c
23 * @brief code for bootstrapping via hostlist servers
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet-daemon-hostlist_client.h"
28#include "gnunet_core_service.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_protocols.h"
31#include "gnunet_statistics_service.h"
32
33#include "gnunet-daemon-hostlist_server.h"
34
35/**
36 * Set if we are allowed to advertise our hostlist to others.
37 */
38static int advertising;
39
40/**
41 * Set if the user wants us to run a hostlist server.
42 */
43static int provide_hostlist;
44
45/**
46 * Handle to hostlist server's connect handler
47 */
48static GNUNET_CORE_ConnectEventHandler server_ch;
49
50/**
51 * Set if we are allowed to learn about peers by accessing
52 * hostlist servers.
53 */
54static int bootstrapping;
55
56/**
57 * Set if the user allows us to learn about new hostlists
58 * from the network.
59 */
60static int learning;
61
62/**
63 * Statistics handle.
64 */
65static struct GNUNET_STATISTICS_Handle *stats;
66
67/**
68 * Handle to the core service (NULL until we've connected to it).
69 */
70static struct GNUNET_CORE_Handle *core;
71
72/**
73 * Handle to the hostlist client's advertisement handler
74 */
75static GNUNET_HOSTLIST_UriHandler client_adv_handler;
76
77/**
78 * Handle to hostlist client's connect handler
79 */
80static GNUNET_CORE_ConnectEventHandler client_ch;
81
82/**
83 * Handle to hostlist client's disconnect handler
84 */
85static GNUNET_CORE_DisconnectEventHandler client_dh;
86
87GNUNET_NETWORK_STRUCT_BEGIN
88
89/**
90 * A HOSTLIST_ADV message is used to exchange information about
91 * hostlist advertisements. This struct is always
92 * followed by the actual url under which the hostlist can be obtained:
93 *
94 * 1) transport-name (0-terminated)
95 * 2) address-length (uint32_t, network byte order; possibly
96 * unaligned!)
97 * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
98 * unaligned!)
99 * 4) address (address-length bytes; possibly unaligned!)
100 */
101struct GNUNET_HOSTLIST_ADV_Message
102{
103 /**
104 * Type will be GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT.
105 */
106 struct GNUNET_MessageHeader header;
107
108 /**
109 * Always zero (for alignment).
110 */
111 uint32_t reserved GNUNET_PACKED;
112};
113GNUNET_NETWORK_STRUCT_END
114
115
116/**
117 * Our own peer identity.
118 */
119static struct GNUNET_PeerIdentity me;
120
121
122/**
123 * Callback invoked once our connection to CORE service is up.
124 *
125 * @param cls NULL
126 * @param my_identity our peer's identity
127 */
128static void
129core_init (void *cls,
130 const struct GNUNET_PeerIdentity *my_identity)
131{
132 me = *my_identity;
133}
134
135
136/**
137 * Core handler for p2p hostlist advertisements
138 *
139 * @param cls closure
140 * @param message advertisement message we got
141 * @return #GNUNET_OK if message is well-formed
142 */
143static int
144check_advertisement (void *cls,
145 const struct GNUNET_MessageHeader *message)
146{
147 size_t size;
148 size_t uri_size;
149 const char *uri;
150
151 size = ntohs (message->size);
152 if (size <= sizeof(struct GNUNET_MessageHeader))
153 {
154 GNUNET_break_op (0);
155 return GNUNET_SYSERR;
156 }
157 uri = (const char *) &message[1];
158 uri_size = size - sizeof(struct GNUNET_MessageHeader);
159 if (uri[uri_size - 1] != '\0')
160 {
161 GNUNET_break_op (0);
162 return GNUNET_SYSERR;
163 }
164 return GNUNET_OK;
165}
166
167
168/**
169 * Core handler for p2p hostlist advertisements
170 *
171 * @param cls closure
172 * @param message advertisement message we got
173 * @return #GNUNET_OK on success
174 */
175static void
176handle_advertisement (void *cls,
177 const struct GNUNET_MessageHeader *message)
178{
179 const char *uri = (const char *) &message[1];
180
181 GNUNET_assert (NULL != client_adv_handler);
182 (void) (*client_adv_handler)(uri);
183}
184
185
186/**
187 * Method called whenever a given peer connects. Wrapper to call both
188 * client's and server's functions
189 *
190 * @param cls closure
191 * @param peer peer identity this notification is about
192 * @param mq queue for sending messages to @a peer
193 * @return peer
194 */
195static void *
196connect_handler (void *cls,
197 const struct GNUNET_PeerIdentity *peer,
198 struct GNUNET_MQ_Handle *mq)
199{
200 if (0 == GNUNET_memcmp (&me,
201 peer))
202 return NULL;
203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204 "A new peer connected, notifying client and server\n");
205 if (NULL != client_ch)
206 GNUNET_assert (NULL ==
207 (*client_ch)(cls,
208 peer,
209 mq));
210 if (NULL != server_ch)
211 GNUNET_assert (NULL ==
212 (*server_ch)(cls,
213 peer,
214 mq));
215 return (void *) peer;
216}
217
218
219/**
220 * Method called whenever a given peer disconnects. Wrapper to call
221 * both client's and server's functions
222 *
223 * @param cls closure
224 * @param peer peer identity this notification is about
225 */
226static void
227disconnect_handler (void *cls,
228 const struct GNUNET_PeerIdentity *peer,
229 void *internal_cls)
230{
231 if (0 == GNUNET_memcmp (&me,
232 peer))
233 return;
234 /* call hostlist client disconnect handler */
235 if (NULL != client_dh)
236 (*client_dh)(cls,
237 peer,
238 NULL);
239}
240
241
242/**
243 * Last task run during shutdown. Disconnects us from
244 * the other services.
245 *
246 * @param cls NULL
247 */
248static void
249cleaning_task (void *cls)
250{
251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
252 "Hostlist daemon is shutting down\n");
253 if (NULL != core)
254 {
255 GNUNET_CORE_disconnect (core);
256 core = NULL;
257 }
258 if (bootstrapping)
259 {
260 GNUNET_HOSTLIST_client_stop ();
261 }
262 if (provide_hostlist)
263 {
264 GNUNET_HOSTLIST_server_stop ();
265 }
266 if (NULL != stats)
267 {
268 GNUNET_STATISTICS_destroy (stats,
269 GNUNET_NO);
270 stats = NULL;
271 }
272}
273
274
275/**
276 * Main function that will be run.
277 *
278 * @param cls closure
279 * @param args remaining command-line arguments
280 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
281 * @param cfg configuration
282 */
283static void
284run (void *cls,
285 char *const *args,
286 const char *cfgfile,
287 const struct GNUNET_CONFIGURATION_Handle *cfg)
288{
289 struct GNUNET_MQ_MessageHandler learn_handlers[] = {
290 GNUNET_MQ_hd_var_size (advertisement,
291 GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT,
292 struct GNUNET_MessageHeader,
293 NULL),
294 GNUNET_MQ_handler_end ()
295 };
296 struct GNUNET_MQ_MessageHandler no_learn_handlers[] = {
297 GNUNET_MQ_handler_end ()
298 };
299
300 if ((! bootstrapping) && (! learning)
301 && (! provide_hostlist)
302 )
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
305 _ (
306 "None of the functions for the hostlist daemon were enabled. I have no reason to run!\n"));
307 return;
308 }
309 stats = GNUNET_STATISTICS_create ("hostlist", cfg);
310 if (NULL == stats)
311 {
312 GNUNET_break (0);
313 return;
314 }
315 if (bootstrapping)
316 GNUNET_HOSTLIST_client_start (cfg,
317 stats,
318 &client_ch,
319 &client_dh,
320 &client_adv_handler,
321 learning);
322 core =
323 GNUNET_CORE_connect (cfg,
324 NULL,
325 &core_init,
326 &connect_handler,
327 &disconnect_handler,
328 learning ? learn_handlers : no_learn_handlers);
329
330
331 if (provide_hostlist)
332 GNUNET_HOSTLIST_server_start (cfg,
333 stats,
334 core,
335 &server_ch,
336 advertising);
337 GNUNET_SCHEDULER_add_shutdown (&cleaning_task,
338 NULL);
339
340 if (NULL == core)
341 {
342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
343 _ ("Failed to connect to `%s' service.\n"), "core");
344 GNUNET_SCHEDULER_shutdown ();
345 return;
346 }
347}
348
349
350/**
351 * The main function for the hostlist daemon.
352 *
353 * @param argc number of arguments from the command line
354 * @param argv command line arguments
355 * @return 0 ok, 1 on error
356 */
357int
358main (int argc, char *const *argv)
359{
360 struct GNUNET_GETOPT_CommandLineOption options[] = {
361 GNUNET_GETOPT_option_flag ('a',
362 "advertise",
363 gettext_noop (
364 "advertise our hostlist to other peers"),
365 &advertising),
366 GNUNET_GETOPT_option_flag ('b',
367 "bootstrap",
368 gettext_noop (
369 "bootstrap using hostlists (it is highly recommended that you always use this option)"),
370 &bootstrapping),
371 GNUNET_GETOPT_option_flag ('e',
372 "enable-learning",
373 gettext_noop (
374 "enable learning about hostlist servers from other peers"),
375 &learning),
376 GNUNET_GETOPT_option_flag ('p',
377 "provide-hostlist",
378 gettext_noop ("provide a hostlist server"),
379 &provide_hostlist),
380 GNUNET_GETOPT_OPTION_END
381 };
382
383 int ret;
384
385 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
386 return 2;
387
388 GNUNET_log_setup ("hostlist", "WARNING", NULL);
389 ret =
390 (GNUNET_OK ==
391 GNUNET_PROGRAM_run (argc, argv,
392 "hostlist",
393 _ ("GNUnet hostlist server and client"),
394 options,
395 &run, NULL)) ? 0 : 1;
396 GNUNET_free_nz ((void *) argv);
397 return ret;
398}
399
400
401/* end of gnunet-daemon-hostlist.c */
diff --git a/src/service/hostlist/gnunet-daemon-hostlist.h b/src/service/hostlist/gnunet-daemon-hostlist.h
new file mode 100644
index 000000000..aec413fe6
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist.h
@@ -0,0 +1,43 @@
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 hostlist/gnunet-daemon-hostlist.h
23 * @brief common internal definitions for hostlist daemon
24 * @author Matthias Wachs
25 */
26#include <stdlib.h>
27#include "platform.h"
28#include "gnunet_core_service.h"
29#include "gnunet_protocols.h"
30#include "gnunet_statistics_service.h"
31#include "gnunet_util_lib.h"
32
33/**
34 * How long can hostlist URLs be?
35 */
36#define MAX_URL_LEN 1000
37
38/**
39 * How many bytes do we download at most from a hostlist server?
40 */
41#define MAX_BYTES_PER_HOSTLISTS 500000
42
43/* end of gnunet-daemon-hostlist.h */
diff --git a/src/service/hostlist/gnunet-daemon-hostlist_client.c b/src/service/hostlist/gnunet-daemon-hostlist_client.c
new file mode 100644
index 000000000..aceea0aaf
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist_client.c
@@ -0,0 +1,1790 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2010, 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 hostlist/gnunet-daemon-hostlist_client.c
22 * @brief hostlist support. Downloads HELLOs via HTTP.
23 * @author Christian Grothoff
24 * @author Matthias Wachs
25 */
26#include "platform.h"
27#include "gnunet-daemon-hostlist_client.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_statistics_service.h"
30#include "gnunet_peerstore_service.h"
31#include "gnunet-daemon-hostlist.h"
32/* Just included for the right curl.h */
33#include "gnunet_curl_lib.h"
34
35
36/**
37 * Number of connections that we must have to NOT download
38 * hostlists anymore.
39 */
40#define MIN_CONNECTIONS 4
41
42/**
43 * Maximum number of hostlist that are saved
44 */
45#define MAX_NUMBER_HOSTLISTS 30
46
47/**
48 * Time interval hostlists are saved to disk
49 */
50#define SAVING_INTERVAL \
51 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
52
53/**
54 * Time interval between two hostlist tests
55 */
56#define TESTING_INTERVAL \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
58
59/**
60 * Time interval for download dispatcher before a download is re-scheduled
61 */
62#define WAITING_INTERVAL \
63 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
64
65/**
66 * Defines concerning the hostlist quality metric
67 */
68
69/**
70 * Initial quality of a new created hostlist
71 */
72#define HOSTLIST_INITIAL 10000
73
74/**
75 * Value subtracted each time a hostlist download fails
76 */
77#define HOSTLIST_FAILED_DOWNLOAD 100
78
79/**
80 * Value added each time a hostlist download is successful
81 */
82#define HOSTLIST_SUCCESSFUL_DOWNLOAD 100
83
84/**
85 * Value added for each valid HELLO received during a hostlist download
86 */
87#define HOSTLIST_SUCCESSFUL_HELLO 1
88
89
90/**
91 * A single hostlist obtained by hostlist advertisements
92 */
93struct Hostlist
94{
95 /**
96 * previous entry, used to manage entries in a double linked list
97 */
98 struct Hostlist *prev;
99
100 /**
101 * next entry, used to manage entries in a double linked list
102 */
103 struct Hostlist *next;
104
105 /**
106 * URI where hostlist can be obtained
107 */
108 const char *hostlist_uri;
109
110 /**
111 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
112 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
113 * initial value = HOSTLIST_INITIAL
114 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
115 * increased every successful download by number of obtained HELLO messages
116 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
117 */
118 uint64_t quality;
119
120 /**
121 * Time the hostlist advertisement was received and the entry was created
122 */
123 struct GNUNET_TIME_Absolute time_creation;
124
125 /**
126 * Last time the hostlist was obtained
127 */
128 struct GNUNET_TIME_Absolute time_last_usage;
129
130 /**
131 * Number of HELLO messages obtained during last download
132 */
133 uint32_t hello_count;
134
135 /**
136 * Number of times the hostlist was successfully obtained
137 */
138 uint32_t times_used;
139};
140
141
142/**
143 * Our configuration.
144 */
145static const struct GNUNET_CONFIGURATION_Handle *cfg;
146
147/**
148 * Statistics handle.
149 */
150static struct GNUNET_STATISTICS_Handle *stats;
151
152/**
153 * Proxy hostname or ip we are using (can be NULL).
154 */
155static char *proxy;
156
157/**
158 * Proxy username we are using (can be NULL).
159 */
160static char *proxy_username;
161
162/**
163 * Proxy password we are using (can be NULL).
164 */
165static char *proxy_password;
166
167/**
168 * Proxy type we are using (can be NULL).
169 */
170static curl_proxytype proxy_type;
171
172/**
173 * Number of bytes valid in 'download_buffer'.
174 */
175static size_t download_pos;
176
177/**
178 * Current URL that we are using.
179 */
180static char *current_url;
181
182/**
183 * Current CURL handle.
184 */
185static CURL *curl;
186
187/**
188 * Current multi-CURL handle.
189 */
190static CURLM *multi;
191
192/**
193 * How many bytes did we download from the current hostlist URL?
194 */
195static uint32_t stat_bytes_downloaded;
196
197/**
198 * Amount of time we wait between hostlist downloads.
199 */
200static struct GNUNET_TIME_Relative hostlist_delay;
201
202/**
203 * ID of the task, checking if hostlist download should take plate
204 */
205static struct GNUNET_SCHEDULER_Task *ti_check_download;
206
207/**
208 * ID of the task downloading the hostlist
209 */
210static struct GNUNET_SCHEDULER_Task *ti_download;
211
212/**
213 * ID of the task saving the hostlsit in a regular interval
214 */
215static struct GNUNET_SCHEDULER_Task *ti_saving_task;
216
217/**
218 * ID of the task called to initiate a download
219 */
220static struct GNUNET_SCHEDULER_Task *ti_download_dispatcher_task;
221
222/**
223 * ID of the task controlling the locking between two hostlist tests
224 */
225static struct GNUNET_SCHEDULER_Task *ti_testing_intervall_task;
226
227/**
228 * At what time MUST the current hostlist request be done?
229 */
230static struct GNUNET_TIME_Absolute end_time;
231
232/**
233 * Head of the linked list used to store hostlists
234 */
235static struct Hostlist *linked_list_head;
236
237/**
238 * Tail of the linked list used to store hostlists
239 */
240static struct Hostlist *linked_list_tail;
241
242/**
243 * Current hostlist used for downloading
244 */
245static struct Hostlist *current_hostlist;
246
247/**
248 * Size of the linked list used to store hostlists
249 */
250static unsigned int linked_list_size;
251
252/**
253 * Head of the linked list used to store hostlists
254 */
255static struct Hostlist *hostlist_to_test;
256
257/**
258 * Handle for our statistics GET operation.
259 */
260static struct GNUNET_STATISTICS_GetHandle *sget;
261
262/**
263 * Set to GNUNET_YES if the current URL had some problems.
264 */
265static int stat_bogus_url;
266
267/**
268 * Value controlling if a hostlist is tested at the moment
269 */
270static int stat_testing_hostlist;
271
272/**
273 * Value controlling if a hostlist testing is allowed at the moment
274 */
275static int stat_testing_allowed;
276
277/**
278 * Value controlling if a hostlist download is running at the moment
279 */
280static int stat_download_in_progress;
281
282/**
283 * Value saying if a preconfigured bootstrap server is used
284 */
285static unsigned int stat_use_bootstrap;
286
287/**
288 * Set if we are allowed to learn new hostlists and use them
289 */
290static int stat_learning;
291
292/**
293 * Value saying if hostlist download was successful
294 */
295static unsigned int stat_download_successful;
296
297/**
298 * Value saying how many valid HELLO messages were obtained during download
299 */
300static unsigned int stat_hellos_obtained;
301
302/**
303 * Number of active connections (according to core service).
304 */
305static unsigned int stat_connection_count;
306
307/**
308 * Handle to the PEERSTORE service.
309 */
310static struct GNUNET_PEERSTORE_Handle *peerstore;
311
312
313static void
314shc_cont (void *cls, int success)
315{
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "Hostlist entry stored successfully!\n");
318}
319
320
321/**
322 * Process downloaded bits by calling callback on each HELLO.
323 *
324 * @param ptr buffer with downloaded data
325 * @param size size of a record
326 * @param nmemb number of records downloaded
327 * @param ctx unused
328 * @return number of bytes that were processed (always size*nmemb)
329 */
330static size_t
331callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
332{
333 static char download_buffer[GNUNET_MAX_MESSAGE_SIZE - 1];
334 struct GNUNET_PEERSTORE_StoreHelloContext *shc;
335 const char *cbuf = ptr;
336 const struct GNUNET_MessageHeader *msg;
337 size_t total;
338 size_t cpy;
339 size_t left;
340 uint16_t msize;
341
342 total = size * nmemb;
343 stat_bytes_downloaded += total;
344 if ((total == 0) || (stat_bogus_url))
345 {
346 return total; /* ok, no data or bogus data */
347 }
348
349 GNUNET_STATISTICS_update (stats,
350 gettext_noop (
351 "# bytes downloaded from hostlist servers"),
352 (int64_t) total,
353 GNUNET_NO);
354 left = total;
355 while ((left > 0) || (download_pos > 0))
356 {
357 cpy = GNUNET_MIN (left, GNUNET_MAX_MESSAGE_SIZE - 1 - download_pos);
358 GNUNET_memcpy (&download_buffer[download_pos], cbuf, cpy);
359 cbuf += cpy;
360 download_pos += cpy;
361 left -= cpy;
362 if (download_pos < sizeof(struct GNUNET_MessageHeader))
363 {
364 GNUNET_assert (0 == left);
365 break;
366 }
367 msg = (const struct GNUNET_MessageHeader *) download_buffer;
368 msize = ntohs (msg->size);
369 if (msize < sizeof(struct GNUNET_MessageHeader))
370 {
371 GNUNET_STATISTICS_update (
372 stats,
373 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
374 1,
375 GNUNET_NO);
376 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
377 _ ("Invalid `%s' message received from hostlist at `%s'\n"),
378 "HELLO",
379 current_url);
380 stat_hellos_obtained++;
381 stat_bogus_url = 1;
382 return total;
383 }
384 if (download_pos < msize)
385 {
386 GNUNET_assert (left == 0);
387 break;
388 }
389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390 "Received valid `%s' message from hostlist server.\n",
391 "HELLO");
392 GNUNET_STATISTICS_update (
393 stats,
394 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
395 1,
396 GNUNET_NO);
397 stat_hellos_obtained++;
398 shc = GNUNET_PEERSTORE_hello_add (peerstore,
399 msg,
400 shc_cont,
401 shc);
402 memmove (download_buffer, &download_buffer[msize], download_pos - msize);
403 download_pos -= msize;
404 }
405 return total;
406}
407
408
409/**
410 * Obtain a hostlist URL that we should use.
411 *
412 * @return NULL if there is no URL available
413 */
414static char *
415get_bootstrap_server ()
416{
417 char *servers;
418 char *ret;
419 size_t urls;
420 size_t pos;
421
422 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
423 "HOSTLIST",
424 "SERVERS",
425 &servers))
426 {
427 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
428 "hostlist",
429 "SERVERS");
430 return NULL;
431 }
432
433 urls = 0;
434 if (strlen (servers) > 0)
435 {
436 urls++;
437 pos = strlen (servers) - 1;
438 while (pos > 0)
439 {
440 if (servers[pos] == ' ')
441 urls++;
442 pos--;
443 }
444 }
445 if (urls == 0)
446 {
447 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
448 "hostlist",
449 "SERVERS");
450 GNUNET_free (servers);
451 return NULL;
452 }
453
454 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
455 pos = strlen (servers) - 1;
456 while (pos > 0)
457 {
458 if (servers[pos] == ' ')
459 {
460 urls--;
461 servers[pos] = '\0';
462 }
463 if (urls == 0)
464 {
465 pos++;
466 break;
467 }
468 pos--;
469 }
470 ret = GNUNET_strdup (&servers[pos]);
471 GNUNET_free (servers);
472 return ret;
473}
474
475
476/**
477 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
478 * @return uri to use, NULL if there is no URL available
479 */
480static char *
481download_get_url ()
482{
483 uint32_t index;
484 unsigned int counter;
485 struct Hostlist *pos;
486
487 if (GNUNET_NO == stat_learning)
488 {
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 "Using preconfigured bootstrap server\n");
491 current_hostlist = NULL;
492 return get_bootstrap_server ();
493 }
494
495 if ((GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test))
496 {
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "Testing new advertised hostlist if it is obtainable\n");
499 current_hostlist = hostlist_to_test;
500 return GNUNET_strdup (hostlist_to_test->hostlist_uri);
501 }
502
503 if ((GNUNET_YES == stat_use_bootstrap) || (linked_list_size == 0))
504 {
505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506 "Using preconfigured bootstrap server\n");
507 current_hostlist = NULL;
508 return get_bootstrap_server ();
509 }
510 index =
511 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
512 counter = 0;
513 pos = linked_list_head;
514 while (counter < index)
515 {
516 pos = pos->next;
517 counter++;
518 }
519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520 "Using learned hostlist `%s'\n",
521 pos->hostlist_uri);
522 current_hostlist = pos;
523 return GNUNET_strdup (pos->hostlist_uri);
524}
525
526
527#define CURL_EASY_SETOPT(c, a, b) \
528 do \
529 { \
530 ret = curl_easy_setopt (c, a, b); \
531 if (CURLE_OK != ret) \
532 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
533 _ ("%s failed at %s:%d: `%s'\n"), \
534 "curl_easy_setopt", \
535 __FILE__, \
536 __LINE__, \
537 curl_easy_strerror (ret)); \
538 } while (0)
539
540
541/**
542 * Method to save hostlist to a file during hostlist client shutdown
543 *
544 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
545 */
546static void
547save_hostlist_file (int shutdown);
548
549
550/**
551 * Add val2 to val1 with overflow check
552 *
553 * @param val1 value 1
554 * @param val2 value 2
555 * @return result
556 */
557static uint64_t
558checked_add (uint64_t val1, uint64_t val2)
559{
560 static uint64_t temp;
561 static uint64_t maxv;
562
563 maxv = 0;
564 maxv--;
565
566 temp = val1 + val2;
567 if (temp < val1)
568 return maxv;
569 return temp;
570}
571
572
573/**
574 * Subtract val2 from val1 with underflow check
575 *
576 * @param val1 value 1
577 * @param val2 value 2
578 * @return result
579 */
580static uint64_t
581checked_sub (uint64_t val1, uint64_t val2)
582{
583 if (val1 <= val2)
584 return 0;
585 return(val1 - val2);
586}
587
588
589/**
590 * Method to check if a URI is in hostlist linked list
591 *
592 * @param uri uri to check
593 * @return #GNUNET_YES if existing in linked list, #GNUNET_NO if not
594 */
595static int
596linked_list_contains (const char *uri)
597{
598 struct Hostlist *pos;
599
600 pos = linked_list_head;
601 while (pos != NULL)
602 {
603 if (0 == strcmp (pos->hostlist_uri, uri))
604 return GNUNET_YES;
605 pos = pos->next;
606 }
607 return GNUNET_NO;
608}
609
610
611/**
612 * Method returning the hostlist element with the lowest quality in the datastore
613 * @return hostlist with lowest quality
614 */
615static struct Hostlist *
616linked_list_get_lowest_quality ()
617{
618 struct Hostlist *pos;
619 struct Hostlist *lowest;
620
621 if (linked_list_size == 0)
622 return NULL;
623 lowest = linked_list_head;
624 pos = linked_list_head->next;
625 while (pos != NULL)
626 {
627 if (pos->quality < lowest->quality)
628 lowest = pos;
629 pos = pos->next;
630 }
631 return lowest;
632}
633
634
635/**
636 * Method to insert a hostlist into the datastore. If datastore
637 * contains maximum number of elements, the elements with lowest
638 * quality is dismissed
639 */
640static void
641insert_hostlist ()
642{
643 struct Hostlist *lowest_quality;
644
645 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
646 {
647 /* No free entries available, replace existing entry */
648 lowest_quality = linked_list_get_lowest_quality ();
649 GNUNET_assert (lowest_quality != NULL);
650 GNUNET_log (
651 GNUNET_ERROR_TYPE_DEBUG,
652 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
653 lowest_quality->hostlist_uri,
654 (unsigned long long) lowest_quality->quality);
655 GNUNET_CONTAINER_DLL_remove (linked_list_head,
656 linked_list_tail,
657 lowest_quality);
658 linked_list_size--;
659 GNUNET_free (lowest_quality);
660 }
661 GNUNET_CONTAINER_DLL_insert (linked_list_head,
662 linked_list_tail,
663 hostlist_to_test);
664 linked_list_size++;
665 GNUNET_STATISTICS_set (stats,
666 gettext_noop ("# advertised hostlist URIs"),
667 linked_list_size,
668 GNUNET_NO);
669 stat_testing_hostlist = GNUNET_NO;
670}
671
672
673/**
674 * Method updating hostlist statistics
675 */
676static void
677update_hostlist ()
678{
679 char *stat;
680
681 if (((stat_use_bootstrap == GNUNET_NO) && (NULL != current_hostlist)) ||
682 ((stat_testing_hostlist == GNUNET_YES) && (NULL != current_hostlist)))
683 {
684 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
685 "Updating hostlist statics for URI `%s'\n",
686 current_hostlist->hostlist_uri);
687 current_hostlist->hello_count = stat_hellos_obtained;
688 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get ();
689 current_hostlist->quality =
690 checked_add (current_hostlist->quality,
691 (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
692 if (GNUNET_YES == stat_download_successful)
693 {
694 current_hostlist->times_used++;
695 current_hostlist->quality =
696 checked_add (current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
697 GNUNET_asprintf (&stat,
698 gettext_noop ("# advertised URI `%s' downloaded"),
699 current_hostlist->hostlist_uri);
700
701 GNUNET_STATISTICS_update (stats, stat, 1, GNUNET_YES);
702 GNUNET_free (stat);
703 }
704 else
705 current_hostlist->quality =
706 checked_sub (current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD);
707 }
708 current_hostlist = NULL;
709 /* Alternating the usage of preconfigured and learned hostlists */
710
711 if (stat_testing_hostlist == GNUNET_YES)
712 return;
713
714 if (GNUNET_YES == stat_learning)
715 {
716 if (stat_use_bootstrap == GNUNET_YES)
717 stat_use_bootstrap = GNUNET_NO;
718 else
719 stat_use_bootstrap = GNUNET_YES;
720 }
721 else
722 stat_use_bootstrap = GNUNET_YES;
723}
724
725
726/**
727 * Clean up the state from the task that downloaded the
728 * hostlist and schedule the next task.
729 */
730static void
731clean_up ()
732{
733 CURLMcode mret;
734
735 if ((stat_testing_hostlist == GNUNET_YES) &&
736 (GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
737 {
738 GNUNET_log (
739 GNUNET_ERROR_TYPE_INFO,
740 _ (
741 "Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
742 hostlist_to_test->hostlist_uri);
743 }
744
745 if (stat_testing_hostlist == GNUNET_YES)
746 {
747 stat_testing_hostlist = GNUNET_NO;
748 }
749 if (NULL != hostlist_to_test)
750 {
751 GNUNET_free (hostlist_to_test);
752 hostlist_to_test = NULL;
753 }
754
755 if (NULL != multi)
756 {
757 mret = curl_multi_remove_handle (multi, curl);
758 if (mret != CURLM_OK)
759 {
760 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761 _ ("%s failed at %s:%d: `%s'\n"),
762 "curl_multi_remove_handle",
763 __FILE__,
764 __LINE__,
765 curl_multi_strerror (mret));
766 }
767 mret = curl_multi_cleanup (multi);
768 if (mret != CURLM_OK)
769 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
770 _ ("%s failed at %s:%d: `%s'\n"),
771 "curl_multi_cleanup",
772 __FILE__,
773 __LINE__,
774 curl_multi_strerror (mret));
775 multi = NULL;
776 }
777 if (NULL != curl)
778 {
779 curl_easy_cleanup (curl);
780 curl = NULL;
781 }
782 GNUNET_free (current_url);
783 current_url = NULL;
784 stat_bytes_downloaded = 0;
785 stat_download_in_progress = GNUNET_NO;
786}
787
788
789/**
790 * Task that is run when we are ready to receive more data from the hostlist
791 * server.
792 *
793 * @param cls closure, unused
794 */
795static void
796task_download (void *cls);
797
798
799/**
800 * Ask CURL for the select set and then schedule the
801 * receiving task with the scheduler.
802 */
803static void
804download_prepare ()
805{
806 CURLMcode mret;
807 fd_set rs;
808 fd_set ws;
809 fd_set es;
810 int max;
811 struct GNUNET_NETWORK_FDSet *grs;
812 struct GNUNET_NETWORK_FDSet *gws;
813 long timeout;
814 struct GNUNET_TIME_Relative rtime;
815
816 max = -1;
817 FD_ZERO (&rs);
818 FD_ZERO (&ws);
819 FD_ZERO (&es);
820 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
821 if (mret != CURLM_OK)
822 {
823 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
824 _ ("%s failed at %s:%d: `%s'\n"),
825 "curl_multi_fdset",
826 __FILE__,
827 __LINE__,
828 curl_multi_strerror (mret));
829 clean_up ();
830 return;
831 }
832 mret = curl_multi_timeout (multi, &timeout);
833 if (mret != CURLM_OK)
834 {
835 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
836 _ ("%s failed at %s:%d: `%s'\n"),
837 "curl_multi_timeout",
838 __FILE__,
839 __LINE__,
840 curl_multi_strerror (mret));
841 clean_up ();
842 return;
843 }
844 rtime = GNUNET_TIME_relative_min (
845 GNUNET_TIME_absolute_get_remaining (end_time),
846 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
847 grs = GNUNET_NETWORK_fdset_create ();
848 gws = GNUNET_NETWORK_fdset_create ();
849 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
850 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
851 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
852 "Scheduling task for hostlist download using cURL\n");
853 ti_download = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
854 rtime,
855 grs,
856 gws,
857 &task_download,
858 multi);
859 GNUNET_NETWORK_fdset_destroy (gws);
860 GNUNET_NETWORK_fdset_destroy (grs);
861}
862
863
864static void
865task_download (void *cls)
866{
867 int running;
868 struct CURLMsg *msg;
869 CURLMcode mret;
870
871 ti_download = NULL;
872 if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
873 {
874 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
875 _ ("Timeout trying to download hostlist from `%s'\n"),
876 current_url);
877 update_hostlist ();
878 clean_up ();
879 return;
880 }
881 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
882 "Ready for processing hostlist client request\n");
883 do
884 {
885 running = 0;
886 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
887 {
888 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
889 _ (
890 "Download limit of %u bytes exceeded, stopping download\n"),
891 MAX_BYTES_PER_HOSTLISTS);
892 clean_up ();
893 return;
894 }
895 mret = curl_multi_perform (multi, &running);
896 if (running == 0)
897 {
898 do
899 {
900 msg = curl_multi_info_read (multi, &running);
901 GNUNET_break (msg != NULL);
902 if (msg == NULL)
903 break;
904 switch (msg->msg)
905 {
906 case CURLMSG_DONE:
907 if ((msg->data.result != CURLE_OK) &&
908 (msg->data.result != CURLE_GOT_NOTHING))
909 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
910 _ ("Download of hostlist from `%s' failed: `%s'\n"),
911 current_url,
912 curl_easy_strerror (msg->data.result));
913 else
914 {
915 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
916 _ ("Download of hostlist `%s' completed.\n"),
917 current_url);
918 stat_download_successful = GNUNET_YES;
919 update_hostlist ();
920 if (GNUNET_YES == stat_testing_hostlist)
921 {
922 GNUNET_log (
923 GNUNET_ERROR_TYPE_INFO,
924 _ ("Adding successfully tested hostlist `%s' datastore.\n"),
925 current_url);
926 insert_hostlist ();
927 hostlist_to_test = NULL;
928 stat_testing_hostlist = GNUNET_NO;
929 }
930 }
931 clean_up ();
932 return;
933
934 default:
935 break;
936 }
937 }
938 while ((running > 0));
939 }
940 }
941 while (mret == CURLM_CALL_MULTI_PERFORM);
942
943 if (mret != CURLM_OK)
944 {
945 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
946 _ ("%s failed at %s:%d: `%s'\n"),
947 "curl_multi_perform",
948 __FILE__,
949 __LINE__,
950 curl_multi_strerror (mret));
951 clean_up ();
952 }
953 download_prepare ();
954}
955
956
957/**
958 * Main function that will download a hostlist and process its
959 * data.
960 */
961static void
962download_hostlist ()
963{
964 CURLcode ret;
965 CURLMcode mret;
966
967
968 current_url = download_get_url ();
969 if (current_url == NULL)
970 return;
971 curl = curl_easy_init ();
972 multi = NULL;
973 if (curl == NULL)
974 {
975 GNUNET_break (0);
976 clean_up ();
977 return;
978 }
979 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
980 _ ("Bootstrapping using hostlist at `%s'.\n"),
981 current_url);
982
983 stat_download_in_progress = GNUNET_YES;
984 stat_download_successful = GNUNET_NO;
985 stat_hellos_obtained = 0;
986 stat_bytes_downloaded = 0;
987
988 GNUNET_STATISTICS_update (stats,
989 gettext_noop ("# hostlist downloads initiated"),
990 1,
991 GNUNET_NO);
992 if (NULL != proxy)
993 {
994 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
995 CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
996 if (NULL != proxy_username)
997 CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
998 if (NULL != proxy_password)
999 CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
1000 }
1001 download_pos = 0;
1002 stat_bogus_url = 0;
1003 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
1004 if (ret != CURLE_OK)
1005 {
1006 clean_up ();
1007 return;
1008 }
1009 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
1010 if (ret != CURLE_OK)
1011 {
1012 clean_up ();
1013 return;
1014 }
1015 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
1016 CURL_EASY_SETOPT (curl,
1017 CURLOPT_REDIR_PROTOCOLS,
1018 CURLPROTO_HTTP | CURLPROTO_HTTPS);
1019 CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1020 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
1021 /* no need to abort if the above failed */
1022 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1023 if (ret != CURLE_OK)
1024 {
1025 clean_up ();
1026 return;
1027 }
1028 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1029#if 0
1030 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1031#endif
1032 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_MAX_MESSAGE_SIZE);
1033 if (0 == strncmp (current_url, "http", 4))
1034 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1035 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1036 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1037 multi = curl_multi_init ();
1038 if (multi == NULL)
1039 {
1040 GNUNET_break (0);
1041 /* clean_up (); */
1042 return;
1043 }
1044 mret = curl_multi_add_handle (multi, curl);
1045 if (mret != CURLM_OK)
1046 {
1047 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1048 _ ("%s failed at %s:%d: `%s'\n"),
1049 "curl_multi_add_handle",
1050 __FILE__,
1051 __LINE__,
1052 curl_multi_strerror (mret));
1053 mret = curl_multi_cleanup (multi);
1054 if (mret != CURLM_OK)
1055 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1056 _ ("%s failed at %s:%d: `%s'\n"),
1057 "curl_multi_cleanup",
1058 __FILE__,
1059 __LINE__,
1060 curl_multi_strerror (mret));
1061 multi = NULL;
1062 clean_up ();
1063 return;
1064 }
1065 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1066 download_prepare ();
1067}
1068
1069
1070static void
1071task_download_dispatcher (void *cls)
1072{
1073 ti_download_dispatcher_task = NULL;
1074 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1075 if (GNUNET_NO == stat_download_in_progress)
1076 {
1077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1078 download_hostlist ();
1079 }
1080 else
1081 {
1082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083 "Download in progress, have to wait...\n");
1084 ti_download_dispatcher_task =
1085 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1086 &task_download_dispatcher,
1087 NULL);
1088 }
1089}
1090
1091
1092/**
1093 * Task that checks if we should try to download a hostlist.
1094 * If so, we initiate the download, otherwise we schedule
1095 * this task again for a later time.
1096 */
1097static void
1098task_check (void *cls)
1099{
1100 static int once;
1101 struct GNUNET_TIME_Relative delay;
1102
1103 ti_check_download = NULL;
1104 if (stats == NULL)
1105 {
1106 curl_global_cleanup ();
1107 return; /* in shutdown */
1108 }
1109 if ((stat_connection_count < MIN_CONNECTIONS) &&
1110 (NULL == ti_download_dispatcher_task))
1111 ti_download_dispatcher_task =
1112 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1113
1114 delay = hostlist_delay;
1115 if (0 == hostlist_delay.rel_value_us)
1116 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1117 else
1118 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1119 if (hostlist_delay.rel_value_us >
1120 GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1121 hostlist_delay =
1122 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1123 (1 + stat_connection_count));
1124 GNUNET_STATISTICS_set (stats,
1125 gettext_noop (
1126 "# milliseconds between hostlist downloads"),
1127 hostlist_delay.rel_value_us / 1000LL,
1128 GNUNET_YES);
1129 if (0 == once)
1130 {
1131 delay = GNUNET_TIME_UNIT_ZERO;
1132 once = 1;
1133 }
1134 GNUNET_log (
1135 GNUNET_ERROR_TYPE_INFO,
1136 _ ("Have %u/%u connections. Will consider downloading hostlist in %s\n"),
1137 stat_connection_count,
1138 MIN_CONNECTIONS,
1139 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1140 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1141}
1142
1143
1144/**
1145 * This tasks sets hostlist testing to allowed after interval between to testings is reached
1146 *
1147 * @param cls closure
1148 */
1149static void
1150task_testing_intervall_reset (void *cls)
1151{
1152 ti_testing_intervall_task = NULL;
1153 stat_testing_allowed = GNUNET_OK;
1154 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1155 "Testing new hostlist advertisements is allowed again\n");
1156}
1157
1158
1159/**
1160 * Task that writes hostlist entries to a file on a regular base
1161 *
1162 * @param cls closure
1163 */
1164static void
1165task_hostlist_saving (void *cls)
1166{
1167 ti_saving_task = NULL;
1168 save_hostlist_file (GNUNET_NO);
1169
1170 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1171 "Hostlists will be saved to file again in %s\n",
1172 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1173 GNUNET_YES));
1174 ti_saving_task =
1175 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL, &task_hostlist_saving, NULL);
1176}
1177
1178
1179/**
1180 * Method called whenever a given peer connects.
1181 *
1182 * @param cls closure
1183 * @param peer peer identity this notification is about
1184 * @param mq message queue for transmissions to @a peer
1185 */
1186static void *
1187handler_connect (void *cls,
1188 const struct GNUNET_PeerIdentity *peer,
1189 struct GNUNET_MQ_Handle *mq)
1190{
1191 GNUNET_assert (stat_connection_count < UINT_MAX);
1192 stat_connection_count++;
1193 GNUNET_STATISTICS_update (stats,
1194 gettext_noop ("# active connections"),
1195 1,
1196 GNUNET_NO);
1197 return NULL;
1198}
1199
1200
1201/**
1202 * Method called whenever a given peer disconnects.
1203 *
1204 * @param cls closure
1205 * @param peer peer identity this notification is about
1206 */
1207static void
1208handler_disconnect (void *cls,
1209 const struct GNUNET_PeerIdentity *peer,
1210 void *internal_cls)
1211{
1212 GNUNET_assert (stat_connection_count > 0);
1213 stat_connection_count--;
1214 GNUNET_STATISTICS_update (stats,
1215 gettext_noop ("# active connections"),
1216 -1,
1217 GNUNET_NO);
1218}
1219
1220
1221/**
1222 * Method called whenever an advertisement message arrives.
1223 *
1224 * @param uri the advertised URI
1225 */
1226static void
1227handler_advertisement (const char *uri)
1228{
1229 size_t uri_size;
1230 struct Hostlist *hostlist;
1231
1232 uri_size = strlen (uri) + 1;
1233 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1234 "Hostlist client received advertisement containing URI `%s'\n",
1235 uri);
1236 if (GNUNET_NO != linked_list_contains (uri))
1237 {
1238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1239 return;
1240 }
1241
1242 if (GNUNET_NO == stat_testing_allowed)
1243 {
1244 GNUNET_log (
1245 GNUNET_ERROR_TYPE_DEBUG,
1246 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1247 return;
1248 }
1249 if (GNUNET_YES == stat_testing_hostlist)
1250 {
1251 GNUNET_log (
1252 GNUNET_ERROR_TYPE_DEBUG,
1253 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1254 return;
1255 }
1256
1257 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + uri_size);
1258 hostlist->hostlist_uri = (const char *) &hostlist[1];
1259 GNUNET_memcpy (&hostlist[1], uri, uri_size);
1260 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1261 hostlist->quality = HOSTLIST_INITIAL;
1262 hostlist_to_test = hostlist;
1263
1264 stat_testing_hostlist = GNUNET_YES;
1265 stat_testing_allowed = GNUNET_NO;
1266 ti_testing_intervall_task =
1267 GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1268 &task_testing_intervall_reset,
1269 NULL);
1270
1271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1272 "Testing new hostlist advertisements is locked for the next %s\n",
1273 GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1274 GNUNET_YES));
1275
1276 ti_download_dispatcher_task =
1277 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1278}
1279
1280
1281/**
1282 * Continuation called by the statistics code once
1283 * we go the stat. Initiates hostlist download scheduling.
1284 *
1285 * @param cls closure
1286 * @param success #GNUNET_OK if statistics were
1287 * successfully obtained, #GNUNET_SYSERR if not.
1288 */
1289static void
1290primary_task (void *cls, int success)
1291{
1292 if (NULL != ti_check_download)
1293 {
1294 GNUNET_SCHEDULER_cancel (ti_check_download);
1295 ti_check_download = NULL;
1296 }
1297 sget = NULL;
1298 GNUNET_assert (NULL != stats);
1299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1300 "Statistics request done, scheduling hostlist download\n");
1301 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1302}
1303
1304
1305/**
1306 * Continuation called by the statistics code once
1307 * we go the stat. Initiates hostlist download scheduling.
1308 *
1309 * @param cls closure
1310 */
1311static void
1312stat_timeout_task (void *cls)
1313{
1314 GNUNET_STATISTICS_get_cancel (sget);
1315 sget = NULL;
1316 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1317}
1318
1319
1320/**
1321 * We've received the previous delay value from statistics. Remember it.
1322 *
1323 * @param cls NULL, unused
1324 * @param subsystem should be "hostlist", unused
1325 * @param name will be "milliseconds between hostlist downloads", unused
1326 * @param value previous delay value, in milliseconds (!)
1327 * @param is_persistent unused, will be #GNUNET_YES
1328 */
1329static int
1330process_stat (void *cls,
1331 const char *subsystem,
1332 const char *name,
1333 uint64_t value,
1334 int is_persistent)
1335{
1336 hostlist_delay.rel_value_us = value * 1000LL;
1337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1338 "Initial time between hostlist downloads is %s\n",
1339 GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1340 GNUNET_YES));
1341 return GNUNET_OK;
1342}
1343
1344
1345/**
1346 * Method to load persistent hostlist file during hostlist client startup
1347 */
1348static void
1349load_hostlist_file ()
1350{
1351 char *filename;
1352 char *uri;
1353 char *emsg;
1354 struct Hostlist *hostlist;
1355 uint32_t times_used;
1356 uint32_t hellos_returned;
1357 uint64_t quality;
1358 uint64_t last_used;
1359 uint64_t created;
1360 uint32_t counter;
1361 struct GNUNET_BIO_ReadHandle *rh;
1362
1363 uri = NULL;
1364 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1365 "HOSTLIST",
1366 "HOSTLISTFILE",
1367 &filename))
1368 {
1369 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1370 "hostlist",
1371 "HOSTLISTFILE");
1372 return;
1373 }
1374
1375 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1376 _ ("Loading saved hostlist entries from file `%s' \n"),
1377 filename);
1378 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1379 {
1380 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1381 _ ("Hostlist file `%s' does not exist\n"),
1382 filename);
1383 GNUNET_free (filename);
1384 return;
1385 }
1386
1387 rh = GNUNET_BIO_read_open_file (filename);
1388 if (NULL == rh)
1389 {
1390 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1391 _ (
1392 "Could not open file `%s' for reading to load hostlists: %s\n"),
1393 filename,
1394 strerror (errno));
1395 GNUNET_free (filename);
1396 return;
1397 }
1398
1399 counter = 0;
1400 struct GNUNET_BIO_ReadSpec rs[] = {
1401 GNUNET_BIO_read_spec_int32 ("times used", (int32_t *) &times_used),
1402 GNUNET_BIO_read_spec_int64 ("quality", (int64_t *) &quality),
1403 GNUNET_BIO_read_spec_int64 ("last used", (int64_t *) &last_used),
1404 GNUNET_BIO_read_spec_int64 ("created", (int64_t *) &created),
1405 GNUNET_BIO_read_spec_int32 ("hellos returned",
1406 (int32_t *) &hellos_returned),
1407 GNUNET_BIO_read_spec_end (),
1408 };
1409 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1410 (NULL != uri) &&
1411 (GNUNET_OK == GNUNET_BIO_read_spec_commit (rh, rs)))
1412 {
1413 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + strlen (uri) + 1);
1414 hostlist->hello_count = hellos_returned;
1415 hostlist->hostlist_uri = (const char *) &hostlist[1];
1416 GNUNET_memcpy (&hostlist[1], uri, strlen (uri) + 1);
1417 hostlist->quality = quality;
1418 hostlist->time_creation.abs_value_us = created;
1419 hostlist->time_last_usage.abs_value_us = last_used;
1420 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1421 linked_list_size++;
1422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1423 "Added hostlist entry with URI `%s' \n",
1424 hostlist->hostlist_uri);
1425 GNUNET_free (uri);
1426 uri = NULL;
1427 counter++;
1428 if (counter >= MAX_NUMBER_HOSTLISTS)
1429 break;
1430 }
1431
1432 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1433 _ ("%u hostlist URIs loaded from file\n"),
1434 counter);
1435 GNUNET_STATISTICS_set (stats,
1436 gettext_noop ("# hostlist URIs read from file"),
1437 counter,
1438 GNUNET_YES);
1439 GNUNET_STATISTICS_set (stats,
1440 gettext_noop ("# advertised hostlist URIs"),
1441 linked_list_size,
1442 GNUNET_NO);
1443
1444 GNUNET_free (uri);
1445 emsg = NULL;
1446 (void) GNUNET_BIO_read_close (rh, &emsg);
1447 if (emsg != NULL)
1448 GNUNET_free (emsg);
1449 GNUNET_free (filename);
1450}
1451
1452
1453/**
1454 * Method to save persistent hostlist file during hostlist client shutdown
1455 *
1456 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1457 */
1458static void
1459save_hostlist_file (int shutdown)
1460{
1461 char *filename;
1462 struct Hostlist *pos;
1463 struct GNUNET_BIO_WriteHandle *wh;
1464 int ok;
1465 uint32_t counter;
1466
1467 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1468 "HOSTLIST",
1469 "HOSTLISTFILE",
1470 &filename))
1471 {
1472 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1473 "hostlist",
1474 "HOSTLISTFILE");
1475 return;
1476 }
1477 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1478 {
1479 GNUNET_free (filename);
1480 return;
1481 }
1482 wh = GNUNET_BIO_write_open_file (filename);
1483 if (NULL == wh)
1484 {
1485 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1486 _ (
1487 "Could not open file `%s' for writing to save hostlists: %s\n"),
1488 filename,
1489 strerror (errno));
1490 GNUNET_free (filename);
1491 return;
1492 }
1493 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1494 _ ("Writing %u hostlist URIs to `%s'\n"),
1495 linked_list_size,
1496 filename);
1497 /* add code to write hostlists to file using bio */
1498 ok = GNUNET_YES;
1499 counter = 0;
1500 while (NULL != (pos = linked_list_head))
1501 {
1502 if (GNUNET_YES == shutdown)
1503 {
1504 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1505 linked_list_size--;
1506 }
1507 if (GNUNET_YES == ok)
1508 {
1509 struct GNUNET_BIO_WriteSpec ws[] = {
1510 GNUNET_BIO_write_spec_string ("hostlist uri", pos->hostlist_uri),
1511 GNUNET_BIO_write_spec_int32 ("times used",
1512 (int32_t *) &pos->times_used),
1513 GNUNET_BIO_write_spec_int64 ("quality", (int64_t *) &pos->quality),
1514 GNUNET_BIO_write_spec_int64 (
1515 "last usage",
1516 (int64_t *) &pos->time_last_usage.abs_value_us),
1517 GNUNET_BIO_write_spec_int64 (
1518 "creation time",
1519 (int64_t *) &pos->time_creation.abs_value_us),
1520 GNUNET_BIO_write_spec_int32 ("hellos count",
1521 (int32_t *) &pos->hello_count),
1522 GNUNET_BIO_write_spec_end (),
1523 };
1524 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
1525 {
1526 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1527 _ ("Error writing hostlist URIs to file `%s'\n"),
1528 filename);
1529 ok = GNUNET_NO;
1530 }
1531 }
1532
1533 if (GNUNET_YES == shutdown)
1534 GNUNET_free (pos);
1535 counter++;
1536 if (counter >= MAX_NUMBER_HOSTLISTS)
1537 break;
1538 }
1539 GNUNET_STATISTICS_set (stats,
1540 gettext_noop ("# hostlist URIs written to file"),
1541 counter,
1542 GNUNET_YES);
1543
1544 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1545 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1546 _ ("Error writing hostlist URIs to file `%s'\n"),
1547 filename);
1548 GNUNET_free (filename);
1549}
1550
1551
1552/**
1553 * Start downloading hostlists from hostlist servers as necessary.
1554 *
1555 * @param c configuration to use
1556 * @param st statistics handle to use
1557 * @param[out] ch set to handler for CORE connect events
1558 * @param[out] dh set to handler for CORE disconnect events
1559 * @param[out] msgh set to handler for CORE advertisement messages
1560 * @param learn should we learn hostlist URLs from CORE
1561 * @return #GNUNET_OK on success
1562 */
1563int
1564GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1565 struct GNUNET_STATISTICS_Handle *st,
1566 GNUNET_CORE_ConnectEventHandler *ch,
1567 GNUNET_CORE_DisconnectEventHandler *dh,
1568 GNUNET_HOSTLIST_UriHandler *msgh,
1569 int learn)
1570{
1571 char *filename;
1572 char *proxytype_str;
1573 int result;
1574
1575 GNUNET_assert (NULL != st);
1576 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1577 {
1578 GNUNET_break (0);
1579 return GNUNET_SYSERR;
1580 }
1581 cfg = c;
1582 stats = st;
1583
1584 /* Read proxy configuration */
1585 peerstore = GNUNET_PEERSTORE_connect (c);
1586 if (GNUNET_OK ==
1587 GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "PROXY", &proxy))
1588 {
1589 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found proxy host: `%s'\n", proxy);
1590 /* proxy username */
1591 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1592 "HOSTLIST",
1593 "PROXY_USERNAME",
1594 &proxy_username))
1595 {
1596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1597 "Found proxy username name: `%s'\n",
1598 proxy_username);
1599 }
1600
1601 /* proxy password */
1602 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1603 "HOSTLIST",
1604 "PROXY_PASSWORD",
1605 &proxy_password))
1606 {
1607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1608 "Found proxy password name: `%s'\n",
1609 proxy_password);
1610 }
1611
1612 /* proxy type */
1613 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1614 "HOSTLIST",
1615 "PROXY_TYPE",
1616 &proxytype_str))
1617 {
1618 if (GNUNET_OK != GNUNET_STRINGS_utf8_toupper (proxytype_str,
1619 proxytype_str))
1620 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1621 "Unable to convert `%s' to UTF-8 uppercase\n",
1622 proxytype_str);
1623 proxy_type = CURLPROXY_HTTP;
1624 if (0 == strcmp (proxytype_str, "HTTP"))
1625 proxy_type = CURLPROXY_HTTP;
1626 else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1627 proxy_type = CURLPROXY_HTTP_1_0;
1628 else if (0 == strcmp (proxytype_str, "SOCKS4"))
1629 proxy_type = CURLPROXY_SOCKS4;
1630 else if (0 == strcmp (proxytype_str, "SOCKS5"))
1631 proxy_type = CURLPROXY_SOCKS5;
1632 else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1633 proxy_type = CURLPROXY_SOCKS4A;
1634 else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1635 proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1636 else
1637 {
1638 GNUNET_log (
1639 GNUNET_ERROR_TYPE_ERROR,
1640 _ (
1641 "Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1642 proxytype_str);
1643 GNUNET_free (proxytype_str);
1644 GNUNET_free (proxy);
1645 proxy = NULL;
1646 GNUNET_free (proxy_username);
1647 proxy_username = NULL;
1648 GNUNET_free (proxy_password);
1649 proxy_password = NULL;
1650
1651 return GNUNET_SYSERR;
1652 }
1653 }
1654 GNUNET_free (proxytype_str);
1655 }
1656
1657 stat_learning = learn;
1658 *ch = &handler_connect;
1659 *dh = &handler_disconnect;
1660 linked_list_head = NULL;
1661 linked_list_tail = NULL;
1662 stat_use_bootstrap = GNUNET_YES;
1663 stat_testing_hostlist = GNUNET_NO;
1664 stat_testing_allowed = GNUNET_YES;
1665
1666 if (GNUNET_YES == stat_learning)
1667 {
1668 *msgh = &handler_advertisement;
1669 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1670 _ ("Learning is enabled on this peer\n"));
1671 load_hostlist_file ();
1672 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1673 "Hostlists will be saved to file again in %s\n",
1674 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1675 GNUNET_YES));
1676 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1677 &task_hostlist_saving,
1678 NULL);
1679 }
1680 else
1681 {
1682 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1683 _ ("Learning is not enabled on this peer\n"));
1684 *msgh = NULL;
1685 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1686 "HOSTLIST",
1687 "HOSTLISTFILE",
1688 &filename))
1689 {
1690 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1691 {
1692 result = remove (filename);
1693 if (0 == result)
1694 GNUNET_log (
1695 GNUNET_ERROR_TYPE_INFO,
1696 _ (
1697 "Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1698 filename);
1699 else
1700 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1701 "remove",
1702 filename);
1703 }
1704 }
1705 GNUNET_free (filename);
1706 }
1707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1708 "Loading stats value on hostlist download frequency\n");
1709 sget = GNUNET_STATISTICS_get (stats,
1710 "hostlist",
1711 gettext_noop (
1712 "# milliseconds between hostlist downloads"),
1713 &primary_task,
1714 &process_stat,
1715 NULL);
1716 if (NULL == sget)
1717 {
1718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1719 "Statistics request failed, scheduling hostlist download\n");
1720 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1721 }
1722 else
1723 {
1724 ti_check_download = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1725 &stat_timeout_task,
1726 NULL);
1727 }
1728 return GNUNET_OK;
1729}
1730
1731
1732/**
1733 * Stop downloading hostlists from hostlist servers as necessary.
1734 */
1735void
1736GNUNET_HOSTLIST_client_stop ()
1737{
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1739 if (NULL != sget)
1740 {
1741 GNUNET_STATISTICS_get_cancel (sget);
1742 sget = NULL;
1743 }
1744 stats = NULL;
1745 if (GNUNET_YES == stat_learning)
1746 save_hostlist_file (GNUNET_YES);
1747 if (NULL != ti_saving_task)
1748 {
1749 GNUNET_SCHEDULER_cancel (ti_saving_task);
1750 ti_saving_task = NULL;
1751 }
1752 if (NULL != ti_download_dispatcher_task)
1753 {
1754 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1755 ti_download_dispatcher_task = NULL;
1756 }
1757 if (NULL != ti_testing_intervall_task)
1758 {
1759 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1760 ti_testing_intervall_task = NULL;
1761 }
1762 if (NULL != ti_download)
1763 {
1764 GNUNET_SCHEDULER_cancel (ti_download);
1765 ti_download = NULL;
1766 update_hostlist ();
1767 clean_up ();
1768 }
1769 if (NULL != ti_check_download)
1770 {
1771 GNUNET_SCHEDULER_cancel (ti_check_download);
1772 ti_check_download = NULL;
1773 curl_global_cleanup ();
1774 }
1775 GNUNET_free (proxy);
1776 proxy = NULL;
1777 GNUNET_free (proxy_username);
1778 proxy_username = NULL;
1779 GNUNET_free (proxy_password);
1780 proxy_password = NULL;
1781 if (NULL != peerstore)
1782 {
1783 GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
1784 peerstore = NULL;
1785 }
1786 cfg = NULL;
1787}
1788
1789
1790/* end of gnunet-daemon-hostlist_client.c */
diff --git a/src/service/hostlist/gnunet-daemon-hostlist_client.h b/src/service/hostlist/gnunet-daemon-hostlist_client.h
new file mode 100644
index 000000000..2ee40d961
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist_client.h
@@ -0,0 +1,70 @@
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 hostlist/gnunet-daemon-hostlist_client.h
22 * @brief hostlist support. Downloads HELLOs via HTTP.
23 * @author Christian Grothoff
24 */
25#ifndef GNUNET_DAEMON_HOSTLIST_CLIENT_H
26#define GNUNET_DAEMON_HOSTLIST_CLIENT_H
27
28#include "gnunet_core_service.h"
29#include "gnunet_statistics_service.h"
30#include "gnunet_util_lib.h"
31
32
33/**
34 * Function that handles an advertised URI.
35 *
36 * @param uri 0-termianted URI of a hostlist
37 */
38typedef void
39(*GNUNET_HOSTLIST_UriHandler)(const char *uri);
40
41
42/**
43 * Start downloading hostlists from hostlist servers as necessary.
44 *
45 * @param c configuration to use
46 * @param st statistics handle to use
47 * @param[out] ch set to handler for CORE connect events
48 * @param[out] dh set to handler for CORE disconnect events
49 * @param[out] msgh set to handler for CORE advertisement messages
50 * @param learn should we learn hostlist URLs from CORE
51 * @return #GNUNET_OK on success
52 */
53int
54GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
55 struct GNUNET_STATISTICS_Handle *st,
56 GNUNET_CORE_ConnectEventHandler *ch,
57 GNUNET_CORE_DisconnectEventHandler *dh,
58 GNUNET_HOSTLIST_UriHandler *msgh,
59 int learn);
60
61
62/**
63 * Stop downloading hostlists from hostlist servers as necessary.
64 */
65void
66GNUNET_HOSTLIST_client_stop (void);
67
68
69#endif
70/* end of gnunet-daemon-hostlist_client.h */
diff --git a/src/service/hostlist/gnunet-daemon-hostlist_server.c b/src/service/hostlist/gnunet-daemon-hostlist_server.c
new file mode 100644
index 000000000..c4a7aab03
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist_server.c
@@ -0,0 +1,890 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 2009, 2010, 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/**
22 * @file hostlist/gnunet-daemon-hostlist_server.c
23 * @author Christian Grothoff
24 * @author Matthias Wachs
25 * @author David Barksdale
26 * @brief application to provide an integrated hostlist HTTP server
27 */
28#include "platform.h"
29#include <microhttpd.h>
30#include "gnunet-daemon-hostlist_server.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_peerstore_service.h"
33#include "gnunet-daemon-hostlist.h"
34#include "gnunet_resolver_service.h"
35#include "gnunet_mhd_compat.h"
36
37
38/**
39 * How long until our hostlist advertisement transmission via CORE should
40 * time out?
41 */
42#define GNUNET_ADV_TIMEOUT \
43 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
44
45/**
46 * Map with hellos we build the hostlist with.
47 */
48struct GNUNET_CONTAINER_MultiPeerMap *hellos;
49
50/**
51 * Handle to the HTTP server as provided by libmicrohttpd for IPv6.
52 */
53static struct MHD_Daemon *daemon_handle_v6;
54
55/**
56 * Handle to the HTTP server as provided by libmicrohttpd for IPv4.
57 */
58static struct MHD_Daemon *daemon_handle_v4;
59
60/**
61 * Our configuration.
62 */
63static const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65/**
66 * For keeping statistics.
67 */
68static struct GNUNET_STATISTICS_Handle *stats;
69
70/**
71 * Handle to the core service (NULL until we've connected to it).
72 */
73static struct GNUNET_CORE_Handle *core;
74
75/**
76 * The task to delayed start the notification process intially.
77 * We like to give transport some time to give us our hello to distribute it.
78 */
79struct GNUNET_SCHEDULER_Task *peerstore_notify_task;
80
81/**
82 * Our peerstore notification context. We use notification
83 * to instantly learn about new peers as they are discovered.
84 */
85static struct GNUNET_PEERSTORE_NotifyContext *peerstore_notify;
86
87/**
88 * Our primary task for IPv4.
89 */
90static struct GNUNET_SCHEDULER_Task *hostlist_task_v4;
91
92/**
93 * Our primary task for IPv6.
94 */
95static struct GNUNET_SCHEDULER_Task *hostlist_task_v6;
96
97/**
98 * Our canonical response.
99 */
100static struct MHD_Response *response;
101
102/**
103 * Handle to the PEERSTORE service.
104 */
105static struct GNUNET_PEERSTORE_Handle *peerstore;
106
107/**
108 * Set if we are allowed to advertise our hostlist to others.
109 */
110static int advertising;
111
112/**
113 * Buffer for the hostlist address
114 */
115static char *hostlist_uri;
116
117
118/**
119 * Context for #host_processor().
120 */
121struct HostSet
122{
123 /**
124 * Place where we accumulate all of the HELLO messages.
125 */
126 char *data;
127
128 /**
129 * Number of bytes in @e data.
130 */
131 unsigned int size;
132};
133
134
135/**
136 * NULL if we are not currently iterating over peer information.
137 */
138static struct HostSet *builder;
139
140
141/**
142 * Add headers to a request indicating that we allow Cross-Origin Resource
143 * Sharing.
144 *
145 * @param response response to add headers to
146 */
147static void
148add_cors_headers (struct MHD_Response *response)
149{
150 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
151 MHD_add_response_header (response,
152 "Access-Control-Allow-Methods",
153 "GET, OPTIONS");
154 MHD_add_response_header (response, "Access-Control-Max-Age", "86400");
155}
156
157
158/**
159 * Function that assembles our response.
160 */
161static void
162finish_response ()
163{
164 if (NULL != response)
165 MHD_destroy_response (response);
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167 "Creating hostlist response with %u bytes\n",
168 (unsigned int) builder->size);
169 response = MHD_create_response_from_buffer (builder->size,
170 builder->data,
171 MHD_RESPMEM_MUST_FREE);
172 add_cors_headers (response);
173 if ((NULL == daemon_handle_v4) && (NULL == daemon_handle_v6))
174 {
175 MHD_destroy_response (response);
176 response = NULL;
177 }
178 GNUNET_STATISTICS_set (stats,
179 gettext_noop ("bytes in hostlist"),
180 builder->size,
181 GNUNET_YES);
182 GNUNET_free (builder);
183 builder = NULL;
184}
185
186
187/**
188 * Callback that processes each of the known HELLOs for the
189 * hostlist response construction.
190 *
191 * @param cls closure, NULL
192 * @param peer id of the peer, NULL for last call
193 * @param hello hello message for the peer (can be NULL)
194 * @param err_msg message
195 */
196static enum GNUNET_GenericReturnValue
197host_processor (void *cls,
198 const struct GNUNET_PeerIdentity *peer,
199 void *value)
200{
201 (void *) cls;
202 size_t old;
203 size_t s;
204 struct GNUNET_MessageHeader *hello = value;
205 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
206 struct GNUNET_TIME_Absolute hello_exp;
207
208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
209 "host_processor\n");
210 old = builder->size;
211 s = ntohs (hello->size);
212 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213 "Received %u bytes of `%s' from peer `%s' for hostlist.\n",
214 (unsigned int) s,
215 "HELLO",
216 GNUNET_i2s (peer));
217 if ((old + s >= GNUNET_MAX_MALLOC_CHECKED) ||
218 (old + s >= MAX_BYTES_PER_HOSTLISTS))
219 {
220 /* too large, skip! */
221 GNUNET_STATISTICS_update (stats,
222 gettext_noop (
223 "bytes not included in hostlist (size limit)"),
224 s,
225 GNUNET_NO);
226 return GNUNET_YES;
227 }
228 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
229 "Adding peer `%s' to hostlist (%u bytes)\n",
230 GNUNET_i2s (peer),
231 (unsigned int) s);
232 GNUNET_array_grow (builder->data, builder->size, old + s);
233 GNUNET_memcpy (&builder->data[old], hello, s);
234
235 return GNUNET_YES;
236}
237
238
239/**
240 * Hostlist access policy (very permissive, allows everything).
241 * Returns #MHD_NO only if we are not yet ready to serve.
242 *
243 * @param cls closure
244 * @param addr address information from the client
245 * @param addrlen length of @a addr
246 * @return #MHD_YES if connection is allowed, #MHD_NO if not (we are not ready)
247 */
248static MHD_RESULT
249accept_policy_callback (void *cls,
250 const struct sockaddr *addr,
251 socklen_t addrlen)
252{
253 if (NULL == response)
254 {
255 GNUNET_log (
256 GNUNET_ERROR_TYPE_DEBUG,
257 "Received request for hostlist, but I am not yet ready; rejecting!\n");
258 return MHD_NO;
259 }
260 return MHD_YES; /* accept all */
261}
262
263
264/**
265 * Main request handler.
266 *
267 * @param cls argument given together with the function
268 * pointer when the handler was registered with MHD
269 * @param connection
270 * @param url the requested url
271 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
272 * #MHD_HTTP_METHOD_PUT, etc.)
273 * @param version the HTTP version string (e.g.
274 * #MHD_HTTP_VERSION_1_1)
275 * @param upload_data the data being uploaded (excluding HEADERS,
276 * for a POST that fits into memory and that is encoded
277 * with a supported encoding, the POST data will NOT be
278 * given in upload_data and is instead available as
279 * part of #MHD_get_connection_values; very large POST
280 * data *will* be made available incrementally in
281 * @a upload_data)
282 * @param upload_data_size set initially to the size of the
283 * @a upload_data provided; the method must update this
284 * value to the number of bytes NOT processed;
285 * @param con_cls pointer that the callback can set to some
286 * address and that will be preserved by MHD for future
287 * calls for this request; since the access handler may
288 * be called many times (e.g. for a PUT/POST operation
289 * with plenty of upload data) this allows the application
290 * to easily associate some request-specific state.
291 * If necessary, this state can be cleaned up in the
292 * global #MHD_RequestCompletedCallback (which
293 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
294 * Initially, `*con_cls` will be NULL.
295 * @return #MHD_YES if the connection was handled successfully,
296 * #MHD_NO if the socket must be closed due to a serious
297 * error while handling the request
298 */
299static MHD_RESULT
300access_handler_callback (void *cls,
301 struct MHD_Connection *connection,
302 const char *url,
303 const char *method,
304 const char *version,
305 const char *upload_data,
306 size_t *upload_data_size,
307 void **con_cls)
308{
309 static int dummy;
310
311 /* CORS pre-flight request */
312 if (0 == strcmp (MHD_HTTP_METHOD_OPTIONS, method))
313 {
314 struct MHD_Response *options_response;
315 int rc;
316
317 options_response =
318 MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT);
319 add_cors_headers (options_response);
320 rc = MHD_queue_response (connection, MHD_HTTP_OK, options_response);
321 MHD_destroy_response (options_response);
322 return rc;
323 }
324 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
325 {
326 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
327 _ ("Refusing `%s' request to hostlist server\n"),
328 method);
329 GNUNET_STATISTICS_update (stats,
330 gettext_noop (
331 "hostlist requests refused (not HTTP GET)"),
332 1,
333 GNUNET_YES);
334 return MHD_NO;
335 }
336 if (NULL == *con_cls)
337 {
338 (*con_cls) = &dummy;
339 return MHD_YES;
340 }
341 if (0 != *upload_data_size)
342 {
343 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
344 _ ("Refusing `%s' request with %llu bytes of upload data\n"),
345 method,
346 (unsigned long long) *upload_data_size);
347 GNUNET_STATISTICS_update (stats,
348 gettext_noop (
349 "hostlist requests refused (upload data)"),
350 1,
351 GNUNET_YES);
352 return MHD_NO; /* do not support upload data */
353 }
354 if (NULL == response)
355 {
356 GNUNET_log (
357 GNUNET_ERROR_TYPE_WARNING,
358 _ (
359 "Could not handle hostlist request since I do not have a response yet\n"));
360 GNUNET_STATISTICS_update (stats,
361 gettext_noop (
362 "hostlist requests refused (not ready)"),
363 1,
364 GNUNET_YES);
365 return MHD_NO; /* internal error, no response yet */
366 }
367 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
368 _ ("Received request for our hostlist\n"));
369 GNUNET_STATISTICS_update (stats,
370 gettext_noop ("hostlist requests processed"),
371 1,
372 GNUNET_YES);
373 return MHD_queue_response (connection, MHD_HTTP_OK, response);
374}
375
376
377/**
378 * Handler called by CORE when CORE is ready to transmit message
379 *
380 * @param cls closure with the `const struct GNUNET_PeerIdentity *` of
381 * the peer we are sending to
382 * @param size size of buffer to copy message to
383 * @param buf buffer to copy message to
384 * @return number of bytes copied to @a buf
385 */
386static void
387adv_transmit (struct GNUNET_MQ_Handle *mq)
388{
389 static uint64_t hostlist_adv_count;
390 size_t uri_size; /* Including \0 termination! */
391 struct GNUNET_MessageHeader *header;
392 struct GNUNET_MQ_Envelope *env;
393
394 uri_size = strlen (hostlist_uri) + 1;
395 env = GNUNET_MQ_msg_extra (header,
396 uri_size,
397 GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
398 GNUNET_memcpy (&header[1], hostlist_uri, uri_size);
399 GNUNET_MQ_env_set_options (env,
400 GNUNET_MQ_PREF_CORK_ALLOWED
401 | GNUNET_MQ_PREF_UNRELIABLE);
402 GNUNET_MQ_send (mq, env);
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404 "Sent advertisement message: Copied %u bytes into buffer!\n",
405 (unsigned int) uri_size);
406 hostlist_adv_count++;
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408 " # Sent advertisement message: %llu\n",
409 (unsigned long long) hostlist_adv_count);
410 GNUNET_STATISTICS_update (stats,
411 gettext_noop ("# hostlist advertisements send"),
412 1,
413 GNUNET_NO);
414}
415
416
417/**
418 * Method called whenever a given peer connects.
419 *
420 * @param cls closure
421 * @param peer peer identity this notification is about
422 * @param mq queue for transmission to @a peer
423 * @return NULL (must!)
424 */
425static void *
426connect_handler (void *cls,
427 const struct GNUNET_PeerIdentity *peer,
428 struct GNUNET_MQ_Handle *mq)
429{
430 size_t size;
431
432 if (! advertising)
433 return NULL;
434 if (NULL == hostlist_uri)
435 return NULL;
436 size = strlen (hostlist_uri) + 1;
437 if (size + sizeof(struct GNUNET_MessageHeader) >= GNUNET_MAX_MESSAGE_SIZE)
438 {
439 GNUNET_break (0);
440 return NULL;
441 }
442 size += sizeof(struct GNUNET_MessageHeader);
443 if (NULL == core)
444 {
445 GNUNET_break (0);
446 return NULL;
447 }
448 GNUNET_log (
449 GNUNET_ERROR_TYPE_DEBUG,
450 "Asked CORE to transmit advertisement message with a size of %u bytes to peer `%s'\n",
451 (unsigned int) size,
452 GNUNET_i2s (peer));
453 adv_transmit (mq);
454 return NULL;
455}
456
457
458/**
459 * PEERSTORE calls this function to let us know about a possible peer
460 * that we might want to connect to.
461 *
462 * @param cls closure (not used)
463 * @param peer potential peer to connect to
464 * @param hello HELLO for this peer (or NULL)
465 * @param err_msg NULL if successful, otherwise contains error message
466 */
467static void
468process_notify (void *cls,
469 const struct GNUNET_PeerIdentity *peer,
470 const struct GNUNET_MessageHeader *hello,
471 const char *err_msg)
472{
473 unsigned int map_size;
474 struct GNUNET_MessageHeader *hello_cpy;
475 struct GNUNET_PeerIdentity *peer_cpy;
476
477 map_size = GNUNET_CONTAINER_multipeermap_size (hellos);
478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479 "Peerstore is notifying us to rebuild our hostlist map size %u\n",
480 map_size);
481 if (NULL != err_msg)
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
484 _ ("Error in communication with PEERSTORE service: %s\n"),
485 err_msg);
486 return;
487 }
488 if (NULL != builder)
489 {
490 GNUNET_free (builder->data);
491 builder->size = 0;
492 builder->data = NULL;
493 }
494 else
495 {
496 builder = GNUNET_new (struct HostSet);
497 }
498
499 peer_cpy = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
500 GNUNET_memcpy (peer_cpy, peer, sizeof (struct GNUNET_PeerIdentity));
501 hello_cpy = GNUNET_malloc (ntohs (hello->size));
502 GNUNET_memcpy (hello_cpy, hello, ntohs (hello->size));
503 GNUNET_assert (GNUNET_YES ==
504 GNUNET_CONTAINER_multipeermap_put (hellos,
505 peer_cpy,
506 (struct
507 GNUNET_MessageHeader *)
508 hello_cpy,
509 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
510 if (0 != GNUNET_CONTAINER_multipeermap_iterate (hellos,
511 &host_processor,
512 NULL))
513 finish_response ();
514 map_size = GNUNET_CONTAINER_multipeermap_size (hellos);
515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
516 "1 Peerstore is notifying us to rebuild our hostlist map size %u peer %s\n",
517 map_size,
518 GNUNET_i2s (peer));
519}
520
521
522/**
523 * Function that queries MHD's select sets and
524 * starts the task waiting for them.
525 */
526static struct GNUNET_SCHEDULER_Task *
527prepare_daemon (struct MHD_Daemon *daemon_handle);
528
529
530/**
531 * Call MHD to process pending requests and then go back
532 * and schedule the next run.
533 *
534 * @param cls the `struct MHD_Daemon` of the HTTP server to run
535 */
536static void
537run_daemon (void *cls)
538{
539 struct MHD_Daemon *daemon_handle = cls;
540
541 if (daemon_handle == daemon_handle_v4)
542 hostlist_task_v4 = NULL;
543 else
544 hostlist_task_v6 = NULL;
545 GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
546 if (daemon_handle == daemon_handle_v4)
547 hostlist_task_v4 = prepare_daemon (daemon_handle);
548 else
549 hostlist_task_v6 = prepare_daemon (daemon_handle);
550}
551
552
553/**
554 * Function that queries MHD's select sets and
555 * starts the task waiting for them.
556 *
557 * @param daemon_handle HTTP server to prepare to run
558 */
559static struct GNUNET_SCHEDULER_Task *
560prepare_daemon (struct MHD_Daemon *daemon_handle)
561{
562 struct GNUNET_SCHEDULER_Task *ret;
563 fd_set rs;
564 fd_set ws;
565 fd_set es;
566 struct GNUNET_NETWORK_FDSet *wrs;
567 struct GNUNET_NETWORK_FDSet *wws;
568 int max;
569 MHD_UNSIGNED_LONG_LONG timeout;
570 int haveto;
571 struct GNUNET_TIME_Relative tv;
572
573 FD_ZERO (&rs);
574 FD_ZERO (&ws);
575 FD_ZERO (&es);
576 wrs = GNUNET_NETWORK_fdset_create ();
577 wws = GNUNET_NETWORK_fdset_create ();
578 max = -1;
579 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
580 haveto = MHD_get_timeout (daemon_handle, &timeout);
581 if (haveto == MHD_YES)
582 tv.rel_value_us = (uint64_t) timeout * 1000LL;
583 else
584 tv = GNUNET_TIME_UNIT_FOREVER_REL;
585 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
586 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
587 ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
588 tv,
589 wrs,
590 wws,
591 &run_daemon,
592 daemon_handle);
593 GNUNET_NETWORK_fdset_destroy (wrs);
594 GNUNET_NETWORK_fdset_destroy (wws);
595 return ret;
596}
597
598
599static void
600start_notify (void *cls)
601{
602 (void) cls;
603
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
605 "Starting to process new hellos to add to hostlist.\n");
606 peerstore_notify =
607 GNUNET_PEERSTORE_hello_changed_notify (peerstore, GNUNET_NO,
608 &process_notify, NULL);
609}
610
611
612/**
613 * Start server offering our hostlist.
614 *
615 * @param c configuration to use
616 * @param st statistics handle to use
617 * @param co core handle to use
618 * @param[out] server_ch set to handler for CORE connect events
619 * @param advertise #GNUNET_YES if we should advertise our hostlist
620 * @return #GNUNET_OK on success
621 */
622int
623GNUNET_HOSTLIST_server_start (const struct GNUNET_CONFIGURATION_Handle *c,
624 struct GNUNET_STATISTICS_Handle *st,
625 struct GNUNET_CORE_Handle *co,
626 GNUNET_CORE_ConnectEventHandler *server_ch,
627 int advertise)
628{
629 unsigned long long port;
630 char *hostname;
631 char *ipv4;
632 char *ipv6;
633 size_t size;
634 struct in_addr i4;
635 struct in6_addr i6;
636 struct sockaddr_in v4;
637 struct sockaddr_in6 v6;
638 const struct sockaddr *sa4;
639 const struct sockaddr *sa6;
640
641 hellos = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES);
642 advertising = advertise;
643 if (! advertising)
644 {
645 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
646 "Advertising not enabled on this hostlist server\n");
647 }
648 else
649 {
650 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
651 "Advertising enabled on this hostlist server\n");
652 }
653 cfg = c;
654 stats = st;
655 peerstore = GNUNET_PEERSTORE_connect (cfg);
656 if (NULL == peerstore)
657 {
658 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
659 _ ("Could not access PEERSTORE service. Exiting.\n"));
660 return GNUNET_SYSERR;
661 }
662 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
663 "HOSTLIST",
664 "HTTPPORT",
665 &port))
666 return GNUNET_SYSERR;
667 if ((0 == port) || (port > UINT16_MAX))
668 {
669 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
670 _ ("Invalid port number %llu. Exiting.\n"),
671 port);
672 return GNUNET_SYSERR;
673 }
674
675 if (GNUNET_SYSERR ==
676 GNUNET_CONFIGURATION_get_value_string (cfg,
677 "HOSTLIST",
678 "EXTERNAL_DNS_NAME",
679 &hostname))
680 hostname = GNUNET_RESOLVER_local_fqdn_get ();
681 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
682 _ ("Hostlist service starts on %s:%llu\n"),
683 hostname,
684 port);
685 if (NULL != hostname)
686 {
687 size = strlen (hostname);
688 if (size + 15 > MAX_URL_LEN)
689 {
690 GNUNET_break (0);
691 }
692 else
693 {
694 GNUNET_asprintf (&hostlist_uri,
695 "http://%s:%u/",
696 hostname,
697 (unsigned int) port);
698 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
699 _ ("Address to obtain hostlist: `%s'\n"),
700 hostlist_uri);
701 }
702 GNUNET_free (hostname);
703 }
704
705 if (GNUNET_CONFIGURATION_have_value (cfg, "HOSTLIST", "BINDTOIPV4"))
706 {
707 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
708 "HOSTLIST",
709 "BINDTOIP",
710 &ipv4))
711 {
712 GNUNET_log (
713 GNUNET_ERROR_TYPE_WARNING,
714 _ ("BINDTOIP does not a valid IPv4 address! Ignoring BINDTOIPV4.\n"));
715 }
716 }
717 else
718 ipv4 = NULL;
719 if (GNUNET_CONFIGURATION_have_value (cfg, "HOSTLIST", "BINDTOIPV6"))
720 {
721 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
722 "HOSTLIST",
723 "BINDTOIP",
724 &ipv6))
725 {
726 GNUNET_log (
727 GNUNET_ERROR_TYPE_WARNING,
728 _ ("BINDTOIP does not a valid IPv4 address! Ignoring BINDTOIPV6.\n"));
729 }
730 }
731 else
732 ipv6 = NULL;
733 sa4 = NULL;
734 if (NULL != ipv4)
735 {
736 if (1 == inet_pton (AF_INET, ipv4, &i4))
737 {
738 memset (&v4, 0, sizeof(v4));
739 v4.sin_family = AF_INET;
740 v4.sin_addr = i4;
741 v4.sin_port = htons (port);
742#if HAVE_SOCKADDR_IN_SIN_LEN
743 v4.sin_len = sizeof(v4);
744#endif
745 sa4 = (const struct sockaddr *) &v4;
746 }
747 else
748 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
749 _ (
750 "`%s' is not a valid IPv4 address! Ignoring BINDTOIPV4.\n"),
751 ipv4);
752 GNUNET_free (ipv4);
753 }
754 sa6 = NULL;
755 if (NULL != ipv6)
756 {
757 if (1 == inet_pton (AF_INET6, ipv6, &i6))
758 {
759 memset (&v6, 0, sizeof(v6));
760 v6.sin6_family = AF_INET6;
761 v6.sin6_addr = i6;
762 v6.sin6_port = htons (port);
763#if HAVE_SOCKADDR_IN_SIN_LEN
764 v6.sin6_len = sizeof(v6);
765#endif
766 sa6 = (const struct sockaddr *) &v6;
767 }
768 else
769 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
770 _ (
771 "`%s' is not a valid IPv6 address! Ignoring BINDTOIPV6.\n"),
772 ipv6);
773 GNUNET_free (ipv6);
774 }
775
776 daemon_handle_v6 = MHD_start_daemon (MHD_USE_IPv6 | MHD_USE_DEBUG,
777 (uint16_t) port,
778 &accept_policy_callback,
779 NULL,
780 &access_handler_callback,
781 NULL,
782 MHD_OPTION_CONNECTION_LIMIT,
783 (unsigned int) 128,
784 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
785 (unsigned int) 32,
786 MHD_OPTION_CONNECTION_TIMEOUT,
787 (unsigned int) 16,
788 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
789 (size_t) (16 * 1024),
790 MHD_OPTION_SOCK_ADDR,
791 sa6,
792 MHD_OPTION_END);
793 daemon_handle_v4 = MHD_start_daemon (MHD_NO_FLAG | MHD_USE_DEBUG,
794 (uint16_t) port,
795 &accept_policy_callback,
796 NULL,
797 &access_handler_callback,
798 NULL,
799 MHD_OPTION_CONNECTION_LIMIT,
800 (unsigned int) 128,
801 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
802 (unsigned int) 32,
803 MHD_OPTION_CONNECTION_TIMEOUT,
804 (unsigned int) 16,
805 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
806 (size_t) (16 * 1024),
807 MHD_OPTION_SOCK_ADDR,
808 sa4,
809 MHD_OPTION_END);
810
811 if ((NULL == daemon_handle_v6) && (NULL == daemon_handle_v4))
812 {
813 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
814 _ ("Could not start hostlist HTTP server on port %u\n"),
815 (unsigned short) port);
816 return GNUNET_SYSERR;
817 }
818
819 core = co;
820 *server_ch = &connect_handler;
821 if (NULL != daemon_handle_v4)
822 hostlist_task_v4 = prepare_daemon (daemon_handle_v4);
823 if (NULL != daemon_handle_v6)
824 hostlist_task_v6 = prepare_daemon (daemon_handle_v6);
825 peerstore_notify_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
826 start_notify,
827 NULL);
828 return GNUNET_OK;
829}
830
831
832/**
833 * Stop server offering our hostlist.
834 */
835void
836GNUNET_HOSTLIST_server_stop ()
837{
838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist server shutdown\n");
839 if (NULL != hostlist_task_v6)
840 {
841 GNUNET_SCHEDULER_cancel (hostlist_task_v6);
842 hostlist_task_v6 = NULL;
843 }
844 if (NULL != hostlist_task_v4)
845 {
846 GNUNET_SCHEDULER_cancel (hostlist_task_v4);
847 hostlist_task_v4 = NULL;
848 }
849 if (NULL != daemon_handle_v4)
850 {
851 MHD_stop_daemon (daemon_handle_v4);
852 daemon_handle_v4 = NULL;
853 }
854 if (NULL != daemon_handle_v6)
855 {
856 MHD_stop_daemon (daemon_handle_v6);
857 daemon_handle_v6 = NULL;
858 }
859 if (NULL != response)
860 {
861 MHD_destroy_response (response);
862 response = NULL;
863 }
864 if (NULL != peerstore_notify)
865 {
866 GNUNET_PEERSTORE_hello_changed_notify_cancel (peerstore_notify);
867 peerstore_notify = NULL;
868 }
869 else if (NULL != peerstore_notify_task)
870 {
871 GNUNET_SCHEDULER_cancel (peerstore_notify_task);
872 }
873 if (NULL != builder)
874 {
875 GNUNET_free (builder->data);
876 GNUNET_free (builder);
877 builder = NULL;
878 }
879 if (NULL != peerstore)
880 {
881 GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
882 peerstore = NULL;
883 }
884 cfg = NULL;
885 stats = NULL;
886 core = NULL;
887}
888
889
890/* end of gnunet-daemon-hostlist_server.c */
diff --git a/src/service/hostlist/gnunet-daemon-hostlist_server.h b/src/service/hostlist/gnunet-daemon-hostlist_server.h
new file mode 100644
index 000000000..13ba21e82
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist_server.h
@@ -0,0 +1,61 @@
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 hostlist/gnunet-daemon-hostlist_server.h
23 * @brief hostlist support. Downloads HELLOs via HTTP.
24 * @author Christian Grothoff
25 */
26
27#ifndef GNUNET_DAEMON_HOSTLIST_SERVER_H
28#define GNUNET_DAEMON_HOSTLIST_SERVER_H
29
30#include "gnunet_core_service.h"
31#include "gnunet_statistics_service.h"
32#include "gnunet_util_lib.h"
33
34
35/**
36 * Start server offering our hostlist.
37 *
38 * @param c configuration to use
39 * @param st statistics handle to use
40 * @param co core handle to use
41 * @param[out] server_ch set to handler for CORE connect events
42 * @param advertise #GNUNET_YES if we should advertise our hostlist
43 * @return #GNUNET_OK on success
44 */
45int
46GNUNET_HOSTLIST_server_start (const struct GNUNET_CONFIGURATION_Handle *c,
47 struct GNUNET_STATISTICS_Handle *st,
48 struct GNUNET_CORE_Handle *core,
49 GNUNET_CORE_ConnectEventHandler *server_ch,
50 int advertise);
51
52
53/**
54 * Stop server offering our hostlist.
55 */
56void
57GNUNET_HOSTLIST_server_stop (void);
58
59
60#endif
61/* end of gnunet-daemon-hostlist_server.h */
diff --git a/src/service/hostlist/hostlist.conf b/src/service/hostlist/hostlist.conf
new file mode 100644
index 000000000..d994ce9fb
--- /dev/null
+++ b/src/service/hostlist/hostlist.conf
@@ -0,0 +1,45 @@
1[hostlist]
2IMMEDIATE_START = YES
3NOARMBIND = YES
4BINARY = gnunet-daemon-hostlist
5
6# port for hostlist http server
7HTTPPORT = 8080
8
9# External DNS name other peers should use to access this hostlist
10# EXTERNAL_DNS_NAME =
11
12# Where do we store URLs of other hostlists we have learned?
13HOSTLISTFILE = $GNUNET_CONFIG_HOME/hostlist/learned.txt
14
15# Options:
16# -p : provide a hostlist as a hostlist servers
17# -b : bootstrap using configured hostlist servers
18# -e : enable learning advertised hostlists
19# -a : advertise hostlist to other servers
20OPTIONS = -b
21
22# Default list of hostlist servers for bootstrapping
23SERVERS = http://v15.gnunet.org/hostlist https://gnunet.io/hostlist
24# http://silent.0xdeadc0de.eu:8080/
25
26# bind hostlist http server to a specific IPv4
27# BINDTOIPV4 =
28
29# bind hostlist http server to a specific IPv6
30# BINDTOIPV6 =
31
32# Hostname or IP of proxy server for downloading hostlists
33# PROXY =
34
35# User name for proxy server
36# PROXY_USERNAME =
37# User password for proxy server
38# PROXY_PASSWORD =
39
40# Type of proxy server,
41# Valid values: HTTP, HTTP_1_0, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME
42# Default: HTTP
43# PROXY_TYPE = HTTP
44
45
diff --git a/src/service/hostlist/hostlists_learn_peer2.file b/src/service/hostlist/hostlists_learn_peer2.file
new file mode 100644
index 000000000..19d031c3e
--- /dev/null
+++ b/src/service/hostlist/hostlists_learn_peer2.file
Binary files differ
diff --git a/src/service/hostlist/learning_data.conf b/src/service/hostlist/learning_data.conf
new file mode 100644
index 000000000..4252f2524
--- /dev/null
+++ b/src/service/hostlist/learning_data.conf
@@ -0,0 +1,8 @@
1@INLINE@ test_hostlist_defaults.conf
2[hostlist]
3HTTPPORT = 28080
4OPTIONS = -b -e
5SERVERS = http://gnunet.org:8080/
6PORT = 23354
7HOSTNAME = localhost
8
diff --git a/src/service/hostlist/meson.build b/src/service/hostlist/meson.build
new file mode 100644
index 000000000..c05578334
--- /dev/null
+++ b/src/service/hostlist/meson.build
@@ -0,0 +1,26 @@
1gnunetdaemonhostlist_src = ['gnunet-daemon-hostlist.c',
2 'gnunet-daemon-hostlist_server.c',
3 'gnunet-daemon-hostlist_client.c']
4
5configure_file(input : 'hostlist.conf',
6 output : 'hostlist.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11
12if get_option('monolith')
13 subdir_done()
14endif
15executable ('gnunet-daemon-hostlist',
16 gnunetdaemonhostlist_src,
17 dependencies: [libgnunetutil_dep,
18 libgnunetcore_dep,
19 libgnunethello_dep,
20 libgnunetpeerstore_dep,
21 libgnunetstatistics_dep,
22 mhd_dep,
23 curl_dep],
24 include_directories: [incdir, configuration_inc],
25 install:true,
26 install_dir: get_option('libdir')/'gnunet'/'libexec')
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist.c b/src/service/hostlist/test_gnunet_daemon_hostlist.c
new file mode 100644
index 000000000..063db2f99
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist.c
@@ -0,0 +1,264 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009, 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 hostlist/test_gnunet_daemon_hostlist.c
22 * @brief test for gnunet_daemon_hostslist.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_transport_service.h"
29#include "gnunet_transport_hello_service.h"
30
31
32/**
33 * How long until we give up on transmitting the message?
34 */
35#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 150)
36
37static int ok;
38
39static struct GNUNET_SCHEDULER_Task *timeout_task;
40
41struct PeerContext
42{
43 struct GNUNET_CONFIGURATION_Handle *cfg;
44 struct GNUNET_TRANSPORT_CoreHandle *th;
45 struct GNUNET_MessageHeader *hello;
46 struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
47 struct GNUNET_OS_Process *arm_proc;
48};
49
50static struct PeerContext p1;
51
52static struct PeerContext p2;
53
54
55static void
56clean_up (void *cls)
57{
58 if (NULL != p1.th)
59 {
60 if (NULL != p1.ghh)
61 {
62 GNUNET_TRANSPORT_hello_get_cancel (p1.ghh);
63 p1.ghh = NULL;
64 }
65 GNUNET_TRANSPORT_core_disconnect (p1.th);
66 p1.th = NULL;
67 }
68 if (NULL != p2.th)
69 {
70 if (NULL != p2.ghh)
71 {
72 GNUNET_TRANSPORT_hello_get_cancel (p2.ghh);
73 p2.ghh = NULL;
74 }
75 GNUNET_TRANSPORT_core_disconnect (p2.th);
76 p2.th = NULL;
77 }
78 GNUNET_SCHEDULER_shutdown ();
79}
80
81
82/**
83 * Timeout, give up.
84 */
85static void
86timeout_error (void *cls)
87{
88 timeout_task = NULL;
89 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
90 "Timeout trying to connect peers, test failed.\n");
91 clean_up (NULL);
92}
93
94
95/**
96 * Function called to notify transport users that another
97 * peer connected to us.
98 *
99 * @param cls closure
100 * @param peer the peer that connected
101 * @param mq message queue to send messages to the peer
102 */
103static void *
104notify_connect (void *cls,
105 const struct GNUNET_PeerIdentity *peer,
106 struct GNUNET_MQ_Handle *mq)
107{
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected, shutting down.\n");
109 ok = 0;
110 if (NULL != timeout_task)
111 {
112 GNUNET_SCHEDULER_cancel (timeout_task);
113 timeout_task = NULL;
114 }
115 GNUNET_SCHEDULER_add_now (&clean_up, NULL);
116 return NULL;
117}
118
119
120static void
121process_hello (void *cls, const struct GNUNET_MessageHeader *message)
122{
123 struct PeerContext *p = cls;
124
125 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
126 p->ghh = NULL;
127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128 "Received HELLO, starting hostlist service.\n");
129}
130
131
132static void
133setup_peer (struct PeerContext *p, const char *cfgname)
134{
135 char *binary;
136
137 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
138 p->cfg = GNUNET_CONFIGURATION_create ();
139 p->arm_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
140 | GNUNET_OS_USE_PIPE_CONTROL,
141 NULL,
142 NULL,
143 NULL,
144 binary,
145 "gnunet-service-arm",
146 "-c",
147 cfgname,
148 NULL);
149 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
150 p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
151 NULL,
152 NULL,
153 p,
154 &notify_connect,
155 NULL,
156 NULL);
157 GNUNET_assert (NULL != p->th);
158 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
159 GNUNET_TRANSPORT_AC_ANY,
160 &process_hello,
161 p);
162 GNUNET_free (binary);
163}
164
165
166static void
167waitpid_task (void *cls)
168{
169 struct PeerContext *p = cls;
170
171 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Killing ARM process.\n");
172 if (0 != GNUNET_OS_process_kill (p->arm_proc, GNUNET_TERM_SIG))
173 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
174 if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc))
175 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
177 "ARM process %u stopped\n",
178 GNUNET_OS_process_get_pid (p->arm_proc));
179 GNUNET_OS_process_destroy (p->arm_proc);
180 p->arm_proc = NULL;
181 GNUNET_CONFIGURATION_destroy (p->cfg);
182}
183
184
185static void
186stop_arm (struct PeerContext *p)
187{
188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking ARM to stop core service\n");
189 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &waitpid_task, p);
190}
191
192
193/**
194 * Try again to connect to transport service.
195 */
196static void
197shutdown_task (void *cls)
198{
199 stop_arm (&p1);
200 stop_arm (&p2);
201}
202
203
204static void
205run (void *cls,
206 char *const *args,
207 const char *cfgfile,
208 const struct GNUNET_CONFIGURATION_Handle *cfg)
209{
210 GNUNET_assert (ok == 1);
211 ok++;
212 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_error, NULL);
213 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
214 setup_peer (&p1, "test_gnunet_daemon_hostlist_peer1.conf");
215 setup_peer (&p2, "test_gnunet_daemon_hostlist_peer2.conf");
216}
217
218
219static int
220check ()
221{
222 char *const argv[] = { "test-gnunet-daemon-hostlist",
223 "-c",
224 "test_gnunet_daemon_hostlist_data.conf",
225 NULL };
226 struct GNUNET_GETOPT_CommandLineOption options[] =
227 { GNUNET_GETOPT_OPTION_END };
228
229 ok = 1;
230 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
231 argv,
232 "test-gnunet-daemon-hostlist",
233 "nohelp",
234 options,
235 &run,
236 &ok);
237 return ok;
238}
239
240
241int
242main (int argc, char *argv[])
243{
244 int ret;
245
246 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer1.conf",
247 "GNUNET_TEST_HOME");
248 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer2.conf",
249 "GNUNET_TEST_HOME");
250 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_data.conf",
251 "GNUNET_TEST_HOME");
252 GNUNET_log_setup ("test-gnunet-daemon-hostlist", "WARNING", NULL);
253 ret = check ();
254 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer1.conf",
255 "GNUNET_TEST_HOME");
256 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer2.conf",
257 "GNUNET_TEST_HOME");
258 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_data.conf",
259 "GNUNET_TEST_HOME");
260 return ret;
261}
262
263
264/* end of test_gnunet_daemon_hostlist.c */
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist_data.conf b/src/service/hostlist/test_gnunet_daemon_hostlist_data.conf
new file mode 100644
index 000000000..9f853c244
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist_data.conf
@@ -0,0 +1,28 @@
1@INLINE@ test_hostlist_defaults.conf
2[transport-tcp]
3PORT = 12968
4
5[arm]
6PORT = 12966
7
8[statistics]
9PORT = 12967
10
11[resolver]
12PORT = 12964
13
14[peerinfo]
15PORT = 12969
16
17[transport]
18PORT = 12965
19
20[core]
21PORT = 12970
22
23[hostlist]
24HTTPPORT = 28080
25SERVERS = http://gnunet.org:8080/
26PORT = 23354
27HOSTNAME = localhost
28
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist_learning.c b/src/service/hostlist/test_gnunet_daemon_hostlist_learning.c
new file mode 100644
index 000000000..a0656f770
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist_learning.c
@@ -0,0 +1,593 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009, 2010, 2011, 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 hostlist/test_gnunet_daemon_hostlist_learning.c
22 * @brief test for gnunet_daemon_hostslist.c
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_arm_service.h"
28#include "gnunet_core_service.h"
29#include "gnunet_transport_service.h"
30#include "gnunet_resolver_service.h"
31#include "gnunet_statistics_service.h"
32
33#define MAX_URL_LEN 1000
34
35/**
36 * How long until wait until testcases fails
37 */
38#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 180)
39
40#define CHECK_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, \
41 1)
42
43
44struct PeerContext
45{
46 struct GNUNET_CONFIGURATION_Handle *cfg;
47 struct GNUNET_MessageHeader *hello;
48 struct GNUNET_CORE_Handle *core;
49 struct GNUNET_STATISTICS_Handle *stats;
50 struct GNUNET_OS_Process *arm_proc;
51};
52
53static int timeout;
54
55static int adv_sent;
56
57static int adv_arrived;
58
59static int learned_hostlist_saved;
60
61static int learned_hostlist_downloaded;
62
63static char *current_adv_uri;
64
65static const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67static struct GNUNET_SCHEDULER_Task *timeout_task;
68
69static struct GNUNET_SCHEDULER_Task *check_task;
70
71static struct PeerContext adv_peer;
72
73static struct PeerContext learn_peer;
74
75static struct GNUNET_STATISTICS_GetHandle *download_stats;
76
77static struct GNUNET_STATISTICS_GetHandle *urisrecv_stat;
78
79static struct GNUNET_STATISTICS_GetHandle *advsent_stat;
80
81
82static void
83shutdown_testcase ()
84{
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Shutdown testcase....\n");
87 if (NULL != timeout_task)
88 {
89 GNUNET_SCHEDULER_cancel (timeout_task);
90 timeout_task = NULL;
91 }
92 if (NULL != download_stats)
93 {
94 GNUNET_STATISTICS_get_cancel (download_stats);
95 download_stats = NULL;
96 }
97 if (NULL != urisrecv_stat)
98 {
99 GNUNET_STATISTICS_get_cancel (urisrecv_stat);
100 urisrecv_stat = NULL;
101 }
102 if (NULL != advsent_stat)
103 {
104 GNUNET_STATISTICS_get_cancel (advsent_stat);
105 advsent_stat = NULL;
106 }
107 if (NULL != adv_peer.stats)
108 {
109 GNUNET_STATISTICS_destroy (adv_peer.stats, GNUNET_NO);
110 adv_peer.stats = NULL;
111 }
112 if (NULL != learn_peer.stats)
113 {
114 GNUNET_STATISTICS_destroy (learn_peer.stats, GNUNET_NO);
115 learn_peer.stats = NULL;
116 }
117 if (NULL != check_task)
118 {
119 GNUNET_SCHEDULER_cancel (check_task);
120 check_task = NULL;
121 }
122 if (NULL != current_adv_uri)
123 {
124 GNUNET_free (current_adv_uri);
125 current_adv_uri = NULL;
126 }
127 if (NULL != adv_peer.core)
128 {
129 GNUNET_CORE_disconnect (adv_peer.core);
130 adv_peer.core = NULL;
131 }
132 if (NULL != learn_peer.core)
133 {
134 GNUNET_CORE_disconnect (learn_peer.core);
135 learn_peer.core = NULL;
136 }
137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
138 "Killing hostlist server ARM process.\n");
139 if (0 != GNUNET_OS_process_kill (adv_peer.arm_proc,
140 GNUNET_TERM_SIG))
141 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
142 "kill");
143 if (GNUNET_OK !=
144 GNUNET_OS_process_wait (adv_peer.arm_proc))
145 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
146 "waitpid");
147 GNUNET_OS_process_destroy (adv_peer.arm_proc);
148 adv_peer.arm_proc = NULL;
149 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150 "Killing hostlist client ARM process.\n");
151 if (0 != GNUNET_OS_process_kill (learn_peer.arm_proc,
152 GNUNET_TERM_SIG))
153 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
154 "kill");
155 if (GNUNET_OK !=
156 GNUNET_OS_process_wait (learn_peer.arm_proc))
157 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
158 "waitpid");
159 GNUNET_OS_process_destroy (learn_peer.arm_proc);
160 learn_peer.arm_proc = NULL;
161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162 "Shutdown complete....\n");
163}
164
165
166/**
167 * Timeout, give up.
168 */
169static void
170timeout_error (void *cls)
171{
172 timeout_task = NULL;
173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 "Timeout while executing testcase, test failed.\n");
175 timeout = GNUNET_YES;
176 shutdown_testcase ();
177}
178
179
180static void
181process_downloads_done (void *cls, int success)
182{
183 download_stats = NULL;
184}
185
186
187static void
188do_shutdown (void *cls)
189{
190 shutdown_testcase ();
191}
192
193
194static int
195process_downloads (void *cls,
196 const char *subsystem,
197 const char *name,
198 uint64_t value,
199 int is_persistent)
200{
201 if ((value >= 2) &&
202 (GNUNET_NO == learned_hostlist_downloaded))
203 {
204 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
205 "Peer has successfully downloaded advertised URI\n");
206 learned_hostlist_downloaded = GNUNET_YES;
207 if ((learned_hostlist_saved == GNUNET_YES) && (adv_sent == GNUNET_YES))
208 {
209 GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
210 }
211 }
212 return GNUNET_OK;
213}
214
215
216static void
217process_uris_recv_done (void *cls, int success)
218{
219 urisrecv_stat = NULL;
220}
221
222
223static int
224process_uris_recv (void *cls,
225 const char *subsystem,
226 const char *name,
227 uint64_t value,
228 int is_persistent)
229{
230 struct PeerContext *pc = cls;
231
232 if ((pc == &learn_peer) &&
233 (value == 1) &&
234 (learned_hostlist_saved == GNUNET_NO))
235 {
236 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
237 "Peer has successfully saved advertised URI\n");
238 learned_hostlist_saved = GNUNET_YES;
239 if ((learned_hostlist_downloaded == GNUNET_YES) &&
240 (adv_sent == GNUNET_YES))
241 {
242 GNUNET_SCHEDULER_add_now (&do_shutdown,
243 NULL);
244 }
245 }
246 return GNUNET_OK;
247}
248
249
250static void
251process_adv_sent_done (void *cls, int success)
252{
253 advsent_stat = NULL;
254}
255
256
257static int
258process_adv_sent (void *cls,
259 const char *subsystem,
260 const char *name,
261 uint64_t value,
262 int is_persistent)
263{
264 if ((value >= 1) && (adv_sent == GNUNET_NO))
265 {
266 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267 "Server has successfully sent advertisement\n");
268 adv_sent = GNUNET_YES;
269 if ((learned_hostlist_downloaded == GNUNET_YES) &&
270 (learned_hostlist_saved == GNUNET_YES))
271 {
272 GNUNET_SCHEDULER_add_now (&do_shutdown,
273 NULL);
274 }
275 }
276 return GNUNET_OK;
277}
278
279
280/**
281 * Check the server statistics regularly
282 */
283static void
284check_statistics (void *cls)
285{
286 char *stat;
287
288 check_task = NULL;
289 GNUNET_asprintf (&stat,
290 gettext_noop ("# advertised URI `%s' downloaded"),
291 current_adv_uri);
292 if (NULL != learn_peer.stats)
293 {
294 if (NULL != download_stats)
295 GNUNET_STATISTICS_get_cancel (download_stats);
296 download_stats =
297 GNUNET_STATISTICS_get (learn_peer.stats,
298 "hostlist",
299 stat,
300 &process_downloads_done,
301 &process_downloads,
302 &learn_peer);
303 if (NULL != urisrecv_stat)
304 GNUNET_STATISTICS_get_cancel (urisrecv_stat);
305 urisrecv_stat =
306 GNUNET_STATISTICS_get (learn_peer.stats, "hostlist",
307 gettext_noop ("# advertised hostlist URIs"),
308 &process_uris_recv_done, &process_uris_recv,
309 &learn_peer);
310 }
311 GNUNET_free (stat);
312 if (NULL != adv_peer.stats)
313 {
314 if (NULL != advsent_stat)
315 GNUNET_STATISTICS_get_cancel (advsent_stat);
316 advsent_stat =
317 GNUNET_STATISTICS_get (adv_peer.stats, "hostlist",
318 gettext_noop ("# hostlist advertisements send"),
319 &process_adv_sent_done,
320 &process_adv_sent,
321 NULL);
322 }
323 check_task =
324 GNUNET_SCHEDULER_add_delayed (CHECK_INTERVAL,
325 &check_statistics,
326 NULL);
327}
328
329
330static int
331check_ad_arrive (void *cls,
332 const struct GNUNET_MessageHeader *message)
333{
334 const char *end = (const char *) &message[1];
335
336 if ('\0' != end[ntohs (message->size) - sizeof(struct GNUNET_MessageHeader)
337 - 1])
338 {
339 GNUNET_break (0);
340 return GNUNET_SYSERR;
341 }
342 return GNUNET_OK;
343}
344
345
346static void
347handle_ad_arrive (void *cls,
348 const struct GNUNET_MessageHeader *message)
349{
350 char *hostname;
351 char *expected_uri;
352 unsigned long long port;
353 const char *end;
354
355 if (GNUNET_SYSERR ==
356 GNUNET_CONFIGURATION_get_value_number (adv_peer.cfg,
357 "HOSTLIST",
358 "HTTPPORT",
359 &port))
360 {
361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362 "Could not read advertising server's configuration\n");
363 return;
364 }
365
366 if (GNUNET_SYSERR ==
367 GNUNET_CONFIGURATION_get_value_string (adv_peer.cfg,
368 "HOSTLIST",
369 "EXTERNAL_DNS_NAME",
370 &hostname))
371 hostname = GNUNET_RESOLVER_local_fqdn_get ();
372 GNUNET_asprintf (&expected_uri,
373 "http://%s:%u/",
374 hostname != NULL ? hostname : "localhost",
375 (unsigned int) port);
376 end = (const char *) &message[1];
377 current_adv_uri = GNUNET_strdup (end);
378 if (0 == strcmp (expected_uri,
379 current_adv_uri))
380 {
381 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
382 "Received hostlist advertisement with URI `%s' as expected\n",
383 current_adv_uri);
384 adv_arrived = GNUNET_YES;
385 adv_sent = GNUNET_YES;
386 }
387 else
388 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389 "Expected URI `%s' and received URI `%s' differ\n",
390 expected_uri,
391 current_adv_uri);
392 GNUNET_free (expected_uri);
393 GNUNET_free (hostname);
394}
395
396
397static void
398setup_learn_peer (struct PeerContext *p,
399 const char *cfgname)
400{
401 struct GNUNET_MQ_MessageHandler learn_handlers[] = {
402 GNUNET_MQ_hd_var_size (ad_arrive,
403 GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT,
404 struct GNUNET_MessageHeader,
405 NULL),
406 GNUNET_MQ_handler_end ()
407 };
408 char *filename;
409 unsigned int result;
410 char *binary;
411
412 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
413 p->cfg = GNUNET_CONFIGURATION_create ();
414 p->arm_proc =
415 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
416 | GNUNET_OS_USE_PIPE_CONTROL,
417 NULL, NULL, NULL,
418 binary,
419 "gnunet-service-arm",
420 "-c", cfgname, NULL);
421 GNUNET_assert (GNUNET_OK ==
422 GNUNET_CONFIGURATION_load (p->cfg,
423 cfgname));
424 if (GNUNET_OK ==
425 GNUNET_CONFIGURATION_get_value_string (p->cfg,
426 "HOSTLIST",
427 "HOSTLISTFILE",
428 &filename))
429 {
430 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
431 {
432 result = unlink (filename);
433 if (result == 0)
434 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
435 _ ("Hostlist file `%s' was removed\n"),
436 filename);
437 }
438 GNUNET_free (filename);
439 }
440 p->core = GNUNET_CORE_connect (p->cfg,
441 NULL,
442 NULL,
443 NULL,
444 NULL,
445 learn_handlers);
446 GNUNET_assert (NULL != p->core);
447 p->stats = GNUNET_STATISTICS_create ("hostlist",
448 p->cfg);
449 GNUNET_assert (NULL != p->stats);
450 GNUNET_free (binary);
451}
452
453
454static void
455setup_adv_peer (struct PeerContext *p,
456 const char *cfgname)
457{
458 char *binary;
459
460 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
461 p->cfg = GNUNET_CONFIGURATION_create ();
462 p->arm_proc =
463 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
464 | GNUNET_OS_USE_PIPE_CONTROL,
465 NULL, NULL, NULL,
466 binary,
467 "gnunet-service-arm",
468 "-c", cfgname, NULL);
469 GNUNET_assert (GNUNET_OK ==
470 GNUNET_CONFIGURATION_load (p->cfg,
471 cfgname));
472 p->stats = GNUNET_STATISTICS_create ("hostlist", p->cfg);
473 GNUNET_assert (NULL != p->stats);
474 GNUNET_free (binary);
475}
476
477
478static void
479run (void *cls,
480 char *const *args,
481 const char *cfgfile,
482 const struct GNUNET_CONFIGURATION_Handle *c)
483{
484 timeout = GNUNET_NO;
485 adv_sent = GNUNET_NO;
486
487 adv_arrived = 0;
488 learned_hostlist_saved = GNUNET_NO;
489 learned_hostlist_downloaded = GNUNET_NO;
490
491 cfg = c;
492
493 setup_adv_peer (&adv_peer,
494 "test_learning_adv_peer.conf");
495 setup_learn_peer (&learn_peer,
496 "test_learning_learn_peer.conf");
497 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
498 &timeout_error,
499 NULL);
500 check_task =
501 GNUNET_SCHEDULER_add_delayed (CHECK_INTERVAL,
502 &check_statistics,
503 NULL);
504}
505
506
507static int
508check ()
509{
510 unsigned int failed;
511
512 char *const argv[] = {
513 "test-gnunet-daemon-hostlist-learning",
514 "-c", "learning_data.conf",
515 NULL
516 };
517 struct GNUNET_GETOPT_CommandLineOption options[] = {
518 GNUNET_GETOPT_OPTION_END
519 };
520
521 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
522 argv,
523 "test-gnunet-daemon-hostlist-learning",
524 "nohelp",
525 options,
526 &run,
527 NULL);
528 failed = GNUNET_NO;
529 if (timeout == GNUNET_YES)
530 {
531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532 "Testcase timeout\n");
533 failed = GNUNET_YES;
534 }
535 if (adv_arrived != GNUNET_YES)
536 {
537 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
538 "Learning peer did not receive advertisement from server\n");
539 failed = GNUNET_YES;
540 }
541 if (learned_hostlist_saved == GNUNET_NO)
542 {
543 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
544 "Advertised hostlist was not saved in datastore\n");
545 failed = GNUNET_YES;
546 }
547 if (learned_hostlist_downloaded == GNUNET_NO)
548 {
549 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
550 "Advertised hostlist could not be downloaded from server\n");
551 failed = GNUNET_YES;
552 }
553 if (adv_sent == GNUNET_NO)
554 {
555 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
556 "Advertised was not sent from server to client\n");
557 failed = GNUNET_YES;
558 }
559 if (GNUNET_YES == failed)
560 return GNUNET_YES;
561 return GNUNET_NO;
562}
563
564
565int
566main (int argc, char *argv[])
567{
568 int ret;
569
570 GNUNET_DISK_purge_cfg_dir ("test_learning_learn_peer.conf",
571 "GNUNET_TEST_HOME");
572 GNUNET_DISK_purge_cfg_dir ("test_learning_adv_peer.conf",
573 "GNUNET_TEST_HOME");
574 GNUNET_log_setup ("test-gnunet-daemon-hostlist",
575 "WARNING",
576 NULL);
577 ret = check ();
578 GNUNET_DISK_purge_cfg_dir ("test_learning_learn_peer.conf",
579 "GNUNET_TEST_HOME");
580 GNUNET_DISK_purge_cfg_dir ("test_learning_adv_peer.conf",
581 "GNUNET_TEST_HOME");
582 if (GNUNET_YES ==
583 GNUNET_DISK_file_test ("hostlists_learn_peer.file"))
584 {
585 if (0 == unlink ("hostlists_learn_peer.file"))
586 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
587 "Hostlist file hostlists_learn_peer.file was removed\n");
588 }
589 return ret;
590}
591
592
593/* end of test_gnunet_daemon_hostlist_learning.c */
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist_peer1.conf b/src/service/hostlist/test_gnunet_daemon_hostlist_peer1.conf
new file mode 100644
index 000000000..00c57c1e5
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist_peer1.conf
@@ -0,0 +1,44 @@
1@INLINE@ test_hostlist_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist-peer-1/
4
5[transport-tcp]
6PORT = 12968
7
8[arm]
9PORT = 12966
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock
11
12[statistics]
13PORT = 12967
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock
15
16[resolver]
17PORT = 12964
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock
19
20[peerinfo]
21PORT = 12969
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock
23
24[transport]
25PORT = 12965
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock
27
28[core]
29PORT = 12970
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-core.sock
31
32[hostlist]
33HTTPPORT = 12980
34HOSTLISTFILE = hostlists_peer1.file
35OPTIONS = -b -p
36SERVERS = http://localhost:22981/
37IMMEDIATE_START = YES
38
39[ats]
40PORT = 12971
41UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock
42
43[topology]
44IMMEDIATE_START = YES
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist_peer2.conf b/src/service/hostlist/test_gnunet_daemon_hostlist_peer2.conf
new file mode 100644
index 000000000..6bcd63fe7
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist_peer2.conf
@@ -0,0 +1,44 @@
1@INLINE@ test_hostlist_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist-peer-2/
4
5[transport-tcp]
6PORT = 22968
7
8[arm]
9PORT = 22966
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock
11
12[statistics]
13PORT = 22967
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock
15
16[resolver]
17PORT = 22964
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock
19
20[peerinfo]
21PORT = 22969
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock
23
24[transport]
25PORT = 22965
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock
27
28[core]
29PORT = 22970
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-core.sock
31
32[hostlist]
33HTTPPORT = 22981
34HOSTLISTFILE = hostlists_peer2.file
35OPTIONS = -b -p
36SERVERS = http://localhost:12980/
37IMMEDIATE_START = YES
38
39[ats]
40PORT = 22971
41UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock
42
43[topology]
44IMMEDIATE_START = YES
diff --git a/src/service/hostlist/test_gnunet_daemon_hostlist_reconnect.c b/src/service/hostlist/test_gnunet_daemon_hostlist_reconnect.c
new file mode 100644
index 000000000..321f96f3d
--- /dev/null
+++ b/src/service/hostlist/test_gnunet_daemon_hostlist_reconnect.c
@@ -0,0 +1,263 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010, 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 hostlist/test_gnunet_daemon_hostlist_reconnect.c
22 * @brief test for gnunet-daemon-hostslist.c; tries to re-start the peers
23 * and connect a second time
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_arm_service.h"
29#include "gnunet_transport_service.h"
30#include "gnunet_transport_hello_service.h"
31
32/**
33 * How long until we give up on transmitting the message?
34 */
35#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 150)
36
37static int ok;
38
39static struct GNUNET_SCHEDULER_Task *timeout_task;
40
41struct PeerContext
42{
43 struct GNUNET_CONFIGURATION_Handle *cfg;
44 struct GNUNET_TRANSPORT_CoreHandle *th;
45 struct GNUNET_MessageHeader *hello;
46 struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
47 struct GNUNET_OS_Process *arm_proc;
48};
49
50static struct PeerContext p1;
51
52static struct PeerContext p2;
53
54
55/**
56 * Timeout, give up.
57 */
58static void
59timeout_error (void *cls)
60{
61 timeout_task = NULL;
62 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
63 "Timeout trying to connect peers, test failed.\n");
64 GNUNET_SCHEDULER_shutdown ();
65}
66
67
68/**
69 * Function called to notify transport users that another
70 * peer connected to us.
71 *
72 * @param cls closure
73 * @param peer the peer that connected
74 * @param mq message queue to send to @a peer
75 * @return NULL
76 */
77static void *
78notify_connect (void *cls,
79 const struct GNUNET_PeerIdentity *peer,
80 struct GNUNET_MQ_Handle *mq)
81{
82 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected, shutting down.\n");
83 ok = 0;
84 GNUNET_SCHEDULER_shutdown ();
85 return NULL;
86}
87
88
89static void
90process_hello (void *cls, const struct GNUNET_MessageHeader *message)
91{
92 struct PeerContext *p = cls;
93
94 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
95 p->ghh = NULL;
96 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97 "Received HELLO, starting hostlist service.\n");
98}
99
100
101static void
102setup_peer (struct PeerContext *p, const char *cfgname)
103{
104 char *binary;
105
106 binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
107 p->cfg = GNUNET_CONFIGURATION_create ();
108 p->arm_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR
109 | GNUNET_OS_USE_PIPE_CONTROL,
110 NULL,
111 NULL,
112 NULL,
113 binary,
114 "gnunet-service-arm",
115 "-c",
116 cfgname,
117 NULL);
118 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
119 p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
120 NULL,
121 NULL,
122 p,
123 &notify_connect,
124 NULL,
125 NULL);
126 GNUNET_assert (NULL != p->th);
127 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
128 GNUNET_TRANSPORT_AC_ANY,
129 &process_hello,
130 p);
131 GNUNET_free (binary);
132}
133
134
135static void
136waitpid_task (void *cls)
137{
138 struct PeerContext *p = cls;
139
140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Killing ARM process.\n");
141 if (0 != GNUNET_OS_process_kill (p->arm_proc, GNUNET_TERM_SIG))
142 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
143 if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc))
144 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
146 "ARM process %u stopped\n",
147 GNUNET_OS_process_get_pid (p->arm_proc));
148 GNUNET_OS_process_destroy (p->arm_proc);
149 p->arm_proc = NULL;
150 GNUNET_CONFIGURATION_destroy (p->cfg);
151}
152
153
154static void
155stop_arm (struct PeerContext *p)
156{
157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asking ARM to stop core service\n");
158 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &waitpid_task, p);
159}
160
161
162/**
163 * Try again to connect to transport service.
164 */
165static void
166shutdown_task (void *cls)
167{
168 if (NULL != timeout_task)
169 {
170 GNUNET_SCHEDULER_cancel (timeout_task);
171 timeout_task = NULL;
172 }
173 if (NULL != p1.ghh)
174 {
175 GNUNET_TRANSPORT_hello_get_cancel (p1.ghh);
176 p1.ghh = NULL;
177 }
178 if (NULL != p1.th)
179 {
180 GNUNET_TRANSPORT_core_disconnect (p1.th);
181 p1.th = NULL;
182 }
183 if (NULL != p2.ghh)
184 {
185 GNUNET_TRANSPORT_hello_get_cancel (p2.ghh);
186 p2.ghh = NULL;
187 }
188 if (NULL != p2.th)
189 {
190 GNUNET_TRANSPORT_core_disconnect (p2.th);
191 p2.th = NULL;
192 }
193 stop_arm (&p1);
194 stop_arm (&p2);
195}
196
197
198static void
199run (void *cls,
200 char *const *args,
201 const char *cfgfile,
202 const struct GNUNET_CONFIGURATION_Handle *cfg)
203{
204 GNUNET_assert (ok == 1);
205 ok++;
206 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_error, NULL);
207 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
208 setup_peer (&p1, "test_gnunet_daemon_hostlist_peer1.conf");
209 setup_peer (&p2, "test_gnunet_daemon_hostlist_peer2.conf");
210}
211
212
213int
214main (int argcx, char *argvx[])
215{
216 static char *const argv[] = { "test-gnunet-daemon-hostlist",
217 "-c",
218 "test_gnunet_daemon_hostlist_data.conf",
219 NULL };
220 static struct GNUNET_GETOPT_CommandLineOption options[] = {
221 GNUNET_GETOPT_OPTION_END
222 };
223
224 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer1.conf",
225 "GNUNET_TEST_HOME");
226 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer2.conf",
227 "GNUNET_TEST_HOME");
228 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_data.conf",
229 "GNUNET_TEST_HOME");
230 GNUNET_log_setup ("test-gnunet-daemon-hostlist", "WARNING", NULL);
231 ok = 1;
232 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
233 argv,
234 "test-gnunet-daemon-hostlist",
235 "nohelp",
236 options,
237 &run,
238 &ok);
239 if (0 == ok)
240 {
241 fprintf (stderr, "%s", ".");
242 /* now do it again */
243 ok = 1;
244 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1,
245 argv,
246 "test-gnunet-daemon-hostlist",
247 "nohelp",
248 options,
249 &run,
250 &ok);
251 fprintf (stderr, "%s", ".\n");
252 }
253 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer1.conf",
254 "GNUNET_TEST_HOME");
255 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_peer2.conf",
256 "GNUNET_TEST_HOME");
257 GNUNET_DISK_purge_cfg_dir ("test_gnunet_daemon_hostlist_data.conf",
258 "GNUNET_TEST_HOME");
259 return ok;
260}
261
262
263/* end of test_gnunet_daemon_hostlist_reconnect.c */
diff --git a/src/service/hostlist/test_hostlist_defaults.conf b/src/service/hostlist/test_hostlist_defaults.conf
new file mode 100644
index 000000000..dcd2790b0
--- /dev/null
+++ b/src/service/hostlist/test_hostlist_defaults.conf
@@ -0,0 +1,18 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
3
4[PATHS]
5GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist/
6
7[transport]
8PLUGINS = tcp
9
10[peerinfo]
11NO_IO = YES
12
13[core]
14PORT = 12470
15
16[nat]
17RETURN_LOCAL_ADDRESSES = YES
18
diff --git a/src/service/hostlist/test_learning_adv_peer.conf b/src/service/hostlist/test_learning_adv_peer.conf
new file mode 100644
index 000000000..3bee3c0c7
--- /dev/null
+++ b/src/service/hostlist/test_learning_adv_peer.conf
@@ -0,0 +1,45 @@
1@INLINE@ test_hostlist_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist-peer-1/
4
5[transport-tcp]
6PORT = 22968
7
8[arm]
9PORT = 22966
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-arm.sock
11
12[statistics]
13PORT = 22967
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-statistics.sock
15
16[resolver]
17PORT = 22964
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-resolver.sock
19
20[peerinfo]
21PORT = 22969
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-peerinfo.sock
23
24[transport]
25PORT = 22965
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-transport.sock
27
28[core]
29PORT = 22970
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p2-service-core.sock
31
32[hostlist]
33HTTPPORT = 12981
34HOSTLISTFILE = hostlists_adv_peer.file
35OPTIONS = -p -a
36SERVERS = http://localhost:12981/
37EXTERNAL_DNS_NAME = localhost
38IMMEDIATE_START = YES
39
40[ats]
41PORT = 22971
42UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-ats-p2-service-core.sock
43
44[topology]
45IMMEDIATE_START = YES
diff --git a/src/service/hostlist/test_learning_learn_peer.conf b/src/service/hostlist/test_learning_learn_peer.conf
new file mode 100644
index 000000000..0dafe6302
--- /dev/null
+++ b/src/service/hostlist/test_learning_learn_peer.conf
@@ -0,0 +1,44 @@
1@INLINE@ test_hostlist_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist-peer-2/
4
5[transport-tcp]
6PORT = 12968
7
8[arm]
9PORT = 12966
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-arm.sock
11
12[statistics]
13PORT = 12967
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-statistics.sock
15
16[resolver]
17PORT = 12964
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-resolver.sock
19
20[peerinfo]
21PORT = 12969
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-peerinfo.sock
23
24[transport]
25PORT = 12965
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-transport.sock
27
28[core]
29PORT = 12970
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-hostlist-p1-service-core.sock
31
32[hostlist]
33HTTPPORT = 12980
34HOSTLISTFILE = hostlists_learn_peer.file
35OPTIONS = -b -e
36SERVERS = http://localhost:12981/
37IMMEDIATE_START = YES
38
39[ats]
40PORT = 12971
41UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-ats-p1-service-core.sock
42
43[topology]
44IMMEDIATE_START = YES
diff --git a/src/service/hostlist/test_learning_learn_peer2.conf b/src/service/hostlist/test_learning_learn_peer2.conf
new file mode 100644
index 000000000..dc2956dcc
--- /dev/null
+++ b/src/service/hostlist/test_learning_learn_peer2.conf
@@ -0,0 +1,40 @@
1@INLINE@ test_hostlist_defaults.conf
2[PATHS]
3GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-hostlist-peer-3/
4
5[transport-tcp]
6PORT = 32968
7
8[arm]
9PORT = 32966
10UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-arm.sock
11
12[statistics]
13PORT = 32967
14UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-statistics.sock
15
16[resolver]
17PORT = 32964
18UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-resolver.sock
19
20[peerinfo]
21PORT = 32969
22UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-peerinfo.sock
23
24[transport]
25PORT = 32965
26UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-transport.sock
27
28[core]
29PORT = 32970
30UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p3-service-core.sock
31
32[hostlist]
33HTTPPORT = 32980
34HOSTLISTFILE = hostlists_learn_peer2.file
35OPTIONS = -b -e
36SERVERS = http://localhost:12981/
37IMMEDIATE_START = YES
38
39[topology]
40IMMEDIATE_START = YES
diff --git a/src/service/topology/.gitignore b/src/service/topology/.gitignore
new file mode 100644
index 000000000..cfa95ec7e
--- /dev/null
+++ b/src/service/topology/.gitignore
@@ -0,0 +1,2 @@
1gnunet-daemon-topology
2test_gnunet_daemon_topology
diff --git a/src/service/topology/Makefile.am b/src/service/topology/Makefile.am
new file mode 100644
index 000000000..7f5ad1893
--- /dev/null
+++ b/src/service/topology/Makefile.am
@@ -0,0 +1,47 @@
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
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12dist_pkgcfg_DATA = \
13 topology.conf
14
15
16libexec_PROGRAMS = \
17 gnunet-daemon-topology
18
19gnunet_daemon_topology_SOURCES = \
20 gnunet-daemon-topology.c
21gnunet_daemon_topology_LDADD = \
22 $(top_builddir)/src/service/core/libgnunetcore.la \
23 $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \
24 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
25 $(top_builddir)/src/service/transport/libgnunettransportapplication.la \
26 $(top_builddir)/src/lib/hello/libgnunethello.la \
27 $(top_builddir)/src/lib/util/libgnunetutil.la \
28 $(GN_LIBINTL)
29
30
31check_PROGRAMS = \
32 test_gnunet_daemon_topology
33
34# if ENABLE_TEST_RUN
35# AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
36# TESTS = $(check_PROGRAMS)
37# endif
38
39test_gnunet_daemon_topology_SOURCES = \
40 test_gnunet_daemon_topology.c
41test_gnunet_daemon_topology_LDADD = \
42 $(top_builddir)/src/testbed/libgnunettestbed.la \
43 $(top_builddir)/src/service/statistics/libgnunetstatistics.la \
44 $(top_builddir)/src/lib/util/libgnunetutil.la
45
46EXTRA_DIST = \
47 test_gnunet_daemon_topology_data.conf
diff --git a/src/service/topology/gnunet-daemon-topology.c b/src/service/topology/gnunet-daemon-topology.c
new file mode 100644
index 000000000..efabac0fc
--- /dev/null
+++ b/src/service/topology/gnunet-daemon-topology.c
@@ -0,0 +1,1033 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2007-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 topology/gnunet-daemon-topology.c
23 * @brief code for maintaining the overlay topology
24 * @author Christian Grothoff
25 *
26 * This daemon combines one Function:
27 * - gossping HELLOs
28 *
29 */
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_hello_uri_lib.h"
33#include "gnunet_constants.h"
34#include "gnunet_core_service.h"
35#include "gnunet_protocols.h"
36#include "gnunet_peerstore_service.h"
37#include "gnunet_statistics_service.h"
38#include "gnunet_transport_application_service.h"
39
40
41/**
42 * At what frequency do we sent HELLOs to a peer?
43 */
44#define HELLO_ADVERTISEMENT_MIN_FREQUENCY \
45 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
46
47/**
48 * After what time period do we expire the HELLO Bloom filter?
49 */
50#define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY \
51 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
52
53
54/**
55 * Record for neighbours and blacklisted peers.
56 */
57struct Peer
58{
59 /**
60 * Which peer is this entry about?
61 */
62 struct GNUNET_PeerIdentity pid;
63
64 /**
65 * Our handle for transmitting to this peer; NULL
66 * if peer is not connected.
67 */
68 struct GNUNET_MQ_Handle *mq;
69
70 /**
71 * Pointer to the hello uri of this peer; can be NULL.
72 */
73 struct GNUNET_MessageHeader *hello;
74
75 /**
76 * Bloom filter used to mark which peers already got the HELLO
77 * from this peer.
78 */
79 struct GNUNET_CONTAINER_BloomFilter *filter;
80
81 /**
82 * Next time we are allowed to transmit a HELLO to this peer?
83 */
84 struct GNUNET_TIME_Absolute next_hello_allowed;
85
86 /**
87 * When should we reset the bloom filter of this entry?
88 */
89 struct GNUNET_TIME_Absolute filter_expiration;
90
91 /**
92 * ID of task we use to wait for the time to send the next HELLO
93 * to this peer.
94 */
95 struct GNUNET_SCHEDULER_Task *hello_delay_task;
96
97 /**
98 * Transport suggest handle.
99 */
100 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ash;
101
102 /**
103 * How much would we like to connect to this peer?
104 */
105 uint32_t strength;
106
107};
108
109
110/**
111 * The task to delayed start the notification process intially.
112 * We like to give transport some time to give us our hello to distribute it.
113 */
114struct GNUNET_SCHEDULER_Task *peerstore_notify_task;
115
116
117/**
118 * Our peerstore notification context. We use notification
119 * to instantly learn about new peers as they are discovered.
120 */
121static struct GNUNET_PEERSTORE_NotifyContext *peerstore_notify;
122
123/**
124 * Our configuration.
125 */
126static const struct GNUNET_CONFIGURATION_Handle *cfg;
127
128/**
129 * Handle to the CORE service.
130 */
131static struct GNUNET_CORE_Handle *handle;
132
133/**
134 * Handle to the PEERSTORE service.
135 */
136static struct GNUNET_PEERSTORE_Handle *ps;
137
138/**
139 * Handle to Transport service.
140 */
141struct GNUNET_TRANSPORT_ApplicationHandle *transport;
142
143/**
144 * Identity of this peer.
145 */
146static struct GNUNET_PeerIdentity my_identity;
147
148/**
149 * Our private key.
150 */
151static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
152
153/**
154 * All of our current neighbours and all peers for
155 * which we have HELLOs. So pretty much everyone. Maps peer identities
156 * to `struct Peer *` values.
157 */
158static struct GNUNET_CONTAINER_MultiPeerMap *peers;
159
160/**
161 * Handle for reporting statistics.
162 */
163static struct GNUNET_STATISTICS_Handle *stats;
164
165/**
166 * Task scheduled to asynchronously reconsider adding/removing
167 * peer connectivity suggestions.
168 */
169static struct GNUNET_SCHEDULER_Task *add_task;
170
171/**
172 * Number of peers that we are currently connected to.
173 */
174static unsigned int connection_count;
175
176/**
177 * Target number of connections.
178 */
179static unsigned int target_connection_count;
180
181/**
182 * Free all resources associated with the given peer.
183 *
184 * @param cls closure (not used)
185 * @param pid identity of the peer
186 * @param value peer to free
187 * @return #GNUNET_YES (always: continue to iterate)
188 */
189static int
190free_peer (void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
191{
192 struct Peer *pos = value;
193
194 GNUNET_break (NULL == pos->mq);
195 GNUNET_break (GNUNET_OK ==
196 GNUNET_CONTAINER_multipeermap_remove (peers, pid, pos));
197 if (NULL != pos->hello_delay_task)
198 {
199 GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
200 pos->hello_delay_task = NULL;
201 }
202 if (NULL != pos->ash)
203 {
204 GNUNET_TRANSPORT_application_suggest_cancel (pos->ash);
205 pos->ash = NULL;
206 }
207 if (NULL != pos->hello)
208 {
209 GNUNET_free (pos->hello);
210 pos->hello = NULL;
211 }
212 if (NULL != pos->filter)
213 {
214 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
215 pos->filter = NULL;
216 }
217 GNUNET_free (pos);
218 return GNUNET_YES;
219}
220
221
222/**
223 * Recalculate how much we want to be connected to the specified peer
224 * and let ATS know about the result.
225 *
226 * @param pos peer to consider connecting to
227 */
228static void
229attempt_connect (struct Peer *pos)
230{
231 uint32_t strength;
232 struct GNUNET_BANDWIDTH_Value32NBO bw;
233
234 if (0 == GNUNET_memcmp (&my_identity, &pos->pid))
235 return; /* This is myself, nothing to do. */
236 if (connection_count < target_connection_count)
237 strength = 1;
238 else
239 strength = 0;
240 if (NULL != pos->mq)
241 strength *= 2; /* existing connections preferred */
242 if (strength == pos->strength)
243 return; /* nothing to do */
244 if (NULL != pos->ash)
245 {
246 GNUNET_TRANSPORT_application_suggest_cancel (pos->ash);
247 pos->ash = NULL;
248 }
249 pos->strength = strength;
250 if (0 != strength)
251 {
252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
253 "Asking to connect to `%s' with strength %u\n",
254 GNUNET_i2s (&pos->pid),
255 (unsigned int) strength);
256 GNUNET_STATISTICS_update (stats,
257 gettext_noop ("# connect requests issued to ATS"),
258 1,
259 GNUNET_NO);
260 // TODO Use strength somehow.
261 bw.value__ = 0;
262 pos->ash = GNUNET_TRANSPORT_application_suggest (transport,
263 &pos->pid,
264 GNUNET_MQ_PRIO_BEST_EFFORT,
265 bw);
266 }
267}
268
269
270/**
271 * Create a new entry in the peer list.
272 *
273 * @param peer identity of the new entry
274 * @param hello hello message, can be NULL
275 * @return the new entry
276 */
277static struct Peer *
278make_peer (const struct GNUNET_PeerIdentity *peer,
279 const struct GNUNET_MessageHeader *hello)
280{
281 struct Peer *ret;
282
283 ret = GNUNET_new (struct Peer);
284 ret->pid = *peer;
285 if (NULL != hello)
286 {
287 ret->hello = GNUNET_malloc (ntohs (hello->size));
288 GNUNET_memcpy (ret->hello, hello, ntohs (hello->size));
289 }
290 GNUNET_break (GNUNET_OK ==
291 GNUNET_CONTAINER_multipeermap_put (
292 peers,
293 peer,
294 ret,
295 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
296 return ret;
297}
298
299
300/**
301 * Setup bloom filter for the given peer entry.
302 *
303 * @param peer entry to initialize
304 */
305static void
306setup_filter (struct Peer *peer)
307{
308 struct GNUNET_HashCode hc;
309
310 /* 2^{-5} chance of not sending a HELLO to a peer is
311 * acceptably small (if the filter is 50% full);
312 * 64 bytes of memory are small compared to the rest
313 * of the data structure and would only really become
314 * "useless" once a HELLO has been passed on to ~100
315 * other peers, which is likely more than enough in
316 * any case; hence 64, 5 as bloomfilter parameters. */peer->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, 64, 5);
317 peer->filter_expiration =
318 GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
319 /* never send a peer its own HELLO */
320 GNUNET_CRYPTO_hash (&peer->pid, sizeof(struct GNUNET_PeerIdentity), &hc);
321 GNUNET_CONTAINER_bloomfilter_add (peer->filter, &hc);
322}
323
324
325/**
326 * Closure for #find_advertisable_hello().
327 */
328struct FindAdvHelloContext
329{
330 /**
331 * Peer we want to advertise to.
332 */
333 struct Peer *peer;
334
335 /**
336 * Where to store the result (peer selected for advertising).
337 */
338 struct Peer *result;
339
340 /**
341 * Maximum HELLO size we can use right now.
342 */
343 size_t max_size;
344
345 struct GNUNET_TIME_Relative next_adv;
346};
347
348
349/**
350 * Find a peer that would be reasonable for advertising.
351 *
352 * @param cls closure
353 * @param pid identity of a peer
354 * @param value 'struct Peer*' for the peer we are considering
355 * @return #GNUNET_YES (continue iteration)
356 */
357static int
358find_advertisable_hello (void *cls,
359 const struct GNUNET_PeerIdentity *pid,
360 void *value)
361{
362 struct FindAdvHelloContext *fah = cls;
363 struct Peer *pos = value;
364 struct GNUNET_TIME_Relative rst_time;
365 struct GNUNET_HashCode hc;
366 size_t hs;
367
368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369 "find_advertisable_hello\n");
370 if (pos == fah->peer)
371 return GNUNET_YES;
372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373 "find_advertisable_hello 2\n");
374 if (pos->hello == NULL)
375 return GNUNET_YES;
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377 "find_advertisable_hello 3\n");
378 rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
379 if (0 == rst_time.rel_value_us)
380 {
381 /* time to discard... */
382 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
383 setup_filter (pos);
384 }
385 fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv);
386 hs = pos->hello->size;
387 if (hs > fah->max_size)
388 return GNUNET_YES;
389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390 "find_advertisable_hello 4\n");
391 GNUNET_CRYPTO_hash (&fah->peer->pid,
392 sizeof(struct GNUNET_PeerIdentity),
393 &hc);
394 if (GNUNET_NO == GNUNET_CONTAINER_bloomfilter_test (pos->filter, &hc))
395 fah->result = pos;
396 return GNUNET_YES;
397}
398
399
400/**
401 * Calculate when we would like to send the next HELLO to this
402 * peer and ask for it.
403 *
404 * @param cls for which peer to schedule the HELLO
405 */
406static void
407schedule_next_hello (void *cls)
408{
409 struct Peer *pl = cls;
410 struct FindAdvHelloContext fah;
411 struct GNUNET_MQ_Envelope *env;
412 size_t want;
413 struct GNUNET_TIME_Relative delay;
414 struct GNUNET_HashCode hc;
415
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "schedule_next_hello\n");
418 pl->hello_delay_task = NULL;
419 GNUNET_assert (NULL != pl->mq);
420 /* find applicable HELLOs */
421 fah.peer = pl;
422 fah.result = NULL;
423 fah.max_size = GNUNET_MAX_MESSAGE_SIZE - 1;
424 fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
425 GNUNET_CONTAINER_multipeermap_iterate (peers, &find_advertisable_hello, &fah);
426 pl->hello_delay_task =
427 GNUNET_SCHEDULER_add_delayed (fah.next_adv, &schedule_next_hello, pl);
428 if (NULL == fah.result)
429 return;
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "schedule_next_hello 2\n");
432 delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
433 if (0 != delay.rel_value_us)
434 return;
435
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
437 "schedule_next_hello 3\n");
438 want = ntohs (fah.result->hello->size);
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 "Sending HELLO with %u bytes for peer %s\n",
441 (unsigned int) want,
442 GNUNET_i2s (&pl->pid));
443 env = GNUNET_MQ_msg_copy (fah.result->hello);
444 GNUNET_MQ_send (pl->mq, env);
445
446 /* avoid sending this one again soon */
447 GNUNET_CRYPTO_hash (&pl->pid, sizeof(struct GNUNET_PeerIdentity), &hc);
448 GNUNET_CONTAINER_bloomfilter_add (fah.result->filter, &hc);
449
450 GNUNET_STATISTICS_update (stats,
451 gettext_noop ("# HELLO messages gossipped"),
452 1,
453 GNUNET_NO);
454 /* prepare to send the next one */
455 pl->next_hello_allowed =
456 GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
457 if (NULL != pl->hello_delay_task)
458 GNUNET_SCHEDULER_cancel (pl->hello_delay_task);
459 pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl);
460}
461
462
463/**
464 * Cancel existing requests for sending HELLOs to this peer
465 * and recalculate when we should send HELLOs to it based
466 * on our current state (something changed!).
467 *
468 * @param cls closure `struct Peer` to skip, or NULL
469 * @param pid identity of a peer
470 * @param value `struct Peer *` for the peer
471 * @return #GNUNET_YES (always)
472 */
473static int
474reschedule_hellos (void *cls,
475 const struct GNUNET_PeerIdentity *pid,
476 void *value)
477{
478 (void) cls;
479 struct Peer *peer = value;
480
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Reschedule for `%s'\n",
483 GNUNET_i2s (&peer->pid));
484 if (NULL == peer->mq)
485 return GNUNET_YES;
486 if (NULL != peer->hello_delay_task)
487 {
488 GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
489 peer->hello_delay_task = NULL;
490 }
491 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
492 "Schedule_next_hello\n");
493 peer->hello_delay_task =
494 GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer);
495 return GNUNET_YES;
496}
497
498
499/**
500 * Method called whenever a peer connects.
501 *
502 * @param cls closure
503 * @param peer peer identity this notification is about
504 * @param mq message queue for communicating with @a peer
505 * @return our `struct Peer` for @a peer
506 */
507static void *
508connect_notify (void *cls,
509 const struct GNUNET_PeerIdentity *peer,
510 struct GNUNET_MQ_Handle *mq)
511{
512 struct Peer *pos;
513
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 "Core told us that we are connecting to `%s'\n",
516 GNUNET_i2s (peer));
517 if (0 == GNUNET_memcmp (&my_identity, peer))
518 return NULL;
519 GNUNET_MQ_set_options (mq, GNUNET_MQ_PRIO_BEST_EFFORT);
520 connection_count++;
521 GNUNET_STATISTICS_set (stats,
522 gettext_noop ("# peers connected"),
523 connection_count,
524 GNUNET_NO);
525 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
526 if (NULL == pos)
527 {
528 pos = make_peer (peer, NULL);
529 }
530 else
531 {
532 GNUNET_assert (NULL == pos->mq);
533 }
534 pos->mq = mq;
535 reschedule_hellos (NULL, peer, pos);
536 return pos;
537}
538
539
540/**
541 * Try to add more peers to our connection set.
542 *
543 * @param cls closure, not used
544 * @param pid identity of a peer
545 * @param value `struct Peer *` for the peer
546 * @return #GNUNET_YES (continue to iterate)
547 */
548static int
549try_add_peers (void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
550{
551 struct Peer *pos = value;
552
553 attempt_connect (pos);
554 return GNUNET_YES;
555}
556
557
558/**
559 * Add peers and schedule connection attempt
560 *
561 * @param cls unused, NULL
562 */
563static void
564add_peer_task (void *cls)
565{
566 add_task = NULL;
567
568 GNUNET_CONTAINER_multipeermap_iterate (peers, &try_add_peers, NULL);
569}
570
571
572/**
573 * Method called whenever a peer disconnects.
574 *
575 * @param cls closure
576 * @param peer peer identity this notification is about
577 * @param internal_cls the `struct Peer` for this peer
578 */
579static void
580disconnect_notify (void *cls,
581 const struct GNUNET_PeerIdentity *peer,
582 void *internal_cls)
583{
584 struct Peer *pos = internal_cls;
585
586 if (NULL == pos)
587 return; /* myself, we're shutting down */
588 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
589 "Core told us that we disconnected from `%s'\n",
590 GNUNET_i2s (peer));
591 if (NULL == pos->mq)
592 {
593 GNUNET_break (0);
594 return;
595 }
596 pos->mq = NULL;
597 connection_count--;
598 if (NULL != pos->hello_delay_task)
599 {
600 GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
601 pos->hello_delay_task = NULL;
602 }
603 GNUNET_STATISTICS_set (stats,
604 gettext_noop ("# peers connected"),
605 connection_count,
606 GNUNET_NO);
607 if ((connection_count < target_connection_count) &&
608 (NULL == add_task))
609 add_task = GNUNET_SCHEDULER_add_now (&add_peer_task, NULL);
610
611}
612
613
614/**
615 * Iterator called on each address.
616 *
617 * @param cls flag that we will set if we see any addresses
618 * @param address the address of the peer
619 * @return #GNUNET_SYSERR always, to terminate iteration
620 */
621static void
622address_iterator (void *cls,
623 const char *uri)
624{
625 int *flag = cls;
626
627 *flag = *flag + 1;
628 //*flag = GNUNET_YES;
629}
630
631
632/**
633 * We've gotten a HELLO from another peer. Consider it for
634 * advertising.
635 *
636 * @param hello the HELLO we got
637 */
638static void
639consider_for_advertising (const struct GNUNET_MessageHeader *hello)
640{
641 int num_addresses_old;
642 int num_addresses_new;
643 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (hello);
644 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder);
645 struct Peer *peer;
646 uint16_t size;
647
648 GNUNET_HELLO_builder_iterate (builder,
649 pid,
650 &address_iterator,
651 &num_addresses_new);
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653 "consider 0 for %s\n",
654 GNUNET_i2s (pid));
655 if (0 == num_addresses_new)
656 {
657 GNUNET_HELLO_builder_free (builder);
658 return; /* no point in advertising this one... */
659 }
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "consider 1\n");
662 if (NULL == pid)
663 {
664 GNUNET_HELLO_builder_free (builder);
665 return;
666 }
667 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668 "consider 2\n");
669 peer = GNUNET_CONTAINER_multipeermap_get (peers, pid);
670 if (NULL == peer)
671 {
672 peer = make_peer (pid, hello);
673 }
674 else if (NULL != peer->hello)
675 {
676 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
677 struct GNUNET_TIME_Absolute new_hello_exp =
678 GNUNET_HELLO_builder_get_expiration_time (hello);
679 struct GNUNET_TIME_Absolute old_hello_exp =
680 GNUNET_HELLO_builder_get_expiration_time (peer->hello);
681 struct GNUNET_HELLO_Builder *builder_old = GNUNET_HELLO_builder_from_msg (peer->hello);
682 struct GNUNET_PeerIdentity *pid_old = GNUNET_HELLO_builder_get_id (builder_old);
683
684 GNUNET_HELLO_builder_iterate (builder_old,
685 pid_old,
686 &address_iterator,
687 &num_addresses_old);
688 if (GNUNET_TIME_absolute_cmp (new_hello_exp, >, now) &&
689 (GNUNET_TIME_absolute_cmp (new_hello_exp, >, old_hello_exp) ||
690 num_addresses_old < num_addresses_new))
691 {
692 GNUNET_free (peer->hello);
693 size = ntohs (hello->size);
694 peer->hello = GNUNET_malloc (size);
695 GNUNET_memcpy (peer->hello, hello, size);
696 }
697 else
698 {
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700 "consider 3\n");
701 GNUNET_HELLO_builder_free (builder);
702 return;
703 }
704 }
705 else
706 {
707 size = ntohs (hello->size);
708 peer->hello = GNUNET_malloc (size);
709 GNUNET_memcpy (peer->hello, hello, size);
710 }
711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
712 "Found HELLO from peer `%s' for advertising\n",
713 GNUNET_i2s (pid));
714 if (NULL != peer->filter)
715 {
716 GNUNET_CONTAINER_bloomfilter_free (peer->filter);
717 peer->filter = NULL;
718 }
719 setup_filter (peer);
720 /* since we have a new HELLO to pick from, re-schedule all
721 * HELLO requests that are not bound by the HELLO send rate! */
722 GNUNET_CONTAINER_multipeermap_iterate (peers, &reschedule_hellos, NULL);
723 GNUNET_HELLO_builder_free (builder);
724}
725
726
727/**
728 * PEERSTORE calls this function to let us know about a possible peer
729 * that we might want to connect to.
730 *
731 * @param cls closure (not used)
732 * @param peer potential peer to connect to
733 * @param hello HELLO for this peer (or NULL)
734 * @param err_msg NULL if successful, otherwise contains error message
735 */
736static void
737process_peer (void *cls,
738 const struct GNUNET_PeerIdentity *peer,
739 const struct GNUNET_MessageHeader *hello,
740 const char *err_msg)
741{
742 struct Peer *pos;
743
744 if (NULL != err_msg)
745 {
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 _ ("Error in communication with PEERSTORE service: %s\n"),
748 err_msg);
749 GNUNET_PEERSTORE_hello_changed_notify_cancel (peerstore_notify);
750 peerstore_notify =
751 GNUNET_PEERSTORE_hello_changed_notify (ps, GNUNET_NO, &process_peer,
752 NULL);
753 return;
754 }
755 GNUNET_assert (NULL != peer);
756 if (NULL == hello)
757 {
758 /* free existing HELLO, if any */
759 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
760 if (NULL != pos)
761 {
762 GNUNET_free (pos->hello);
763 pos->hello = NULL;
764 if (NULL != pos->filter)
765 {
766 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
767 pos->filter = NULL;
768 }
769 if (NULL == pos->mq)
770 free_peer (NULL, &pos->pid, pos);
771 }
772 return;
773 }
774 consider_for_advertising (hello);
775 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
776 if (NULL == pos)
777 pos = make_peer (peer, hello);
778 attempt_connect (pos);
779}
780
781static void
782start_notify (void *cls)
783{
784 (void) cls;
785
786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting to process new hellos for gossiping.\n");
787 peerstore_notify =
788 GNUNET_PEERSTORE_hello_changed_notify (ps, GNUNET_NO, &process_peer, NULL);
789}
790
791/**
792 * Function called after #GNUNET_CORE_connect has succeeded
793 * (or failed for good).
794 *
795 * @param cls closure
796 * @param my_id ID of this peer, NULL if we failed
797 */
798static void
799core_init (void *cls, const struct GNUNET_PeerIdentity *my_id)
800{
801 if (NULL == my_id)
802 {
803 GNUNET_log (
804 GNUNET_ERROR_TYPE_ERROR,
805 _ ("Failed to connect to core service, can not manage topology!\n"));
806 GNUNET_SCHEDULER_shutdown ();
807 return;
808 }
809 my_identity = *my_id;
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "I am peer `%s'\n", GNUNET_i2s (my_id));
811 peerstore_notify_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
812 start_notify,
813 NULL);
814}
815
816
817/**
818 * This function is called whenever an encrypted HELLO message is
819 * received.
820 *
821 * @param cls closure with the peer identity of the sender
822 * @param message the actual HELLO message
823 * @return #GNUNET_OK if @a message is well-formed
824 * #GNUNET_SYSERR if @a message is invalid
825 */
826static int
827check_hello (void *cls, const struct GNUNET_MessageHeader *message)
828{
829 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (
830 message);
831 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder);
832
833 if (NULL == pid)
834 {
835 GNUNET_break_op (0);
836 return GNUNET_SYSERR;
837 }
838 return GNUNET_OK;
839}
840
841
842static void
843shc_cont (void *cls, int success)
844{
845 GNUNET_free (cls);
846}
847
848
849/**
850 * This function is called whenever an encrypted HELLO message is
851 * received.
852 *
853 * @param cls closure with the peer identity of the sender
854 * @param message the actual HELLO message
855 */
856static void
857handle_hello (void *cls, const struct GNUNET_MessageHeader *message)
858{
859 struct GNUNET_PEERSTORE_StoreHelloContext *shc;
860 const struct GNUNET_PeerIdentity *other = cls;
861 struct Peer *peer;
862 struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (
863 message);
864 struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder);
865
866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867 "Received encrypted HELLO from peer `%s'",
868 GNUNET_i2s (other));
869 GNUNET_STATISTICS_update (stats,
870 gettext_noop ("# HELLO messages received"),
871 1,
872 GNUNET_NO);
873 GNUNET_HELLO_builder_from_msg (message);
874 shc = GNUNET_PEERSTORE_hello_add (ps, message, &shc_cont, shc);
875 GNUNET_HELLO_builder_free (builder);
876}
877
878
879/**
880 * Last task run during shutdown. Disconnects us from
881 * the transport and core.
882 *
883 * @param cls unused, NULL
884 */
885static void
886cleaning_task (void *cls)
887{
888 if (NULL != peerstore_notify)
889 {
890 GNUNET_PEERSTORE_hello_changed_notify_cancel (peerstore_notify);
891 peerstore_notify = NULL;
892 }
893 else if (NULL != peerstore_notify_task)
894 {
895 GNUNET_SCHEDULER_cancel (peerstore_notify_task);
896 }
897 if (NULL != handle)
898 {
899 GNUNET_CORE_disconnect (handle);
900 handle = NULL;
901 }
902 if (NULL != add_task)
903 {
904 GNUNET_SCHEDULER_cancel (add_task);
905 add_task = NULL;
906 }
907 GNUNET_CONTAINER_multipeermap_iterate (peers, &free_peer, NULL);
908 GNUNET_CONTAINER_multipeermap_destroy (peers);
909 peers = NULL;
910 if (NULL != transport)
911 {
912 GNUNET_TRANSPORT_application_done (transport);
913 transport = NULL;
914 }
915 if (NULL != ps)
916 {
917 GNUNET_PEERSTORE_disconnect (ps, GNUNET_YES);
918 ps = NULL;
919 }
920 if (NULL != stats)
921 {
922 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
923 stats = NULL;
924 }
925}
926
927
928/**
929 * Main function that will be run.
930 *
931 * @param cls closure
932 * @param args remaining command-line arguments
933 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
934 * @param c configuration
935 */
936static void
937run (void *cls,
938 char *const *args,
939 const char *cfgfile,
940 const struct GNUNET_CONFIGURATION_Handle *c)
941{
942 struct GNUNET_MQ_MessageHandler handlers[] =
943 { GNUNET_MQ_hd_var_size (hello,
944 GNUNET_MESSAGE_TYPE_HELLO_URI,
945 struct GNUNET_MessageHeader,
946 NULL),
947 GNUNET_MQ_handler_end () };
948 unsigned long long opt;
949
950 cfg = c;
951 my_private_key =
952 GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
953 stats = GNUNET_STATISTICS_create ("topology", cfg);
954
955 if (GNUNET_OK !=
956 GNUNET_CONFIGURATION_get_value_number (cfg,
957 "TOPOLOGY",
958 "TARGET-CONNECTION-COUNT",
959 &opt))
960 opt = 16;
961 target_connection_count = (unsigned int) opt;
962 peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2,
963 GNUNET_NO);
964 transport = GNUNET_TRANSPORT_application_init (cfg);
965 ps = GNUNET_PEERSTORE_connect (cfg);
966 handle = GNUNET_CORE_connect (cfg,
967 NULL,
968 &core_init,
969 &connect_notify,
970 &disconnect_notify,
971 handlers);
972 GNUNET_SCHEDULER_add_shutdown (&cleaning_task, NULL);
973 if (NULL == handle)
974 {
975 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
976 _ ("Failed to connect to `%s' service.\n"),
977 "core");
978 GNUNET_SCHEDULER_shutdown ();
979 return;
980 }
981}
982
983
984/**
985 * The main function for the topology daemon.
986 *
987 * @param argc number of arguments from the command line
988 * @param argv command line arguments
989 * @return 0 ok, 1 on error
990 */
991int
992main (int argc, char *const *argv)
993{
994 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
995 GNUNET_GETOPT_OPTION_END
996 };
997 int ret;
998
999 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1000 return 2;
1001
1002 ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
1003 argv,
1004 "gnunet-daemon-topology",
1005 _ ("GNUnet topology control"),
1006 options,
1007 &run,
1008 NULL))
1009 ? 0
1010 : 1;
1011 GNUNET_free_nz ((void *) argv);
1012 return ret;
1013}
1014
1015
1016#if defined(__linux__) && defined(__GLIBC__)
1017#include <malloc.h>
1018
1019/**
1020 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1021 */
1022void __attribute__ ((constructor))
1023GNUNET_ARM_memory_init ()
1024{
1025 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1026 mallopt (M_TOP_PAD, 1 * 1024);
1027 malloc_trim (0);
1028}
1029
1030
1031#endif
1032
1033/* end of gnunet-daemon-topology.c */
diff --git a/src/service/topology/meson.build b/src/service/topology/meson.build
new file mode 100644
index 000000000..b196d98f2
--- /dev/null
+++ b/src/service/topology/meson.build
@@ -0,0 +1,43 @@
1libgnunetfriends_src = ['friends.c']
2
3gnunetdaemontopology_src = ['gnunet-daemon-topology.c']
4
5configure_file(input : 'topology.conf',
6 output : 'topology.conf',
7 configuration : cdata,
8 install: true,
9 install_dir: pkgcfgdir)
10
11
12if get_option('monolith')
13 foreach p : libgnunetfriends_src
14 gnunet_src += 'topology/' + p
15 endforeach
16 subdir_done()
17endif
18
19libgnunetfriends = library('gnunetfriends',
20 libgnunetfriends_src,
21 soversion: '0',
22 version: '0.0.0',
23 dependencies: libgnunetutil_dep,
24 include_directories: [incdir, configuration_inc],
25 install: true,
26 install_dir: get_option('libdir'))
27pkg.generate(libgnunetfriends, url: 'https://www.gnunet.org',
28 description : 'Provides API for accessing the friends service')
29libgnunetfriends_dep = declare_dependency(link_with : libgnunetfriends)
30
31executable ('gnunet-daemon-topology',
32 gnunetdaemontopology_src,
33 dependencies: [libgnunetfriends_dep,
34 libgnunetutil_dep,
35 libgnunetcore_dep,
36 libgnunetpeerstore_dep,
37 libgnunetstatistics_dep,
38 libgnunettransportapplication_dep,
39 libgnunethello_dep],
40 include_directories: [incdir, configuration_inc],
41 install: true,
42 install_dir: get_option('libdir') / 'gnunet' / 'libexec')
43
diff --git a/src/service/topology/test_gnunet_daemon_topology.c b/src/service/topology/test_gnunet_daemon_topology.c
new file mode 100644
index 000000000..6f9758b09
--- /dev/null
+++ b/src/service/topology/test_gnunet_daemon_topology.c
@@ -0,0 +1,300 @@
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 topology/test_gnunet_daemon_topology.c
22 * @brief testcase for topology maintenance code
23 * @author Christian Grothoff
24 * @author xrs
25 */
26#include "platform.h"
27#include "gnunet_testbed_service.h"
28#include "gnunet_statistics_service.h"
29
30
31#define NUM_PEERS 8
32
33/*
34 * The threshold defines the number of connection that are needed
35 * for one peer to pass the test. Be aware that setting NUM_PEERS
36 * too high can cause bandwidth problems for the testing peers.
37 * Normal should be 5KB/s per peer. See gnunet-config -s ats.
38 * schanzen 12/2019: This _only_ makes sense if we connect to the
39 * actual network as in the test we do not connect to more than 1 peer.
40 * => reducing to 1 for now, was NUM_PEERS / 2
41 */
42#define THRESHOLD 1
43
44/**
45 * How long until we give up on connecting the peers?
46 */
47#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
48
49/*
50 * Store manual connections.
51 */
52static unsigned int connect_left;
53
54/*
55 * Result of the testcase.
56 */
57static int result;
58
59/*
60 * Peers that reached the threshold of connections.
61 */
62static int checked_peers;
63
64/*
65 * Testbed operations.
66 */
67struct GNUNET_TESTBED_Operation *op[NUM_PEERS];
68
69/*
70 * Timeout for testcase.
71 */
72static struct GNUNET_SCHEDULER_Task *timeout_tid;
73
74/*
75 * Peer context for every testbed peer.
76 */
77struct peerctx
78{
79 int index;
80 struct GNUNET_STATISTICS_Handle *statistics;
81 int connections;
82 int reported; /* GNUNET_NO | GNUNET_YES */
83};
84
85
86static void
87shutdown_task (void *cls)
88{
89 unsigned int i;
90
91 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
92 "Shutting down testcase\n");
93
94 for (i = 0; i < NUM_PEERS; i++)
95 {
96 if (NULL != op[i])
97 GNUNET_TESTBED_operation_done (op[i]);
98 }
99
100 if (NULL != timeout_tid)
101 GNUNET_SCHEDULER_cancel (timeout_tid);
102}
103
104
105static void
106timeout_task (void *cls)
107{
108 timeout_tid = NULL;
109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110 "Testcase timeout\n");
111
112 result = GNUNET_SYSERR;
113 GNUNET_SCHEDULER_shutdown ();
114}
115
116
117/*
118 * The function is called every time the topology of connected
119 * peers to a peer changes.
120 */
121int
122statistics_iterator (void *cls,
123 const char *subsystem,
124 const char *name,
125 uint64_t value,
126 int is_persistent)
127{
128 struct peerctx *p_ctx = (struct peerctx*) cls;
129
130 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
131 "Peer %d: %s = %llu\n",
132 p_ctx->index,
133 name,
134 (unsigned long long) value);
135
136 if (p_ctx->connections < value)
137 p_ctx->connections = value;
138
139 if ((THRESHOLD <= value) && (GNUNET_NO == p_ctx->reported))
140 {
141 p_ctx->reported = GNUNET_YES;
142 checked_peers++;
143 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
144 "Peer %d successfully connected to at least %d peers once.\n",
145 p_ctx->index,
146 THRESHOLD);
147
148 if (checked_peers == NUM_PEERS)
149 {
150 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
151 "Test OK: All peers have connected to %d peers once.\n",
152 THRESHOLD);
153 result = GNUNET_YES;
154 GNUNET_SCHEDULER_shutdown ();
155 }
156 }
157
158 return GNUNET_YES;
159}
160
161
162static void *
163ca_statistics (void *cls,
164 const struct GNUNET_CONFIGURATION_Handle *cfg)
165{
166 return GNUNET_STATISTICS_create ("topology", cfg);
167}
168
169
170void
171da_statistics (void *cls,
172 void *op_result)
173{
174 struct peerctx *p_ctx = (struct peerctx *) cls;
175
176 GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel
177 (p_ctx->statistics, "topology", "# peers connected",
178 statistics_iterator, p_ctx));
179
180 GNUNET_STATISTICS_destroy (p_ctx->statistics, GNUNET_NO);
181 p_ctx->statistics = NULL;
182
183 GNUNET_free (p_ctx);
184}
185
186
187static void
188service_connect_complete (void *cls,
189 struct GNUNET_TESTBED_Operation *op,
190 void *ca_result,
191 const char *emsg)
192{
193 int ret;
194 struct peerctx *p_ctx = (struct peerctx*) cls;
195
196 if (NULL == ca_result)
197 GNUNET_SCHEDULER_shutdown ();
198
199 p_ctx->statistics = ca_result;
200
201 ret = GNUNET_STATISTICS_watch (ca_result,
202 "topology",
203 "# peers connected",
204 statistics_iterator,
205 p_ctx);
206
207 if (GNUNET_NO == ret)
208 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
209 "call to GNUNET_STATISTICS_watch() failed\n");
210}
211
212
213static void
214notify_connect_complete (void *cls,
215 struct GNUNET_TESTBED_Operation *op,
216 const char *emsg)
217{
218 GNUNET_TESTBED_operation_done (op);
219 if (NULL != emsg)
220 {
221 fprintf (stderr, "Failed to connect two peers: %s\n", emsg);
222 result = GNUNET_SYSERR;
223 GNUNET_SCHEDULER_shutdown ();
224 return;
225 }
226 connect_left--;
227}
228
229
230static void
231do_connect (void *cls,
232 struct GNUNET_TESTBED_RunHandle *h,
233 unsigned int num_peers,
234 struct GNUNET_TESTBED_Peer **peers,
235 unsigned int links_succeeded,
236 unsigned int links_failed)
237{
238 unsigned int i;
239 struct peerctx *p_ctx;
240
241 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
242 "Threshold is set to %d.\n",
243 THRESHOLD);
244
245 GNUNET_assert (NUM_PEERS == num_peers);
246
247 for (i = 0; i < NUM_PEERS; i++)
248 {
249 p_ctx = GNUNET_new (struct peerctx);
250 p_ctx->index = i;
251 p_ctx->connections = 0;
252 p_ctx->reported = GNUNET_NO;
253
254 if (i < NUM_PEERS - 1)
255 {
256 connect_left++;
257 GNUNET_TESTBED_overlay_connect (NULL,
258 &notify_connect_complete, NULL,
259 peers[i], peers[i + 1]);
260 }
261
262 op[i] =
263 GNUNET_TESTBED_service_connect (cls,
264 peers[i],
265 "statistics",
266 service_connect_complete,
267 p_ctx, /* cls of completion cb */
268 ca_statistics, /* connect adapter */
269 da_statistics, /* disconnect adapter */
270 p_ctx);
271 }
272
273 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
274 timeout_tid =
275 GNUNET_SCHEDULER_add_delayed (TIMEOUT,
276 &timeout_task,
277 NULL);
278}
279
280
281int
282main (int argc, char *argv[])
283{
284 result = GNUNET_SYSERR;
285 checked_peers = 0;
286
287 (void) GNUNET_TESTBED_test_run ("test-gnunet-daemon-topology",
288 "test_gnunet_daemon_topology_data.conf",
289 NUM_PEERS,
290 0, NULL, NULL,
291 &do_connect, NULL);
292 GNUNET_DISK_purge_cfg_dir
293 ("test_gnunet_daemon_topology_data.conf",
294 "GNUNET_TEST_HOME");
295
296 return (GNUNET_OK != result) ? 1 : 0;
297}
298
299
300/* end of test_gnunet_daemon_topology.c */
diff --git a/src/service/topology/test_gnunet_daemon_topology_data.conf b/src/service/topology/test_gnunet_daemon_topology_data.conf
new file mode 100644
index 000000000..b1b3e9b88
--- /dev/null
+++ b/src/service/topology/test_gnunet_daemon_topology_data.conf
@@ -0,0 +1,34 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-topology/
3
4[transport]
5PLUGINS = tcp
6#PREFIX = xterm -e xterm -T transport -e gdb -x cmd --args
7#PREFIX = valgrind --tool=memcheck --log-file=logs%p
8
9[testbed]
10OVERLAY_TOPOLOGY=LINE
11
12[transport-tcp]
13BINDTO = 127.0.0.1
14
15# Do not try to connect to external network
16[hostlist]
17OPTIONS =
18
19[nat]
20DISABLEV6 = YES
21ENABLE_UPNP = NO
22BEHIND_NAT = NO
23ALLOW_NAT = NO
24INTERNAL_ADDRESS = 127.0.0.1
25EXTERNAL_ADDRESS = 127.0.0.1
26USE_LOCALADDR = NO
27USE_HOSTNAME = NO
28
29[nse]
30WORKBITS = 0
31
32[rps]
33START_ON_DEMAND = NO
34IMMEDIATE_START = NO
diff --git a/src/service/topology/topology.conf b/src/service/topology/topology.conf
new file mode 100644
index 000000000..45d8dd0c7
--- /dev/null
+++ b/src/service/topology/topology.conf
@@ -0,0 +1,8 @@
1[topology]
2IMMEDIATE_START = YES
3NOARMBIND = YES
4MINIMUM-FRIENDS = 0
5FRIENDS-ONLY = NO
6TARGET-CONNECTION-COUNT = 16
7FRIENDS = $GNUNET_CONFIG_HOME/topology/friends.txt
8BINARY = gnunet-daemon-topology