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 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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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;
209 * Get the filename under which we would store the GNUNET_HELLO_Message
210 * for the given host and protocol.
212 * @param id peer for which we need the filename for the HELLO
213 * @return filename of the form DIRECTORY/HOSTID
216 get_host_filename (const struct GNUNET_PeerIdentity *id)
220 if (NULL == networkIdDirectory)
222 GNUNET_asprintf (&fn,
226 GNUNET_i2s_full (id));
232 * Broadcast information about the given entry to all
235 * @param entry entry to broadcast about
238 notify_all (struct HostEntry *entry)
240 struct InfoMessage *msg_pub;
241 struct InfoMessage *msg_friend;
243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244 "Notifying all clients about peer `%s'\n",
245 GNUNET_i2s(&entry->identity));
246 msg_pub = make_info_message (entry,
248 GNUNET_notification_context_broadcast (notify_list,
251 GNUNET_free (msg_pub);
252 msg_friend = make_info_message (entry,
254 GNUNET_notification_context_broadcast (notify_friend_only_list,
257 GNUNET_free (msg_friend);
262 * Bind a host address (hello) to a hostId.
264 * @param peer the peer for which this is a hello
265 * @param hello the verified (!) hello message
268 update_hello (const struct GNUNET_PeerIdentity *peer,
269 const struct GNUNET_HELLO_Message *hello);
273 * Try to read the HELLOs in the given filename and discard expired
274 * addresses. Removes the file if one the HELLO is malformed. If all
275 * addresses are expired, the HELLO is also removed (but the HELLO
276 * with the public key is still returned if it was found and valid).
277 * The file can contain multiple HELLO messages.
279 * @param fn name of the file
280 * @param unlink_garbage if #GNUNET_YES, try to remove useless files
281 * @param r ReadHostFileContext to store the resutl
284 read_host_file (const char *fn,
286 struct ReadHostFileContext *r)
288 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
290 struct GNUNET_TIME_Absolute now;
292 const struct GNUNET_HELLO_Message *hello;
293 struct GNUNET_HELLO_Message *hello_clean;
297 r->friend_only_hello = NULL;
300 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
302 size_total = GNUNET_DISK_fn_read (fn,
305 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
306 "Read %d bytes from `%s'\n",
309 if ( (size_total < 0) ||
310 (((size_t) size_total) < sizeof (struct GNUNET_MessageHeader)) )
312 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
313 _("Failed to parse HELLO in file `%s': %s\n"),
315 "File has invalid size");
316 if ( (GNUNET_YES == unlink_garbage) &&
317 (0 != UNLINK (fn)) &&
319 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
326 while (read_pos < (size_t) size_total)
328 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
329 size_hello = GNUNET_HELLO_size (hello);
330 if ( (0 == size_hello) ||
331 (((size_t) size_total) - read_pos < size_hello) )
333 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334 _("Failed to parse HELLO in file `%s'\n"),
338 if ((GNUNET_YES == unlink_garbage) &&
339 (0 != UNLINK (fn)) &&
341 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
347 if ( (GNUNET_YES == unlink_garbage) &&
348 (0 != TRUNCATE (fn, read_pos)) &&
350 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
357 now = GNUNET_TIME_absolute_get ();
358 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
362 if (NULL == hello_clean)
364 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
365 _("Failed to parse HELLO in file `%s'\n"),
367 if ((GNUNET_YES == unlink_garbage) &&
368 (0 != UNLINK (fn)) &&
370 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
376 (void) GNUNET_HELLO_iterate_addresses (hello_clean,
383 GNUNET_free (hello_clean);
387 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
389 if (NULL == r->hello)
390 r->hello = hello_clean;
394 GNUNET_free (r->hello);
395 r->hello = hello_clean;
400 if (NULL == r->friend_only_hello)
401 r->friend_only_hello = hello_clean;
405 GNUNET_free (r->friend_only_hello);
406 r->friend_only_hello = hello_clean;
409 read_pos += size_hello;
414 /* no addresses left, remove from disk */
415 if ( (GNUNET_YES == unlink_garbage) &&
417 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422 "Found `%s' and `%s' HELLO message in file\n",
423 (NULL != r->hello) ? "public" : "NON-public",
424 (NULL != r->friend_only_hello) ? "friend only" : "NO friend only");
429 * Add a host to the list and notify clients about this event
431 * @param identity the identity of the host
432 * @return the HostEntry
434 static struct HostEntry *
435 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
437 struct HostEntry *entry;
438 struct ReadHostFileContext r;
441 entry = GNUNET_CONTAINER_multipeermap_get (hostmap,
445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446 "Adding new peer `%s'\n",
447 GNUNET_i2s (identity));
448 GNUNET_STATISTICS_update (stats,
449 gettext_noop ("# peers known"),
452 entry = GNUNET_new (struct HostEntry);
453 entry->identity = *identity;
454 GNUNET_assert (GNUNET_OK ==
455 GNUNET_CONTAINER_multipeermap_put (hostmap,
458 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
460 fn = get_host_filename (identity);
467 update_hello (identity,
469 if (NULL != r.friend_only_hello)
470 update_hello (identity,
471 r.friend_only_hello);
472 GNUNET_free_non_null (r.hello);
473 GNUNET_free_non_null (r.friend_only_hello);
482 * Remove a file that should not be there. LOG
483 * success or failure.
485 * @param fullname name of the file to remove
488 remove_garbage (const char *fullname)
490 if (0 == UNLINK (fullname))
491 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
492 _("File `%s' in directory `%s' does not match naming convention. Removed.\n"),
496 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
503 * Closure for #hosts_directory_scan_callback().
505 struct DirScanContext
508 * #GNUNET_YES if we should remove files that are broken,
509 * #GNUNET_NO if the directory we are iterating over should
510 * be treated as read-only by us.
515 * Counter for the number of (valid) entries found, incremented
516 * by one for each match.
518 unsigned int matched;
523 * Function that is called on each HELLO file in a particular directory.
524 * Try to parse the file and add the HELLO to our list.
526 * @param cls pointer to 'unsigned int' to increment for each file, or NULL
527 * if the file is from a read-only, read-once resource directory
528 * @param fullname name of the file to parse
529 * @return #GNUNET_OK (continue iteration)
532 hosts_directory_scan_callback (void *cls,
533 const char *fullname)
535 struct DirScanContext *dsc = cls;
536 struct GNUNET_PeerIdentity identity;
537 struct ReadHostFileContext r;
538 const char *filename;
539 struct GNUNET_PeerIdentity id_public;
540 struct GNUNET_PeerIdentity id_friend;
541 struct GNUNET_PeerIdentity id;
543 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
544 return GNUNET_OK; /* ignore non-files */
546 filename = strrchr (fullname,
548 if ( (NULL == filename) ||
549 (1 > strlen (filename)) )
554 read_host_file (fullname,
557 if ( (NULL == r.hello) &&
558 (NULL == r.friend_only_hello))
560 if (NULL != r.friend_only_hello)
563 GNUNET_HELLO_get_id (r.friend_only_hello,
566 if (GNUNET_YES == dsc->remove_files)
567 remove_garbage (fullname);
575 GNUNET_HELLO_get_id (r.hello,
578 if (GNUNET_YES == dsc->remove_files)
579 remove_garbage (fullname);
585 if ( (NULL != r.hello) &&
586 (NULL != r.friend_only_hello) &&
587 (0 != GNUNET_memcmp (&id_friend,
590 /* HELLOs are not for the same peer */
592 if (GNUNET_YES == dsc->remove_files)
593 remove_garbage (fullname);
597 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
599 &identity.public_key))
601 if (0 != GNUNET_memcmp (&id, &identity))
603 /* HELLOs are not for the same peer */
605 if (GNUNET_YES == dsc->remove_files)
606 remove_garbage (fullname);
611 /* ok, found something valid, remember HELLO */
612 add_host_to_known_hosts (&id);
615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
616 "Updating peer `%s' public HELLO \n",
618 update_hello (&id, r.hello);
619 GNUNET_free (r.hello);
621 if (NULL != r.friend_only_hello)
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 "Updating peer `%s' friend only HELLO \n",
626 update_hello (&id, r.friend_only_hello);
627 GNUNET_free (r.friend_only_hello);
635 * Call this method periodically to scan data/hosts for new hosts.
640 cron_scan_directory_data_hosts (void *cls)
642 static unsigned int retries;
643 struct DirScanContext dsc;
648 GNUNET_DISK_directory_create (networkIdDirectory))
650 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
651 GNUNET_SCHEDULER_PRIORITY_IDLE,
652 &cron_scan_directory_data_hosts, NULL);
656 dsc.remove_files = GNUNET_YES;
657 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
658 _("Scanning directory `%s'\n"),
660 GNUNET_DISK_directory_scan (networkIdDirectory,
661 &hosts_directory_scan_callback,
663 if ( (0 == dsc.matched) &&
664 (0 == (++retries & 31)) )
665 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
666 _("Still no peers found in `%s'!\n"),
668 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
669 GNUNET_SCHEDULER_PRIORITY_IDLE,
670 &cron_scan_directory_data_hosts,
676 * Update the HELLO of a friend by merging the addresses.
678 * @param hello original hello
679 * @param friend_hello hello with additional addresses
680 * @return merged HELLO
682 static struct GNUNET_HELLO_Message *
683 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
684 const struct GNUNET_HELLO_Message *friend_hello)
686 struct GNUNET_HELLO_Message * res;
687 struct GNUNET_HELLO_Message * tmp;
688 struct GNUNET_PeerIdentity pid;
690 if (NULL != friend_hello)
692 res = GNUNET_HELLO_merge (hello,
694 GNUNET_assert (GNUNET_YES ==
695 GNUNET_HELLO_is_friend_only (res));
700 GNUNET_HELLO_get_id (hello, &pid))
705 tmp = GNUNET_HELLO_create (&pid.public_key,
709 res = GNUNET_HELLO_merge (hello, tmp);
711 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
717 * Bind a host address (hello) to a hostId.
719 * @param peer the peer for which this is a hello
720 * @param hello the verified (!) hello message
723 update_hello (const struct GNUNET_PeerIdentity *peer,
724 const struct GNUNET_HELLO_Message *hello)
727 struct HostEntry *host;
728 struct GNUNET_HELLO_Message *mrg;
729 struct GNUNET_HELLO_Message **dest;
730 struct GNUNET_TIME_Absolute delta;
733 int friend_hello_type;
735 int store_friend_hello;
739 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
740 GNUNET_assert (NULL != host);
742 friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
743 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
744 "Updating %s HELLO for `%s'\n",
745 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
749 if (GNUNET_YES == friend_hello_type)
751 dest = &host->friend_only_hello;
760 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
761 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
765 mrg = GNUNET_HELLO_merge ((*dest),
767 delta = GNUNET_HELLO_equals (mrg,
769 GNUNET_TIME_absolute_get ());
770 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
772 /* no differences, just ignore the update */
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774 "No change in %s HELLO for `%s'\n",
775 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
780 GNUNET_free ((*dest));
784 if ( (NULL != (host->hello)) &&
785 (GNUNET_NO == friend_hello_type) )
787 /* Update friend only hello */
788 mrg = update_friend_hello (host->hello,
789 host->friend_only_hello);
790 if (NULL != host->friend_only_hello)
791 GNUNET_free (host->friend_only_hello);
792 host->friend_only_hello = mrg;
795 if (NULL != host->hello)
796 GNUNET_assert ((GNUNET_NO ==
797 GNUNET_HELLO_is_friend_only (host->hello)));
798 if (NULL != host->friend_only_hello)
799 GNUNET_assert ((GNUNET_YES ==
800 GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
802 fn = get_host_filename (peer);
805 GNUNET_DISK_directory_create_for_file (fn)) )
807 store_hello = GNUNET_NO;
810 if (NULL != host->hello)
811 (void) GNUNET_HELLO_iterate_addresses (host->hello,
817 store_hello = GNUNET_YES;
818 size += GNUNET_HELLO_size (host->hello);
821 if (NULL != host->friend_only_hello)
822 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
826 store_friend_hello = GNUNET_NO;
829 store_friend_hello = GNUNET_YES;
830 size += GNUNET_HELLO_size (host->friend_only_hello);
833 if ( (GNUNET_NO == store_hello) &&
834 (GNUNET_NO == store_friend_hello) )
836 /* no valid addresses, don't put HELLO on disk; in fact,
837 if one exists on disk, remove it */
842 buffer = GNUNET_malloc (size);
845 if (GNUNET_YES == store_hello)
847 GNUNET_memcpy (buffer,
849 GNUNET_HELLO_size (host->hello));
850 pos += GNUNET_HELLO_size (host->hello);
852 if (GNUNET_YES == store_friend_hello)
854 GNUNET_memcpy (&buffer[pos],
855 host->friend_only_hello,
856 GNUNET_HELLO_size (host->friend_only_hello));
857 pos += GNUNET_HELLO_size (host->friend_only_hello);
859 GNUNET_assert (pos == size);
861 if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
862 GNUNET_DISK_PERM_USER_READ |
863 GNUNET_DISK_PERM_USER_WRITE |
864 GNUNET_DISK_PERM_GROUP_READ |
865 GNUNET_DISK_PERM_OTHER_READ))
866 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
868 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869 "Stored %s %s HELLO in %s with total size %u\n",
870 (GNUNET_YES == store_friend_hello) ? "friend-only": "",
871 (GNUNET_YES == store_hello) ? "public": "",
874 GNUNET_free (buffer);
877 GNUNET_free_non_null (fn);
883 * Closure for #add_to_tc()
885 struct TransmitContext
888 * Client to transmit to
890 struct GNUNET_SERVICE_Client *client;
893 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
900 * Do transmit info about peer to given host.
902 * @param cls NULL to hit all hosts, otherwise specifies a particular target
904 * @param value information to transmit
905 * @return #GNUNET_YES (continue to iterate)
908 add_to_tc (void *cls,
909 const struct GNUNET_PeerIdentity *key,
912 struct TransmitContext *tc = cls;
913 struct HostEntry *pos = value;
914 struct InfoMessage *im;
916 struct GNUNET_MQ_Envelope *env;
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_MAX_MESSAGE_SIZE -
926 sizeof (struct InfoMessage));
927 env = GNUNET_MQ_msg_extra (im,
929 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
930 GNUNET_memcpy (&im[1],
933 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
934 "Sending public HELLO with size %u for peer `%s'\n",
938 else if ( (NULL != pos->friend_only_hello) &&
939 (GNUNET_YES == tc->friend_only) )
941 /* Copy friend only HELLO */
942 hs = GNUNET_HELLO_size (pos->friend_only_hello);
943 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
944 sizeof (struct InfoMessage));
945 env = GNUNET_MQ_msg_extra (im,
947 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
948 GNUNET_memcpy (&im[1],
949 pos->friend_only_hello,
951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
952 "Sending friend-only HELLO with size %u for peer `%s'\n",
958 env = GNUNET_MQ_msg (im,
959 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
960 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961 "Adding no HELLO for peer `%s'\n",
964 im->peer = pos->identity;
965 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
972 * @brief delete expired HELLO entries in directory
974 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
975 * @param fn filename to test to see if the HELLO expired
976 * @return #GNUNET_OK (continue iteration)
979 discard_hosts_helper (void *cls,
982 struct GNUNET_TIME_Absolute *now = cls;
983 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
984 const struct GNUNET_HELLO_Message *hello;
985 struct GNUNET_HELLO_Message *new_hello;
987 unsigned int cur_hello_size;
988 unsigned int new_hello_size;
996 GNUNET_DISK_file_size (fn,
1001 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1002 GNUNET_ERROR_TYPE_BULK,
1007 read_size = GNUNET_DISK_fn_read (fn,
1011 if ( (read_size < (int) sizeof (struct GNUNET_MessageHeader)) ||
1012 (fsize > GNUNET_MAX_MESSAGE_SIZE) )
1014 if (0 != UNLINK (fn))
1015 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1016 GNUNET_ERROR_TYPE_BULK,
1022 writebuffer = GNUNET_malloc (read_size);
1025 while (read_pos < read_size)
1027 /* Check each HELLO */
1028 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1029 cur_hello_size = GNUNET_HELLO_size (hello);
1030 if (0 == cur_hello_size)
1032 /* Invalid data, discard */
1033 if (0 != UNLINK (fn))
1034 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1035 GNUNET_ERROR_TYPE_BULK,
1038 GNUNET_free (writebuffer);
1041 new_hello = GNUNET_HELLO_iterate_addresses (hello,
1046 if (NULL != new_hello)
1047 (void) GNUNET_HELLO_iterate_addresses (hello,
1051 if ( (NULL != new_hello) && (0 < cnt) )
1053 /* Store new HELLO to write it when done */
1054 new_hello_size = GNUNET_HELLO_size (new_hello);
1055 GNUNET_memcpy (&writebuffer[write_pos],
1058 write_pos += new_hello_size;
1060 read_pos += cur_hello_size;
1061 GNUNET_free_non_null (new_hello);
1066 GNUNET_DISK_fn_write (fn,
1069 GNUNET_DISK_PERM_USER_READ |
1070 GNUNET_DISK_PERM_USER_WRITE |
1071 GNUNET_DISK_PERM_GROUP_READ |
1072 GNUNET_DISK_PERM_OTHER_READ);
1074 else if (0 != UNLINK (fn))
1075 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1076 GNUNET_ERROR_TYPE_BULK,
1079 GNUNET_free (writebuffer);
1085 * Call this method periodically to scan peerinfo/ for ancient
1091 cron_clean_data_hosts (void *cls)
1093 struct GNUNET_TIME_Absolute now;
1097 now = GNUNET_TIME_absolute_get ();
1098 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1099 _("Cleaning up directory `%s'\n"),
1100 networkIdDirectory);
1101 GNUNET_DISK_directory_scan (networkIdDirectory,
1102 &discard_hosts_helper,
1104 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1105 &cron_clean_data_hosts,
1111 * Check HELLO-message.
1113 * @param cls identification of the client
1114 * @param hello the actual message
1115 * @return #GNUNET_OK if @a hello is well-formed
1118 check_hello (void *cls,
1119 const struct GNUNET_HELLO_Message *hello)
1121 struct GNUNET_PeerIdentity pid;
1125 GNUNET_HELLO_get_id (hello,
1129 return GNUNET_SYSERR;
1136 * Handle HELLO-message.
1138 * @param cls identification of the client
1139 * @param hello the actual message
1142 handle_hello (void *cls,
1143 const struct GNUNET_HELLO_Message *hello)
1145 struct GNUNET_SERVICE_Client *client = cls;
1146 struct GNUNET_PeerIdentity pid;
1148 GNUNET_assert (GNUNET_OK ==
1149 GNUNET_HELLO_get_id (hello,
1151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152 "HELLO message received for peer `%s'\n",
1154 add_host_to_known_hosts (&pid);
1157 GNUNET_SERVICE_client_continue (client);
1162 * Handle GET-message.
1164 * @param cls identification of the client
1165 * @param lpm the actual message
1168 handle_get (void *cls,
1169 const struct ListPeerMessage *lpm)
1171 struct GNUNET_SERVICE_Client *client = cls;
1172 struct TransmitContext tcx;
1173 struct GNUNET_MessageHeader *msg;
1174 struct GNUNET_MQ_Envelope *env;
1176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1177 "GET message received for peer `%s'\n",
1178 GNUNET_i2s (&lpm->peer));
1179 tcx.friend_only = ntohl (lpm->include_friend_only);
1180 tcx.client = client;
1181 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1185 env = GNUNET_MQ_msg (msg,
1186 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1187 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1189 GNUNET_SERVICE_client_continue (client);
1194 * Handle GET-ALL-message.
1196 * @param cls identification of the client
1197 * @param lapm the actual message
1200 handle_get_all (void *cls,
1201 const struct ListAllPeersMessage *lapm)
1203 struct GNUNET_SERVICE_Client *client = cls;
1204 struct TransmitContext tcx;
1205 struct GNUNET_MQ_Envelope *env;
1206 struct GNUNET_MessageHeader *msg;
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "GET_ALL message received\n");
1210 tcx.friend_only = ntohl (lapm->include_friend_only);
1211 tcx.client = client;
1212 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1215 env = GNUNET_MQ_msg (msg,
1216 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1217 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1219 GNUNET_SERVICE_client_continue (client);
1224 * Handle NOTIFY-message.
1226 * @param cls identification of the client
1227 * @param nm the actual message
1230 handle_notify (void *cls,
1231 const struct NotifyMessage *nm)
1233 struct GNUNET_SERVICE_Client *client = cls;
1234 struct GNUNET_MQ_Handle *mq;
1235 struct TransmitContext tcx;
1236 struct GNUNET_MQ_Envelope *env;
1237 struct GNUNET_MessageHeader *msg;
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "NOTIFY message received\n");
1241 mq = GNUNET_SERVICE_client_get_mq (client);
1242 GNUNET_SERVICE_client_mark_monitor (client);
1243 if (ntohl (nm->include_friend_only))
1244 GNUNET_notification_context_add (notify_friend_only_list,
1247 GNUNET_notification_context_add (notify_list,
1249 tcx.friend_only = ntohl (nm->include_friend_only);
1250 tcx.client = client;
1251 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1254 env = GNUNET_MQ_msg (msg,
1255 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1256 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1258 GNUNET_SERVICE_client_continue (client);
1263 * Client connect callback
1266 * @param client server client
1267 * @param mq for @a client
1271 client_connect_cb (void *cls,
1272 struct GNUNET_SERVICE_Client *client,
1273 struct GNUNET_MQ_Handle *mq)
1282 * Client disconnect callback
1285 * @param client server client
1286 * @param app_ctx should be @a client
1289 client_disconnect_cb (void *cls,
1290 struct GNUNET_SERVICE_Client *client,
1294 GNUNET_assert (app_ctx == client);
1299 * Release memory taken by a host entry.
1302 * @param key key of the host entry
1303 * @param value the `struct HostEntry` to free
1304 * @return #GNUNET_YES (continue to iterate)
1307 free_host_entry (void *cls,
1308 const struct GNUNET_PeerIdentity *key,
1311 struct HostEntry *he = value;
1315 GNUNET_free_non_null (he->hello);
1316 GNUNET_free_non_null (he->friend_only_hello);
1323 * Clean up our state. Called during shutdown.
1328 shutdown_task (void *cls)
1331 GNUNET_notification_context_destroy (notify_list);
1333 GNUNET_notification_context_destroy (notify_friend_only_list);
1334 notify_friend_only_list = NULL;
1336 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1339 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1342 GNUNET_STATISTICS_destroy (stats,
1346 if (NULL != cron_clean)
1348 GNUNET_SCHEDULER_cancel (cron_clean);
1351 if (NULL != cron_scan)
1353 GNUNET_SCHEDULER_cancel (cron_scan);
1356 if (NULL != networkIdDirectory)
1358 GNUNET_free (networkIdDirectory);
1359 networkIdDirectory = NULL;
1365 * Start up peerinfo service.
1367 * @param cls closure
1368 * @param cfg configuration to use
1369 * @param service the initialized service
1373 const struct GNUNET_CONFIGURATION_Handle *cfg,
1374 struct GNUNET_SERVICE_Handle *service)
1378 struct DirScanContext dsc;
1385 = GNUNET_CONTAINER_multipeermap_create (1024,
1388 = GNUNET_STATISTICS_create ("peerinfo",
1391 = GNUNET_notification_context_create (0);
1392 notify_friend_only_list
1393 = GNUNET_notification_context_create (0);
1394 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1398 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1400 "USE_INCLUDED_HELLOS");
1401 if (GNUNET_SYSERR == use_included)
1402 use_included = GNUNET_NO;
1403 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1405 if (GNUNET_YES != noio)
1407 GNUNET_assert (GNUNET_OK ==
1408 GNUNET_CONFIGURATION_get_value_filename (cfg,
1411 &networkIdDirectory));
1413 GNUNET_DISK_directory_create (networkIdDirectory))
1415 GNUNET_SCHEDULER_shutdown ();
1420 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1421 &cron_scan_directory_data_hosts,
1425 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1426 &cron_clean_data_hosts,
1428 if (GNUNET_YES == use_included)
1430 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1431 GNUNET_asprintf (&peerdir,
1436 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1437 _("Importing HELLOs from `%s'\n"),
1440 dsc.remove_files = GNUNET_NO;
1442 GNUNET_DISK_directory_scan (peerdir,
1443 &hosts_directory_scan_callback,
1445 GNUNET_free (peerdir);
1449 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1450 _("Skipping import of included HELLOs\n"));
1457 * Define "main" method using service macro.
1461 GNUNET_SERVICE_OPTION_NONE,
1464 &client_disconnect_cb,
1466 GNUNET_MQ_hd_var_size (hello,
1467 GNUNET_MESSAGE_TYPE_HELLO,
1468 struct GNUNET_HELLO_Message,
1470 GNUNET_MQ_hd_fixed_size (get,
1471 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1472 struct ListPeerMessage,
1474 GNUNET_MQ_hd_fixed_size (get_all,
1475 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1476 struct ListAllPeersMessage,
1478 GNUNET_MQ_hd_fixed_size (notify,
1479 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1480 struct NotifyMessage,
1482 GNUNET_MQ_handler_end ());
1485 /* end of gnunet-service-peerinfo.c */