RECLAIM/OIDC: code cleanup
[oweals/gnunet.git] / src / peerinfo / peerinfo_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-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 peerinfo/peerinfo_api.c
23  * @brief API to access peerinfo service
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "peerinfo.h"
30
31 #define LOG(kind,...) GNUNET_log_from (kind, "peerinfo-api",__VA_ARGS__)
32
33
34 /**
35  * Context for an iteration request.
36  */
37 struct GNUNET_PEERINFO_IteratorContext
38 {
39
40   /**
41    * Kept in a DLL.
42    */
43   struct GNUNET_PEERINFO_IteratorContext *next;
44
45   /**
46    * Kept in a DLL.
47    */
48   struct GNUNET_PEERINFO_IteratorContext *prev;
49
50   /**
51    * Handle to the PEERINFO service.
52    */
53   struct GNUNET_PEERINFO_Handle *h;
54
55   /**
56    * Function to call with the results.
57    */
58   GNUNET_PEERINFO_Processor callback;
59
60   /**
61    * Closure for @e callback.
62    */
63   void *callback_cls;
64
65   /**
66    * Peer we are interested in (only valid if iteration was restricted to one peer).
67    */
68   struct GNUNET_PeerIdentity peer;
69
70   /**
71    * Is @e peer set?
72    */
73   int have_peer;
74
75   /**
76    * Only include friends in reply?
77    */
78   int include_friend_only;
79
80 };
81
82
83 /**
84  * Handle to the peerinfo service.
85  */
86 struct GNUNET_PEERINFO_Handle
87 {
88   /**
89    * Our configuration.
90    */
91   const struct GNUNET_CONFIGURATION_Handle *cfg;
92
93   /**
94    * Connection to the service.
95    */
96   struct GNUNET_MQ_Handle *mq;
97
98   /**
99    * Head of iterator DLL.
100    */
101   struct GNUNET_PEERINFO_IteratorContext *ic_head;
102
103   /**
104    * Tail of iterator DLL.
105    */
106   struct GNUNET_PEERINFO_IteratorContext *ic_tail;
107
108   /**
109    * ID for a reconnect task.
110    */
111   struct GNUNET_SCHEDULER_Task *r_task;
112
113 };
114
115
116 /**
117  * Close the existing connection to PEERINFO and reconnect.
118  *
119  * @param h handle to the service
120  */
121 static void
122 reconnect (struct GNUNET_PEERINFO_Handle *h);
123
124
125 /**
126  * Connect to the peerinfo service.
127  *
128  * @param cfg configuration to use
129  * @return NULL on error (configuration related, actual connection
130  *         establishment may happen asynchronously).
131  */
132 struct GNUNET_PEERINFO_Handle *
133 GNUNET_PEERINFO_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
134 {
135   struct GNUNET_PEERINFO_Handle *h;
136
137   h = GNUNET_new (struct GNUNET_PEERINFO_Handle);
138   h->cfg = cfg;
139   reconnect (h);
140   if (NULL == h->mq)
141   {
142     GNUNET_free (h);
143     return NULL;
144   }
145   return h;
146 }
147
148
149 /**
150  * Disconnect from the peerinfo service.  Note that all iterators must
151  * have completed or have been cancelled by the time this function is
152  * called (otherwise, calling this function is a serious error).
153  * Furthermore, if #GNUNET_PEERINFO_add_peer() operations are still
154  * pending, they will be cancelled silently on disconnect.
155  *
156  * @param h handle to disconnect
157  */
158 void
159 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
160 {
161   struct GNUNET_PEERINFO_IteratorContext *ic;
162
163   while (NULL != (ic = h->ic_head))
164   {
165     GNUNET_CONTAINER_DLL_remove (h->ic_head,
166                                  h->ic_tail,
167                                  ic);
168     GNUNET_free (ic);
169   }
170   if (NULL != h->mq)
171   {
172     GNUNET_MQ_destroy (h->mq);
173     h->mq = NULL;
174   }
175   if (NULL != h->r_task)
176   {
177     GNUNET_SCHEDULER_cancel (h->r_task);
178     h->r_task = NULL;
179   }
180   GNUNET_free (h);
181 }
182
183
184 /**
185  * Task scheduled to re-try connecting to the peerinfo service.
186  *
187  * @param cls the `struct GNUNET_PEERINFO_Handle *`
188  */
189 static void
190 reconnect_task (void *cls)
191 {
192   struct GNUNET_PEERINFO_Handle *h = cls;
193
194   h->r_task = NULL;
195   reconnect (h);
196 }
197
198
199 /**
200  * We encountered an error, reconnect to the PEERINFO service.
201  *
202  * @param h handle to reconnect
203  */
204 static void
205 do_reconnect (struct GNUNET_PEERINFO_Handle *h)
206 {
207   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
208
209   GNUNET_MQ_destroy (h->mq);
210   h->mq = NULL;
211   if (NULL != ic)
212   {
213     GNUNET_CONTAINER_DLL_remove (h->ic_head,
214                                  h->ic_tail,
215                                  ic);
216     if (NULL != ic->callback)
217       ic->callback (ic->callback_cls,
218                     NULL,
219                     NULL,
220                     _("Failed to receive response from `PEERINFO' service."));
221     GNUNET_free (ic);
222   }
223   h->r_task = GNUNET_SCHEDULER_add_now (&reconnect_task,
224                                         h);
225 }
226
227
228 /**
229  * We got a disconnect after asking regex to do the announcement.
230  * Retry.
231  *
232  * @param cls the `struct GNUNET_PEERINFO_Handle` to retry
233  * @param error error code
234  */
235 static void
236 mq_error_handler (void *cls,
237                   enum GNUNET_MQ_Error error)
238 {
239   struct GNUNET_PEERINFO_Handle *h = cls;
240
241   do_reconnect (h);
242 }
243
244
245
246 /**
247  * Function called when we receive an info message. Check it is
248  * well-formed.
249  *
250  * @param cls closure
251  * @param im message received
252  * @return #GNUNET_OK if the message is OK
253  */
254 static int
255 check_info (void *cls,
256             const struct InfoMessage *im)
257 {
258   struct GNUNET_PEERINFO_Handle *h = cls;
259   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
260   uint16_t ms = ntohs (im->header.size) - sizeof (*im);
261
262   if (0 != ntohl (im->reserved))
263   {
264     GNUNET_break (0);
265     return GNUNET_SYSERR;
266   }
267   if (NULL == ic)
268   {
269     /* didn't expect a response, bad */
270     GNUNET_break (0);
271     return GNUNET_SYSERR;
272   }
273   if ( (GNUNET_YES == ic->have_peer) &&
274        (0 != GNUNET_memcmp (&ic->peer,
275                      &im->peer)) )
276   {
277     /* bogus message (from a different iteration call?); out of sequence! */
278     LOG (GNUNET_ERROR_TYPE_ERROR,
279          "Received HELLO for peer `%s', expected peer `%s'\n",
280          GNUNET_i2s (&im->peer),
281          GNUNET_i2s (&ic->peer));
282     GNUNET_break (0);
283     return GNUNET_SYSERR;
284   }
285   if (ms > sizeof (struct GNUNET_MessageHeader))
286   {
287     const struct GNUNET_HELLO_Message *hello;
288     struct GNUNET_PeerIdentity id;
289
290     hello = (const struct GNUNET_HELLO_Message *) &im[1];
291     if (ms != GNUNET_HELLO_size (hello))
292     {
293       /* malformed message */
294       GNUNET_break (0);
295       return GNUNET_SYSERR;
296     }
297     if (GNUNET_OK !=
298         GNUNET_HELLO_get_id (hello,
299                              &id))
300     {
301       /* malformed message */
302       GNUNET_break (0);
303       return GNUNET_SYSERR;
304     }
305     if (0 != GNUNET_memcmp (&im->peer,
306                      &id))
307     {
308       /* malformed message */
309       GNUNET_break (0);
310       return GNUNET_SYSERR;
311     }
312   }
313   else if (0 != ms)
314   {
315     /* malformed message */
316     GNUNET_break (0);
317     return GNUNET_SYSERR;
318   }
319   return GNUNET_OK;
320 }
321
322
323 /**
324  * Handle info message.
325  *
326  * @param cls closure
327  * @param im message received
328  */
329 static void
330 handle_info (void *cls,
331              const struct InfoMessage *im)
332 {
333   struct GNUNET_PEERINFO_Handle *h = cls;
334   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
335   const struct GNUNET_HELLO_Message *hello = NULL;
336   uint16_t ms;
337
338   ms = ntohs (im->header.size);
339   if (ms > sizeof (struct InfoMessage))
340     hello = (const struct GNUNET_HELLO_Message *) &im[1];
341   if (NULL != ic->callback)
342     ic->callback (ic->callback_cls,
343                   &im->peer,
344                   hello,
345                   NULL);
346 }
347
348
349 /**
350  * Send the next IC request at the head of the queue.
351  *
352  * @param h handle
353  */
354 static void
355 send_ic_request (struct GNUNET_PEERINFO_Handle *h)
356 {
357   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
358   struct GNUNET_MQ_Envelope *env;
359   struct ListAllPeersMessage *lapm;
360   struct ListPeerMessage *lpm;
361
362   if (NULL == ic)
363   {
364     GNUNET_break (0);
365     return;
366   }
367   if (NULL == h->mq)
368   {
369     GNUNET_break (0);
370     return;
371   }
372   if (GNUNET_NO == ic->have_peer)
373   {
374     LOG (GNUNET_ERROR_TYPE_DEBUG,
375          "Requesting list of peers from PEERINFO service\n");
376     env = GNUNET_MQ_msg (lapm,
377                          GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
378     lapm->include_friend_only = htonl (ic->include_friend_only);
379   }
380   else
381   {
382     LOG (GNUNET_ERROR_TYPE_DEBUG,
383          "Requesting information on peer `%s' from PEERINFO service\n",
384          GNUNET_i2s (&ic->peer));
385     env = GNUNET_MQ_msg (lpm,
386                          GNUNET_MESSAGE_TYPE_PEERINFO_GET);
387     lpm->include_friend_only = htonl (ic->include_friend_only);
388     lpm->peer = ic->peer;
389   }
390   GNUNET_MQ_send (h->mq,
391                   env);
392 }
393
394
395 /**
396  * Type of a function to call when we receive a message from the
397  * service.  Call the iterator with the result and (if applicable)
398  * continue to receive more messages or trigger processing the next
399  * event (if applicable).
400  *
401  * @param cls closure
402  * @param msg message received, NULL on timeout or fatal error
403  */
404 static void
405 handle_end_iteration (void *cls,
406                       const struct GNUNET_MessageHeader *msg)
407 {
408   struct GNUNET_PEERINFO_Handle *h = cls;
409   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
410
411   if (NULL == ic)
412   {
413     /* didn't expect a response, reconnect */
414     GNUNET_break (0);
415     reconnect (h);
416     return;
417   }
418   LOG (GNUNET_ERROR_TYPE_DEBUG,
419        "Received end of list of peers from PEERINFO service\n");
420   GNUNET_CONTAINER_DLL_remove (h->ic_head,
421                                h->ic_tail,
422                                ic);
423   if (NULL != h->ic_head)
424     send_ic_request (h);
425   if (NULL != ic->callback)
426     ic->callback (ic->callback_cls,
427                   NULL,
428                   NULL,
429                   NULL);
430   GNUNET_free (ic);
431 }
432
433
434 /**
435  * Close the existing connection to PEERINFO and reconnect.
436  *
437  * @param h handle to the service
438  */
439 static void
440 reconnect (struct GNUNET_PEERINFO_Handle *h)
441 {
442   struct GNUNET_MQ_MessageHandler handlers[] = {
443     GNUNET_MQ_hd_var_size (info,
444                            GNUNET_MESSAGE_TYPE_PEERINFO_INFO,
445                            struct InfoMessage,
446                            h),
447     GNUNET_MQ_hd_fixed_size (end_iteration,
448                              GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END,
449                              struct GNUNET_MessageHeader,
450                              h),
451     GNUNET_MQ_handler_end ()
452   };
453
454   if (NULL != h->r_task)
455   {
456     GNUNET_SCHEDULER_cancel (h->r_task);
457     h->r_task = NULL;
458   }
459   if (NULL != h->mq)
460   {
461     GNUNET_MQ_destroy (h->mq);
462     h->mq = NULL;
463   }
464   h->mq = GNUNET_CLIENT_connect (h->cfg,
465                                  "peerinfo",
466                                  handlers,
467                                  &mq_error_handler,
468                                  h);
469   if (NULL != h->ic_head)
470     send_ic_request (h);
471 }
472
473
474 /**
475  * Call a method for each known matching host.  The callback method
476  * will be invoked once for each matching host and then finally once
477  * with a NULL pointer.  After that final invocation, the iterator
478  * context must no longer be used.
479  *
480  * Instead of calling this function with `peer == NULL` it is often
481  * better to use #GNUNET_PEERINFO_notify().
482  *
483  * @param h handle to the peerinfo service
484  * @param include_friend_only include HELLO messages for friends only
485  * @param peer restrict iteration to this peer only (can be NULL)
486  * @param callback the method to call for each peer
487  * @param callback_cls closure for @a callback
488  * @return iterator context
489  */
490 struct GNUNET_PEERINFO_IteratorContext *
491 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
492                          int include_friend_only,
493                          const struct GNUNET_PeerIdentity *peer,
494                          GNUNET_PEERINFO_Processor callback,
495                          void *callback_cls)
496 {
497   struct GNUNET_PEERINFO_IteratorContext *ic;
498
499   ic = GNUNET_new (struct GNUNET_PEERINFO_IteratorContext);
500   ic->h = h;
501   ic->include_friend_only = include_friend_only;
502   ic->callback = callback;
503   ic->callback_cls = callback_cls;
504   if (NULL != peer)
505   {
506     ic->have_peer = GNUNET_YES;
507     ic->peer = *peer;
508   }
509   GNUNET_CONTAINER_DLL_insert_tail (h->ic_head,
510                                     h->ic_tail,
511                                     ic);
512   if (h->ic_head == ic)
513     send_ic_request (h);
514   return ic;
515 }
516
517
518 /**
519  * Cancel an iteration over peer information.
520  *
521  * @param ic context of the iterator to cancel
522  */
523 void
524 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
525 {
526   struct GNUNET_PEERINFO_Handle *h = ic->h;
527
528   ic->callback = NULL;
529   if (ic == h->ic_head)
530     return;
531   GNUNET_CONTAINER_DLL_remove (h->ic_head,
532                                h->ic_tail,
533                                ic);
534   GNUNET_free (ic);
535 }
536
537
538 /**
539  * Add a host to the persistent list.  This method operates in
540  * semi-reliable mode: if the transmission is not completed by
541  * the time #GNUNET_PEERINFO_disconnect() is called, it will be
542  * aborted.  Furthermore, if a second HELLO is added for the
543  * same peer before the first one was transmitted, PEERINFO may
544  * merge the two HELLOs prior to transmission to the service.
545  *
546  * @param h handle to the peerinfo service
547  * @param hello the verified (!) HELLO message
548  * @param cont continuation to call when done, NULL is allowed
549  * @param cont_cls closure for @a cont
550  * @return handle to cancel add operation; all pending
551  *         'add' operations will be cancelled automatically
552  *        on disconnect, so it is not necessary to keep this
553  *        handle (unless @a cont is NULL and at some point
554  *        calling @a cont must be prevented)
555  */
556 struct GNUNET_MQ_Envelope *
557 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
558                           const struct GNUNET_HELLO_Message *hello,
559                           GNUNET_SCHEDULER_TaskCallback cont,
560                           void *cont_cls)
561 {
562   struct GNUNET_MQ_Envelope *env;
563   struct GNUNET_PeerIdentity peer;
564
565   if (NULL == h->mq)
566     return NULL;
567   GNUNET_assert (GNUNET_OK ==
568                  GNUNET_HELLO_get_id (hello,
569                                       &peer));
570   LOG (GNUNET_ERROR_TYPE_DEBUG,
571        "Adding peer `%s' to PEERINFO database\n",
572        GNUNET_i2s (&peer));
573   env = GNUNET_MQ_msg_copy ((const struct GNUNET_MessageHeader *) hello);
574   if (NULL != cont)
575     GNUNET_MQ_notify_sent (env,
576                            cont,
577                            cont_cls);
578   GNUNET_MQ_send (h->mq,
579                   env);
580   return env;
581 }
582
583
584 /* end of peerinfo_api.c */