831cd92409d47e7bdc47ef5a6aa2d8441725aa44
[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     LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
278          _("Failed to transmit message to `%s' service.\n"), "PEERINFO");
279     GNUNET_CONTAINER_DLL_remove (h->tq_head, h->tq_tail, tqe);
280     reconnect (h);
281     if (tqe->cont != NULL)
282       tqe->cont (tqe->cont_cls, GNUNET_SYSERR);
283     GNUNET_free (tqe);
284     return 0;
285   }
286   ret = tqe->size;
287   GNUNET_assert (size >= ret);
288   memcpy (buf, &tqe[1], ret);
289 #if DEBUG_PEERINFO
290   LOG (GNUNET_ERROR_TYPE_DEBUG,
291        "Transmitting request of size %u to `%s' service.\n", ret, "PEERINFO");
292 #endif
293   GNUNET_CONTAINER_DLL_remove (h->tq_head, h->tq_tail, tqe);
294   if (tqe->cont != NULL)
295     tqe->cont (tqe->cont_cls, GNUNET_OK);
296   else
297     trigger_transmit (h);
298   GNUNET_free (tqe);
299   return ret;
300 }
301
302
303 /**
304  * Check if we have a request pending in the transmission queue and are
305  * able to transmit it right now.  If so, schedule transmission.
306  *
307  * @param h handle to the service
308  */
309 static void
310 trigger_transmit (struct GNUNET_PEERINFO_Handle *h)
311 {
312   struct TransmissionQueueEntry *tqe;
313
314   if (NULL == (tqe = h->tq_head))
315     return;
316   if (h->th != NULL)
317     return;
318   if (h->in_receive == GNUNET_YES)
319     return;
320   if (NULL == h->client)
321   {
322     reconnect (h);
323     return;
324   }
325   h->th =
326       GNUNET_CLIENT_notify_transmit_ready (h->client, tqe->size,
327                                            GNUNET_TIME_absolute_get_remaining
328                                            (tqe->timeout), GNUNET_YES,
329                                            &do_transmit, h);
330 }
331
332
333 /**
334  * Add a host to the persistent list.  This method operates in
335  * semi-reliable mode: if the transmission is not completed by
336  * the time 'GNUNET_PEERINFO_disconnect' is called, it will be
337  * aborted.  Furthermore, if a second HELLO is added for the
338  * same peer before the first one was transmitted, PEERINFO may
339  * merge the two HELLOs prior to transmission to the service.
340  *
341  * @param h handle to the peerinfo service
342  * @param hello the verified (!) HELLO message
343  */
344 void
345 GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
346                           const struct GNUNET_HELLO_Message *hello)
347 {
348   uint16_t hs = GNUNET_HELLO_size (hello);
349   struct TransmissionQueueEntry *tqe;
350
351 #if DEBUG_PEERINFO
352   struct GNUNET_PeerIdentity peer;
353
354   GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer));
355   LOG (GNUNET_ERROR_TYPE_DEBUG,
356        "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n",
357        GNUNET_i2s (&peer), hs, "HELLO");
358 #endif
359   tqe = GNUNET_malloc (sizeof (struct TransmissionQueueEntry) + hs);
360   tqe->size = hs;
361   tqe->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
362   memcpy (&tqe[1], hello, hs);
363   GNUNET_CONTAINER_DLL_insert_after (h->tq_head, h->tq_tail, h->tq_tail, tqe);
364   trigger_transmit (h);
365 }
366
367
368 /**
369  * Context for an iteration request.
370  */
371 struct GNUNET_PEERINFO_IteratorContext
372 {
373   /**
374    * Handle to the PEERINFO service.
375    */
376   struct GNUNET_PEERINFO_Handle *h;
377
378   /**
379    * Function to call with the results.
380    */
381   GNUNET_PEERINFO_Processor callback;
382
383   /**
384    * Closure for 'callback'.
385    */
386   void *callback_cls;
387
388   /**
389    * Our entry in the transmission queue.
390    */
391   struct TransmissionQueueEntry *tqe;
392
393   /**
394    * Task responsible for timeout.
395    */
396   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
397
398   /**
399    * Timeout for the operation.
400    */
401   struct GNUNET_TIME_Absolute timeout;
402
403   /**
404    * Are we now receiving?
405    */
406   int in_receive;
407 };
408
409
410 /**
411  * Type of a function to call when we receive a message
412  * from the service.
413  *
414  * @param cls closure
415  * @param msg message received, NULL on timeout or fatal error
416  */
417 static void
418 peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg)
419 {
420   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
421   const struct InfoMessage *im;
422   const struct GNUNET_HELLO_Message *hello;
423   uint16_t ms;
424
425   ic->h->in_receive = GNUNET_NO;
426   if (msg == NULL)
427   {
428     reconnect (ic->h);
429     if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
430       GNUNET_SCHEDULER_cancel (ic->timeout_task);
431     if (ic->callback != NULL)
432       ic->callback (ic->callback_cls, NULL, NULL,
433                     _("Failed to receive response from `PEERINFO' service."));
434     GNUNET_free (ic);
435     return;
436   }
437   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
438   {
439 #if DEBUG_PEERINFO
440     LOG (GNUNET_ERROR_TYPE_DEBUG,
441          "Received end of list of peers from `%s' service\n", "PEERINFO");
442 #endif
443     trigger_transmit (ic->h);
444     if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
445       GNUNET_SCHEDULER_cancel (ic->timeout_task);
446     if (ic->callback != NULL)
447       ic->callback (ic->callback_cls, NULL, NULL, NULL);
448     GNUNET_free (ic);
449     return;
450   }
451   ms = ntohs (msg->size);
452   if ((ms < sizeof (struct InfoMessage)) ||
453       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
454   {
455     GNUNET_break (0);
456     reconnect (ic->h);
457     if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
458       GNUNET_SCHEDULER_cancel (ic->timeout_task);
459     if (ic->callback != NULL)
460       ic->callback (ic->callback_cls, NULL, NULL,
461                     _("Received invalid message from `PEERINFO' service.\n"));
462     GNUNET_free (ic);
463     return;
464   }
465   im = (const struct InfoMessage *) msg;
466   GNUNET_break (0 == ntohl (im->reserved));
467   hello = NULL;
468   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
469   {
470     hello = (const struct GNUNET_HELLO_Message *) &im[1];
471     if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
472     {
473       GNUNET_break (0);
474       reconnect (ic->h);
475       if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
476         GNUNET_SCHEDULER_cancel (ic->timeout_task);
477       if (ic->callback != NULL)
478         ic->callback (ic->callback_cls, NULL, NULL,
479                       _("Received invalid message from `PEERINFO' service.\n"));
480       GNUNET_free (ic);
481       return;
482     }
483   }
484 #if DEBUG_PEERINFO
485   LOG (GNUNET_ERROR_TYPE_DEBUG,
486        "Received %u bytes of `%s' information about peer `%s' from `%s' service\n",
487        (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello), "HELLO",
488        GNUNET_i2s (&im->peer), "PEERINFO");
489 #endif
490   ic->h->in_receive = GNUNET_YES;
491   if (ic->callback != NULL)
492     ic->callback (ic->callback_cls, &im->peer, hello, NULL);
493   GNUNET_CLIENT_receive (ic->h->client, &peerinfo_handler, ic,
494                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
495 }
496
497
498 /**
499  * We've transmitted the iteration request.  Now get ready to process
500  * the results (or handle transmission error).
501  *
502  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext'
503  * @param transmit_success GNUNET_OK if transmission worked
504  */
505 static void
506 iterator_start_receive (void *cls, int transmit_success)
507 {
508   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
509
510   if (GNUNET_OK != transmit_success)
511   {
512     if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
513     {
514       GNUNET_SCHEDULER_cancel (ic->timeout_task);
515       ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
516     }
517     reconnect (ic->h);
518     if (ic->callback != NULL)
519       ic->callback (ic->callback_cls, NULL, NULL,
520                     _
521                     ("Failed to transmit iteration request to `PEERINFO' service\n"));
522     GNUNET_free (ic);
523     return;
524   }
525 #if DEBUG_PEERINFO
526   LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for response from `%s' service.\n",
527        "PEERINFO");
528 #endif
529   ic->h->in_receive = GNUNET_YES;
530   ic->in_receive = GNUNET_YES;
531   ic->tqe = NULL;
532   GNUNET_CLIENT_receive (ic->h->client, &peerinfo_handler, ic,
533                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
534 }
535
536
537 /**
538  * Peerinfo iteration request has timed out.
539  *
540  * @param cls the 'struct GNUNET_PEERINFO_IteratorContext*'
541  * @param tc scheduler context
542  */
543 static void
544 signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
545 {
546   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
547
548   ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
549   if (!ic->in_receive)
550     GNUNET_CONTAINER_DLL_remove (ic->h->tq_head, ic->h->tq_tail, ic->tqe);
551   else
552     reconnect (ic->h);
553   ic->callback (ic->callback_cls, NULL, NULL,
554                 _
555                 ("Timeout transmitting iteration request to `PEERINFO' service.\n"));
556   ic->callback = NULL;
557   GNUNET_free_non_null (ic->tqe);
558   GNUNET_free (ic);
559 }
560
561
562 /**
563  * Call a method for each known matching host and change its trust
564  * value.  The callback method will be invoked once for each matching
565  * host and then finally once with a NULL pointer.  After that final
566  * invocation, the iterator context must no longer be used.
567  *
568  * Instead of calling this function with 'peer == NULL' it is often
569  * better to use 'GNUNET_PEERINFO_notify'.
570  *
571  * @param h handle to the peerinfo service
572  * @param peer restrict iteration to this peer only (can be NULL)
573  * @param timeout how long to wait until timing out
574  * @param callback the method to call for each peer
575  * @param callback_cls closure for callback
576  * @return iterator context
577  */
578 struct GNUNET_PEERINFO_IteratorContext *
579 GNUNET_PEERINFO_iterate (struct GNUNET_PEERINFO_Handle *h,
580                          const struct GNUNET_PeerIdentity *peer,
581                          struct GNUNET_TIME_Relative timeout,
582                          GNUNET_PEERINFO_Processor callback, void *callback_cls)
583 {
584   struct GNUNET_MessageHeader *lapm;
585   struct ListPeerMessage *lpm;
586   struct GNUNET_PEERINFO_IteratorContext *ic;
587   struct TransmissionQueueEntry *tqe;
588
589   if (peer == NULL)
590   {
591 #if DEBUG_PEERINFO
592     LOG (GNUNET_ERROR_TYPE_DEBUG,
593          "Requesting list of peers from PEERINFO service\n");
594 #endif
595     tqe =
596         GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
597                        sizeof (struct GNUNET_MessageHeader));
598     tqe->size = sizeof (struct GNUNET_MessageHeader);
599     lapm = (struct GNUNET_MessageHeader *) &tqe[1];
600     lapm->size = htons (sizeof (struct GNUNET_MessageHeader));
601     lapm->type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
602   }
603   else
604   {
605 #if DEBUG_PEERINFO
606     LOG (GNUNET_ERROR_TYPE_DEBUG,
607          "Requesting information on peer `%4s' from PEERINFO service\n",
608          GNUNET_i2s (peer));
609 #endif
610     tqe =
611         GNUNET_malloc (sizeof (struct TransmissionQueueEntry) +
612                        sizeof (struct ListPeerMessage));
613     tqe->size = sizeof (struct ListPeerMessage);
614     lpm = (struct ListPeerMessage *) &tqe[1];
615     lpm->header.size = htons (sizeof (struct ListPeerMessage));
616     lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
617     memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
618   }
619   ic = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext));
620   ic->h = h;
621   ic->tqe = tqe;
622   ic->callback = callback;
623   ic->callback_cls = callback_cls;
624   ic->timeout = GNUNET_TIME_relative_to_absolute (timeout);
625   ic->timeout_task =
626       GNUNET_SCHEDULER_add_delayed (timeout, &signal_timeout, ic);
627   tqe->timeout = ic->timeout;
628   tqe->cont = &iterator_start_receive;
629   tqe->cont_cls = ic;
630   tqe->timeout = ic->timeout;
631   GNUNET_CONTAINER_DLL_insert_after (h->tq_head, h->tq_tail, h->tq_tail, tqe);
632   trigger_transmit (h);
633   return ic;
634 }
635
636
637 /**
638  * Cancel an iteration over peer information.
639  *
640  * @param ic context of the iterator to cancel
641  */
642 void
643 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
644 {
645   if (ic->timeout_task != GNUNET_SCHEDULER_NO_TASK)
646   {
647     GNUNET_SCHEDULER_cancel (ic->timeout_task);
648     ic->timeout_task = GNUNET_SCHEDULER_NO_TASK;
649   }
650   ic->callback = NULL;
651   if (GNUNET_YES == ic->in_receive)
652     return;                     /* need to finish processing */
653   GNUNET_CONTAINER_DLL_remove (ic->h->tq_head, ic->h->tq_tail, ic->tqe);
654   GNUNET_free (ic->tqe);
655   GNUNET_free (ic);
656 }
657
658
659 /* end of peerinfo_api.c */