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