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