af1eb2d1d6e6566716d559b11676a13f989fbb7f
[oweals/gnunet.git] / src / peerinfo / gnunet-service-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file peerinfo/gnunet-service-peerinfo.c
23  * @brief maintains list of known peers
24  *
25  * Code to maintain the list of currently known hosts (in memory
26  * structure of data/hosts/).
27  *
28  * @author Christian Grothoff
29  */
30
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_hello_lib.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_statistics_service.h"
36 #include "peerinfo.h"
37
38 /**
39  * How often do we scan the HOST_DIR for new entries?
40  */
41 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
42
43 /**
44  * How often do we discard old entries in data/hosts/?
45  */
46 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
47
48
49 /**
50  * In-memory cache of known hosts.
51  */
52 struct HostEntry
53 {
54
55   /**
56    * Identity of the peer.
57    */
58   struct GNUNET_PeerIdentity identity;
59
60   /**
61    * Hello for the peer (can be NULL)
62    */
63   struct GNUNET_HELLO_Message *hello;
64
65   /**
66    * Friend only hello for the peer (can be NULL)
67    */
68   struct GNUNET_HELLO_Message *friend_only_hello;
69
70 };
71
72
73 /**
74  * Result of reading a file
75  */
76 struct ReadHostFileContext
77 {
78   /**
79    * Hello for the peer (can be NULL)
80    */
81   struct GNUNET_HELLO_Message *hello;
82
83   /**
84    * Friend only hello for the peer (can be NULL)
85    */
86   struct GNUNET_HELLO_Message *friend_only_hello;
87 };
88
89
90 /**
91  * The in-memory list of known hosts, mapping of
92  * host IDs to 'struct HostEntry*' values.
93  */
94 static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
95
96 /**
97  * Clients to immediately notify about all changes.
98  */
99 static struct GNUNET_NotificationContext *notify_list;
100
101 /**
102  * Clients to immediately notify about all changes,
103  * even for friend-only HELLOs.
104  */
105 static struct GNUNET_NotificationContext *notify_friend_only_list;
106
107 /**
108  * Directory where the hellos are stored in (peerinfo/)
109  */
110 static char *networkIdDirectory;
111
112 /**
113  * Handle for reporting statistics.
114  */
115 static struct GNUNET_STATISTICS_Handle *stats;
116
117 /**
118  * Handle for task to run #cron_clean_data_hosts()
119  */
120 static struct GNUNET_SCHEDULER_Task *cron_clean;
121
122 /**
123  * Handle for task to run #cron_scan_directory_data_hosts()
124  */
125 static struct GNUNET_SCHEDULER_Task *cron_scan;
126
127
128 /**
129  * Notify all clients in the notify list about the
130  * given host entry changing.
131  *
132  * @param he entry of the host for which we generate a notification
133  * @param include_friend_only create public of friend-only message
134  * @return generated notification message
135  */
136 static struct InfoMessage *
137 make_info_message (const struct HostEntry *he,
138                    int include_friend_only)
139 {
140   struct InfoMessage *im;
141   struct GNUNET_HELLO_Message *src;
142   size_t hs;
143
144   if (GNUNET_YES == include_friend_only)
145     src = he->friend_only_hello;
146   else
147     src = he->hello;
148   hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src);
149   im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
150   im->header.size = htons (hs + sizeof (struct InfoMessage));
151   im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
152   im->peer = he->identity;
153   GNUNET_memcpy (&im[1],
154                  src,
155                  hs);
156   return im;
157 }
158
159
160 /**
161  * Address iterator that causes expired entries to be discarded.
162  *
163  * @param cls pointer to the current time
164  * @param address the address
165  * @param expiration expiration time for the address
166  * @return #GNUNET_NO if expiration smaller than the current time
167  */
168 static int
169 discard_expired (void *cls,
170                  const struct GNUNET_HELLO_Address *address,
171                  struct GNUNET_TIME_Absolute expiration)
172 {
173   const struct GNUNET_TIME_Absolute *now = cls;
174
175   if (now->abs_value_us > expiration.abs_value_us)
176   {
177     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
178                 _("Removing expired address of transport `%s'\n"),
179                 address->transport_name);
180     return GNUNET_NO;
181   }
182   return GNUNET_OK;
183 }
184
185
186 /**
187  * Address iterator that counts the remaining addresses.
188  *
189  * @param cls pointer to the counter
190  * @param address the address
191  * @param expiration expiration time for the address
192  * @return #GNUNET_OK (always)
193  */
194 static int
195 count_addresses (void *cls,
196                  const struct GNUNET_HELLO_Address *address,
197                  struct GNUNET_TIME_Absolute expiration)
198 {
199   unsigned int *cnt = cls;
200
201   (*cnt)++;
202   return GNUNET_OK;
203 }
204
205
206 /**
207  * Get the filename under which we would store the GNUNET_HELLO_Message
208  * for the given host and protocol.
209  *
210  * @param id peer for which we need the filename for the HELLO
211  * @return filename of the form DIRECTORY/HOSTID
212  */
213 static char *
214 get_host_filename (const struct GNUNET_PeerIdentity *id)
215 {
216   char *fn;
217
218   if (NULL == networkIdDirectory)
219     return NULL;
220   GNUNET_asprintf (&fn,
221                    "%s%s%s",
222                    networkIdDirectory,
223                    DIR_SEPARATOR_STR,
224                    GNUNET_i2s_full (id));
225   return fn;
226 }
227
228
229 /**
230  * Broadcast information about the given entry to all
231  * clients that care.
232  *
233  * @param entry entry to broadcast about
234  */
235 static void
236 notify_all (struct HostEntry *entry)
237 {
238   struct InfoMessage *msg_pub;
239   struct InfoMessage *msg_friend;
240
241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242               "Notifying all clients about peer `%s'\n",
243               GNUNET_i2s(&entry->identity));
244   msg_pub = make_info_message (entry,
245                                GNUNET_NO);
246   GNUNET_notification_context_broadcast (notify_list,
247                                          &msg_pub->header,
248                                          GNUNET_NO);
249   GNUNET_free (msg_pub);
250   msg_friend = make_info_message (entry,
251                                   GNUNET_YES);
252   GNUNET_notification_context_broadcast (notify_friend_only_list,
253                                          &msg_friend->header,
254                                          GNUNET_NO);
255   GNUNET_free (msg_friend);
256 }
257
258
259 /**
260  * Bind a host address (hello) to a hostId.
261  *
262  * @param peer the peer for which this is a hello
263  * @param hello the verified (!) hello message
264  */
265 static void
266 update_hello (const struct GNUNET_PeerIdentity *peer,
267               const struct GNUNET_HELLO_Message *hello);
268
269
270 /**
271  * Try to read the HELLOs in the given filename and discard expired
272  * addresses.  Removes the file if one the HELLO is malformed.  If all
273  * addresses are expired, the HELLO is also removed (but the HELLO
274  * with the public key is still returned if it was found and valid).
275  * The file can contain multiple HELLO messages.
276  *
277  * @param fn name of the file
278  * @param unlink_garbage if #GNUNET_YES, try to remove useless files
279  * @param r ReadHostFileContext to store the resutl
280  */
281 static void
282 read_host_file (const char *fn,
283                 int unlink_garbage,
284                 struct ReadHostFileContext *r)
285 {
286   char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
287   ssize_t size_total;
288   struct GNUNET_TIME_Absolute now;
289   unsigned int left;
290   const struct GNUNET_HELLO_Message *hello;
291   struct GNUNET_HELLO_Message *hello_clean;
292   size_t read_pos;
293   int size_hello;
294
295   r->friend_only_hello = NULL;
296   r->hello = NULL;
297
298   if (GNUNET_YES != GNUNET_DISK_file_test (fn))
299     return;
300   size_total = GNUNET_DISK_fn_read (fn,
301                                     buffer,
302                                     sizeof (buffer));
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304               "Read %d bytes from `%s'\n",
305               (int) size_total,
306               fn);
307   if (size_total < sizeof (struct GNUNET_MessageHeader))
308   {
309     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310                 _("Failed to parse HELLO in file `%s': %s\n"),
311                 fn,
312                 "File has invalid size");
313     if ( (GNUNET_YES == unlink_garbage) &&
314          (0 != UNLINK (fn)) &&
315          (ENOENT != errno) )
316       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
317                                 "unlink",
318                                 fn);
319     return;
320   }
321
322   read_pos = 0;
323   while (read_pos < size_total)
324   {
325     hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
326     size_hello = GNUNET_HELLO_size (hello);
327     if ( (0 == size_hello) ||
328          (size_total - read_pos < size_hello) )
329     {
330       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
331                   _("Failed to parse HELLO in file `%s'\n"),
332                   fn);
333       if (0 == read_pos)
334       {
335         if ((GNUNET_YES == unlink_garbage) &&
336             (0 != UNLINK (fn)) &&
337             (ENOENT != errno) )
338           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
339                                     "unlink",
340                                     fn);
341       }
342       else
343       {
344         if ( (GNUNET_YES == unlink_garbage) &&
345              (0 != TRUNCATE (fn, read_pos)) &&
346              (ENOENT != errno) )
347           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
348                                     "truncate",
349                                     fn);
350       }
351       return;
352     }
353
354     now = GNUNET_TIME_absolute_get ();
355     hello_clean = GNUNET_HELLO_iterate_addresses (hello,
356                                                   GNUNET_YES,
357                                                   &discard_expired,
358                                                   &now);
359     if (NULL == hello_clean)
360     {
361       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                   _("Failed to parse HELLO in file `%s'\n"),
363                   fn);
364       if ((GNUNET_YES == unlink_garbage) &&
365           (0 != UNLINK (fn)) &&
366           (ENOENT != errno) )
367         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
368                                   "unlink",
369                                   fn);
370       return;
371     }
372     left = 0;
373     (void) GNUNET_HELLO_iterate_addresses (hello_clean,
374                                            GNUNET_NO,
375                                            &count_addresses,
376                                            &left);
377
378     if (0 == left)
379     {
380       GNUNET_free (hello_clean);
381       break;
382     }
383
384     if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
385     {
386       if (NULL == r->hello)
387         r->hello = hello_clean;
388       else
389       {
390         GNUNET_break (0);
391         GNUNET_free (r->hello);
392         r->hello = hello_clean;
393       }
394     }
395     else
396     {
397       if (NULL == r->friend_only_hello)
398         r->friend_only_hello = hello_clean;
399       else
400       {
401         GNUNET_break (0);
402         GNUNET_free (r->friend_only_hello);
403         r->friend_only_hello = hello_clean;
404       }
405     }
406     read_pos += size_hello;
407   }
408
409   if (0 == left)
410   {
411     /* no addresses left, remove from disk */
412     if ( (GNUNET_YES == unlink_garbage) &&
413          (0 != UNLINK (fn)) )
414       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
415                                 "unlink",
416                                 fn);
417   }
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419               "Found `%s' and `%s' HELLO message in file\n",
420               (NULL != r->hello) ? "public" : "NON-public",
421               (NULL != r->friend_only_hello) ? "friend only" : "NO friend only");
422 }
423
424
425 /**
426  * Add a host to the list and notify clients about this event
427  *
428  * @param identity the identity of the host
429  * @return the HostEntry
430  */
431 static struct HostEntry *
432 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
433 {
434   struct HostEntry *entry;
435   struct ReadHostFileContext r;
436   char *fn;
437
438   entry = GNUNET_CONTAINER_multipeermap_get (hostmap,
439                                              identity);
440   if (NULL == entry)
441   {
442     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443                 "Adding new peer `%s'\n",
444                 GNUNET_i2s (identity));
445     GNUNET_STATISTICS_update (stats,
446                               gettext_noop ("# peers known"),
447                               1,
448                               GNUNET_NO);
449     entry = GNUNET_new (struct HostEntry);
450     entry->identity = *identity;
451     GNUNET_assert (GNUNET_OK ==
452                    GNUNET_CONTAINER_multipeermap_put (hostmap,
453                                                       &entry->identity,
454                                                       entry,
455                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
456     notify_all (entry);
457     fn = get_host_filename (identity);
458     if (NULL != fn)
459     {
460       read_host_file (fn,
461                       GNUNET_YES,
462                       &r);
463       if (NULL != r.hello)
464         update_hello (identity,
465                       r.hello);
466       if (NULL != r.friend_only_hello)
467         update_hello (identity,
468                       r.friend_only_hello);
469       GNUNET_free_non_null (r.hello);
470       GNUNET_free_non_null (r.friend_only_hello);
471       GNUNET_free (fn);
472     }
473   }
474   return entry;
475 }
476
477
478 /**
479  * Remove a file that should not be there.  LOG
480  * success or failure.
481  *
482  * @param fullname name of the file to remove
483  */
484 static void
485 remove_garbage (const char *fullname)
486 {
487   if (0 == UNLINK (fullname))
488     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
489                 _("File `%s' in directory `%s' does not match naming convention. Removed.\n"),
490                 fullname,
491                 networkIdDirectory);
492   else
493     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
494                               "unlink",
495                               fullname);
496 }
497
498
499 /**
500  * Closure for #hosts_directory_scan_callback().
501  */
502 struct DirScanContext
503 {
504   /**
505    * #GNUNET_YES if we should remove files that are broken,
506    * #GNUNET_NO if the directory we are iterating over should
507    * be treated as read-only by us.
508    */
509   int remove_files;
510
511   /**
512    * Counter for the number of (valid) entries found, incremented
513    * by one for each match.
514    */
515   unsigned int matched;
516 };
517
518
519 /**
520  * Function that is called on each HELLO file in a particular directory.
521  * Try to parse the file and add the HELLO to our list.
522  *
523  * @param cls pointer to 'unsigned int' to increment for each file, or NULL
524  *            if the file is from a read-only, read-once resource directory
525  * @param fullname name of the file to parse
526  * @return #GNUNET_OK (continue iteration)
527  */
528 static int
529 hosts_directory_scan_callback (void *cls,
530                                const char *fullname)
531 {
532   struct DirScanContext *dsc = cls;
533   struct GNUNET_PeerIdentity identity;
534   struct ReadHostFileContext r;
535   const char *filename;
536   struct GNUNET_PeerIdentity id_public;
537   struct GNUNET_PeerIdentity id_friend;
538   struct GNUNET_PeerIdentity id;
539
540   if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
541     return GNUNET_OK;           /* ignore non-files */
542
543   filename = strrchr (fullname,
544                       DIR_SEPARATOR);
545   if ( (NULL == filename) ||
546        (1 > strlen (filename)) )
547     filename = fullname;
548   else
549     filename ++;
550
551   read_host_file (fullname,
552                   dsc->remove_files,
553                   &r);
554   if ( (NULL == r.hello) &&
555        (NULL == r.friend_only_hello))
556     return GNUNET_OK;
557   if (NULL != r.friend_only_hello)
558   {
559     if (GNUNET_OK !=
560         GNUNET_HELLO_get_id (r.friend_only_hello,
561                              &id_friend))
562       if (GNUNET_YES == dsc->remove_files)
563       {
564         remove_garbage (fullname);
565         return GNUNET_OK;
566       }
567     id = id_friend;
568   }
569   if (NULL != r.hello)
570   {
571     if (GNUNET_OK !=
572         GNUNET_HELLO_get_id (r.hello,
573                              &id_public))
574       if (GNUNET_YES == dsc->remove_files)
575       {
576         remove_garbage (fullname);
577         return GNUNET_OK;
578       }
579     id = id_public;
580   }
581
582   if (  (NULL != r.hello) &&
583         (NULL != r.friend_only_hello) &&
584         (0 != memcmp (&id_friend,
585                       &id_public,
586                       sizeof (id_friend))) )
587   {
588     /* HELLOs are not for the same peer */
589     GNUNET_break (0);
590     if (GNUNET_YES == dsc->remove_files)
591       remove_garbage (fullname);
592     return GNUNET_OK;
593   }
594   if (GNUNET_OK ==
595       GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
596                                                   strlen (filename),
597                                                   &identity.public_key))
598   {
599     if (0 != memcmp (&id, &identity, sizeof (id_friend)))
600     {
601       /* HELLOs are not for the same peer */
602       GNUNET_break (0);
603       if (GNUNET_YES == dsc->remove_files)
604         remove_garbage (fullname);
605       return GNUNET_OK;
606     }
607   }
608
609   /* ok, found something valid, remember HELLO */
610   add_host_to_known_hosts (&id);
611   if (NULL != r.hello)
612   {
613     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614                 "Updating peer `%s' public HELLO \n",
615                 GNUNET_i2s (&id));
616     update_hello (&id, r.hello);
617     GNUNET_free (r.hello);
618   }
619   if (NULL != r.friend_only_hello)
620   {
621     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622                 "Updating peer `%s' friend only HELLO \n",
623                 GNUNET_i2s (&id));
624     update_hello (&id, r.friend_only_hello);
625     GNUNET_free (r.friend_only_hello);
626   }
627   dsc->matched++;
628   return GNUNET_OK;
629 }
630
631
632 /**
633  * Call this method periodically to scan data/hosts for new hosts.
634  *
635  * @param cls unused
636  */
637 static void
638 cron_scan_directory_data_hosts (void *cls)
639 {
640   static unsigned int retries;
641   struct DirScanContext dsc;
642
643   cron_scan = NULL;
644   if (GNUNET_SYSERR ==
645       GNUNET_DISK_directory_create (networkIdDirectory))
646   {
647     cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
648                                                             GNUNET_SCHEDULER_PRIORITY_IDLE,
649                                                             &cron_scan_directory_data_hosts, NULL);
650     return;
651   }
652   dsc.matched = 0;
653   dsc.remove_files = GNUNET_YES;
654   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
655               _("Scanning directory `%s'\n"),
656               networkIdDirectory);
657   GNUNET_DISK_directory_scan (networkIdDirectory,
658                               &hosts_directory_scan_callback,
659                               &dsc);
660   if ( (0 == dsc.matched) &&
661        (0 == (++retries & 31)) )
662     GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
663                 _("Still no peers found in `%s'!\n"),
664                 networkIdDirectory);
665   cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
666                                                           GNUNET_SCHEDULER_PRIORITY_IDLE,
667                                                           &cron_scan_directory_data_hosts,
668                                                           NULL);
669 }
670
671
672 /**
673  * Update the HELLO of a friend by merging the addresses.
674  *
675  * @param hello original hello
676  * @param friend_hello hello with additional addresses
677  * @return merged HELLO
678  */
679 static struct GNUNET_HELLO_Message *
680 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
681                      const struct GNUNET_HELLO_Message *friend_hello)
682 {
683   struct GNUNET_HELLO_Message * res;
684   struct GNUNET_HELLO_Message * tmp;
685   struct GNUNET_PeerIdentity pid;
686
687   if (NULL != friend_hello)
688   {
689     res = GNUNET_HELLO_merge (hello,
690                               friend_hello);
691     GNUNET_assert (GNUNET_YES ==
692                    GNUNET_HELLO_is_friend_only (res));
693     return res;
694   }
695
696   if (GNUNET_OK !=
697       GNUNET_HELLO_get_id (hello, &pid))
698   {
699     GNUNET_break (0);
700     return NULL;
701   }
702   tmp = GNUNET_HELLO_create (&pid.public_key,
703                              NULL,
704                              NULL,
705                              GNUNET_YES);
706   res = GNUNET_HELLO_merge (hello, tmp);
707   GNUNET_free (tmp);
708   GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
709   return res;
710 }
711
712
713 /**
714  * Bind a host address (hello) to a hostId.
715  *
716  * @param peer the peer for which this is a hello
717  * @param hello the verified (!) hello message
718  */
719 static void
720 update_hello (const struct GNUNET_PeerIdentity *peer,
721               const struct GNUNET_HELLO_Message *hello)
722 {
723   char *fn;
724   struct HostEntry *host;
725   struct GNUNET_HELLO_Message *mrg;
726   struct GNUNET_HELLO_Message **dest;
727   struct GNUNET_TIME_Absolute delta;
728   unsigned int cnt;
729   unsigned int size;
730   int friend_hello_type;
731   int store_hello;
732   int store_friend_hello;
733   int pos;
734   char *buffer;
735
736   host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
737   GNUNET_assert (NULL != host);
738
739   friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
740   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
741               "Updating %s HELLO for `%s'\n",
742               (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
743               GNUNET_i2s (peer));
744
745   dest = NULL;
746   if (GNUNET_YES == friend_hello_type)
747   {
748     dest = &host->friend_only_hello;
749   }
750   else
751   {
752     dest = &host->hello;
753   }
754
755   if (NULL == (*dest))
756   {
757     (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
758     GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
759   }
760   else
761   {
762     mrg = GNUNET_HELLO_merge ((*dest),
763                               hello);
764     delta = GNUNET_HELLO_equals (mrg,
765                                  (*dest),
766                                  GNUNET_TIME_absolute_get ());
767     if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
768     {
769       /* no differences, just ignore the update */
770       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
771                   "No change in %s HELLO for `%s'\n",
772                   (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
773                   GNUNET_i2s (peer));
774       GNUNET_free (mrg);
775       return;
776     }
777     GNUNET_free ((*dest));
778     (*dest) = mrg;
779   }
780
781   if ( (NULL != (host->hello)) &&
782        (GNUNET_NO == friend_hello_type) )
783   {
784     /* Update friend only hello */
785     mrg = update_friend_hello (host->hello,
786                                host->friend_only_hello);
787     if (NULL != host->friend_only_hello)
788       GNUNET_free (host->friend_only_hello);
789     host->friend_only_hello = mrg;
790   }
791
792   if (NULL != host->hello)
793     GNUNET_assert ((GNUNET_NO ==
794                     GNUNET_HELLO_is_friend_only (host->hello)));
795   if (NULL != host->friend_only_hello)
796     GNUNET_assert ((GNUNET_YES ==
797                     GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
798
799   fn = get_host_filename (peer);
800   if ( (NULL != fn) &&
801        (GNUNET_OK ==
802         GNUNET_DISK_directory_create_for_file (fn)) )
803   {
804     store_hello = GNUNET_NO;
805     size = 0;
806     cnt = 0;
807     if (NULL != host->hello)
808       (void) GNUNET_HELLO_iterate_addresses (host->hello,
809                                              GNUNET_NO,
810                                              &count_addresses,
811                                              &cnt);
812     if (cnt > 0)
813     {
814       store_hello = GNUNET_YES;
815       size += GNUNET_HELLO_size (host->hello);
816     }
817     cnt = 0;
818     if (NULL != host->friend_only_hello)
819       (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
820                                              GNUNET_NO,
821                                              &count_addresses,
822                                              &cnt);
823     store_friend_hello = GNUNET_NO;
824     if (0 < cnt)
825     {
826       store_friend_hello = GNUNET_YES;
827       size += GNUNET_HELLO_size (host->friend_only_hello);
828     }
829
830     if ( (GNUNET_NO == store_hello) &&
831          (GNUNET_NO == store_friend_hello) )
832     {
833       /* no valid addresses, don't put HELLO on disk; in fact,
834          if one exists on disk, remove it */
835       (void) UNLINK (fn);
836     }
837     else
838     {
839       buffer = GNUNET_malloc (size);
840       pos = 0;
841
842       if (GNUNET_YES == store_hello)
843       {
844         GNUNET_memcpy (buffer,
845                        host->hello,
846                        GNUNET_HELLO_size (host->hello));
847         pos += GNUNET_HELLO_size (host->hello);
848       }
849       if (GNUNET_YES == store_friend_hello)
850       {
851         GNUNET_memcpy (&buffer[pos],
852                 host->friend_only_hello,
853                 GNUNET_HELLO_size (host->friend_only_hello));
854         pos += GNUNET_HELLO_size (host->friend_only_hello);
855       }
856       GNUNET_assert (pos == size);
857
858       if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
859                                                  GNUNET_DISK_PERM_USER_READ |
860                                                  GNUNET_DISK_PERM_USER_WRITE |
861                                                  GNUNET_DISK_PERM_GROUP_READ |
862                                                  GNUNET_DISK_PERM_OTHER_READ))
863         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
864       else
865         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
866                     "Stored %s %s HELLO in %s  with total size %u\n",
867                     (GNUNET_YES == store_friend_hello) ? "friend-only": "",
868                     (GNUNET_YES == store_hello) ? "public": "",
869                     fn,
870                     size);
871       GNUNET_free (buffer);
872     }
873   }
874   GNUNET_free_non_null (fn);
875   notify_all (host);
876 }
877
878
879 /**
880  * Closure for #add_to_tc()
881  */
882 struct TransmitContext
883 {
884   /**
885    * Client to transmit to
886    */
887   struct GNUNET_SERVICE_Client *client;
888
889   /**
890    * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
891    */
892   int friend_only;
893 };
894
895
896 /**
897  * Do transmit info about peer to given host.
898  *
899  * @param cls NULL to hit all hosts, otherwise specifies a particular target
900  * @param key hostID
901  * @param value information to transmit
902  * @return #GNUNET_YES (continue to iterate)
903  */
904 static int
905 add_to_tc (void *cls,
906            const struct GNUNET_PeerIdentity *key,
907            void *value)
908 {
909   struct TransmitContext *tc = cls;
910   struct HostEntry *pos = value;
911   struct InfoMessage *im;
912   uint16_t hs;
913   struct GNUNET_MQ_Envelope *env;
914
915   hs = 0;
916
917   if ( (NULL != pos->hello) &&
918        (GNUNET_NO == tc->friend_only) )
919   {
920         /* Copy public HELLO */
921     hs = GNUNET_HELLO_size (pos->hello);
922     GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
923                    sizeof (struct InfoMessage));
924     env = GNUNET_MQ_msg_extra (im,
925                                hs,
926                                GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
927     GNUNET_memcpy (&im[1],
928                    pos->hello,
929                    hs);
930     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931                 "Sending public HELLO with size %u for peer `%s'\n",
932                 hs,
933                 GNUNET_i2s (key));
934   }
935   else if ( (NULL != pos->friend_only_hello) &&
936             (GNUNET_YES == tc->friend_only) )
937   {
938         /* Copy friend only HELLO */
939     hs = GNUNET_HELLO_size (pos->friend_only_hello);
940     GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
941                    sizeof (struct InfoMessage));
942     env = GNUNET_MQ_msg_extra (im,
943                                hs,
944                                GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
945     GNUNET_memcpy (&im[1],
946                    pos->friend_only_hello,
947                    hs);
948     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
949                 "Sending friend-only HELLO with size %u for peer `%s'\n",
950                 hs,
951                 GNUNET_i2s (key));
952   }
953   else
954   {
955     env = GNUNET_MQ_msg (im,
956                          GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
957     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
958                 "Adding no HELLO for peer `%s'\n",
959                 GNUNET_i2s (key));
960   }
961   im->peer = pos->identity;
962   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
963                   env);
964   return GNUNET_YES;
965 }
966
967
968 /**
969  * @brief delete expired HELLO entries in directory
970  *
971  * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
972  * @param fn filename to test to see if the HELLO expired
973  * @return #GNUNET_OK (continue iteration)
974  */
975 static int
976 discard_hosts_helper (void *cls,
977                       const char *fn)
978 {
979   struct GNUNET_TIME_Absolute *now = cls;
980   char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
981   const struct GNUNET_HELLO_Message *hello;
982   struct GNUNET_HELLO_Message *new_hello;
983   int read_size;
984   unsigned int cur_hello_size;
985   unsigned int new_hello_size;
986   int read_pos;
987   int write_pos;
988   unsigned int cnt;
989   char *writebuffer;
990   uint64_t fsize;
991
992   GNUNET_DISK_file_size (fn, &fsize, GNUNET_YES, GNUNET_YES);
993   read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
994
995   if ((read_size < (int) sizeof (struct GNUNET_MessageHeader)) ||
996       (fsize > GNUNET_MAX_MESSAGE_SIZE))
997   {
998     if (0 != UNLINK (fn))
999       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1000                                 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
1001     return GNUNET_OK;
1002   }
1003
1004   writebuffer = GNUNET_malloc (read_size);
1005   read_pos = 0;
1006   write_pos = 0;
1007   while (read_pos < read_size)
1008   {
1009     /* Check each HELLO */
1010     hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1011     cur_hello_size = GNUNET_HELLO_size (hello);
1012     if (0 == cur_hello_size)
1013     {
1014       /* Invalid data, discard */
1015       if (0 != UNLINK (fn))
1016         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1017                                   GNUNET_ERROR_TYPE_BULK,
1018                                   "unlink",
1019                                   fn);
1020       GNUNET_free (writebuffer);
1021       return GNUNET_OK;
1022     }
1023     new_hello = GNUNET_HELLO_iterate_addresses (hello,
1024                                                 GNUNET_YES,
1025                                                 &discard_expired,
1026                                                 now);
1027     cnt = 0;
1028     if (NULL != new_hello)
1029       (void) GNUNET_HELLO_iterate_addresses (hello,
1030                                              GNUNET_NO,
1031                                              &count_addresses,
1032                                              &cnt);
1033     if ( (NULL != new_hello) && (0 < cnt) )
1034     {
1035       /* Store new HELLO to write it when done */
1036       new_hello_size = GNUNET_HELLO_size (new_hello);
1037       GNUNET_memcpy (&writebuffer[write_pos],
1038                      new_hello,
1039                      new_hello_size);
1040       write_pos += new_hello_size;
1041     }
1042     read_pos += cur_hello_size;
1043     GNUNET_free_non_null (new_hello);
1044   }
1045
1046   if (0 < write_pos)
1047   {
1048     GNUNET_DISK_fn_write (fn,
1049                           writebuffer,
1050                           write_pos,
1051                           GNUNET_DISK_PERM_USER_READ |
1052                           GNUNET_DISK_PERM_USER_WRITE |
1053                           GNUNET_DISK_PERM_GROUP_READ |
1054                           GNUNET_DISK_PERM_OTHER_READ);
1055   }
1056   else if (0 != UNLINK (fn))
1057     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1058                               GNUNET_ERROR_TYPE_BULK,
1059                               "unlink", fn);
1060
1061   GNUNET_free (writebuffer);
1062   return GNUNET_OK;
1063 }
1064
1065
1066 /**
1067  * Call this method periodically to scan peerinfo/ for ancient
1068  * HELLOs to expire.
1069  *
1070  * @param cls unused
1071  */
1072 static void
1073 cron_clean_data_hosts (void *cls)
1074 {
1075   struct GNUNET_TIME_Absolute now;
1076
1077   cron_clean = NULL;
1078   now = GNUNET_TIME_absolute_get ();
1079   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1080               _("Cleaning up directory `%s'\n"),
1081               networkIdDirectory);
1082   GNUNET_DISK_directory_scan (networkIdDirectory,
1083                               &discard_hosts_helper,
1084                               &now);
1085   cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1086                                              &cron_clean_data_hosts,
1087                                              NULL);
1088 }
1089
1090
1091 /**
1092  * Check HELLO-message.
1093  *
1094  * @param cls identification of the client
1095  * @param hello the actual message
1096  * @return #GNUNET_OK if @a hello is well-formed
1097  */
1098 static int
1099 check_hello (void *cls,
1100              const struct GNUNET_HELLO_Message *hello)
1101 {
1102   struct GNUNET_PeerIdentity pid;
1103
1104   if (GNUNET_OK !=
1105       GNUNET_HELLO_get_id (hello,
1106                            &pid))
1107   {
1108     GNUNET_break (0);
1109     return GNUNET_SYSERR;
1110   }
1111   return GNUNET_OK;
1112 }
1113
1114
1115 /**
1116  * Handle HELLO-message.
1117  *
1118  * @param cls identification of the client
1119  * @param hello the actual message
1120  */
1121 static void
1122 handle_hello (void *cls,
1123               const struct GNUNET_HELLO_Message *hello)
1124 {
1125   struct GNUNET_SERVICE_Client *client = cls;
1126   struct GNUNET_PeerIdentity pid;
1127
1128   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129               "HELLO message received for peer `%s'\n",
1130               GNUNET_i2s (&pid));
1131   GNUNET_assert (GNUNET_OK ==
1132                  GNUNET_HELLO_get_id (hello,
1133                                       &pid));
1134   add_host_to_known_hosts (&pid);
1135   update_hello (&pid,
1136                 hello);
1137   GNUNET_SERVICE_client_continue (client);
1138 }
1139
1140
1141 /**
1142  * Handle GET-message.
1143  *
1144  * @param cls identification of the client
1145  * @param lpm the actual message
1146  */
1147 static void
1148 handle_get (void *cls,
1149             const struct ListPeerMessage *lpm)
1150 {
1151   struct GNUNET_SERVICE_Client *client = cls;
1152   struct TransmitContext tcx;
1153   struct GNUNET_MessageHeader *msg;
1154   struct GNUNET_MQ_Envelope *env;
1155
1156   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157               "GET message received for peer `%s'\n",
1158               GNUNET_i2s (&lpm->peer));
1159   tcx.friend_only = ntohl (lpm->include_friend_only);
1160   tcx.client = client;
1161   GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1162                                               &lpm->peer,
1163                                               &add_to_tc,
1164                                               &tcx);
1165   env = GNUNET_MQ_msg (msg,
1166                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1167   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1168                   env);
1169   GNUNET_SERVICE_client_continue (client);
1170 }
1171
1172
1173 /**
1174  * Handle GET-ALL-message.
1175  *
1176  * @param cls identification of the client
1177  * @param lapm the actual message
1178  */
1179 static void
1180 handle_get_all (void *cls,
1181                 const struct ListAllPeersMessage *lapm)
1182 {
1183   struct GNUNET_SERVICE_Client *client = cls;
1184   struct TransmitContext tcx;
1185   struct GNUNET_MQ_Envelope *env;
1186   struct GNUNET_MessageHeader *msg;
1187
1188   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1189               "GET_ALL message received\n");
1190   tcx.friend_only = ntohl (lapm->include_friend_only);
1191   tcx.client = client;
1192   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1193                                          &add_to_tc,
1194                                          &tcx);
1195   env = GNUNET_MQ_msg (msg,
1196                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1197   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1198                   env);
1199   GNUNET_SERVICE_client_continue (client);
1200 }
1201
1202
1203 /**
1204  * Handle NOTIFY-message.
1205  *
1206  * @param cls identification of the client
1207  * @param nm the actual message
1208  */
1209 static void
1210 handle_notify (void *cls,
1211                const struct NotifyMessage *nm)
1212 {
1213   struct GNUNET_SERVICE_Client *client = cls;
1214   struct GNUNET_MQ_Handle *mq;
1215   struct TransmitContext tcx;
1216   struct GNUNET_MQ_Envelope *env;
1217   struct GNUNET_MessageHeader *msg;
1218
1219   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1220               "NOTIFY message received\n");
1221   mq = GNUNET_SERVICE_client_get_mq (client);
1222   GNUNET_SERVICE_client_mark_monitor (client);
1223   if (ntohl (nm->include_friend_only))
1224     GNUNET_notification_context_add (notify_friend_only_list,
1225                                      mq);
1226   else
1227     GNUNET_notification_context_add (notify_list,
1228                                      mq);
1229   tcx.friend_only = ntohl (nm->include_friend_only);
1230   tcx.client = client;
1231   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1232                                          &add_to_tc,
1233                                          &tcx);
1234   env = GNUNET_MQ_msg (msg,
1235                        GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1236   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1237                   env);
1238   GNUNET_SERVICE_client_continue (client);
1239 }
1240
1241
1242 /**
1243  * Client connect callback
1244  *
1245  * @param cls unused
1246  * @param client server client
1247  * @param mq for @a client
1248  * @return @a client
1249  */
1250 static void *
1251 client_connect_cb (void *cls,
1252                    struct GNUNET_SERVICE_Client *client,
1253                    struct GNUNET_MQ_Handle *mq)
1254 {
1255   return client;
1256 }
1257
1258
1259 /**
1260  * Client disconnect callback
1261  *
1262  * @param cls unused
1263  * @param client server client
1264  * @param app_ctx should be @a client
1265  */
1266 static void
1267 client_disconnect_cb (void *cls,
1268                       struct GNUNET_SERVICE_Client *client,
1269                       void *app_ctx)
1270 {
1271   GNUNET_assert (app_ctx == client);
1272 }
1273
1274
1275 /**
1276  * Release memory taken by a host entry.
1277  *
1278  * @param cls NULL
1279  * @param key key of the host entry
1280  * @param value the `struct HostEntry` to free
1281  * @return #GNUNET_YES (continue to iterate)
1282  */
1283 static int
1284 free_host_entry (void *cls,
1285                  const struct GNUNET_PeerIdentity *key,
1286                  void *value)
1287 {
1288   struct HostEntry *he = value;
1289
1290   GNUNET_free_non_null (he->hello);
1291   GNUNET_free_non_null (he->friend_only_hello);
1292   GNUNET_free (he);
1293   return GNUNET_YES;
1294 }
1295
1296
1297 /**
1298  * Clean up our state.  Called during shutdown.
1299  *
1300  * @param cls unused
1301  */
1302 static void
1303 shutdown_task (void *cls)
1304 {
1305   GNUNET_notification_context_destroy (notify_list);
1306   notify_list = NULL;
1307   GNUNET_notification_context_destroy (notify_friend_only_list);
1308   notify_friend_only_list = NULL;
1309
1310   GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1311                                          &free_host_entry,
1312                                          NULL);
1313   GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1314   if (NULL != stats)
1315   {
1316     GNUNET_STATISTICS_destroy (stats,
1317                                GNUNET_NO);
1318     stats = NULL;
1319   }
1320   if (NULL != cron_clean)
1321   {
1322     GNUNET_SCHEDULER_cancel (cron_clean);
1323     cron_clean = NULL;
1324   }
1325   if (NULL != cron_scan)
1326   {
1327     GNUNET_SCHEDULER_cancel (cron_scan);
1328     cron_scan = NULL;
1329   }
1330   if (NULL != networkIdDirectory)
1331   {
1332     GNUNET_free (networkIdDirectory);
1333     networkIdDirectory = NULL;
1334   }
1335 }
1336
1337
1338 /**
1339  * Start up peerinfo service.
1340  *
1341  * @param cls closure
1342  * @param cfg configuration to use
1343  * @param service the initialized service
1344  */
1345 static void
1346 run (void *cls,
1347      const struct GNUNET_CONFIGURATION_Handle *cfg,
1348      struct GNUNET_SERVICE_Handle *service)
1349 {
1350   char *peerdir;
1351   char *ip;
1352   struct DirScanContext dsc;
1353   int noio;
1354   int use_included;
1355
1356   hostmap
1357     = GNUNET_CONTAINER_multipeermap_create (1024,
1358                                             GNUNET_YES);
1359   stats
1360     = GNUNET_STATISTICS_create ("peerinfo",
1361                                 cfg);
1362   notify_list
1363     = GNUNET_notification_context_create (0);
1364   notify_friend_only_list
1365     = GNUNET_notification_context_create (0);
1366   noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1367                                                "peerinfo",
1368                                                "NO_IO");
1369   use_included
1370     = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1371                                             "peerinfo",
1372                                             "USE_INCLUDED_HELLOS");
1373   if (GNUNET_SYSERR == use_included)
1374     use_included = GNUNET_NO;
1375   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1376                                  NULL);
1377   if (GNUNET_YES != noio)
1378   {
1379     GNUNET_assert (GNUNET_OK ==
1380                    GNUNET_CONFIGURATION_get_value_filename (cfg,
1381                                                             "peerinfo",
1382                                                             "HOSTS",
1383                                                             &networkIdDirectory));
1384     if (GNUNET_OK !=
1385         GNUNET_DISK_directory_create (networkIdDirectory))
1386     {
1387       GNUNET_SCHEDULER_shutdown ();
1388       return;
1389     }
1390
1391     cron_scan
1392       = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1393                                             &cron_scan_directory_data_hosts,
1394                                             NULL);
1395
1396     cron_clean
1397       = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1398                                             &cron_clean_data_hosts,
1399                                             NULL);
1400     if (GNUNET_YES == use_included)
1401     {
1402       ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1403       GNUNET_asprintf (&peerdir,
1404                        "%shellos",
1405                        ip);
1406       GNUNET_free (ip);
1407
1408       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409                   _("Importing HELLOs from `%s'\n"),
1410                   peerdir);
1411       dsc.matched = 0;
1412       dsc.remove_files = GNUNET_NO;
1413
1414       GNUNET_DISK_directory_scan (peerdir,
1415                                   &hosts_directory_scan_callback,
1416                                   &dsc);
1417       GNUNET_free (peerdir);
1418     }
1419     else
1420     {
1421       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1422                   _("Skipping import of included HELLOs\n"));
1423     }
1424   }
1425 }
1426
1427
1428 /**
1429  * Define "main" method using service macro.
1430  */
1431 GNUNET_SERVICE_MAIN
1432 ("peerinfo",
1433  GNUNET_SERVICE_OPTION_NONE,
1434  &run,
1435  &client_connect_cb,
1436  &client_disconnect_cb,
1437  NULL,
1438  GNUNET_MQ_hd_var_size (hello,
1439                         GNUNET_MESSAGE_TYPE_HELLO,
1440                         struct GNUNET_HELLO_Message,
1441                         NULL),
1442  GNUNET_MQ_hd_fixed_size (get,
1443                           GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1444                           struct ListPeerMessage,
1445                           NULL),
1446  GNUNET_MQ_hd_fixed_size (get_all,
1447                           GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1448                           struct ListAllPeersMessage,
1449                           NULL),
1450  GNUNET_MQ_hd_fixed_size (notify,
1451                           GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1452                           struct NotifyMessage,
1453                           NULL),
1454  GNUNET_MQ_handler_end ());
1455
1456
1457 /* end of gnunet-service-peerinfo.c */