f3479ab7be34eff01e127b852895607eec77a978
[oweals/gnunet.git] / src / peerinfo / peerinfo_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2007, 2009 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 2, 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_peerinfo_service.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_time_lib.h"
31 #include "peerinfo.h"
32
33 #define ADD_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
34
35
36 struct CAFContext
37 {
38   struct GNUNET_CLIENT_Connection *client;
39   struct GNUNET_MessageHeader *msg;
40 };
41
42
43 static size_t
44 copy_and_free (void *cls, size_t size, void *buf)
45 {
46   struct CAFContext *cc = cls;
47   struct GNUNET_MessageHeader *msg = cc->msg;
48   uint16_t msize;
49
50   if (buf == NULL)
51     {
52 #if DEBUG_PEERINFO
53       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
54                   _
55                   ("Failed to transmit message of type %u to `%s' service.\n"),
56                   ntohs (msg->type), "peerinfo");
57 #endif
58       GNUNET_free (msg);
59       GNUNET_CLIENT_disconnect (cc->client);
60       GNUNET_free (cc);
61       return 0;
62     }
63   msize = ntohs (msg->size);
64   GNUNET_assert (size >= msize);
65   memcpy (buf, msg, msize);
66   GNUNET_free (msg);
67   GNUNET_CLIENT_disconnect (cc->client);
68   GNUNET_free (cc);
69   return msize;
70 }
71
72
73
74 /**
75  * Add a host to the persistent list.
76  *
77  * @param cfg configuration to use
78  * @param sched scheduler to use
79  * @param peer identity of the peer
80  * @param hello the verified (!) HELLO message
81  */
82 void
83 GNUNET_PEERINFO_add_peer (const struct GNUNET_CONFIGURATION_Handle *cfg,
84                           struct GNUNET_SCHEDULER_Handle *sched,
85                           const struct GNUNET_PeerIdentity *peer,
86                           const struct GNUNET_HELLO_Message *hello)
87 {
88   struct GNUNET_CLIENT_Connection *client;
89   struct PeerAddMessage *pam;
90   uint16_t hs;
91   struct CAFContext *cc;
92
93 #if DEBUG_PEERINFO
94   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
95               "Adding peer `%s' to peerinfo database\n",
96               GNUNET_i2s(peer));
97 #endif
98   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
99   if (client == NULL)
100     {
101       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
102                   _("Could not connect to `%s' service.\n"), "peerinfo");
103       return;
104     }
105   hs = GNUNET_HELLO_size (hello);
106   pam = GNUNET_malloc (sizeof (struct PeerAddMessage) + hs);
107   pam->header.size = htons (hs + sizeof (struct PeerAddMessage));
108   pam->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_ADD);
109   memcpy (&pam->peer, peer, sizeof (struct GNUNET_PeerIdentity));
110   memcpy (&pam[1], hello, hs);
111   cc = GNUNET_malloc (sizeof (struct CAFContext));
112   cc->client = client;
113   cc->msg = &pam->header;
114   GNUNET_CLIENT_notify_transmit_ready (client,
115                                        ntohs (pam->header.size),
116                                        ADD_PEER_TIMEOUT, 
117                                        GNUNET_NO,
118                                        &copy_and_free, cc);
119 }
120
121
122 /**
123  * Context for the info handler.
124  */
125 struct GNUNET_PEERINFO_IteratorContext
126 {
127
128   /**
129    * Our connection to the PEERINFO service.
130    */
131   struct GNUNET_CLIENT_Connection *client;
132
133   /**
134    * Function to call with information.
135    */
136   GNUNET_PEERINFO_Processor callback;
137
138   /**
139    * Closure for callback.
140    */
141   void *callback_cls;
142
143   /**
144    * When should we time out?
145    */
146   struct GNUNET_TIME_Absolute timeout;
147
148 };
149
150
151 /**
152  * Type of a function to call when we receive a message
153  * from the service.
154  *
155  * @param cls closure
156  * @param msg message received, NULL on timeout or fatal error
157  */
158 static void
159 info_handler (void *cls, const struct GNUNET_MessageHeader *msg)
160 {
161   struct GNUNET_PEERINFO_IteratorContext *ic = cls;
162   const struct InfoMessage *im;
163   const struct GNUNET_HELLO_Message *hello;
164   uint16_t ms;
165
166   if (msg == NULL)
167     {
168       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
169                   _("Failed to receive response from `%s' service.\n"),
170                   "peerinfo");
171       ic->callback (ic->callback_cls, NULL, NULL, 1);
172       GNUNET_CLIENT_disconnect (ic->client);
173       GNUNET_free (ic);
174       return;
175     }
176   if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END)
177     {
178 #if DEBUG_PEERINFO
179       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180                   "Received end of list of peers from peerinfo database\n");
181 #endif
182       ic->callback (ic->callback_cls, NULL, NULL, 0);
183       GNUNET_CLIENT_disconnect (ic->client);
184       GNUNET_free (ic);
185       return;
186     }
187   ms = ntohs (msg->size);
188   if ((ms < sizeof (struct InfoMessage)) ||
189       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
190     {
191       GNUNET_break (0);
192       ic->callback (ic->callback_cls, NULL, NULL, 2);
193       GNUNET_CLIENT_disconnect (ic->client);
194       GNUNET_free (ic);
195       return;
196     }
197   im = (const struct InfoMessage *) msg;
198   hello = NULL;
199   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
200     {
201       hello = (const struct GNUNET_HELLO_Message *) &im[1];
202       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
203         {
204           GNUNET_break (0);
205           ic->callback (ic->callback_cls, NULL, NULL, 2);
206           GNUNET_CLIENT_disconnect (ic->client);
207           GNUNET_free (ic);
208           return;
209         }
210     }
211 #if DEBUG_PEERINFO
212   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
213               "Received information about peer `%s' from peerinfo database\n",
214               GNUNET_i2s (&im->peer));
215 #endif
216   ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust));
217   GNUNET_CLIENT_receive (ic->client,
218                          &info_handler,
219                          ic,
220                          GNUNET_TIME_absolute_get_remaining (ic->timeout));
221 }
222
223
224 /**
225  * Call a method for each known matching host and change
226  * its trust value.  The method will be invoked once for
227  * each host and then finally once with a NULL pointer.
228  * Note that the last call can be triggered by timeout or
229  * by simply being done; however, the trust argument will
230  * be set to zero if we are done and to 1 if we timed out.
231  *
232  * @param cfg configuration to use
233  * @param sched scheduler to use
234  * @param peer restrict iteration to this peer only (can be NULL)
235  * @param trust_delta how much to change the trust in all matching peers
236  * @param timeout how long to wait until timing out
237  * @param callback the method to call for each peer
238  * @param callback_cls closure for callback
239  * @return NULL on error, otherwise an iterator context
240  */
241 struct GNUNET_PEERINFO_IteratorContext *
242 GNUNET_PEERINFO_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
243                          struct GNUNET_SCHEDULER_Handle *sched,
244                          const struct GNUNET_PeerIdentity *peer,
245                          int trust_delta,
246                          struct GNUNET_TIME_Relative timeout,
247                          GNUNET_PEERINFO_Processor callback,
248                          void *callback_cls)
249 {
250   struct GNUNET_CLIENT_Connection *client;
251   struct ListAllPeersMessage *lapm;
252   struct ListPeerMessage *lpm;
253   struct GNUNET_PEERINFO_IteratorContext *ihc;
254
255   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
256   if (client == NULL)
257     {
258       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
259                   _("Could not connect to `%s' service.\n"), "peerinfo");
260       return NULL;
261     }
262 #if DEBUG_PEERINFO
263   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264               "Requesting list of peers from peerinfo database\n");
265 #endif
266   if (peer == NULL)
267     {
268       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
269                            sizeof (struct ListAllPeersMessage));
270       lapm = (struct ListAllPeersMessage *) &ihc[1];
271       lapm->header.size = htons (sizeof (struct ListAllPeersMessage));
272       lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL);
273       lapm->trust_change = htonl (trust_delta);
274     }
275   else
276     {
277       ihc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_IteratorContext) +
278                            sizeof (struct ListPeerMessage));
279       lpm = (struct ListPeerMessage *) &ihc[1];
280       lpm->header.size = htons (sizeof (struct ListPeerMessage));
281       lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET);
282       lpm->trust_change = htonl (trust_delta);
283       memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity));
284     }
285   ihc->client = client;
286   ihc->callback = callback;
287   ihc->callback_cls = callback_cls;
288   ihc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
289   if (GNUNET_OK != 
290       GNUNET_CLIENT_transmit_and_get_response (client,
291                                                (const struct GNUNET_MessageHeader*) &ihc[1],
292                                                timeout,
293                                                GNUNET_YES,
294                                                &info_handler,
295                                                ihc))
296     {
297       GNUNET_break (0);
298       GNUNET_CLIENT_disconnect (ihc->client);
299       GNUNET_free (ihc);
300       return NULL;
301     }
302   return ihc;
303 }
304
305
306 /**
307  * Cancel an iteration over peer information.
308  *
309  * @param ic context of the iterator to cancel
310  */
311 void
312 GNUNET_PEERINFO_iterate_cancel (struct GNUNET_PEERINFO_IteratorContext *ic)
313 {
314   GNUNET_CLIENT_disconnect (ic->client);
315   GNUNET_free (ic);
316 }
317
318
319 /**
320  * Context for the info handler.
321  */
322 struct GNUNET_PEERINFO_NotifyContext
323 {
324
325   /**
326    * Our connection to the PEERINFO service.
327    */
328   struct GNUNET_CLIENT_Connection *client;
329
330   /**
331    * Function to call with information.
332    */
333   GNUNET_PEERINFO_Processor callback;
334
335   /**
336    * Closure for callback.
337    */
338   void *callback_cls;
339
340   /**
341    * Handle to our initial request for message transmission to
342    * the peerinfo service.
343    */
344   struct GNUNET_CLIENT_TransmitHandle *init;
345
346   /**
347    * Configuration.
348    */
349   const struct GNUNET_CONFIGURATION_Handle *cfg;
350
351   /**
352    * Scheduler.
353    */
354   struct GNUNET_SCHEDULER_Handle *sched;
355 };
356
357
358 /**
359  * Send a request to the peerinfo service to start being
360  * notified about all changes to peer information.
361  *
362  * @param nc our context
363  */
364 static void
365 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
366
367
368 /**
369  * Read notifications from the client handle and pass them
370  * to the callback.
371  *
372  * @param nc our context
373  */
374 static void
375 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc);
376
377
378 /**
379  * Receive a peerinfo information message, process it and
380  * go for more.
381  *
382  * @param cls closure
383  * @param msg message received, NULL on timeout or fatal error
384  */
385 static void
386 process_notification (void *cls,
387                       const struct
388                       GNUNET_MessageHeader * msg)
389 {
390   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
391   const struct InfoMessage *im;
392   const struct GNUNET_HELLO_Message *hello;
393   uint16_t ms;
394
395   if (msg == NULL)
396     {
397       GNUNET_CLIENT_disconnect (nc->client);
398       nc->client = GNUNET_CLIENT_connect (nc->sched, "peerinfo", nc->cfg);
399       request_notifications (nc);
400       return;
401     }
402   ms = ntohs (msg->size);
403   if ((ms < sizeof (struct InfoMessage)) ||
404       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO))
405     {
406       GNUNET_break (0);
407       GNUNET_CLIENT_disconnect (nc->client);
408       nc->client = GNUNET_CLIENT_connect (nc->sched, "peerinfo", nc->cfg);
409       request_notifications (nc);
410       return;
411     }
412   im = (const struct InfoMessage *) msg;
413   hello = NULL;
414   if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader))
415     {
416       hello = (const struct GNUNET_HELLO_Message *) &im[1];
417       if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello))
418         {
419           GNUNET_break (0);
420           GNUNET_CLIENT_disconnect (nc->client);
421           nc->client = GNUNET_CLIENT_connect (nc->sched, "peerinfo", nc->cfg);
422           request_notifications (nc);
423           return;
424         }
425     }
426 #if DEBUG_PEERINFO
427   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428               "Received information about peer `%s' from peerinfo database\n",
429               GNUNET_i2s (&im->peer));
430 #endif
431   nc->callback (nc->callback_cls, &im->peer, hello, ntohl (im->trust));
432   receive_notifications (nc);
433 }
434
435
436 /**
437  * Read notifications from the client handle and pass them
438  * to the callback.
439  *
440  * @param nc our context
441  */
442 static void
443 receive_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
444 {
445   GNUNET_CLIENT_receive (nc->client,
446                          &process_notification,
447                          nc,
448                          GNUNET_TIME_UNIT_FOREVER_REL);
449 }
450
451
452 /**
453  * Transmit our init-notify request, start receiving.
454  *
455  * @param cls closure (our 'struct GNUNET_PEERINFO_NotifyContext')
456  * @param size number of bytes available in buf
457  * @param buf where the callee should write the message
458  * @return number of bytes written to buf
459  */
460 static size_t 
461 transmit_notify_request (void *cls,
462                          size_t size, 
463                          void *buf)
464 {
465   struct GNUNET_PEERINFO_NotifyContext *nc = cls;
466   struct GNUNET_MessageHeader hdr;
467
468   nc->init = NULL;
469   if (buf == NULL)
470     {
471       GNUNET_CLIENT_disconnect (nc->client);
472       nc->client = GNUNET_CLIENT_connect (nc->sched, "peerinfo", nc->cfg);
473       request_notifications (nc);
474       return 0;
475     }
476   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
477   hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
478   hdr.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY);
479   memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
480   receive_notifications (nc);
481   return sizeof (struct GNUNET_MessageHeader);
482 }
483
484
485 /**
486  * Send a request to the peerinfo service to start being
487  * notified about all changes to peer information.
488  *
489  * @param nc our context
490  */
491 static void
492 request_notifications (struct GNUNET_PEERINFO_NotifyContext *nc)
493 {
494   GNUNET_assert (NULL == nc->init);
495   nc->init =GNUNET_CLIENT_notify_transmit_ready (nc->client,
496                                                  sizeof (struct GNUNET_MessageHeader),
497                                                  GNUNET_TIME_UNIT_FOREVER_REL,
498                                                  GNUNET_YES,
499                                                  &transmit_notify_request,
500                                                  nc);
501 }
502
503
504 /**
505  * Call a method whenever our known information about peers
506  * changes.  Initially calls the given function for all known
507  * peers and then only signals changes.
508  *
509  * @param cfg configuration to use
510  * @param sched scheduler to use
511  * @param callback the method to call for each peer
512  * @param callback_cls closure for callback
513  * @return NULL on error
514  */
515 struct GNUNET_PEERINFO_NotifyContext *
516 GNUNET_PEERINFO_notify (const struct GNUNET_CONFIGURATION_Handle *cfg,
517                         struct GNUNET_SCHEDULER_Handle *sched,
518                         GNUNET_PEERINFO_Processor callback,
519                         void *callback_cls)
520 {
521   struct GNUNET_PEERINFO_NotifyContext *nc;
522   struct GNUNET_CLIENT_Connection *client;
523
524   client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg);
525   if (client == NULL)
526     {      
527       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
528                   _("Could not connect to `%s' service.\n"), "peerinfo");
529       return NULL;
530     }
531   nc = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_NotifyContext));
532   nc->sched = sched;
533   nc->cfg = cfg;
534   nc->client = client;
535   nc->callback = callback;
536   nc->callback_cls = callback_cls; 
537   request_notifications (nc);
538   return nc;
539 }
540
541
542 /**
543  * Stop notifying about changes.
544  *
545  * @param nc context to stop notifying
546  */
547 void
548 GNUNET_PEERINFO_notify_cancel (struct GNUNET_PEERINFO_NotifyContext *nc)
549 {
550   if (NULL != nc->init)
551     {
552       GNUNET_CLIENT_notify_transmit_ready_cancel (nc->init);
553       nc->init = NULL;
554     }
555   GNUNET_CLIENT_disconnect (nc->client);
556   GNUNET_free (nc);
557 }
558
559
560 /* end of peerinfo_api.c */