when zone does not match, do not run through the loop anyway
[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
19 /**
20  * @file peerinfo/peerinfo_api.c
21  * @brief API to access peerinfo service
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_protocols.h"
27 #include "peerinfo.h"
28
29 #define LOG(kind,...) GNUNET_log_from (kind, "peerinfo-api",__VA_ARGS__)
30
31
32 /**
33  * Context for an iteration request.
34  */
35 struct GNUNET_PEERINFO_IteratorContext
36 {
37
38   /**
39    * Kept in a DLL.
40    */
41   struct GNUNET_PEERINFO_IteratorContext *next;
42
43   /**
44    * Kept in a DLL.
45    */
46   struct GNUNET_PEERINFO_IteratorContext *prev;
47
48   /**
49    * Handle to the PEERINFO service.
50    */
51   struct GNUNET_PEERINFO_Handle *h;
52
53   /**
54    * Function to call with the results.
55    */
56   GNUNET_PEERINFO_Processor callback;
57
58   /**
59    * Closure for @e callback.
60    */
61   void *callback_cls;
62
63   /**
64    * Peer we are interested in (only valid if iteration was restricted to one peer).
65    */
66   struct GNUNET_PeerIdentity peer;
67
68   /**
69    * Is @e peer set?
70    */
71   int have_peer;
72
73   /**
74    * Only include friends in reply?
75    */
76   int include_friend_only;
77
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 /**
115  * Close the existing connection to PEERINFO and reconnect.
116  *
117  * @param h handle to the service
118  */
119 static void
120 reconnect (struct GNUNET_PEERINFO_Handle *h);
121
122
123 /**
124  * Connect to the peerinfo service.
125  *
126  * @param cfg configuration to use
127  * @return NULL on error (configuration related, actual connection
128  *         establishment may happen asynchronously).
129  */
130 struct GNUNET_PEERINFO_Handle *
131 GNUNET_PEERINFO_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
132 {
133   struct GNUNET_PEERINFO_Handle *h;
134
135   h = GNUNET_new (struct GNUNET_PEERINFO_Handle);
136   h->cfg = cfg;
137   reconnect (h);
138   if (NULL == h->mq)
139   {
140     GNUNET_free (h);
141     return NULL;
142   }
143   return h;
144 }
145
146
147 /**
148  * Disconnect from the peerinfo service.  Note that all iterators must
149  * have completed or have been cancelled by the time this function is
150  * called (otherwise, calling this function is a serious error).
151  * Furthermore, if #GNUNET_PEERINFO_add_peer() operations are still
152  * pending, they will be cancelled silently on disconnect.
153  *
154  * @param h handle to disconnect
155  */
156 void
157 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
158 {
159   struct GNUNET_PEERINFO_IteratorContext *ic;
160
161   while (NULL != (ic = h->ic_head))
162   {
163     GNUNET_CONTAINER_DLL_remove (h->ic_head,
164                                  h->ic_tail,
165                                  ic);
166     GNUNET_free (ic);
167   }
168   if (NULL != h->mq)
169   {
170     GNUNET_MQ_destroy (h->mq);
171     h->mq = NULL;
172   }
173   if (NULL != h->r_task)
174   {
175     GNUNET_SCHEDULER_cancel (h->r_task);
176     h->r_task = NULL;
177   }
178   GNUNET_free (h);
179 }
180
181
182 /**
183  * Task scheduled to re-try connecting to the peerinfo service.
184  *
185  * @param cls the `struct GNUNET_PEERINFO_Handle *`
186  */
187 static void
188 reconnect_task (void *cls)
189 {
190   struct GNUNET_PEERINFO_Handle *h = cls;
191
192   h->r_task = NULL;
193   reconnect (h);
194 }
195
196
197 /**
198  * We encountered an error, reconnect to the PEERINFO service.
199  *
200  * @param h handle to reconnect
201  */
202 static void
203 do_reconnect (struct GNUNET_PEERINFO_Handle *h)
204 {
205   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
206
207   GNUNET_MQ_destroy (h->mq);
208   h->mq = NULL;
209   if (NULL != ic)
210   {
211     GNUNET_CONTAINER_DLL_remove (h->ic_head,
212                                  h->ic_tail,
213                                  ic);
214     if (NULL != ic->callback)
215       ic->callback (ic->callback_cls,
216                     NULL,
217                     NULL,
218                     _("Failed to receive response from `PEERINFO' service."));
219     GNUNET_free (ic);
220   }
221   h->r_task = GNUNET_SCHEDULER_add_now (&reconnect_task,
222                                         h);
223 }
224
225
226 /**
227  * We got a disconnect after asking regex to do the announcement.
228  * Retry.
229  *
230  * @param cls the `struct GNUNET_PEERINFO_Handle` to retry
231  * @param error error code
232  */
233 static void
234 mq_error_handler (void *cls,
235                   enum GNUNET_MQ_Error error)
236 {
237   struct GNUNET_PEERINFO_Handle *h = cls;
238
239   do_reconnect (h);
240 }
241
242
243
244 /**
245  * Function called when we receive an info message. Check it is
246  * well-formed.
247  *
248  * @param cls closure
249  * @param im message received
250  * @return #GNUNET_OK if the message is OK
251  */
252 static int
253 check_info (void *cls,
254             const struct InfoMessage *im)
255 {
256   struct GNUNET_PEERINFO_Handle *h = cls;
257   struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head;
258   uint16_t ms = ntohs (im->header.size) - sizeof (*im);
259
260   if (0 != ntohl (im->reserved))
261   {
262     GNUNET_break (0);
263     return GNUNET_SYSERR;
264   }
265   if (NULL == ic)
266   {
267     /* didn't expect a response, bad */
268     GNUNET_break (0);
269     return GNUNET_SYSERR;
270   }
271   if ( (GNUNET_YES == ic->have_peer) &&
272        (0 != memcmp (&ic->peer,
273                      &im->peer,
274                      sizeof (struct GNUNET_PeerIdentity))) )
275   {
276     /* bogus message (from a different iteration call?); out of sequence! */
277     LOG (GNUNET_ERROR_TYPE_ERROR,
278          "Received HELLO for peer `%s', expected peer `%s'\n",
279          GNUNET_i2s (&im->peer),
280          GNUNET_i2s (&ic->peer));
281     GNUNET_break (0);
282     return GNUNET_SYSERR;
283   }
284   if (ms > sizeof (struct GNUNET_MessageHeader))
285   {
286     const struct GNUNET_HELLO_Message *hello;
287     struct GNUNET_PeerIdentity id;
288
289     hello = (const struct GNUNET_HELLO_Message *) &im[1];
290     if (ms != GNUNET_HELLO_size (hello))
291     {
292       /* malformed message */
293       GNUNET_break (0);
294       return GNUNET_SYSERR;
295     }
296     if (GNUNET_OK !=
297         GNUNET_HELLO_get_id (hello,
298                              &id))
299     {
300       /* malformed message */
301       GNUNET_break (0);
302       return GNUNET_SYSERR;
303     }
304     if (0 != memcmp (&im->peer,
305                      &id,
306                      sizeof (struct GNUNET_PeerIdentity)))
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   {
567     GNUNET_break (0);
568     return NULL;
569   }
570   GNUNET_assert (GNUNET_OK ==
571                  GNUNET_HELLO_get_id (hello,
572                                       &peer));
573   LOG (GNUNET_ERROR_TYPE_DEBUG,
574        "Adding peer `%s' to PEERINFO database\n",
575        GNUNET_i2s (&peer));
576   env = GNUNET_MQ_msg_copy ((const struct GNUNET_MessageHeader *) hello);
577   if (NULL != cont)
578     GNUNET_MQ_notify_sent (env,
579                            cont,
580                            cont_cls);
581   GNUNET_MQ_send (h->mq,
582                   env);
583   return env;
584 }
585
586
587 /* end of peerinfo_api.c */