aboutsummaryrefslogtreecommitdiff
path: root/src/service/dht/plugin_dhtu_gnunet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/dht/plugin_dhtu_gnunet.c')
-rw-r--r--src/service/dht/plugin_dhtu_gnunet.c629
1 files changed, 629 insertions, 0 deletions
diff --git a/src/service/dht/plugin_dhtu_gnunet.c b/src/service/dht/plugin_dhtu_gnunet.c
new file mode 100644
index 000000000..1314f789a
--- /dev/null
+++ b/src/service/dht/plugin_dhtu_gnunet.c
@@ -0,0 +1,629 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @author Christian Grothoff
23 *
24 * @file plugin_dhtu_gnunet.c
25 * @brief plain IP based DHT network underlay
26 */
27#include "platform.h"
28#include "gnunet_dhtu_plugin.h"
29#include "gnunet_core_service.h"
30#include "gnunet_transport_application_service.h"
31#include "gnunet_hello_uri_lib.h"
32#include "gnunet_peerstore_service.h"
33#include "gnunet_nse_service.h"
34#include "plugin_dhtu_gnunet.h"
35
36/**
37 * Opaque handle that the underlay offers for our address to be used when
38 * sending messages to another peer.
39 */
40struct GNUNET_DHTU_Source
41{
42
43 /**
44 * Application context for this source.
45 */
46 void *app_ctx;
47
48};
49
50
51/**
52 * Opaque handle that the underlay offers for the target peer when sending
53 * messages to another peer.
54 */
55struct GNUNET_DHTU_Target
56{
57
58 /**
59 * Application context for this target.
60 */
61 void *app_ctx;
62
63 /**
64 * Our plugin with its environment.
65 */
66 struct Plugin *plugin;
67
68 /**
69 * CORE MQ to send messages to this peer.
70 */
71 struct GNUNET_MQ_Handle *mq;
72
73 /**
74 * Head of preferences expressed for this target.
75 */
76 struct GNUNET_DHTU_PreferenceHandle *ph_head;
77
78 /**
79 * Tail of preferences expressed for this target.
80 */
81 struct GNUNET_DHTU_PreferenceHandle *ph_tail;
82
83 /**
84 * Transport suggest handle.
85 */
86 struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ash;
87
88 /**
89 * Identity of this peer.
90 */
91 struct GNUNET_PeerIdentity pid;
92
93 /**
94 * Preference counter, length of the @a ph_head DLL.
95 */
96 unsigned int ph_count;
97
98};
99
100
101/**
102 * Opaque handle expressing a preference of the DHT to
103 * keep a particular target connected.
104 */
105struct GNUNET_DHTU_PreferenceHandle
106{
107 /**
108 * Kept in a DLL.
109 */
110 struct GNUNET_DHTU_PreferenceHandle *next;
111
112 /**
113 * Kept in a DLL.
114 */
115 struct GNUNET_DHTU_PreferenceHandle *prev;
116
117 /**
118 * Target a preference was expressed for.
119 */
120 struct GNUNET_DHTU_Target *target;
121};
122
123
124/**
125 * Closure for all plugin functions.
126 */
127struct Plugin
128{
129
130 /**
131 * Our "source" address. Traditional CORE API does not tell us which source
132 * it is, so they are all identical.
133 */
134 struct GNUNET_DHTU_Source src;
135
136 /**
137 * Callbacks into the DHT.
138 */
139 struct GNUNET_DHTU_PluginEnvironment *env;
140
141 /**
142 * Handle to the PEERSTORE service.
143 */
144 struct GNUNET_PEERSTORE_Handle *peerstore;
145
146 /**
147 * Handle to the CORE service.
148 */
149 struct GNUNET_CORE_Handle *core;
150
151 /**
152 * Handle to Transport service.
153 */
154 struct GNUNET_TRANSPORT_ApplicationHandle *transport;
155
156 /**
157 * Handle to the NSE service.
158 */
159 struct GNUNET_NSE_Handle *nse;
160
161 /**
162 * Our peerstore notification context. We use notification
163 * to instantly learn about new peers as they are discovered.
164 */
165 struct GNUNET_PEERSTORE_Monitor *peerstore_notify;
166
167 /**
168 * Identity of this peer.
169 */
170 struct GNUNET_PeerIdentity my_identity;
171
172 /**
173 * Our private key.
174 */
175 struct GNUNET_CRYPTO_EddsaPrivateKey *my_priv;
176
177};
178
179
180// #include "../peerinfo-tool/gnunet-peerinfo_plugins.c"
181
182
183/**
184 * Request creation of a session with a peer at the given @a address.
185 *
186 * @param cls closure (internal context for the plugin)
187 * @param pid target identity of the peer to connect to
188 * @param address target address to connect to
189 */
190static void
191gnunet_try_connect (void *cls,
192 const struct GNUNET_PeerIdentity *pid,
193 const char *address)
194{
195 struct Plugin *plugin = cls;
196 enum GNUNET_NetworkType nt = 0;
197 char *addr;
198 const char *eou;
199 int pfx_len;
200
201 eou = strstr (address,
202 "://");
203 if (NULL == eou)
204 {
205 GNUNET_break (0);
206 return;
207 }
208 pfx_len = eou - address;
209 eou += 3;
210 GNUNET_asprintf (&addr,
211 "%.*s-%s",
212 pfx_len,
213 address,
214 eou);
215 GNUNET_TRANSPORT_application_validate (plugin->transport,
216 pid,
217 nt,
218 addr);
219 GNUNET_free (addr);
220}
221
222
223/**
224 * Request underlay to keep the connection to @a target alive if possible.
225 * Hold may be called multiple times to express a strong preference to
226 * keep a connection, say because a @a target is in multiple tables.
227 *
228 * @param cls closure
229 * @param target connection to keep alive
230 */
231static struct GNUNET_DHTU_PreferenceHandle *
232gnunet_hold (void *cls,
233 struct GNUNET_DHTU_Target *target)
234{
235 struct Plugin *plugin = cls;
236 struct GNUNET_DHTU_PreferenceHandle *ph;
237
238 ph = GNUNET_new (struct GNUNET_DHTU_PreferenceHandle);
239 ph->target = target;
240 GNUNET_CONTAINER_DLL_insert (target->ph_head,
241 target->ph_tail,
242 ph);
243 target->ph_count++;
244 if (NULL != target->ash)
245 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
246 target->ash
247 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
248 &target->pid,
249 GNUNET_MQ_PRIO_BEST_EFFORT,
250 GNUNET_BANDWIDTH_ZERO);
251 return ph;
252}
253
254
255/**
256 * Do no long request underlay to keep the connection alive.
257 *
258 * @param cls closure
259 * @param target connection to keep alive
260 */
261static void
262gnunet_drop (struct GNUNET_DHTU_PreferenceHandle *ph)
263{
264 struct GNUNET_DHTU_Target *target = ph->target;
265 struct Plugin *plugin = target->plugin;
266
267 GNUNET_CONTAINER_DLL_remove (target->ph_head,
268 target->ph_tail,
269 ph);
270 target->ph_count--;
271 GNUNET_free (ph);
272 if (NULL != target->ash)
273 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
274 if (0 == target->ph_count)
275 target->ash = NULL;
276 else
277 target->ash
278 = GNUNET_TRANSPORT_application_suggest (plugin->transport,
279 &target->pid,
280 GNUNET_MQ_PRIO_BEST_EFFORT,
281 GNUNET_BANDWIDTH_ZERO);
282}
283
284
285/**
286 * Send message to some other participant over the network. Note that
287 * sending is not guaranteeing that the other peer actually received the
288 * message. For any given @a target, the DHT must wait for the @a
289 * finished_cb to be called before calling send() again.
290 *
291 * @param cls closure (internal context for the plugin)
292 * @param target receiver identification
293 * @param msg message
294 * @param msg_size number of bytes in @a msg
295 * @param finished_cb function called once transmission is done
296 * (not called if @a target disconnects, then only the
297 * disconnect_cb is called).
298 * @param finished_cb_cls closure for @a finished_cb
299 */
300static void
301gnunet_send (void *cls,
302 struct GNUNET_DHTU_Target *target,
303 const void *msg,
304 size_t msg_size,
305 GNUNET_SCHEDULER_TaskCallback finished_cb,
306 void *finished_cb_cls)
307{
308 struct GNUNET_MQ_Envelope *env;
309 struct GNUNET_MessageHeader *cmsg;
310
311 env = GNUNET_MQ_msg_extra (cmsg,
312 msg_size,
313 GNUNET_MESSAGE_TYPE_DHT_CORE);
314 GNUNET_MQ_notify_sent (env,
315 finished_cb,
316 finished_cb_cls);
317 memcpy (&cmsg[1],
318 msg,
319 msg_size);
320 GNUNET_MQ_send (target->mq,
321 env);
322}
323
324
325/**
326 * Method called whenever a given peer connects.
327 *
328 * @param cls closure
329 * @param peer peer identity this notification is about
330 * @return closure associated with @a peer. given to mq callbacks and
331 * #GNUNET_CORE_DisconnectEventHandler
332 */
333static void *
334core_connect_cb (void *cls,
335 const struct GNUNET_PeerIdentity *peer,
336 struct GNUNET_MQ_Handle *mq)
337{
338 struct Plugin *plugin = cls;
339 struct GNUNET_DHTU_Target *target;
340
341 target = GNUNET_new (struct GNUNET_DHTU_Target);
342 target->plugin = plugin;
343 target->mq = mq;
344 target->pid = *peer;
345 plugin->env->connect_cb (plugin->env->cls,
346 target,
347 &target->pid,
348 &target->app_ctx);
349 return target;
350}
351
352
353/**
354 * Method called whenever a peer disconnects.
355 *
356 * @param cls closure
357 * @param peer peer identity this notification is about
358 * @param peer_cls closure associated with peer. given in
359 * #GNUNET_CORE_ConnectEventHandler
360 */
361static void
362core_disconnect_cb (void *cls,
363 const struct GNUNET_PeerIdentity *peer,
364 void *peer_cls)
365{
366 struct Plugin *plugin = cls;
367 struct GNUNET_DHTU_Target *target = peer_cls;
368
369 plugin->env->disconnect_cb (target->app_ctx);
370 if (NULL != target->ash)
371 GNUNET_TRANSPORT_application_suggest_cancel (target->ash);
372 GNUNET_free (target);
373}
374
375
376static void
377add_addr (void *cls,
378 const struct GNUNET_PeerIdentity *pid,
379 const char *addr)
380{
381 struct Plugin *plugin = cls;
382
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384 "peerinfo_cb addr %s\n",
385 addr);
386 plugin->env->address_add_cb (plugin->env->cls,
387 addr,
388 &plugin->src,
389 &plugin->src.app_ctx);
390}
391
392
393/**
394 * Find the @a hello for our identity and then pass
395 * it to the DHT as a URL. Note that we only
396 * add addresses, never remove them, due to limitations
397 * of the current peerinfo/core/transport APIs.
398 * This will change with TNG.
399 *
400 * @param cls a `struct Plugin`
401 * @param peer id of the peer, NULL for last call
402 * @param hello hello message for the peer (can be NULL)
403 * @param err_msg message
404 */
405static void
406peerinfo_cb (void *cls,
407 const struct GNUNET_PEERSTORE_Record *record,
408 const char *emsg)
409{
410 struct Plugin *plugin = cls;
411 struct GNUNET_HELLO_Builder *builder;
412 struct GNUNET_MessageHeader *hello;
413
414 hello = record->value;
415 if (NULL == hello)
416 return;
417 if (0 !=
418 GNUNET_memcmp (&record->peer,
419 &plugin->my_identity))
420 {
421 GNUNET_PEERSTORE_monitor_next (plugin->peerstore_notify, 1);
422 return;
423 }
424 builder = GNUNET_HELLO_builder_from_msg (hello);
425 GNUNET_HELLO_builder_iterate (builder,
426 add_addr,
427 plugin);
428 GNUNET_HELLO_builder_free (builder);
429 GNUNET_PEERSTORE_monitor_next (plugin->peerstore_notify, 1);
430}
431
432static void
433error_cb (void *cls)
434{
435 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
436 "Error in PEERSTORE monitoring\n");
437}
438
439
440static void
441sync_cb (void *cls)
442{
443 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
444 "Done with initial PEERSTORE iteration during monitoring\n");
445}
446
447
448/**
449 * Function called after #GNUNET_CORE_connect has succeeded (or failed
450 * for good). Note that the private key of the peer is intentionally
451 * not exposed here; if you need it, your process should try to read
452 * the private key file directly (which should work if you are
453 * authorized...). Implementations of this function must not call
454 * #GNUNET_CORE_disconnect (other than by scheduling a new task to
455 * do this later).
456 *
457 * @param cls closure
458 * @param my_identity ID of this peer, NULL if we failed
459 */
460static void
461core_init_cb (void *cls,
462 const struct GNUNET_PeerIdentity *my_identity)
463{
464 struct Plugin *plugin = cls;
465
466 plugin->my_identity = *my_identity;
467 plugin->peerstore_notify = GNUNET_PEERSTORE_monitor_start (plugin->env->cfg,
468 GNUNET_YES,
469 "peerstore",
470 NULL,
471 GNUNET_PEERSTORE_HELLO_KEY,
472 &error_cb,
473 NULL,
474 &sync_cb,
475 NULL,
476 &peerinfo_cb,
477 plugin);
478}
479
480
481/**
482 * Anything goes, always return #GNUNET_OK.
483 *
484 * @param cls unused
485 * @param msg message to check
486 * @return #GNUNET_OK if all is fine
487 */
488static int
489check_core_message (void *cls,
490 const struct GNUNET_MessageHeader *msg)
491{
492 (void) cls;
493 (void) msg;
494 return GNUNET_OK;
495}
496
497
498/**
499 * Handle message from CORE for the DHT. Passes it to the
500 * DHT logic.
501 *
502 * @param cls a `struct GNUNET_DHTU_Target` of the sender
503 * @param msg the message we received
504 */
505static void
506handle_core_message (void *cls,
507 const struct GNUNET_MessageHeader *msg)
508{
509 struct GNUNET_DHTU_Target *origin = cls;
510 struct Plugin *plugin = origin->plugin;
511
512 plugin->env->receive_cb (plugin->env->cls,
513 &origin->app_ctx,
514 &plugin->src.app_ctx,
515 &msg[1],
516 ntohs (msg->size) - sizeof (*msg));
517}
518
519
520/**
521 * Callback to call when network size estimate is updated.
522 *
523 * @param cls closure
524 * @param timestamp time when the estimate was received from the server (or created by the server)
525 * @param logestimate the log(Base 2) value of the current network size estimate
526 * @param std_dev standard deviation for the estimate
527 */
528static void
529nse_cb (void *cls,
530 struct GNUNET_TIME_Absolute timestamp,
531 double logestimate,
532 double std_dev)
533{
534 struct Plugin *plugin = cls;
535
536 plugin->env->network_size_cb (plugin->env->cls,
537 timestamp,
538 logestimate,
539 std_dev);
540}
541
542
543/**
544 * Exit point from the plugin.
545 *
546 * @param cls closure (our `struct Plugin`)
547 * @return NULL
548 */
549void *
550DHTU_gnunet_done (void *cls)
551{
552 struct GNUNET_DHTU_PluginFunctions *api = cls;
553 struct Plugin *plugin = api->cls;
554
555 if (NULL != plugin->nse)
556 GNUNET_NSE_disconnect (plugin->nse);
557 plugin->env->network_size_cb (plugin->env->cls,
558 GNUNET_TIME_UNIT_FOREVER_ABS,
559 0.0,
560 0.0);
561 if (NULL != plugin->core)
562 GNUNET_CORE_disconnect (plugin->core);
563 if (NULL != plugin->transport)
564 GNUNET_TRANSPORT_application_done (plugin->transport);
565 if (NULL != plugin->peerstore_notify)
566 GNUNET_PEERSTORE_monitor_stop (plugin->peerstore_notify);
567 if (NULL != plugin->peerstore)
568 GNUNET_PEERSTORE_disconnect (plugin->peerstore);
569 // GPI_plugins_unload ();
570 GNUNET_free (plugin->my_priv);
571 GNUNET_free (plugin);
572 GNUNET_free (api);
573 return NULL;
574}
575
576
577/**
578 * Entry point for the plugin.
579 *
580 * @param cls closure (the `struct GNUNET_DHTU_PluginEnvironment`)
581 * @return the plugin's API
582 */
583void *
584DHTU_gnunet_init (void *cls)
585{
586 struct GNUNET_DHTU_PluginEnvironment *env = cls;
587 struct GNUNET_DHTU_PluginFunctions *api;
588 struct Plugin *plugin;
589 struct GNUNET_MQ_MessageHandler handlers[] = {
590 GNUNET_MQ_hd_var_size (core_message,
591 GNUNET_MESSAGE_TYPE_DHT_CORE,
592 struct GNUNET_MessageHeader,
593 NULL),
594 GNUNET_MQ_handler_end ()
595 };
596
597 plugin = GNUNET_new (struct Plugin);
598 plugin->my_priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (
599 env->cfg);
600 plugin->env = env;
601 api = GNUNET_new (struct GNUNET_DHTU_PluginFunctions);
602 api->cls = plugin;
603 api->try_connect = &gnunet_try_connect;
604 api->hold = &gnunet_hold;
605 api->drop = &gnunet_drop;
606 api->send = &gnunet_send;
607 plugin->peerstore = GNUNET_PEERSTORE_connect (env->cfg);
608 plugin->transport = GNUNET_TRANSPORT_application_init (env->cfg);
609 plugin->core = GNUNET_CORE_connect (env->cfg,
610 plugin,
611 &core_init_cb,
612 &core_connect_cb,
613 &core_disconnect_cb,
614 handlers);
615 plugin->nse = GNUNET_NSE_connect (env->cfg,
616 &nse_cb,
617 plugin);
618 if ( (NULL == plugin->transport) ||
619 (NULL == plugin->core) ||
620 (NULL == plugin->nse) )
621 {
622 GNUNET_break (0);
623 GNUNET_free (api);
624 DHTU_gnunet_done (plugin);
625 return NULL;
626 }
627 // GPI_plugins_load (env->cfg);
628 return api;
629}