f70b8fde108c5c505da316282ed2634bc2ad08ff
[oweals/gnunet.git] / src / peerinfo / peerinfo_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009, 2010, 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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_client_lib.h"
28 #include "gnunet_container_lib.h"
29 #include "gnunet_peerinfo_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_time_lib.h"
32 #include "peerinfo.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "peerinfo-api",__VA_ARGS__)
35
36
37 /**
38  * Entry in the transmission queue to PEERINFO service.  We use
39  * the same structure for queueing 'iteration' requests and
40  * actual 'add' messages.
41  */
42 struct GNUNET_PEERINFO_AddContext
43 {
44   /**
45    * This is a linked list.
46    */
47   struct GNUNET_PEERINFO_AddContext *next;
48
49   /**
50    * This is a linked list.
51    */
52   struct GNUNET_PEERINFO_AddContext *prev;
53
54   /**
55    * Function to call after request has been transmitted, or NULL.
56    */
57   GNUNET_PEERINFO_Continuation cont;
58
59   /**
60    * Closure for 'cont'.
61    */
62   void *cont_cls;
63
64   /**
65    * Number of bytes of the request message (follows after this struct).
66    */
67   size_t size;
68
69 };
70
71
72 /**
73  * Context for an iteration request.
74  */
75 struct GNUNET_PEERINFO_IteratorContext
76 {
77
78   /**
79    * Kept in a DLL.
80    */
81   struct GNUNET_PEERINFO_IteratorContext *next;
82
83   /**
84    * Kept in a DLL.
85    */
86   struct GNUNET_PEERINFO_IteratorContext *prev;
87
88   /**
89    * Handle to the PEERINFO service.
90    */
91   struct GNUNET_PEERINFO_Handle *h;
92
93   /**
94    * Function to call with the results.
95    */
96   GNUNET_PEERINFO_Processor callback;
97
98   /**
99    * Closure for 'callback'.
100    */
101   void *callback_cls;
102
103   /**
104    * Our entry in the transmission queue.
105    */
106   struct GNUNET_PEERINFO_AddContext *ac;
107
108   /**
109    * Task responsible for timeout.
110    */
111   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
112
113   /**
114    * Timeout for the operation.
115    */
116   struct GNUNET_TIME_Absolute timeout;
117
118   /**
119    * Are we now receiving?
120    */
121   int in_receive;
122 };
123
124
125 /**
126  * Handle to the peerinfo service.
127  */
128 struct GNUNET_PEERINFO_Handle
129 {
130   /**
131    * Our configuration.
132    */
133   const struct GNUNET_CONFIGURATION_Handle *cfg;
134
135   /**
136    * Connection to the service.
137    */
138   struct GNUNET_CLIENT_Connection *client;
139
140   /**
141    * Head of transmission queue.
142    */
143   struct GNUNET_PEERINFO_AddContext *ac_head;
144
145   /**
146    * Tail of transmission queue.
147    */
148   struct GNUNET_PEERINFO_AddContext *ac_tail;
149
150   /**
151    * Handle for the current transmission request, or NULL if none is pending.
152    */
153   struct GNUNET_CLIENT_TransmitHandle *th;
154
155   /**
156    * Head of iterator DLL.
157    */
158   struct GNUNET_PEERINFO_IteratorContext *ic_head;
159
160   /**
161    * Tail of iterator DLL.
162    */
163   struct GNUNET_PEERINFO_IteratorContext *ic_tail;
164
165   /**
166    * ID for a reconnect task.
167    */
168   GNUNET_SCHEDULER_TaskIdentifier r_task;
169
170   /**
171    * Set to GNUNET_YES if we are currently receiving replies from the
172    * service.
173    */
174   int in_receive;
175
176 };
177
178
179 /**
180  * Connect to the peerinfo service.
181  *
182  * @param cfg configuration to use
183  * @return NULL on error (configuration related, actual connection
184  *         establishment may happen asynchronously).
185  */
186 struct GNUNET_PEERINFO_Handle *
187 GNUNET_PEERINFO_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
188 {
189   struct GNUNET_PEERINFO_Handle *h;
190
191   h = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_Handle));
192   h->client = GNUNET_CLIENT_connect ("peerinfo", cfg);
193   h->cfg = cfg;
194   return h;
195 }
196
197
198 /**
199  * Disconnect from the peerinfo service.  Note that all iterators must
200  * have completed or have been cancelled by the time this function is
201  * called (otherwise, calling this function is a serious error).
202  * Furthermore, if 'GNUNET_PEERINFO_add_peer' operations are still
203  * pending, they will be cancelled silently on disconnect.
204  *
205  * @param h handle to disconnect
206  */
207 void
208 GNUNET_PEERINFO_disconnect (struct GNUNET_PEERINFO_Handle *h)
209 {
210   struct GNUNET_PEERINFO_AddContext *ac;
211   struct GNUNET_PEERINFO_IteratorContext *ic;
212
213   while (NULL != (ic = h->ic_head))
214   {
215     GNUNET_break (GNUNET_YES == ic->in_receive);
216     ic->in_receive = GNUNET_NO;
217     GNUNET_PEERINFO_iterate_cancel (ic);
218   }
219   while (NULL != (ac = h->ac_head))
220   {
221     GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
222     if (NULL != ac->cont)
223       ac->cont (ac->cont_cls, _("aborted due to explicit disconnect request"));
224     GNUNET_free (ac);
225   }
226   if (NULL != h->th)
227   {
228     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
229     h->th = NULL;
230   }
231   if (NULL != h->client)
232   {
233     GNUNET_CLIENT_disconnect (h->client);
234     h->client = NULL;
235   }
236   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
237   {
238     GNUNET_SCHEDULER_cancel (h->r_task);
239     h->r_task = GNUNET_SCHEDULER_NO_TASK;
240   }
241   GNUNET_free (h);
242 }
243
244
245 /**
246  * Check if we have a request pending in the transmission queue and are
247  * able to transmit it right now.  If so, schedule transmission.
248  *
249  * @param h handle to the service
250  */
251 static void
252 trigger_transmit (struct GNUNET_PEERINFO_Handle *h);
253
254
255 /**
256  * Close the existing connection to PEERINFO and reconnect.
257  *
258  * @param h handle to the service
259  */
260 static void
261 reconnect (struct GNUNET_PEERINFO_Handle *h);
262
263
264 /**
265  * Task scheduled to re-try connecting to the peerinfo service.
266  *
267  * @param cls the 'struct GNUNET_PEERINFO_Handle'
268  * @param tc scheduler context
269  */
270 static void
271 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
272 {
273   struct GNUNET_PEERINFO_Handle *h = cls;
274
275   h->r_task = GNUNET_SCHEDULER_NO_TASK;
276   reconnect (h);
277 }
278
279
280 /**
281  * Close the existing connection to PEERINFO and reconnect.
282  *
283  * @param h handle to the service
284  */
285 static void
286 reconnect (struct GNUNET_PEERINFO_Handle *h)
287 {
288   if (GNUNET_SCHEDULER_NO_TASK != h->r_task)
289   {
290     GNUNET_SCHEDULER_cancel (h->r_task);
291     h->r_task = GNUNET_SCHEDULER_NO_TASK;
292   }
293   if (NULL != h->th)
294   {
295     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
296     h->th = NULL;
297   }
298   if (NULL != h->client)
299   {
300     GNUNET_CLIENT_disconnect (h->client);
301     h->client = NULL;
302   }
303   h->in_receive = GNUNET_NO;
304   h->client = GNUNET_CLIENT_connect ("peerinfo", h->cfg);
305   if (NULL == h->client)
306   {
307     h->r_task =
308         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &reconnect_task,
309                                       h);
310     return;
311   }
312   trigger_transmit (h);
313 }
314
315
316 /**
317  * Transmit the request at the head of the transmission queue
318  * and trigger continuation (if any).
319  *
320  * @param cls the 'struct GNUNET_PEERINFO_Handle' (with the queue)
321  * @param size size of the buffer (0 on error)
322  * @param buf where to copy the message
323  * @return number of bytes copied to buf
324  */
325 static size_t
326 do_transmit (void *cls, size_t size, void *buf)
327 {
328   struct GNUNET_PEERINFO_Handle *h = cls;
329   struct GNUNET_PEERINFO_AddContext *ac = h->ac_head;
330   size_t ret;
331
332   h->th = NULL;
333   if (NULL == ac)
334     return 0; /* request was cancelled in the meantime */
335   if (NULL == buf)
336   {
337     /* peerinfo service died */
338     LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
339          "Failed to transmit message to `%s' service.\n", "PEERINFO");
340     GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
341     reconnect (h);
342     if (NULL != ac->cont)
343       ac->cont (ac->cont_cls, _("failed to transmit request (service down?)"));
344     GNUNET_free (ac);
345     return 0;
346   }
347   ret = ac->size;
348   if (size < ret)
349   {
350     /* change in head of queue (i.e. cancel + add), try again */
351     trigger_transmit (h);
352     return 0;
353   }
354   LOG (GNUNET_ERROR_TYPE_DEBUG,
355        "Transmitting request of size %u to `%s' service.\n", ret, "PEERINFO");
356   memcpy (buf, &ac[1], ret);
357   GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ac);
358   trigger_transmit (h);
359   if (NULL != ac->cont)
360     ac->cont (ac->cont_cls, NULL);
361   GNUNET_free (ac);
362   return ret;
363 }
364
365
366 /**
367  * Check if we have a request pending in the transmission queue and are
368  * able to transmit it right now.  If so, schedule transmission.
369  *
370  * @param h handle to the service
371  */
372 static void
373 trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
374 {
375   struct GNUNET_PEERINFO_AddContext *ac;
376
377   if (NULL == (ac = h->ac_head))
378     return; /* no requests queued */
379   if (NULL != h->th)
380     return; /* request already pending */
381   if (GNUNET_YES == h->in_receive)
382     return; /* still reading replies from last request */
383   if (NULL == h->client)
384   {
385     /* disconnected, try to reconnect */
386     reconnect (h);
387     return;
388   }
389   h->th =
390     GNUNET_CLIENT_notify_transmit_ready (h->client, ac->size,
391                                          GNUNET_TIME_UNIT_FOREVER_REL,
392                                          GNUNET_YES,
393                                          &do_transmit, h);
394 }
395
396
397 /**
398  * Add a host to the persistent list.  This method operates in
399  * semi-reliable mode: if the transmission is not completed by
400  * the time 'GNUNET_PEERINFO_disconnect' is called, it will be
401  * aborted.  Furthermore, if a second HELLO is added for the
402  * same peer before the first one was transmitted, PEERINFO may
403  * merge the two HELLOs prior to transmission to the service.
404  *
405  * @param h handle to the peerinfo service
406  * @param hello the verified (!) HELLO message
407  */
408 struct GNUNET_PEERINFO_AddContext *
409 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
410                           const struct GNUNET_HELLO_Message *hello,
411                           GNUNET_PEERINFO_Continuation cont,
412                           void *cont_cls)
413 {
414   uint16_t hs = GNUNET_HELLO_size (hello);
415   struct GNUNET_PEERINFO_AddContext *ac;
416   struct GNUNET_PeerIdentity peer;
417
418   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
419   LOG (GNUNET_ERROR_TYPE_DEBUG,
420        "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
421        GNUNET_i2s (&peer), hs, "HELLO");
422   ac = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) + hs);
423   ac->size = hs;
424   ac->cont = cont;
425   ac->cont_cls = cont_cls;
426   memcpy (&ac[1], hello, hs);
427   GNUNET_CONTAINER_DLL_insert_after (h->ac_head, h->ac_tail, h->ac_tail, ac);
428   trigger_transmit (h);
429   return ac;
430 }
431
432
433 /**
434  * Type of a function to call when we receive a message from the
435  * service.  Call the iterator with the result and (if applicable)
436  * continue to receive more messages or trigger processing the next
437  * event (if applicable).
438  *
439  * @param cls closure
440  * @param msg message received, NULL on timeout or fatal error
441  */
442 static void
443 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
444 {
445   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
446   struct GNUNET_PEERINFO_Handle *h = ic->h;
447   const struct InfoMessage *im;
448   const struct GNUNET_HELLO_Message *hello;
449   GNUNET_PEERINFO_Processor cb;
450   void *cb_cls;
451   uint16_t ms;
452
453   h->in_receive = GNUNET_NO;
454   ic->in_receive = GNUNET_NO;
455   cb = ic->callback;
456   cb_cls = ic->callback_cls;
457   if (NULL == msg)
458   {
459     /* peerinfo service died, signal error */
460     GNUNET_PEERINFO_iterate_cancel (ic);
461     reconnect (h);
462     if (NULL != cb)
463       cb (cb_cls, NULL, NULL,
464           _("Failed to receive response from `PEERINFO' service."));
465     return;
466   }
467   if (GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END == ntohs (msg->type))
468   {
469     /* normal end of list of peers, signal end, process next pending request */
470     LOG (GNUNET_ERROR_TYPE_DEBUG,
471          "Received end of list of peers from `%s' service\n", "PEERINFO");
472     GNUNET_PEERINFO_iterate_cancel (ic);
473     trigger_transmit (h);
474     if (NULL != cb)
475       cb (cb_cls, NULL, NULL, NULL);
476     return;
477   }
478   ms = ntohs (msg->size);
479   if ((ms < sizeof (struct InfoMessage)) ||
480       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
481   {
482     /* malformed message */
483     GNUNET_break (0);
484     GNUNET_PEERINFO_iterate_cancel (ic);
485     reconnect (h);
486     if (NULL != cb)
487       cb (cb_cls, NULL, NULL,
488           _("Received invalid message from `PEERINFO' service."));
489     return;
490   }
491   im = (const struct InfoMessage *) msg;
492   GNUNET_break (0 == ntohl (im->reserved));
493   hello = NULL;
494   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
495   {
496     hello = (const struct GNUNET_HELLO_Message *) &im[1];
497     if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
498     {
499       /* malformed message */
500       GNUNET_break (0);
501       GNUNET_PEERINFO_iterate_cancel (ic);
502       reconnect (h);
503       if (NULL != cb)      
504         cb (cb_cls, NULL, NULL,
505             _("Received invalid message from `PEERINFO' service."));
506       return;
507     }
508   }
509   /* normal data message */
510   LOG (GNUNET_ERROR_TYPE_DEBUG,
511        "Received %u bytes of `%s' information about peer `%s' from `%s' service\n",
512        (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello), "HELLO",
513        GNUNET_i2s (&im->peer), "PEERINFO");
514   h->in_receive = GNUNET_YES;
515   ic->in_receive = GNUNET_YES;
516   GNUNET_CLIENT_receive (h->client, &peerinfo_handler, ic,
517                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
518   if (NULL != cb)
519     cb (cb_cls, &im->peer, hello, NULL);
520 }
521
522
523 /**
524  * We've transmitted the iteration request.  Now get ready to process
525  * the results (or handle transmission error).
526  *
527  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext'
528  * @param emsg error message, NULL if transmission worked
529  */
530 static void
531 iterator_start_receive (void *cls, const char *emsg)
532 {
533   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
534   struct GNUNET_PEERINFO_Handle *h = ic->h;
535   GNUNET_PEERINFO_Processor cb;
536   void *cb_cls;
537
538   ic->ac = NULL;
539   if (NULL != emsg)
540   {
541     cb = ic->callback;
542     cb_cls = ic->callback_cls;
543     GNUNET_PEERINFO_iterate_cancel (ic);
544     reconnect (h);
545     if (NULL != cb)
546       cb (cb_cls, NULL, NULL, emsg);
547     return;
548   }
549   LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for response from `%s' service.\n",
550        "PEERINFO");
551   h->in_receive = GNUNET_YES;
552   ic->in_receive = GNUNET_YES;
553   GNUNET_CLIENT_receive (h->client, &peerinfo_handler, ic,
554                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
555 }
556
557
558 /**
559  * Peerinfo iteration request has timed out.
560  *
561  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext*'
562  * @param tc scheduler context
563  */
564 static void
565 signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
566 {
567   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
568   GNUNET_PEERINFO_Processor cb;
569   void *cb_cls;
570
571   ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
572   cb = ic->callback;
573   cb_cls = ic->callback_cls;
574   GNUNET_PEERINFO_iterate_cancel (ic);
575   if (NULL != cb)
576     cb (cb_cls, NULL, NULL,
577         _("Timeout transmitting iteration request to `PEERINFO' service."));
578 }
579
580
581 /**
582  * Call a method for each known matching host and change its trust
583  * value.  The callback method will be invoked once for each matching
584  * host and then finally once with a NULL pointer.  After that final
585  * invocation, the iterator context must no longer be used.
586  *
587  * Instead of calling this function with 'peer == NULL' it is often
588  * better to use 'GNUNET_PEERINFO_notify'.
589  *
590  * @param h handle to the peerinfo service
591  * @param peer restrict iteration to this peer only (can be NULL)
592  * @param timeout how long to wait until timing out
593  * @param callback the method to call for each peer
594  * @param callback_cls closure for callback
595  * @return iterator context
596  */
597 struct GNUNET_PEERINFO_IteratorContext *
598 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
599                          const struct GNUNET_PeerIdentity *peer,
600                          struct GNUNET_TIME_Relative timeout,
601                          GNUNET_PEERINFO_Processor callback, void *callback_cls)
602 {
603   struct GNUNET_MessageHeader *lapm;
604   struct ListPeerMessage *lpm;
605   struct GNUNET_PEERINFO_IteratorContext *ic;
606   struct GNUNET_PEERINFO_AddContext *ac;
607
608   if (NULL == peer)
609   {
610     LOG (GNUNET_ERROR_TYPE_DEBUG,
611          "Requesting list of peers from PEERINFO service\n");
612     ac =
613         GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) +
614                        sizeof (struct GNUNET_MessageHeader));
615     ac->size = sizeof (struct GNUNET_MessageHeader);
616     lapm = (struct GNUNET_MessageHeader *) &ac[1];
617     lapm->size = htons (sizeof (struct GNUNET_MessageHeader));
618     lapm->type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
619   }
620   else
621   {
622     LOG (GNUNET_ERROR_TYPE_DEBUG,
623          "Requesting information on peer `%4s' from PEERINFO service\n",
624          GNUNET_i2s (peer));
625     ac =
626         GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) +
627                        sizeof (struct ListPeerMessage));
628     ac->size = sizeof (struct ListPeerMessage);
629     lpm = (struct ListPeerMessage *) &ac[1];
630     lpm->header.size = htons (sizeof (struct ListPeerMessage));
631     lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
632     memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
633   }
634   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext));
635   ic->h = h;
636   ic->ac = ac;
637   ic->callback = callback;
638   ic->callback_cls = callback_cls;
639   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
640   ic->timeout_task =
641       GNUNET_SCHEDULER_add_delayed (timeout, &signal_timeout, ic);
642   ac->cont = &iterator_start_receive;
643   ac->cont_cls = ic;
644   GNUNET_CONTAINER_DLL_insert_after (h->ac_head, h->ac_tail, h->ac_tail, ac);
645   GNUNET_CONTAINER_DLL_insert (h->ic_head,
646                                h->ic_tail,
647                                ic);
648   trigger_transmit (h);
649   return ic;
650 }
651
652
653 /**
654  * Cancel an iteration over peer information.
655  *
656  * @param ic context of the iterator to cancel
657  */
658 void
659 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
660 {
661   struct GNUNET_PEERINFO_Handle *h;
662
663   h = ic->h;
664   GNUNET_CONTAINER_DLL_remove (h->ic_head,
665                                h->ic_tail,
666                                ic);
667   if (GNUNET_SCHEDULER_NO_TASK != ic->timeout_task)
668   {
669     GNUNET_SCHEDULER_cancel (ic->timeout_task);
670     ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
671   }
672   ic->callback = NULL;
673   if (GNUNET_YES == ic->in_receive)
674     return;                     /* need to finish processing */
675   if (NULL != ic->ac)
676   {
677     GNUNET_CONTAINER_DLL_remove (h->ac_head, h->ac_tail, ic->ac);
678     GNUNET_free (ic->ac);
679   }
680   GNUNET_free (ic);
681 }
682
683
684 /* end of peerinfo_api.c */