2 This file is part of GNUnet.
3 Copyright (C) 2001-2014 GNUnet e.V.
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.
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.
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.
22 * @file peerinfo/gnunet-service-peerinfo.c
23 * @brief maintains list of known peers
25 * Code to maintain the list of currently known hosts (in memory
26 * structure of data/hosts/).
28 * @author Christian Grothoff
32 #include "gnunet_util_lib.h"
33 #include "gnunet_hello_lib.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_statistics_service.h"
39 * How often do we scan the HOST_DIR for new entries?
41 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
44 * How often do we discard old entries in data/hosts/?
46 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
50 * In-memory cache of known hosts.
56 * Identity of the peer.
58 struct GNUNET_PeerIdentity identity;
61 * Hello for the peer (can be NULL)
63 struct GNUNET_HELLO_Message *hello;
66 * Friend only hello for the peer (can be NULL)
68 struct GNUNET_HELLO_Message *friend_only_hello;
73 * Transmit context for GET requests
75 struct TransmitContext
78 * Server transmit context
80 struct GNUNET_SERVER_TransmitContext *tc;
83 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
90 * Result of reading a file
92 struct ReadHostFileContext
95 * Hello for the peer (can be NULL)
97 struct GNUNET_HELLO_Message *hello;
100 * Friend only hello for the peer (can be NULL)
102 struct GNUNET_HELLO_Message *friend_only_hello;
107 * Client notification context
109 struct NotificationContext
114 struct NotificationContext *prev;
119 struct NotificationContext *next;
124 struct GNUNET_SERVER_Client *client;
127 * Interested in friend only HELLO?
129 int include_friend_only;
134 * The in-memory list of known hosts, mapping of
135 * host IDs to 'struct HostEntry*' values.
137 static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
140 * Clients to immediately notify about all changes.
142 static struct GNUNET_SERVER_NotificationContext *notify_list;
145 * Directory where the hellos are stored in (peerinfo/)
147 static char *networkIdDirectory;
150 * Handle for reporting statistics.
152 static struct GNUNET_STATISTICS_Handle *stats;
155 * DLL of notification contexts: head
157 static struct NotificationContext *nc_head;
160 * DLL of notification contexts: tail
162 static struct NotificationContext *nc_tail;
165 * Handle for task to run #cron_clean_data_hosts()
167 static struct GNUNET_SCHEDULER_Task *cron_clean;
170 * Handle for task to run #cron_scan_directory_hosts()
172 static struct GNUNET_SCHEDULER_Task *cron_scan;
176 * Notify all clients in the notify list about the
177 * given host entry changing.
179 * @param he entry of the host for which we generate a notification
180 * @param include_friend_only create public of friend-only message
181 * @return generated notification message
183 static struct InfoMessage *
184 make_info_message (const struct HostEntry *he,
185 int include_friend_only)
187 struct InfoMessage *im;
188 struct GNUNET_HELLO_Message *src;
191 if (GNUNET_YES == include_friend_only)
192 src = he->friend_only_hello;
196 hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src);
197 im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
198 im->header.size = htons (hs + sizeof (struct InfoMessage));
199 im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
200 im->peer = he->identity;
202 memcpy (&im[1], src, hs);
208 * Address iterator that causes expired entries to be discarded.
210 * @param cls pointer to the current time
211 * @param address the address
212 * @param expiration expiration time for the address
213 * @return #GNUNET_NO if expiration smaller than the current time
216 discard_expired (void *cls,
217 const struct GNUNET_HELLO_Address *address,
218 struct GNUNET_TIME_Absolute expiration)
220 const struct GNUNET_TIME_Absolute *now = cls;
222 if (now->abs_value_us > expiration.abs_value_us)
224 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
225 _("Removing expired address of transport `%s'\n"),
226 address->transport_name);
234 * Address iterator that counts the remaining addresses.
236 * @param cls pointer to the counter
237 * @param address the address
238 * @param expiration expiration time for the address
239 * @return #GNUNET_OK (always)
242 count_addresses (void *cls,
243 const struct GNUNET_HELLO_Address *address,
244 struct GNUNET_TIME_Absolute expiration)
246 unsigned int *cnt = cls;
254 * Get the filename under which we would store the GNUNET_HELLO_Message
255 * for the given host and protocol.
257 * @param id peer for which we need the filename for the HELLO
258 * @return filename of the form DIRECTORY/HOSTID
261 get_host_filename (const struct GNUNET_PeerIdentity *id)
265 if (NULL == networkIdDirectory)
267 GNUNET_asprintf (&fn, "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR,
268 GNUNET_i2s_full (id));
274 * Broadcast information about the given entry to all
277 * @param entry entry to broadcast about
280 notify_all (struct HostEntry *entry)
282 struct InfoMessage *msg_pub;
283 struct InfoMessage *msg_friend;
284 struct NotificationContext *cur;
286 msg_pub = make_info_message (entry, GNUNET_NO);
287 msg_friend = make_info_message (entry, GNUNET_YES);
288 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289 "Notifying all clients about peer `%s'\n",
290 GNUNET_i2s(&entry->identity));
291 for (cur = nc_head; NULL != cur; cur = cur->next)
293 if (GNUNET_NO == cur->include_friend_only)
295 GNUNET_SERVER_notification_context_unicast (notify_list,
300 if (GNUNET_YES == cur->include_friend_only)
302 GNUNET_SERVER_notification_context_unicast (notify_list,
308 GNUNET_free (msg_pub);
309 GNUNET_free (msg_friend);
314 * Bind a host address (hello) to a hostId.
316 * @param peer the peer for which this is a hello
317 * @param hello the verified (!) hello message
320 update_hello (const struct GNUNET_PeerIdentity *peer,
321 const struct GNUNET_HELLO_Message *hello);
325 * Try to read the HELLOs in the given filename and discard expired
326 * addresses. Removes the file if one the HELLO is malformed. If all
327 * addresses are expired, the HELLO is also removed (but the HELLO
328 * with the public key is still returned if it was found and valid).
329 * The file can contain multiple HELLO messages.
331 * @param fn name of the file
332 * @param unlink_garbage if #GNUNET_YES, try to remove useless files
333 * @param r ReadHostFileContext to store the resutl
336 read_host_file (const char *fn,
338 struct ReadHostFileContext *r)
340 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
341 unsigned int size_total;
342 struct GNUNET_TIME_Absolute now;
344 const struct GNUNET_HELLO_Message *hello;
345 struct GNUNET_HELLO_Message *hello_clean;
349 r->friend_only_hello = NULL;
352 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
354 size_total = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 "Read %u bytes from `%s'\n",
359 if (size_total < sizeof (struct GNUNET_MessageHeader))
361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362 _("Failed to parse HELLO in file `%s': %s\n"),
363 fn, "Fail has invalid size");
364 if ( (GNUNET_YES == unlink_garbage) &&
365 (0 != UNLINK (fn)) &&
367 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
374 while (read_pos < size_total)
376 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
377 size_hello = GNUNET_HELLO_size (hello);
378 if ( (0 == size_hello) ||
379 (size_total - read_pos < size_hello) )
381 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382 _("Failed to parse HELLO in file `%s'\n"),
386 if ((GNUNET_YES == unlink_garbage) &&
387 (0 != UNLINK (fn)) &&
389 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
395 if ((GNUNET_YES == unlink_garbage) &&
396 (0 != TRUNCATE (fn, read_pos)) &&
398 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
405 now = GNUNET_TIME_absolute_get ();
406 hello_clean = GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES,
407 &discard_expired, &now);
408 if (NULL == hello_clean)
410 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
411 _("Failed to parse HELLO in file `%s'\n"),
413 if ((GNUNET_YES == unlink_garbage) &&
414 (0 != UNLINK (fn)) &&
416 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
422 (void) GNUNET_HELLO_iterate_addresses (hello_clean, GNUNET_NO,
423 &count_addresses, &left);
427 GNUNET_free (hello_clean);
431 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
433 if (NULL == r->hello)
434 r->hello = hello_clean;
438 GNUNET_free (r->hello);
439 r->hello = hello_clean;
444 if (NULL == r->friend_only_hello)
445 r->friend_only_hello = hello_clean;
449 GNUNET_free (r->friend_only_hello);
450 r->friend_only_hello = hello_clean;
453 read_pos += size_hello;
458 /* no addresses left, remove from disk */
459 if ( (GNUNET_YES == unlink_garbage) &&
461 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Found `%s' and `%s' HELLO message in file\n",
467 (NULL != r->hello) ? "public" : "NON-public",
468 (NULL != r->friend_only_hello) ? "friend only" : "NO friend only");
473 * Add a host to the list and notify clients about this event
475 * @param identity the identity of the host
476 * @return the HostEntry
478 static struct HostEntry *
479 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
481 struct HostEntry *entry;
482 struct ReadHostFileContext r;
485 entry = GNUNET_CONTAINER_multipeermap_get (hostmap, identity);
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "Adding new peer `%s'\n",
490 GNUNET_i2s (identity));
491 GNUNET_STATISTICS_update (stats, gettext_noop ("# peers known"), 1,
493 entry = GNUNET_new (struct HostEntry);
494 entry->identity = *identity;
495 GNUNET_assert (GNUNET_OK ==
496 GNUNET_CONTAINER_multipeermap_put (hostmap,
499 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
501 fn = get_host_filename (identity);
504 read_host_file (fn, GNUNET_YES, &r);
506 update_hello (identity, r.hello);
507 if (NULL != r.friend_only_hello)
508 update_hello (identity, r.friend_only_hello);
509 GNUNET_free_non_null (r.hello);
510 GNUNET_free_non_null (r.friend_only_hello);
519 * Remove a file that should not be there. LOG
520 * success or failure.
522 * @param fullname name of the file to remove
525 remove_garbage (const char *fullname)
527 if (0 == UNLINK (fullname))
528 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
529 _("File `%s' in directory `%s' does not match naming convention. Removed.\n"),
533 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
540 * Closure for #hosts_directory_scan_callback().
542 struct DirScanContext
545 * #GNUNET_YES if we should remove files that are broken,
546 * #GNUNET_NO if the directory we are iterating over should
547 * be treated as read-only by us.
552 * Counter for the number of (valid) entries found, incremented
553 * by one for each match.
555 unsigned int matched;
560 * Function that is called on each HELLO file in a particular directory.
561 * Try to parse the file and add the HELLO to our list.
563 * @param cls pointer to 'unsigned int' to increment for each file, or NULL
564 * if the file is from a read-only, read-once resource directory
565 * @param fullname name of the file to parse
566 * @return #GNUNET_OK (continue iteration)
569 hosts_directory_scan_callback (void *cls,
570 const char *fullname)
572 struct DirScanContext *dsc = cls;
573 struct GNUNET_PeerIdentity identity;
574 struct ReadHostFileContext r;
575 const char *filename;
576 struct GNUNET_PeerIdentity id_public;
577 struct GNUNET_PeerIdentity id_friend;
578 struct GNUNET_PeerIdentity id;
580 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
581 return GNUNET_OK; /* ignore non-files */
583 filename = strrchr (fullname, DIR_SEPARATOR);
584 if ((NULL == filename) || (1 > strlen (filename)))
589 read_host_file (fullname, dsc->remove_files, &r);
590 if ( (NULL == r.hello) && (NULL == r.friend_only_hello))
592 if (NULL != r.friend_only_hello)
594 if (GNUNET_OK != GNUNET_HELLO_get_id (r.friend_only_hello, &id_friend))
595 if (GNUNET_YES == dsc->remove_files)
597 remove_garbage (fullname);
604 if (GNUNET_OK != GNUNET_HELLO_get_id (r.hello, &id_public))
605 if (GNUNET_YES == dsc->remove_files)
607 remove_garbage (fullname);
613 if ( (NULL != r.hello) && (NULL != r.friend_only_hello) &&
614 (0 != memcmp (&id_friend, &id_public, sizeof (id_friend))) )
616 /* HELLOs are not for the same peer */
618 if (GNUNET_YES == dsc->remove_files)
619 remove_garbage (fullname);
623 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
625 &identity.public_key))
627 if (0 != memcmp (&id, &identity, sizeof (id_friend)))
629 /* HELLOs are not for the same peer */
631 if (GNUNET_YES == dsc->remove_files)
632 remove_garbage (fullname);
637 /* ok, found something valid, remember HELLO */
638 add_host_to_known_hosts (&id);
641 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642 "Updating peer `%s' public HELLO \n",
644 update_hello (&id, r.hello);
645 GNUNET_free (r.hello);
647 if (NULL != r.friend_only_hello)
649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650 "Updating peer `%s' friend only HELLO \n",
652 update_hello (&id, r.friend_only_hello);
653 GNUNET_free (r.friend_only_hello);
661 * Call this method periodically to scan data/hosts for new hosts.
666 cron_scan_directory_data_hosts (void *cls)
668 static unsigned int retries;
669 struct DirScanContext dsc;
673 GNUNET_DISK_directory_create (networkIdDirectory))
675 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
676 GNUNET_SCHEDULER_PRIORITY_IDLE,
677 &cron_scan_directory_data_hosts, NULL);
681 dsc.remove_files = GNUNET_YES;
682 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
683 _("Scanning directory `%s'\n"),
685 GNUNET_DISK_directory_scan (networkIdDirectory,
686 &hosts_directory_scan_callback, &dsc);
687 if ((0 == dsc.matched) && (0 == (++retries & 31)))
688 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
689 _("Still no peers found in `%s'!\n"),
691 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
692 GNUNET_SCHEDULER_PRIORITY_IDLE,
693 &cron_scan_directory_data_hosts,
699 * Update the HELLO of a friend by merging the addresses.
701 * @param hello original hello
702 * @param friend_hello hello with additional addresses
703 * @return merged HELLO
705 static struct GNUNET_HELLO_Message *
706 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
707 const struct GNUNET_HELLO_Message *friend_hello)
709 struct GNUNET_HELLO_Message * res;
710 struct GNUNET_HELLO_Message * tmp;
711 struct GNUNET_PeerIdentity pid;
713 if (NULL != friend_hello)
715 res = GNUNET_HELLO_merge (hello, friend_hello);
716 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
721 GNUNET_HELLO_get_id (hello, &pid))
726 tmp = GNUNET_HELLO_create (&pid.public_key,
730 res = GNUNET_HELLO_merge (hello, tmp);
732 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
738 * Bind a host address (hello) to a hostId.
740 * @param peer the peer for which this is a hello
741 * @param hello the verified (!) hello message
744 update_hello (const struct GNUNET_PeerIdentity *peer,
745 const struct GNUNET_HELLO_Message *hello)
748 struct HostEntry *host;
749 struct GNUNET_HELLO_Message *mrg;
750 struct GNUNET_HELLO_Message **dest;
751 struct GNUNET_TIME_Absolute delta;
754 int friend_hello_type;
756 int store_friend_hello;
760 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
761 GNUNET_assert (NULL != host);
763 friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
764 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
765 "Updating %s HELLO for `%s'\n",
766 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
770 if (GNUNET_YES == friend_hello_type)
772 dest = &host->friend_only_hello;
781 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
782 memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
786 mrg = GNUNET_HELLO_merge ((*dest), hello);
787 delta = GNUNET_HELLO_equals (mrg, (*dest), GNUNET_TIME_absolute_get ());
788 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
790 /* no differences, just ignore the update */
791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
792 "No change in %s HELLO for `%s'\n",
793 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
798 GNUNET_free ((*dest));
802 if ( (NULL != (host->hello)) &&
803 (GNUNET_NO == friend_hello_type) )
805 /* Update friend only hello */
806 mrg = update_friend_hello (host->hello, host->friend_only_hello);
807 if (NULL != host->friend_only_hello)
808 GNUNET_free (host->friend_only_hello);
809 host->friend_only_hello = mrg;
812 if (NULL != host->hello)
813 GNUNET_assert ((GNUNET_NO ==
814 GNUNET_HELLO_is_friend_only (host->hello)));
815 if (NULL != host->friend_only_hello)
816 GNUNET_assert ((GNUNET_YES ==
817 GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
819 fn = get_host_filename (peer);
822 GNUNET_DISK_directory_create_for_file (fn)) )
824 store_hello = GNUNET_NO;
827 if (NULL != host->hello)
828 (void) GNUNET_HELLO_iterate_addresses (host->hello,
834 store_hello = GNUNET_YES;
835 size += GNUNET_HELLO_size (host->hello);
838 if (NULL != host->friend_only_hello)
839 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
843 store_friend_hello = GNUNET_NO;
846 store_friend_hello = GNUNET_YES;
847 size += GNUNET_HELLO_size (host->friend_only_hello);
850 if ( (GNUNET_NO == store_hello) &&
851 (GNUNET_NO == store_friend_hello) )
853 /* no valid addresses, don't put HELLO on disk; in fact,
854 if one exists on disk, remove it */
859 buffer = GNUNET_malloc (size);
862 if (GNUNET_YES == store_hello)
866 GNUNET_HELLO_size (host->hello));
867 pos += GNUNET_HELLO_size (host->hello);
869 if (GNUNET_YES == store_friend_hello)
871 memcpy (&buffer[pos],
872 host->friend_only_hello,
873 GNUNET_HELLO_size (host->friend_only_hello));
874 pos += GNUNET_HELLO_size (host->friend_only_hello);
876 GNUNET_assert (pos == size);
878 if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
879 GNUNET_DISK_PERM_USER_READ |
880 GNUNET_DISK_PERM_USER_WRITE |
881 GNUNET_DISK_PERM_GROUP_READ |
882 GNUNET_DISK_PERM_OTHER_READ))
883 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
885 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
886 "Stored %s %s HELLO in %s with total size %u\n",
887 (GNUNET_YES == store_friend_hello) ? "friend-only": "",
888 (GNUNET_YES == store_hello) ? "public": "",
890 GNUNET_free (buffer);
893 GNUNET_free_non_null (fn);
899 * Do transmit info about peer to given host.
901 * @param cls NULL to hit all hosts, otherwise specifies a particular target
903 * @param value information to transmit
904 * @return #GNUNET_YES (continue to iterate)
907 add_to_tc (void *cls,
908 const struct GNUNET_PeerIdentity *key,
911 struct TransmitContext *tc = cls;
912 struct HostEntry *pos = value;
913 struct InfoMessage *im;
915 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
918 im = (struct InfoMessage *) buf;
920 if ( (NULL != pos->hello) &&
921 (GNUNET_NO == tc->friend_only) )
923 /* Copy public HELLO */
924 hs = GNUNET_HELLO_size (pos->hello);
925 GNUNET_assert (hs < GNUNET_SERVER_MAX_MESSAGE_SIZE -
926 sizeof (struct InfoMessage));
927 memcpy (&im[1], pos->hello, hs);
928 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929 "Sending public HELLO with size %u for peer `%4s'\n",
930 hs, GNUNET_i2s (key));
932 else if ( (NULL != pos->friend_only_hello) &&
933 (GNUNET_YES == tc->friend_only) )
935 /* Copy friend only HELLO */
936 hs = GNUNET_HELLO_size (pos->friend_only_hello);
937 GNUNET_assert (hs < GNUNET_SERVER_MAX_MESSAGE_SIZE -
938 sizeof (struct InfoMessage));
939 memcpy (&im[1], pos->friend_only_hello, hs);
940 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
941 "Sending friend-only HELLO with size %u for peer `%4s'\n",
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Adding no HELLO for peer `%s'\n",
952 im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
953 im->header.size = htons (sizeof (struct InfoMessage) + hs);
954 im->reserved = htonl (0);
955 im->peer = pos->identity;
956 GNUNET_SERVER_transmit_context_append_message (tc->tc, &im->header);
962 * @brief delete expired HELLO entries in directory
964 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
965 * @param fn filename to test to see if the HELLO expired
966 * @return #GNUNET_OK (continue iteration)
969 discard_hosts_helper (void *cls,
972 struct GNUNET_TIME_Absolute *now = cls;
973 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
974 const struct GNUNET_HELLO_Message *hello;
975 struct GNUNET_HELLO_Message *new_hello;
977 unsigned int cur_hello_size;
978 unsigned int new_hello_size;
984 read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
985 if (read_size < (int) sizeof (struct GNUNET_MessageHeader))
987 if (0 != UNLINK (fn))
988 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
989 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
993 writebuffer = GNUNET_malloc (read_size);
996 while (read_pos < read_size)
998 /* Check each HELLO */
999 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1000 cur_hello_size = GNUNET_HELLO_size (hello);
1001 if (0 == cur_hello_size)
1003 /* Invalid data, discard */
1004 if (0 != UNLINK (fn))
1005 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1006 GNUNET_ERROR_TYPE_BULK,
1010 new_hello = GNUNET_HELLO_iterate_addresses (hello,
1012 &discard_expired, now);
1014 if (NULL != new_hello)
1015 (void) GNUNET_HELLO_iterate_addresses (hello,
1017 &count_addresses, &cnt);
1018 if ( (NULL != new_hello) && (0 < cnt) )
1020 /* Store new HELLO to write it when done */
1021 new_hello_size = GNUNET_HELLO_size (new_hello);
1022 memcpy (&writebuffer[write_pos], new_hello, new_hello_size);
1023 write_pos += new_hello_size;
1025 read_pos += cur_hello_size;
1026 GNUNET_free_non_null (new_hello);
1031 GNUNET_DISK_fn_write (fn, writebuffer,write_pos,
1032 GNUNET_DISK_PERM_USER_READ |
1033 GNUNET_DISK_PERM_USER_WRITE |
1034 GNUNET_DISK_PERM_GROUP_READ |
1035 GNUNET_DISK_PERM_OTHER_READ);
1037 else if (0 != UNLINK (fn))
1038 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1039 GNUNET_ERROR_TYPE_BULK,
1042 GNUNET_free (writebuffer);
1048 * Call this method periodically to scan peerinfo/ for ancient
1054 cron_clean_data_hosts (void *cls)
1056 struct GNUNET_TIME_Absolute now;
1059 now = GNUNET_TIME_absolute_get ();
1060 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1061 _("Cleaning up directory `%s'\n"),
1062 networkIdDirectory);
1063 GNUNET_DISK_directory_scan (networkIdDirectory,
1064 &discard_hosts_helper,
1066 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1067 &cron_clean_data_hosts,
1073 * Handle HELLO-message.
1075 * @param cls closure
1076 * @param client identification of the client
1077 * @param message the actual message
1080 handle_hello (void *cls,
1081 struct GNUNET_SERVER_Client *client,
1082 const struct GNUNET_MessageHeader *message)
1084 const struct GNUNET_HELLO_Message *hello;
1085 struct GNUNET_PeerIdentity pid;
1087 hello = (const struct GNUNET_HELLO_Message *) message;
1088 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
1091 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095 "`%s' message received for peer `%4s'\n",
1098 add_host_to_known_hosts (&pid);
1099 update_hello (&pid, hello);
1100 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1105 * Handle GET-message.
1107 * @param cls closure
1108 * @param client identification of the client
1109 * @param message the actual message
1112 handle_get (void *cls,
1113 struct GNUNET_SERVER_Client *client,
1114 const struct GNUNET_MessageHeader *message)
1116 const struct ListPeerMessage *lpm;
1117 struct TransmitContext tcx;
1119 lpm = (const struct ListPeerMessage *) message;
1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121 "`%s' message received for peer `%4s'\n",
1123 GNUNET_i2s (&lpm->peer));
1124 tcx.friend_only = ntohl (lpm->include_friend_only);
1125 tcx.tc = GNUNET_SERVER_transmit_context_create (client);
1126 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap, &lpm->peer,
1128 GNUNET_SERVER_transmit_context_append_data (tcx.tc, NULL, 0,
1129 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1130 GNUNET_SERVER_transmit_context_run (tcx.tc, GNUNET_TIME_UNIT_FOREVER_REL);
1135 * Handle GET-ALL-message.
1137 * @param cls closure
1138 * @param client identification of the client
1139 * @param message the actual message
1142 handle_get_all (void *cls, struct GNUNET_SERVER_Client *client,
1143 const struct GNUNET_MessageHeader *message)
1145 const struct ListAllPeersMessage *lapm;
1146 struct TransmitContext tcx;
1148 lapm = (const struct ListAllPeersMessage *) message;
1149 tcx.friend_only = ntohl (lapm->include_friend_only);
1150 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1151 "`%s' message received\n",
1153 GNUNET_SERVER_disable_receive_done_warning (client);
1154 tcx.tc = GNUNET_SERVER_transmit_context_create (client);
1155 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1158 GNUNET_SERVER_transmit_context_append_data (tcx.tc, NULL, 0,
1159 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1160 GNUNET_SERVER_transmit_context_run (tcx.tc, GNUNET_TIME_UNIT_FOREVER_REL);
1166 * Pass the given client the information we have in the respective
1167 * host entry; the client is already in the notification context.
1169 * @param cls the `struct GNUNET_SERVER_Client` to notify
1170 * @param key key for the value (unused)
1171 * @param value the `struct HostEntry` to notify the client about
1172 * @return #GNUNET_YES (always, continue to iterate)
1175 do_notify_entry (void *cls,
1176 const struct GNUNET_PeerIdentity *key,
1179 struct NotificationContext *nc = cls;
1180 struct HostEntry *he = value;
1181 struct InfoMessage *msg;
1183 if ( (NULL == he->hello) &&
1184 (GNUNET_NO == nc->include_friend_only) )
1186 /* We have no public hello */
1190 if ( (NULL == he->friend_only_hello) &&
1191 (GNUNET_YES == nc->include_friend_only) )
1193 /* We have no friend hello */
1197 msg = make_info_message (he, nc->include_friend_only);
1198 GNUNET_SERVER_notification_context_unicast (notify_list,
1208 * Handle NOTIFY-message.
1210 * @param cls closure
1211 * @param client identification of the client
1212 * @param message the actual message
1215 handle_notify (void *cls,
1216 struct GNUNET_SERVER_Client *client,
1217 const struct GNUNET_MessageHeader *message)
1219 struct NotifyMessage *nm = (struct NotifyMessage *) message;
1220 struct NotificationContext *nc;
1222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223 "`%s' message received\n",
1225 nc = GNUNET_new (struct NotificationContext);
1226 nc->client = client;
1227 nc->include_friend_only = ntohl (nm->include_friend_only);
1229 GNUNET_CONTAINER_DLL_insert (nc_head, nc_tail, nc);
1230 GNUNET_SERVER_client_mark_monitor (client);
1231 GNUNET_SERVER_notification_context_add (notify_list, client);
1232 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &do_notify_entry, nc);
1233 GNUNET_SERVER_receive_done (client, GNUNET_OK);
1238 * Client disconnect callback
1241 * @param client server client
1244 disconnect_cb (void *cls,
1245 struct GNUNET_SERVER_Client *client)
1247 struct NotificationContext *cur;
1249 for (cur = nc_head; NULL != cur; cur = cur->next)
1250 if (cur->client == client)
1254 GNUNET_CONTAINER_DLL_remove (nc_head, nc_tail, cur);
1260 * Release memory taken by a host entry.
1263 * @param key key of the host entry
1264 * @param value the `struct HostEntry` to free
1265 * @return #GNUNET_YES (continue to iterate)
1268 free_host_entry (void *cls,
1269 const struct GNUNET_PeerIdentity *key,
1272 struct HostEntry *he = value;
1274 GNUNET_free_non_null (he->hello);
1275 GNUNET_free_non_null (he->friend_only_hello);
1282 * Clean up our state. Called during shutdown.
1287 shutdown_task (void *cls)
1289 struct NotificationContext *cur;
1290 struct NotificationContext *next;
1292 GNUNET_SERVER_notification_context_destroy (notify_list);
1295 for (cur = nc_head; NULL != cur; cur = next)
1298 GNUNET_CONTAINER_DLL_remove (nc_head, nc_tail, cur);
1301 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1304 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1307 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1310 if (NULL != cron_clean)
1312 GNUNET_SCHEDULER_cancel (cron_clean);
1315 if (NULL != cron_scan)
1317 GNUNET_SCHEDULER_cancel (cron_scan);
1324 * Start up peerinfo service.
1326 * @param cls closure
1327 * @param server the initialized server
1328 * @param cfg configuration to use
1332 struct GNUNET_SERVER_Handle *server,
1333 const struct GNUNET_CONFIGURATION_Handle *cfg)
1335 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1336 {&handle_hello, NULL, GNUNET_MESSAGE_TYPE_HELLO, 0},
1337 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1338 sizeof (struct ListPeerMessage)},
1339 {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1340 sizeof (struct ListAllPeersMessage)},
1341 {&handle_notify, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1342 sizeof (struct NotifyMessage)},
1347 struct DirScanContext dsc;
1351 hostmap = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES);
1352 stats = GNUNET_STATISTICS_create ("peerinfo", cfg);
1353 notify_list = GNUNET_SERVER_notification_context_create (server, 0);
1354 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg, "peerinfo", "NO_IO");
1355 use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1357 "USE_INCLUDED_HELLOS");
1358 if (GNUNET_SYSERR == use_included)
1359 use_included = GNUNET_NO;
1360 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1362 if (GNUNET_YES != noio)
1364 GNUNET_assert (GNUNET_OK ==
1365 GNUNET_CONFIGURATION_get_value_filename (cfg, "peerinfo",
1367 &networkIdDirectory));
1369 GNUNET_DISK_directory_create (networkIdDirectory))
1371 GNUNET_SCHEDULER_shutdown ();
1375 cron_scan = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1376 &cron_scan_directory_data_hosts,
1379 cron_clean = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1380 &cron_clean_data_hosts,
1382 if (GNUNET_YES == use_included)
1384 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1385 GNUNET_asprintf (&peerdir,
1390 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1391 _("Importing HELLOs from `%s'\n"),
1394 dsc.remove_files = GNUNET_NO;
1396 GNUNET_DISK_directory_scan (peerdir,
1397 &hosts_directory_scan_callback,
1399 GNUNET_free (peerdir);
1403 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1404 _("Skipping import of included HELLOs\n"));
1407 GNUNET_SERVER_add_handlers (server, handlers);
1408 GNUNET_SERVER_disconnect_notify (server,
1415 * The main function for the peerinfo service.
1417 * @param argc number of arguments from the command line
1418 * @param argv command line arguments
1419 * @return 0 ok, 1 on error
1429 GNUNET_SERVICE_run (argc, argv,
1431 GNUNET_SERVICE_OPTION_NONE,
1432 &run, NULL)) ? 0 : 1;
1433 GNUNET_free_non_null (networkIdDirectory);
1438 /* end of gnunet-service-peerinfo.c */