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