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