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