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