implement GNUNET_NETWORK_test_port_free() for testcases to conveniently check if...
[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
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
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;
338   uint16_t ms;
339
340   ms = ntohs (im->header.size);
341   hello = (0 == ms) ? NULL : (const struct GNUNET_HELLO_Message *) &im[1];
342   if (NULL != ic->callback)
343     ic->callback (ic->callback_cls,
344                   &im->peer,
345                   hello,
346                   NULL);
347 }
348
349
350 /**
351  * Send the next IC request at the head of the queue.
352  *
353  * @param h handle
354  */
355 static void
356 send_ic_request (struct GNUNET_PEERINFO_Handle *h)
357 {
358   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
359   struct GNUNET_MQ_Envelope *env;
360   struct ListAllPeersMessage *lapm;
361   struct ListPeerMessage *lpm;
362
363   if (NULL == ic)
364   {
365     GNUNET_break (0);
366     return;
367   }
368   if (NULL == h->mq)
369   {
370     GNUNET_break (0);
371     return;
372   }
373   if (GNUNET_NO == ic->have_peer)
374   {
375     LOG (GNUNET_ERROR_TYPE_DEBUG,
376          "Requesting list of peers from PEERINFO service\n");
377     env = GNUNET_MQ_msg (lapm,
378                          GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
379     lapm->include_friend_only = htonl (ic->include_friend_only);
380   }
381   else
382   {
383     LOG (GNUNET_ERROR_TYPE_DEBUG,
384          "Requesting information on peer `%s' from PEERINFO service\n",
385          GNUNET_i2s (&ic->peer));
386     env = GNUNET_MQ_msg (lpm,
387                          GNUNET_MESSAGE_TYPE_PEERINFO_GET);
388     lpm->include_friend_only = htonl (ic->include_friend_only);
389     lpm->peer = ic->peer;
390   }
391   GNUNET_MQ_send (h->mq,
392                   env);
393 }
394
395
396 /**
397  * Type of a function to call when we receive a message from the
398  * service.  Call the iterator with the result and (if applicable)
399  * continue to receive more messages or trigger processing the next
400  * event (if applicable).
401  *
402  * @param cls closure
403  * @param msg message received, NULL on timeout or fatal error
404  */
405 static void
406 handle_end_iteration (void *cls,
407                       const struct GNUNET_MessageHeader *msg)
408 {
409   struct GNUNET_PEERINFO_Handle *h = cls;
410   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
411
412   if (NULL == ic)
413   {
414     /* didn't expect a response, reconnect */
415     GNUNET_break (0);
416     reconnect (h);
417     return;
418   }
419   LOG (GNUNET_ERROR_TYPE_DEBUG,
420        "Received end of list of peers from PEERINFO service\n");
421   GNUNET_CONTAINER_DLL_remove (h->ic_head,
422                                h->ic_tail,
423                                ic);
424   if (NULL != h->ic_head)
425     send_ic_request (h);
426   if (NULL != ic->callback)
427     ic->callback (ic->callback_cls,
428                   NULL,
429                   NULL,
430                   NULL);
431   GNUNET_free (ic);
432 }
433
434
435 /**
436  * Close the existing connection to PEERINFO and reconnect.
437  *
438  * @param h handle to the service
439  */
440 static void
441 reconnect (struct GNUNET_PEERINFO_Handle *h)
442 {
443   struct GNUNET_MQ_MessageHandler handlers[] = {
444     GNUNET_MQ_hd_var_size (info,
445                            GNUNET_MESSAGE_TYPE_PEERINFO_INFO,
446                            struct InfoMessage,
447                            h),
448     GNUNET_MQ_hd_fixed_size (end_iteration,
449                              GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END,
450                              struct GNUNET_MessageHeader,
451                              h),
452     GNUNET_MQ_handler_end ()
453   };
454
455   if (NULL != h->r_task)
456   {
457     GNUNET_SCHEDULER_cancel (h->r_task);
458     h->r_task = NULL;
459   }
460   if (NULL != h->mq)
461   {
462     GNUNET_MQ_destroy (h->mq);
463     h->mq = NULL;
464   }
465   h->mq = GNUNET_CLIENT_connect (h->cfg,
466                                  "peerinfo",
467                                  handlers,
468                                  &mq_error_handler,
469                                  h);
470   if (NULL != h->ic_head)
471     send_ic_request (h);
472 }
473
474
475 /**
476  * Call a method for each known matching host.  The callback method
477  * will be invoked once for each matching host and then finally once
478  * with a NULL pointer.  After that final invocation, the iterator
479  * context must no longer be used.
480  *
481  * Instead of calling this function with `peer == NULL` it is often
482  * better to use #GNUNET_PEERINFO_notify().
483  *
484  * @param h handle to the peerinfo service
485  * @param include_friend_only include HELLO messages for friends only
486  * @param peer restrict iteration to this peer only (can be NULL)
487  * @param callback the method to call for each peer
488  * @param callback_cls closure for @a callback
489  * @return iterator context
490  */
491 struct GNUNET_PEERINFO_IteratorContext *
492 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
493                          int include_friend_only,
494                          const struct GNUNET_PeerIdentity *peer,
495                          GNUNET_PEERINFO_Processor callback,
496                          void *callback_cls)
497 {
498   struct GNUNET_PEERINFO_IteratorContext *ic;
499
500   ic = GNUNET_new (struct GNUNET_PEERINFO_IteratorContext);
501   ic->h = h;
502   ic->include_friend_only = include_friend_only;
503   ic->callback = callback;
504   ic->callback_cls = callback_cls;
505   if (NULL != peer)
506   {
507     ic->have_peer = GNUNET_YES;
508     ic->peer = *peer;
509   }
510   GNUNET_CONTAINER_DLL_insert_tail (h->ic_head,
511                                     h->ic_tail,
512                                     ic);
513   if (h->ic_head == ic)
514     send_ic_request (h);
515   return ic;
516 }
517
518
519 /**
520  * Cancel an iteration over peer information.
521  *
522  * @param ic context of the iterator to cancel
523  */
524 void
525 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
526 {
527   struct GNUNET_PEERINFO_Handle *h = ic->h;
528
529   ic->callback = NULL;
530   if (ic == h->ic_head)
531     return;
532   GNUNET_CONTAINER_DLL_remove (h->ic_head,
533                                h->ic_tail,
534                                ic);
535   GNUNET_free (ic);
536 }
537
538
539 /**
540  * Add a host to the persistent list.  This method operates in
541  * semi-reliable mode: if the transmission is not completed by
542  * the time #GNUNET_PEERINFO_disconnect() is called, it will be
543  * aborted.  Furthermore, if a second HELLO is added for the
544  * same peer before the first one was transmitted, PEERINFO may
545  * merge the two HELLOs prior to transmission to the service.
546  *
547  * @param h handle to the peerinfo service
548  * @param hello the verified (!) HELLO message
549  * @param cont continuation to call when done, NULL is allowed
550  * @param cont_cls closure for @a cont
551  * @return handle to cancel add operation; all pending
552  *         'add' operations will be cancelled automatically
553  *        on disconnect, so it is not necessary to keep this
554  *        handle (unless @a cont is NULL and at some point
555  *        calling @a cont must be prevented)
556  */
557 struct GNUNET_MQ_Envelope *
558 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
559                           const struct GNUNET_HELLO_Message *hello,
560                           GNUNET_SCHEDULER_TaskCallback cont,
561                           void *cont_cls)
562 {
563   struct GNUNET_MQ_Envelope *env;
564   struct GNUNET_PeerIdentity peer;
565
566   if (NULL == h->mq)
567     return NULL;
568   GNUNET_assert (GNUNET_OK ==
569                  GNUNET_HELLO_get_id (hello,
570                                       &peer));
571   LOG (GNUNET_ERROR_TYPE_DEBUG,
572        "Adding peer `%s' to PEERINFO database\n",
573        GNUNET_i2s (&peer));
574   env = GNUNET_MQ_msg_copy ((const struct GNUNET_MessageHeader *) hello);
575   if (NULL != cont)
576     GNUNET_MQ_notify_sent (env,
577                            cont,
578                            cont_cls);
579   GNUNET_MQ_send (h->mq,
580                   env);
581   return env;
582 }
583
584
585 /* end of peerinfo_api.c */