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