src: for every AGPL3.0 file, add SPDX identifier.
[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 != memcmp (&ic->peer,
275                      &im->peer,
276                      sizeof (struct GNUNET_PeerIdentity))) )
277   {
278     /* bogus message (from a different iteration call?); out of sequence! */
279     LOG (GNUNET_ERROR_TYPE_ERROR,
280          "Received HELLO for peer `%s', expected peer `%s'\n",
281          GNUNET_i2s (&im->peer),
282          GNUNET_i2s (&ic->peer));
283     GNUNET_break (0);
284     return GNUNET_SYSERR;
285   }
286   if (ms > sizeof (struct GNUNET_MessageHeader))
287   {
288     const struct GNUNET_HELLO_Message *hello;
289     struct GNUNET_PeerIdentity id;
290
291     hello = (const struct GNUNET_HELLO_Message *) &im[1];
292     if (ms != GNUNET_HELLO_size (hello))
293     {
294       /* malformed message */
295       GNUNET_break (0);
296       return GNUNET_SYSERR;
297     }
298     if (GNUNET_OK !=
299         GNUNET_HELLO_get_id (hello,
300                              &id))
301     {
302       /* malformed message */
303       GNUNET_break (0);
304       return GNUNET_SYSERR;
305     }
306     if (0 != memcmp (&im->peer,
307                      &id,
308                      sizeof (struct GNUNET_PeerIdentity)))
309     {
310       /* malformed message */
311       GNUNET_break (0);
312       return GNUNET_SYSERR;
313     }
314   }
315   else if (0 != ms)
316   {
317     /* malformed message */
318     GNUNET_break (0);
319     return GNUNET_SYSERR;
320   }
321   return GNUNET_OK;
322 }
323
324
325 /**
326  * Handle info message.
327  *
328  * @param cls closure
329  * @param im message received
330  */
331 static void
332 handle_info (void *cls,
333              const struct InfoMessage *im)
334 {
335   struct GNUNET_PEERINFO_Handle *h = cls;
336   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
337   const struct GNUNET_HELLO_Message *hello = NULL;
338   uint16_t ms;
339
340   ms = ntohs (im->header.size);
341   if (ms > sizeof (struct InfoMessage))
342     hello = (const struct GNUNET_HELLO_Message *) &im[1];
343   if (NULL != ic->callback)
344     ic->callback (ic->callback_cls,
345                   &im->peer,
346                   hello,
347                   NULL);
348 }
349
350
351 /**
352  * Send the next IC request at the head of the queue.
353  *
354  * @param h handle
355  */
356 static void
357 send_ic_request (struct GNUNET_PEERINFO_Handle *h)
358 {
359   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
360   struct GNUNET_MQ_Envelope *env;
361   struct ListAllPeersMessage *lapm;
362   struct ListPeerMessage *lpm;
363
364   if (NULL == ic)
365   {
366     GNUNET_break (0);
367     return;
368   }
369   if (NULL == h->mq)
370   {
371     GNUNET_break (0);
372     return;
373   }
374   if (GNUNET_NO == ic->have_peer)
375   {
376     LOG (GNUNET_ERROR_TYPE_DEBUG,
377          "Requesting list of peers from PEERINFO service\n");
378     env = GNUNET_MQ_msg (lapm,
379                          GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
380     lapm->include_friend_only = htonl (ic->include_friend_only);
381   }
382   else
383   {
384     LOG (GNUNET_ERROR_TYPE_DEBUG,
385          "Requesting information on peer `%s' from PEERINFO service\n",
386          GNUNET_i2s (&ic->peer));
387     env = GNUNET_MQ_msg (lpm,
388                          GNUNET_MESSAGE_TYPE_PEERINFO_GET);
389     lpm->include_friend_only = htonl (ic->include_friend_only);
390     lpm->peer = ic->peer;
391   }
392   GNUNET_MQ_send (h->mq,
393                   env);
394 }
395
396
397 /**
398  * Type of a function to call when we receive a message from the
399  * service.  Call the iterator with the result and (if applicable)
400  * continue to receive more messages or trigger processing the next
401  * event (if applicable).
402  *
403  * @param cls closure
404  * @param msg message received, NULL on timeout or fatal error
405  */
406 static void
407 handle_end_iteration (void *cls,
408                       const struct GNUNET_MessageHeader *msg)
409 {
410   struct GNUNET_PEERINFO_Handle *h = cls;
411   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
412
413   if (NULL == ic)
414   {
415     /* didn't expect a response, reconnect */
416     GNUNET_break (0);
417     reconnect (h);
418     return;
419   }
420   LOG (GNUNET_ERROR_TYPE_DEBUG,
421        "Received end of list of peers from PEERINFO service\n");
422   GNUNET_CONTAINER_DLL_remove (h->ic_head,
423                                h->ic_tail,
424                                ic);
425   if (NULL != h->ic_head)
426     send_ic_request (h);
427   if (NULL != ic->callback)
428     ic->callback (ic->callback_cls,
429                   NULL,
430                   NULL,
431                   NULL);
432   GNUNET_free (ic);
433 }
434
435
436 /**
437  * Close the existing connection to PEERINFO and reconnect.
438  *
439  * @param h handle to the service
440  */
441 static void
442 reconnect (struct GNUNET_PEERINFO_Handle *h)
443 {
444   struct GNUNET_MQ_MessageHandler handlers[] = {
445     GNUNET_MQ_hd_var_size (info,
446                            GNUNET_MESSAGE_TYPE_PEERINFO_INFO,
447                            struct InfoMessage,
448                            h),
449     GNUNET_MQ_hd_fixed_size (end_iteration,
450                              GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END,
451                              struct GNUNET_MessageHeader,
452                              h),
453     GNUNET_MQ_handler_end ()
454   };
455
456   if (NULL != h->r_task)
457   {
458     GNUNET_SCHEDULER_cancel (h->r_task);
459     h->r_task = NULL;
460   }
461   if (NULL != h->mq)
462   {
463     GNUNET_MQ_destroy (h->mq);
464     h->mq = NULL;
465   }
466   h->mq = GNUNET_CLIENT_connect (h->cfg,
467                                  "peerinfo",
468                                  handlers,
469                                  &mq_error_handler,
470                                  h);
471   if (NULL != h->ic_head)
472     send_ic_request (h);
473 }
474
475
476 /**
477  * Call a method for each known matching host.  The callback method
478  * will be invoked once for each matching host and then finally once
479  * with a NULL pointer.  After that final invocation, the iterator
480  * context must no longer be used.
481  *
482  * Instead of calling this function with `peer == NULL` it is often
483  * better to use #GNUNET_PEERINFO_notify().
484  *
485  * @param h handle to the peerinfo service
486  * @param include_friend_only include HELLO messages for friends only
487  * @param peer restrict iteration to this peer only (can be NULL)
488  * @param callback the method to call for each peer
489  * @param callback_cls closure for @a callback
490  * @return iterator context
491  */
492 struct GNUNET_PEERINFO_IteratorContext *
493 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
494                          int include_friend_only,
495                          const struct GNUNET_PeerIdentity *peer,
496                          GNUNET_PEERINFO_Processor callback,
497                          void *callback_cls)
498 {
499   struct GNUNET_PEERINFO_IteratorContext *ic;
500
501   ic = GNUNET_new (struct GNUNET_PEERINFO_IteratorContext);
502   ic->h = h;
503   ic->include_friend_only = include_friend_only;
504   ic->callback = callback;
505   ic->callback_cls = callback_cls;
506   if (NULL != peer)
507   {
508     ic->have_peer = GNUNET_YES;
509     ic->peer = *peer;
510   }
511   GNUNET_CONTAINER_DLL_insert_tail (h->ic_head,
512                                     h->ic_tail,
513                                     ic);
514   if (h->ic_head == ic)
515     send_ic_request (h);
516   return ic;
517 }
518
519
520 /**
521  * Cancel an iteration over peer information.
522  *
523  * @param ic context of the iterator to cancel
524  */
525 void
526 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
527 {
528   struct GNUNET_PEERINFO_Handle *h = ic->h;
529
530   ic->callback = NULL;
531   if (ic == h->ic_head)
532     return;
533   GNUNET_CONTAINER_DLL_remove (h->ic_head,
534                                h->ic_tail,
535                                ic);
536   GNUNET_free (ic);
537 }
538
539
540 /**
541  * Add a host to the persistent list.  This method operates in
542  * semi-reliable mode: if the transmission is not completed by
543  * the time #GNUNET_PEERINFO_disconnect() is called, it will be
544  * aborted.  Furthermore, if a second HELLO is added for the
545  * same peer before the first one was transmitted, PEERINFO may
546  * merge the two HELLOs prior to transmission to the service.
547  *
548  * @param h handle to the peerinfo service
549  * @param hello the verified (!) HELLO message
550  * @param cont continuation to call when done, NULL is allowed
551  * @param cont_cls closure for @a cont
552  * @return handle to cancel add operation; all pending
553  *         'add' operations will be cancelled automatically
554  *        on disconnect, so it is not necessary to keep this
555  *        handle (unless @a cont is NULL and at some point
556  *        calling @a cont must be prevented)
557  */
558 struct GNUNET_MQ_Envelope *
559 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
560                           const struct GNUNET_HELLO_Message *hello,
561                           GNUNET_SCHEDULER_TaskCallback cont,
562                           void *cont_cls)
563 {
564   struct GNUNET_MQ_Envelope *env;
565   struct GNUNET_PeerIdentity peer;
566
567   if (NULL == h->mq)
568   {
569     GNUNET_break (0);
570     return NULL;
571   }
572   GNUNET_assert (GNUNET_OK ==
573                  GNUNET_HELLO_get_id (hello,
574                                       &peer));
575   LOG (GNUNET_ERROR_TYPE_DEBUG,
576        "Adding peer `%s' to PEERINFO database\n",
577        GNUNET_i2s (&peer));
578   env = GNUNET_MQ_msg_copy ((const struct GNUNET_MessageHeader *) hello);
579   if (NULL != cont)
580     GNUNET_MQ_notify_sent (env,
581                            cont,
582                            cont_cls);
583   GNUNET_MQ_send (h->mq,
584                   env);
585   return env;
586 }
587
588
589 /* end of peerinfo_api.c */