2 This file is part of GNUnet.
3 Copyright (C) 2001-2016 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;
74 * Result of reading a file
76 struct ReadHostFileContext
79 * Hello for the peer (can be NULL)
81 struct GNUNET_HELLO_Message *hello;
84 * Friend only hello for the peer (can be NULL)
86 struct GNUNET_HELLO_Message *friend_only_hello;
91 * The in-memory list of known hosts, mapping of
92 * host IDs to 'struct HostEntry*' values.
94 static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
97 * Clients to immediately notify about all changes.
99 static struct GNUNET_NotificationContext *notify_list;
102 * Clients to immediately notify about all changes,
103 * even for friend-only HELLOs.
105 static struct GNUNET_NotificationContext *notify_friend_only_list;
108 * Directory where the hellos are stored in (peerinfo/)
110 static char *networkIdDirectory;
113 * Handle for reporting statistics.
115 static struct GNUNET_STATISTICS_Handle *stats;
118 * Handle for task to run #cron_clean_data_hosts()
120 static struct GNUNET_SCHEDULER_Task *cron_clean;
123 * Handle for task to run #cron_scan_directory_data_hosts()
125 static struct GNUNET_SCHEDULER_Task *cron_scan;
129 * Notify all clients in the notify list about the
130 * given host entry changing.
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
136 static struct InfoMessage *
137 make_info_message (const struct HostEntry *he,
138 int include_friend_only)
140 struct InfoMessage *im;
141 struct GNUNET_HELLO_Message *src;
144 if (GNUNET_YES == include_friend_only)
145 src = he->friend_only_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],
161 * Address iterator that causes expired entries to be discarded.
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
169 discard_expired (void *cls,
170 const struct GNUNET_HELLO_Address *address,
171 struct GNUNET_TIME_Absolute expiration)
173 const struct GNUNET_TIME_Absolute *now = cls;
175 if (now->abs_value_us > expiration.abs_value_us)
177 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
178 _("Removing expired address of transport `%s'\n"),
179 address->transport_name);
187 * Address iterator that counts the remaining addresses.
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)
195 count_addresses (void *cls,
196 const struct GNUNET_HELLO_Address *address,
197 struct GNUNET_TIME_Absolute expiration)
199 unsigned int *cnt = cls;
207 * Get the filename under which we would store the GNUNET_HELLO_Message
208 * for the given host and protocol.
210 * @param id peer for which we need the filename for the HELLO
211 * @return filename of the form DIRECTORY/HOSTID
214 get_host_filename (const struct GNUNET_PeerIdentity *id)
218 if (NULL == networkIdDirectory)
220 GNUNET_asprintf (&fn,
224 GNUNET_i2s_full (id));
230 * Broadcast information about the given entry to all
233 * @param entry entry to broadcast about
236 notify_all (struct HostEntry *entry)
238 struct InfoMessage *msg_pub;
239 struct InfoMessage *msg_friend;
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,
246 GNUNET_notification_context_broadcast (notify_list,
249 GNUNET_free (msg_pub);
250 msg_friend = make_info_message (entry,
252 GNUNET_notification_context_broadcast (notify_friend_only_list,
255 GNUNET_free (msg_friend);
260 * Bind a host address (hello) to a hostId.
262 * @param peer the peer for which this is a hello
263 * @param hello the verified (!) hello message
266 update_hello (const struct GNUNET_PeerIdentity *peer,
267 const struct GNUNET_HELLO_Message *hello);
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.
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
282 read_host_file (const char *fn,
284 struct ReadHostFileContext *r)
286 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
288 struct GNUNET_TIME_Absolute now;
290 const struct GNUNET_HELLO_Message *hello;
291 struct GNUNET_HELLO_Message *hello_clean;
295 r->friend_only_hello = NULL;
298 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
300 size_total = GNUNET_DISK_fn_read (fn,
303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304 "Read %d bytes from `%s'\n",
307 if (size_total < sizeof (struct GNUNET_MessageHeader))
309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310 _("Failed to parse HELLO in file `%s': %s\n"),
312 "File has invalid size");
313 if ( (GNUNET_YES == unlink_garbage) &&
314 (0 != UNLINK (fn)) &&
316 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
323 while (read_pos < size_total)
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) )
330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
331 _("Failed to parse HELLO in file `%s'\n"),
335 if ((GNUNET_YES == unlink_garbage) &&
336 (0 != UNLINK (fn)) &&
338 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
344 if ( (GNUNET_YES == unlink_garbage) &&
345 (0 != TRUNCATE (fn, read_pos)) &&
347 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
354 now = GNUNET_TIME_absolute_get ();
355 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
359 if (NULL == hello_clean)
361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362 _("Failed to parse HELLO in file `%s'\n"),
364 if ((GNUNET_YES == unlink_garbage) &&
365 (0 != UNLINK (fn)) &&
367 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
373 (void) GNUNET_HELLO_iterate_addresses (hello_clean,
380 GNUNET_free (hello_clean);
384 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
386 if (NULL == r->hello)
387 r->hello = hello_clean;
391 GNUNET_free (r->hello);
392 r->hello = hello_clean;
397 if (NULL == r->friend_only_hello)
398 r->friend_only_hello = hello_clean;
402 GNUNET_free (r->friend_only_hello);
403 r->friend_only_hello = hello_clean;
406 read_pos += size_hello;
411 /* no addresses left, remove from disk */
412 if ( (GNUNET_YES == unlink_garbage) &&
414 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
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");
426 * Add a host to the list and notify clients about this event
428 * @param identity the identity of the host
429 * @return the HostEntry
431 static struct HostEntry *
432 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
434 struct HostEntry *entry;
435 struct ReadHostFileContext r;
438 entry = GNUNET_CONTAINER_multipeermap_get (hostmap,
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"),
449 entry = GNUNET_new (struct HostEntry);
450 entry->identity = *identity;
451 GNUNET_assert (GNUNET_OK ==
452 GNUNET_CONTAINER_multipeermap_put (hostmap,
455 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
457 fn = get_host_filename (identity);
464 update_hello (identity,
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);
479 * Remove a file that should not be there. LOG
480 * success or failure.
482 * @param fullname name of the file to remove
485 remove_garbage (const char *fullname)
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"),
493 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
500 * Closure for #hosts_directory_scan_callback().
502 struct DirScanContext
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.
512 * Counter for the number of (valid) entries found, incremented
513 * by one for each match.
515 unsigned int matched;
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.
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)
529 hosts_directory_scan_callback (void *cls,
530 const char *fullname)
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;
540 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
541 return GNUNET_OK; /* ignore non-files */
543 filename = strrchr (fullname,
545 if ( (NULL == filename) ||
546 (1 > strlen (filename)) )
551 read_host_file (fullname,
554 if ( (NULL == r.hello) &&
555 (NULL == r.friend_only_hello))
557 if (NULL != r.friend_only_hello)
560 GNUNET_HELLO_get_id (r.friend_only_hello,
562 if (GNUNET_YES == dsc->remove_files)
564 remove_garbage (fullname);
572 GNUNET_HELLO_get_id (r.hello,
574 if (GNUNET_YES == dsc->remove_files)
576 remove_garbage (fullname);
582 if ( (NULL != r.hello) &&
583 (NULL != r.friend_only_hello) &&
584 (0 != memcmp (&id_friend,
586 sizeof (id_friend))) )
588 /* HELLOs are not for the same peer */
590 if (GNUNET_YES == dsc->remove_files)
591 remove_garbage (fullname);
595 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
597 &identity.public_key))
599 if (0 != memcmp (&id, &identity, sizeof (id_friend)))
601 /* HELLOs are not for the same peer */
603 if (GNUNET_YES == dsc->remove_files)
604 remove_garbage (fullname);
609 /* ok, found something valid, remember HELLO */
610 add_host_to_known_hosts (&id);
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "Updating peer `%s' public HELLO \n",
616 update_hello (&id, r.hello);
617 GNUNET_free (r.hello);
619 if (NULL != r.friend_only_hello)
621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622 "Updating peer `%s' friend only HELLO \n",
624 update_hello (&id, r.friend_only_hello);
625 GNUNET_free (r.friend_only_hello);
633 * Call this method periodically to scan data/hosts for new hosts.
638 cron_scan_directory_data_hosts (void *cls)
640 static unsigned int retries;
641 struct DirScanContext dsc;
645 GNUNET_DISK_directory_create (networkIdDirectory))
647 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
648 GNUNET_SCHEDULER_PRIORITY_IDLE,
649 &cron_scan_directory_data_hosts, NULL);
653 dsc.remove_files = GNUNET_YES;
654 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
655 _("Scanning directory `%s'\n"),
657 GNUNET_DISK_directory_scan (networkIdDirectory,
658 &hosts_directory_scan_callback,
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"),
665 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
666 GNUNET_SCHEDULER_PRIORITY_IDLE,
667 &cron_scan_directory_data_hosts,
673 * Update the HELLO of a friend by merging the addresses.
675 * @param hello original hello
676 * @param friend_hello hello with additional addresses
677 * @return merged HELLO
679 static struct GNUNET_HELLO_Message *
680 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
681 const struct GNUNET_HELLO_Message *friend_hello)
683 struct GNUNET_HELLO_Message * res;
684 struct GNUNET_HELLO_Message * tmp;
685 struct GNUNET_PeerIdentity pid;
687 if (NULL != friend_hello)
689 res = GNUNET_HELLO_merge (hello,
691 GNUNET_assert (GNUNET_YES ==
692 GNUNET_HELLO_is_friend_only (res));
697 GNUNET_HELLO_get_id (hello, &pid))
702 tmp = GNUNET_HELLO_create (&pid.public_key,
706 res = GNUNET_HELLO_merge (hello, tmp);
708 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
714 * Bind a host address (hello) to a hostId.
716 * @param peer the peer for which this is a hello
717 * @param hello the verified (!) hello message
720 update_hello (const struct GNUNET_PeerIdentity *peer,
721 const struct GNUNET_HELLO_Message *hello)
724 struct HostEntry *host;
725 struct GNUNET_HELLO_Message *mrg;
726 struct GNUNET_HELLO_Message **dest;
727 struct GNUNET_TIME_Absolute delta;
730 int friend_hello_type;
732 int store_friend_hello;
736 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
737 GNUNET_assert (NULL != host);
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",
746 if (GNUNET_YES == friend_hello_type)
748 dest = &host->friend_only_hello;
757 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
758 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
762 mrg = GNUNET_HELLO_merge ((*dest),
764 delta = GNUNET_HELLO_equals (mrg,
766 GNUNET_TIME_absolute_get ());
767 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
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",
777 GNUNET_free ((*dest));
781 if ( (NULL != (host->hello)) &&
782 (GNUNET_NO == friend_hello_type) )
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;
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)));
799 fn = get_host_filename (peer);
802 GNUNET_DISK_directory_create_for_file (fn)) )
804 store_hello = GNUNET_NO;
807 if (NULL != host->hello)
808 (void) GNUNET_HELLO_iterate_addresses (host->hello,
814 store_hello = GNUNET_YES;
815 size += GNUNET_HELLO_size (host->hello);
818 if (NULL != host->friend_only_hello)
819 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
823 store_friend_hello = GNUNET_NO;
826 store_friend_hello = GNUNET_YES;
827 size += GNUNET_HELLO_size (host->friend_only_hello);
830 if ( (GNUNET_NO == store_hello) &&
831 (GNUNET_NO == store_friend_hello) )
833 /* no valid addresses, don't put HELLO on disk; in fact,
834 if one exists on disk, remove it */
839 buffer = GNUNET_malloc (size);
842 if (GNUNET_YES == store_hello)
844 GNUNET_memcpy (buffer,
846 GNUNET_HELLO_size (host->hello));
847 pos += GNUNET_HELLO_size (host->hello);
849 if (GNUNET_YES == store_friend_hello)
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);
856 GNUNET_assert (pos == size);
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);
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": "",
871 GNUNET_free (buffer);
874 GNUNET_free_non_null (fn);
880 * Closure for #add_to_tc()
882 struct TransmitContext
885 * Client to transmit to
887 struct GNUNET_SERVICE_Client *client;
890 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
897 * Do transmit info about peer to given host.
899 * @param cls NULL to hit all hosts, otherwise specifies a particular target
901 * @param value information to transmit
902 * @return #GNUNET_YES (continue to iterate)
905 add_to_tc (void *cls,
906 const struct GNUNET_PeerIdentity *key,
909 struct TransmitContext *tc = cls;
910 struct HostEntry *pos = value;
911 struct InfoMessage *im;
913 struct GNUNET_MQ_Envelope *env;
917 if ( (NULL != pos->hello) &&
918 (GNUNET_NO == tc->friend_only) )
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,
926 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
927 GNUNET_memcpy (&im[1],
930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931 "Sending public HELLO with size %u for peer `%s'\n",
935 else if ( (NULL != pos->friend_only_hello) &&
936 (GNUNET_YES == tc->friend_only) )
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,
944 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
945 GNUNET_memcpy (&im[1],
946 pos->friend_only_hello,
948 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
949 "Sending friend-only HELLO with size %u for peer `%s'\n",
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",
961 im->peer = pos->identity;
962 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
969 * @brief delete expired HELLO entries in directory
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)
976 discard_hosts_helper (void *cls,
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;
984 unsigned int cur_hello_size;
985 unsigned int new_hello_size;
991 read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer));
992 if (read_size < (int) sizeof (struct GNUNET_MessageHeader))
994 if (0 != UNLINK (fn))
995 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
996 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
1000 writebuffer = GNUNET_malloc (read_size);
1003 while (read_pos < read_size)
1005 /* Check each HELLO */
1006 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1007 cur_hello_size = GNUNET_HELLO_size (hello);
1008 if (0 == cur_hello_size)
1010 /* Invalid data, discard */
1011 if (0 != UNLINK (fn))
1012 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1013 GNUNET_ERROR_TYPE_BULK,
1016 GNUNET_free (writebuffer);
1019 new_hello = GNUNET_HELLO_iterate_addresses (hello,
1024 if (NULL != new_hello)
1025 (void) GNUNET_HELLO_iterate_addresses (hello,
1029 if ( (NULL != new_hello) && (0 < cnt) )
1031 /* Store new HELLO to write it when done */
1032 new_hello_size = GNUNET_HELLO_size (new_hello);
1033 GNUNET_memcpy (&writebuffer[write_pos],
1036 write_pos += new_hello_size;
1038 read_pos += cur_hello_size;
1039 GNUNET_free_non_null (new_hello);
1044 GNUNET_DISK_fn_write (fn,
1047 GNUNET_DISK_PERM_USER_READ |
1048 GNUNET_DISK_PERM_USER_WRITE |
1049 GNUNET_DISK_PERM_GROUP_READ |
1050 GNUNET_DISK_PERM_OTHER_READ);
1052 else if (0 != UNLINK (fn))
1053 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1054 GNUNET_ERROR_TYPE_BULK,
1057 GNUNET_free (writebuffer);
1063 * Call this method periodically to scan peerinfo/ for ancient
1069 cron_clean_data_hosts (void *cls)
1071 struct GNUNET_TIME_Absolute now;
1074 now = GNUNET_TIME_absolute_get ();
1075 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1076 _("Cleaning up directory `%s'\n"),
1077 networkIdDirectory);
1078 GNUNET_DISK_directory_scan (networkIdDirectory,
1079 &discard_hosts_helper,
1081 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1082 &cron_clean_data_hosts,
1088 * Check HELLO-message.
1090 * @param cls identification of the client
1091 * @param hello the actual message
1092 * @return #GNUNET_OK if @a hello is well-formed
1095 check_hello (void *cls,
1096 const struct GNUNET_HELLO_Message *hello)
1098 struct GNUNET_PeerIdentity pid;
1101 GNUNET_HELLO_get_id (hello,
1105 return GNUNET_SYSERR;
1112 * Handle HELLO-message.
1114 * @param cls identification of the client
1115 * @param hello the actual message
1118 handle_hello (void *cls,
1119 const struct GNUNET_HELLO_Message *hello)
1121 struct GNUNET_SERVICE_Client *client = cls;
1122 struct GNUNET_PeerIdentity pid;
1124 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125 "HELLO message received for peer `%s'\n",
1127 GNUNET_assert (GNUNET_OK ==
1128 GNUNET_HELLO_get_id (hello,
1130 add_host_to_known_hosts (&pid);
1133 GNUNET_SERVICE_client_continue (client);
1138 * Handle GET-message.
1140 * @param cls identification of the client
1141 * @param lpm the actual message
1144 handle_get (void *cls,
1145 const struct ListPeerMessage *lpm)
1147 struct GNUNET_SERVICE_Client *client = cls;
1148 struct TransmitContext tcx;
1149 struct GNUNET_MessageHeader *msg;
1150 struct GNUNET_MQ_Envelope *env;
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "GET message received for peer `%s'\n",
1154 GNUNET_i2s (&lpm->peer));
1155 tcx.friend_only = ntohl (lpm->include_friend_only);
1156 tcx.client = client;
1157 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1161 env = GNUNET_MQ_msg (msg,
1162 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1163 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1165 GNUNET_SERVICE_client_continue (client);
1170 * Handle GET-ALL-message.
1172 * @param cls identification of the client
1173 * @param lapm the actual message
1176 handle_get_all (void *cls,
1177 const struct ListAllPeersMessage *lapm)
1179 struct GNUNET_SERVICE_Client *client = cls;
1180 struct TransmitContext tcx;
1181 struct GNUNET_MQ_Envelope *env;
1182 struct GNUNET_MessageHeader *msg;
1184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185 "GET_ALL message received\n");
1186 tcx.friend_only = ntohl (lapm->include_friend_only);
1187 tcx.client = client;
1188 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1191 env = GNUNET_MQ_msg (msg,
1192 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1193 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1195 GNUNET_SERVICE_client_continue (client);
1200 * Handle NOTIFY-message.
1202 * @param cls identification of the client
1203 * @param nm the actual message
1206 handle_notify (void *cls,
1207 const struct NotifyMessage *nm)
1209 struct GNUNET_SERVICE_Client *client = cls;
1210 struct GNUNET_MQ_Handle *mq;
1211 struct TransmitContext tcx;
1212 struct GNUNET_MQ_Envelope *env;
1213 struct GNUNET_MessageHeader *msg;
1215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1216 "NOTIFY message received\n");
1217 mq = GNUNET_SERVICE_client_get_mq (client);
1218 GNUNET_SERVICE_client_mark_monitor (client);
1219 if (ntohl (nm->include_friend_only))
1220 GNUNET_notification_context_add (notify_friend_only_list,
1223 GNUNET_notification_context_add (notify_list,
1225 tcx.friend_only = ntohl (nm->include_friend_only);
1226 tcx.client = client;
1227 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1230 env = GNUNET_MQ_msg (msg,
1231 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1232 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1234 GNUNET_SERVICE_client_continue (client);
1239 * Client connect callback
1242 * @param client server client
1243 * @param mq for @a client
1247 client_connect_cb (void *cls,
1248 struct GNUNET_SERVICE_Client *client,
1249 struct GNUNET_MQ_Handle *mq)
1256 * Client disconnect callback
1259 * @param client server client
1260 * @param app_ctx should be @a client
1263 client_disconnect_cb (void *cls,
1264 struct GNUNET_SERVICE_Client *client,
1267 GNUNET_assert (app_ctx == client);
1272 * Release memory taken by a host entry.
1275 * @param key key of the host entry
1276 * @param value the `struct HostEntry` to free
1277 * @return #GNUNET_YES (continue to iterate)
1280 free_host_entry (void *cls,
1281 const struct GNUNET_PeerIdentity *key,
1284 struct HostEntry *he = value;
1286 GNUNET_free_non_null (he->hello);
1287 GNUNET_free_non_null (he->friend_only_hello);
1294 * Clean up our state. Called during shutdown.
1299 shutdown_task (void *cls)
1301 GNUNET_notification_context_destroy (notify_list);
1303 GNUNET_notification_context_destroy (notify_friend_only_list);
1304 notify_friend_only_list = NULL;
1306 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1309 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1312 GNUNET_STATISTICS_destroy (stats,
1316 if (NULL != cron_clean)
1318 GNUNET_SCHEDULER_cancel (cron_clean);
1321 if (NULL != cron_scan)
1323 GNUNET_SCHEDULER_cancel (cron_scan);
1326 if (NULL != networkIdDirectory)
1328 GNUNET_free (networkIdDirectory);
1329 networkIdDirectory = NULL;
1335 * Start up peerinfo service.
1337 * @param cls closure
1338 * @param cfg configuration to use
1339 * @param service the initialized service
1343 const struct GNUNET_CONFIGURATION_Handle *cfg,
1344 struct GNUNET_SERVICE_Handle *service)
1348 struct DirScanContext dsc;
1353 = GNUNET_CONTAINER_multipeermap_create (1024,
1356 = GNUNET_STATISTICS_create ("peerinfo",
1359 = GNUNET_notification_context_create (0);
1360 notify_friend_only_list
1361 = GNUNET_notification_context_create (0);
1362 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1366 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1368 "USE_INCLUDED_HELLOS");
1369 if (GNUNET_SYSERR == use_included)
1370 use_included = GNUNET_NO;
1371 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1373 if (GNUNET_YES != noio)
1375 GNUNET_assert (GNUNET_OK ==
1376 GNUNET_CONFIGURATION_get_value_filename (cfg,
1379 &networkIdDirectory));
1381 GNUNET_DISK_directory_create (networkIdDirectory))
1383 GNUNET_SCHEDULER_shutdown ();
1388 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1389 &cron_scan_directory_data_hosts,
1393 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1394 &cron_clean_data_hosts,
1396 if (GNUNET_YES == use_included)
1398 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1399 GNUNET_asprintf (&peerdir,
1404 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1405 _("Importing HELLOs from `%s'\n"),
1408 dsc.remove_files = GNUNET_NO;
1410 GNUNET_DISK_directory_scan (peerdir,
1411 &hosts_directory_scan_callback,
1413 GNUNET_free (peerdir);
1417 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1418 _("Skipping import of included HELLOs\n"));
1425 * Define "main" method using service macro.
1429 GNUNET_SERVICE_OPTION_NONE,
1432 &client_disconnect_cb,
1434 GNUNET_MQ_hd_var_size (hello,
1435 GNUNET_MESSAGE_TYPE_HELLO,
1436 struct GNUNET_HELLO_Message,
1438 GNUNET_MQ_hd_fixed_size (get,
1439 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1440 struct ListPeerMessage,
1442 GNUNET_MQ_hd_fixed_size (get_all,
1443 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1444 struct ListAllPeersMessage,
1446 GNUNET_MQ_hd_fixed_size (notify,
1447 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1448 struct NotifyMessage,
1450 GNUNET_MQ_handler_end ());
1453 /* end of gnunet-service-peerinfo.c */