src: for every AGPL3.0 file, add SPDX identifier.
[oweals/gnunet.git] / src / dv / dv_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009--2013, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21 /**
22  * @file dv/dv_api.c
23  * @brief library to access the DV service
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dv_service.h"
30 #include "gnunet_protocols.h"
31 #include "dv.h"
32 #include "gnunet_transport_plugin.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__)
35
36
37 /**
38  * Information we track for each peer.
39  */
40 struct ConnectedPeer
41 {
42
43   /**
44    * Identity of the peer.
45    */
46   struct GNUNET_PeerIdentity pid;
47
48 };
49
50
51 /**
52  * Handle to the DV service.
53  */
54 struct GNUNET_DV_ServiceHandle
55 {
56
57   /**
58    * Connection to DV service.
59    */
60   struct GNUNET_MQ_Handle *mq;
61
62   /**
63    * Our configuration.
64    */
65   const struct GNUNET_CONFIGURATION_Handle *cfg;
66
67   /**
68    * Closure for the callbacks.
69    */
70   void *cls;
71
72   /**
73    * Function to call on connect events.
74    */
75   GNUNET_DV_ConnectCallback connect_cb;
76
77   /**
78    * Function to call on distance change events.
79    */
80   GNUNET_DV_DistanceChangedCallback distance_cb;
81
82   /**
83    * Function to call on disconnect events.
84    */
85   GNUNET_DV_DisconnectCallback disconnect_cb;
86
87   /**
88    * Function to call on receiving messages events.
89    */
90   GNUNET_DV_MessageReceivedCallback message_cb;
91
92   /**
93    * Information tracked per connected peer.  Maps peer
94    * identities to `struct ConnectedPeer` entries.
95    */
96   struct GNUNET_CONTAINER_MultiPeerMap *peers;
97
98 };
99
100
101 /**
102  * Disconnect and then reconnect to the DV service.
103  *
104  * @param sh service handle
105  */
106 static void
107 reconnect (struct GNUNET_DV_ServiceHandle *sh);
108
109
110 /**
111  * We got disconnected from the service and thus all of the
112  * connections need to be torn down.
113  *
114  * @param cls the `struct GNUNET_DV_ServiceHandle`
115  * @param key a peer identity
116  * @param value a `struct ConnectedPeer` to clean up
117  * @return #GNUNET_OK (continue to iterate)
118  */
119 static int
120 cleanup_send_cb (void *cls,
121                  const struct GNUNET_PeerIdentity *key,
122                  void *value)
123 {
124   struct GNUNET_DV_ServiceHandle *sh = cls;
125   struct ConnectedPeer *peer = value;
126
127   GNUNET_assert (GNUNET_YES ==
128                  GNUNET_CONTAINER_multipeermap_remove (sh->peers,
129                                                        key,
130                                                        peer));
131   sh->disconnect_cb (sh->cls,
132                      key);
133   GNUNET_free (peer);
134   return GNUNET_OK;
135 }
136
137
138 /**
139  * Handles a message sent from the DV service to us.
140  * Parse it out and give it to the plugin.
141  *
142  * @param cls the handle to the DV API
143  * @param cm the message that was received
144  */
145 static void
146 handle_connect (void *cls,
147                 const struct GNUNET_DV_ConnectMessage *cm)
148 {
149   struct GNUNET_DV_ServiceHandle *sh = cls;
150   struct ConnectedPeer *peer;
151
152   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
153                                             &cm->peer);
154   if (NULL != peer)
155   {
156     GNUNET_break (0);
157     reconnect (sh);
158     return;
159   }
160   peer = GNUNET_new (struct ConnectedPeer);
161   peer->pid = cm->peer;
162   GNUNET_assert (GNUNET_OK ==
163                  GNUNET_CONTAINER_multipeermap_put (sh->peers,
164                                                     &peer->pid,
165                                                     peer,
166                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
167   sh->connect_cb (sh->cls,
168                   &cm->peer,
169                   ntohl (cm->distance),
170                   (enum GNUNET_NetworkType) ntohl (cm->network));
171 }
172
173
174 /**
175  * Handles a message sent from the DV service to us.
176  * Parse it out and give it to the plugin.
177  *
178  * @param cls the handle to the DV API
179  * @param dm the message that was received
180  */
181 static void
182 handle_disconnect (void *cls,
183                    const struct GNUNET_DV_DisconnectMessage *dm)
184 {
185   struct GNUNET_DV_ServiceHandle *sh = cls;
186   struct ConnectedPeer *peer;
187
188   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
189                                             &dm->peer);
190   if (NULL == peer)
191   {
192     GNUNET_break (0);
193     reconnect (sh);
194     return;
195   }
196   cleanup_send_cb (sh,
197                    &dm->peer,
198                    peer);
199 }
200
201
202 /**
203  * Handles a message sent from the DV service to us.
204  * Parse it out and give it to the plugin.
205  *
206  * @param cls the handle to the DV API
207  * @param msg the message that was received
208  */
209 static void
210 handle_distance_update (void *cls,
211                         const struct GNUNET_DV_DistanceUpdateMessage *dum)
212 {
213   struct GNUNET_DV_ServiceHandle *sh = cls;
214   struct ConnectedPeer *peer;
215
216   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
217                                             &dum->peer);
218   if (NULL == peer)
219   {
220     GNUNET_break (0);
221     reconnect (sh);
222     return;
223   }
224   sh->distance_cb (sh->cls,
225                    &dum->peer,
226                    ntohl (dum->distance),
227                    (enum GNUNET_NetworkType) ntohl (dum->network));
228 }
229
230
231 /**
232  * Handles a message sent from the DV service to us.
233  * Parse it out and give it to the plugin.
234  *
235  * @param cls the handle to the DV API
236  * @param rm the message that was received
237  */
238 static int
239 check_received (void *cls,
240                 const struct GNUNET_DV_ReceivedMessage *rm)
241 {
242   struct GNUNET_DV_ServiceHandle *sh = cls;
243   const struct GNUNET_MessageHeader *payload;
244
245   if (NULL ==
246       GNUNET_CONTAINER_multipeermap_get (sh->peers,
247                                          &rm->sender))
248   {
249     GNUNET_break (0);
250     return GNUNET_SYSERR;
251   }
252   if (ntohs (rm->header.size) - sizeof (struct GNUNET_DV_ReceivedMessage) <
253       sizeof (*payload))
254   {
255     GNUNET_break (0);
256     return GNUNET_SYSERR;
257   }
258   payload = (const struct GNUNET_MessageHeader *) &rm[1];
259   if (ntohs (rm->header.size) !=
260       sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
261   {
262     GNUNET_break (0);
263     return GNUNET_SYSERR;
264   }
265   return GNUNET_OK;
266 }
267
268
269 /**
270  * Handles a message sent from the DV service to us.
271  * Parse it out and give it to the plugin.
272  *
273  * @param cls the handle to the DV API
274  * @param rm the message that was received
275  */
276 static void
277 handle_received (void *cls,
278                  const struct GNUNET_DV_ReceivedMessage *rm)
279 {
280   struct GNUNET_DV_ServiceHandle *sh = cls;
281   const struct GNUNET_MessageHeader *payload;
282
283   payload = (const struct GNUNET_MessageHeader *) &rm[1];
284   sh->message_cb (sh->cls,
285                   &rm->sender,
286                   ntohl (rm->distance),
287                   payload);
288 }
289
290
291 /**
292  * Generic error handler, called with the appropriate error code and
293  * the same closure specified at the creation of the message queue.
294  * Not every message queue implementation supports an error handler.
295  *
296  * @param cls closure with the `struct GNUNET_DV_ServiceHandle *`
297  * @param error error code
298  */
299 static void
300 mq_error_handler (void *cls,
301                   enum GNUNET_MQ_Error error)
302 {
303   struct GNUNET_DV_ServiceHandle *sh = cls;
304
305   reconnect (sh);
306 }
307
308
309 /**
310  * Disconnect and then reconnect to the DV service.
311  *
312  * @param sh service handle
313  */
314 static void
315 reconnect (struct GNUNET_DV_ServiceHandle *sh)
316 {
317   struct GNUNET_MQ_MessageHandler handlers[] = {
318     GNUNET_MQ_hd_fixed_size (connect,
319                              GNUNET_MESSAGE_TYPE_DV_CONNECT,
320                              struct GNUNET_DV_ConnectMessage,
321                              sh),
322     GNUNET_MQ_hd_fixed_size (disconnect,
323                              GNUNET_MESSAGE_TYPE_DV_DISCONNECT,
324                              struct GNUNET_DV_DisconnectMessage,
325                              sh),
326     GNUNET_MQ_hd_fixed_size (distance_update,
327                              GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED,
328                              struct GNUNET_DV_DistanceUpdateMessage,
329                              sh),
330     GNUNET_MQ_hd_var_size (received,
331                            GNUNET_MESSAGE_TYPE_DV_RECV,
332                            struct GNUNET_DV_ReceivedMessage,
333                            sh),
334     GNUNET_MQ_handler_end ()
335   };
336   struct GNUNET_MessageHeader *sm;
337   struct GNUNET_MQ_Envelope *env;
338
339   if (NULL != sh->mq)
340   {
341     GNUNET_MQ_destroy (sh->mq);
342     sh->mq = NULL;
343   }
344   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
345                                          &cleanup_send_cb,
346                                          sh);
347   LOG (GNUNET_ERROR_TYPE_DEBUG,
348        "Connecting to DV service\n");
349   sh->mq = GNUNET_CLIENT_connect (sh->cfg,
350                                   "dv",
351                                   handlers,
352                                   &mq_error_handler,
353                                   sh);
354   if (NULL == sh->mq)
355   {
356     GNUNET_break (0);
357     return;
358   }
359   env = GNUNET_MQ_msg (sm,
360                        GNUNET_MESSAGE_TYPE_DV_START);
361   GNUNET_MQ_send (sh->mq,
362                   env);
363 }
364
365
366 /**
367  * Connect to the DV service.
368  *
369  * @param cfg configuration
370  * @param cls closure for callbacks
371  * @param connect_cb function to call on connects
372  * @param distance_cb function to call if distances change
373  * @param disconnect_cb function to call on disconnects
374  * @param message_cb function to call if we receive messages
375  * @return handle to access the service
376  */
377 struct GNUNET_DV_ServiceHandle *
378 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
379                            void *cls,
380                            GNUNET_DV_ConnectCallback connect_cb,
381                            GNUNET_DV_DistanceChangedCallback distance_cb,
382                            GNUNET_DV_DisconnectCallback disconnect_cb,
383                            GNUNET_DV_MessageReceivedCallback message_cb)
384 {
385   struct GNUNET_DV_ServiceHandle *sh;
386
387   sh = GNUNET_new (struct GNUNET_DV_ServiceHandle);
388   sh->cfg = cfg;
389   sh->cls = cls;
390   sh->connect_cb = connect_cb;
391   sh->distance_cb = distance_cb;
392   sh->disconnect_cb = disconnect_cb;
393   sh->message_cb = message_cb;
394   sh->peers = GNUNET_CONTAINER_multipeermap_create (128,
395                                                     GNUNET_YES);
396   reconnect (sh);
397   return sh;
398 }
399
400
401 /**
402  * Disconnect from DV service.
403  *
404  * @param sh service handle
405  */
406 void
407 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
408 {
409   if (NULL == sh)
410     return;
411   if (NULL != sh->mq)
412   {
413     GNUNET_MQ_destroy (sh->mq);
414     sh->mq = NULL;
415   }
416   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
417                                          &cleanup_send_cb,
418                                          sh);
419   GNUNET_CONTAINER_multipeermap_destroy (sh->peers);
420   GNUNET_free (sh);
421 }
422
423
424 /**
425  * Send a message via DV service.
426  *
427  * @param sh service handle
428  * @param target intended recpient
429  * @param msg message payload
430  */
431 void
432 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
433                 const struct GNUNET_PeerIdentity *target,
434                 const struct GNUNET_MessageHeader *msg)
435 {
436   struct GNUNET_DV_SendMessage *sm;
437   struct ConnectedPeer *peer;
438   struct GNUNET_MQ_Envelope *env;
439
440   if (ntohs (msg->size) + sizeof (*sm) >= GNUNET_MAX_MESSAGE_SIZE)
441   {
442     GNUNET_break (0);
443     return;
444   }
445   LOG (GNUNET_ERROR_TYPE_DEBUG,
446        "Asked to send %u bytes of type %u to %s\n",
447        (unsigned int) ntohs (msg->size),
448        (unsigned int) ntohs (msg->type),
449        GNUNET_i2s (target));
450   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
451                                             target);
452   if (NULL == peer)
453   {
454     GNUNET_break (0);
455     return;
456   }
457   GNUNET_assert (NULL != sh->mq);
458   env = GNUNET_MQ_msg_nested_mh (sm,
459                                  GNUNET_MESSAGE_TYPE_DV_SEND,
460                                  msg);
461   sm->target = *target;
462   GNUNET_MQ_send (sh->mq,
463                   env);
464 }
465
466
467 /* end of dv_api.c */