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 != memcmp (&id_friend,
589 sizeof (id_friend))) )
591 /* HELLOs are not for the same peer */
593 if (GNUNET_YES == dsc->remove_files)
594 remove_garbage (fullname);
598 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
600 &identity.public_key))
602 if (0 != memcmp (&id, &identity, sizeof (id_friend)))
604 /* HELLOs are not for the same peer */
606 if (GNUNET_YES == dsc->remove_files)
607 remove_garbage (fullname);
612 /* ok, found something valid, remember HELLO */
613 add_host_to_known_hosts (&id);
616 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
617 "Updating peer `%s' public HELLO \n",
619 update_hello (&id, r.hello);
620 GNUNET_free (r.hello);
622 if (NULL != r.friend_only_hello)
624 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625 "Updating peer `%s' friend only HELLO \n",
627 update_hello (&id, r.friend_only_hello);
628 GNUNET_free (r.friend_only_hello);
636 * Call this method periodically to scan data/hosts for new hosts.
641 cron_scan_directory_data_hosts (void *cls)
643 static unsigned int retries;
644 struct DirScanContext dsc;
649 GNUNET_DISK_directory_create (networkIdDirectory))
651 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
652 GNUNET_SCHEDULER_PRIORITY_IDLE,
653 &cron_scan_directory_data_hosts, NULL);
657 dsc.remove_files = GNUNET_YES;
658 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
659 _("Scanning directory `%s'\n"),
661 GNUNET_DISK_directory_scan (networkIdDirectory,
662 &hosts_directory_scan_callback,
664 if ( (0 == dsc.matched) &&
665 (0 == (++retries & 31)) )
666 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
667 _("Still no peers found in `%s'!\n"),
669 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
670 GNUNET_SCHEDULER_PRIORITY_IDLE,
671 &cron_scan_directory_data_hosts,
677 * Update the HELLO of a friend by merging the addresses.
679 * @param hello original hello
680 * @param friend_hello hello with additional addresses
681 * @return merged HELLO
683 static struct GNUNET_HELLO_Message *
684 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
685 const struct GNUNET_HELLO_Message *friend_hello)
687 struct GNUNET_HELLO_Message * res;
688 struct GNUNET_HELLO_Message * tmp;
689 struct GNUNET_PeerIdentity pid;
691 if (NULL != friend_hello)
693 res = GNUNET_HELLO_merge (hello,
695 GNUNET_assert (GNUNET_YES ==
696 GNUNET_HELLO_is_friend_only (res));
701 GNUNET_HELLO_get_id (hello, &pid))
706 tmp = GNUNET_HELLO_create (&pid.public_key,
710 res = GNUNET_HELLO_merge (hello, tmp);
712 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
718 * Bind a host address (hello) to a hostId.
720 * @param peer the peer for which this is a hello
721 * @param hello the verified (!) hello message
724 update_hello (const struct GNUNET_PeerIdentity *peer,
725 const struct GNUNET_HELLO_Message *hello)
728 struct HostEntry *host;
729 struct GNUNET_HELLO_Message *mrg;
730 struct GNUNET_HELLO_Message **dest;
731 struct GNUNET_TIME_Absolute delta;
734 int friend_hello_type;
736 int store_friend_hello;
740 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
741 GNUNET_assert (NULL != host);
743 friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Updating %s HELLO for `%s'\n",
746 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
750 if (GNUNET_YES == friend_hello_type)
752 dest = &host->friend_only_hello;
761 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
762 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
766 mrg = GNUNET_HELLO_merge ((*dest),
768 delta = GNUNET_HELLO_equals (mrg,
770 GNUNET_TIME_absolute_get ());
771 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
773 /* no differences, just ignore the update */
774 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775 "No change in %s HELLO for `%s'\n",
776 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
781 GNUNET_free ((*dest));
785 if ( (NULL != (host->hello)) &&
786 (GNUNET_NO == friend_hello_type) )
788 /* Update friend only hello */
789 mrg = update_friend_hello (host->hello,
790 host->friend_only_hello);
791 if (NULL != host->friend_only_hello)
792 GNUNET_free (host->friend_only_hello);
793 host->friend_only_hello = mrg;
796 if (NULL != host->hello)
797 GNUNET_assert ((GNUNET_NO ==
798 GNUNET_HELLO_is_friend_only (host->hello)));
799 if (NULL != host->friend_only_hello)
800 GNUNET_assert ((GNUNET_YES ==
801 GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
803 fn = get_host_filename (peer);
806 GNUNET_DISK_directory_create_for_file (fn)) )
808 store_hello = GNUNET_NO;
811 if (NULL != host->hello)
812 (void) GNUNET_HELLO_iterate_addresses (host->hello,
818 store_hello = GNUNET_YES;
819 size += GNUNET_HELLO_size (host->hello);
822 if (NULL != host->friend_only_hello)
823 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
827 store_friend_hello = GNUNET_NO;
830 store_friend_hello = GNUNET_YES;
831 size += GNUNET_HELLO_size (host->friend_only_hello);
834 if ( (GNUNET_NO == store_hello) &&
835 (GNUNET_NO == store_friend_hello) )
837 /* no valid addresses, don't put HELLO on disk; in fact,
838 if one exists on disk, remove it */
843 buffer = GNUNET_malloc (size);
846 if (GNUNET_YES == store_hello)
848 GNUNET_memcpy (buffer,
850 GNUNET_HELLO_size (host->hello));
851 pos += GNUNET_HELLO_size (host->hello);
853 if (GNUNET_YES == store_friend_hello)
855 GNUNET_memcpy (&buffer[pos],
856 host->friend_only_hello,
857 GNUNET_HELLO_size (host->friend_only_hello));
858 pos += GNUNET_HELLO_size (host->friend_only_hello);
860 GNUNET_assert (pos == size);
862 if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
863 GNUNET_DISK_PERM_USER_READ |
864 GNUNET_DISK_PERM_USER_WRITE |
865 GNUNET_DISK_PERM_GROUP_READ |
866 GNUNET_DISK_PERM_OTHER_READ))
867 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
869 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
870 "Stored %s %s HELLO in %s with total size %u\n",
871 (GNUNET_YES == store_friend_hello) ? "friend-only": "",
872 (GNUNET_YES == store_hello) ? "public": "",
875 GNUNET_free (buffer);
878 GNUNET_free_non_null (fn);
884 * Closure for #add_to_tc()
886 struct TransmitContext
889 * Client to transmit to
891 struct GNUNET_SERVICE_Client *client;
894 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
901 * Do transmit info about peer to given host.
903 * @param cls NULL to hit all hosts, otherwise specifies a particular target
905 * @param value information to transmit
906 * @return #GNUNET_YES (continue to iterate)
909 add_to_tc (void *cls,
910 const struct GNUNET_PeerIdentity *key,
913 struct TransmitContext *tc = cls;
914 struct HostEntry *pos = value;
915 struct InfoMessage *im;
917 struct GNUNET_MQ_Envelope *env;
921 if ( (NULL != pos->hello) &&
922 (GNUNET_NO == tc->friend_only) )
924 /* Copy public HELLO */
925 hs = GNUNET_HELLO_size (pos->hello);
926 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
927 sizeof (struct InfoMessage));
928 env = GNUNET_MQ_msg_extra (im,
930 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
931 GNUNET_memcpy (&im[1],
934 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
935 "Sending public HELLO with size %u for peer `%s'\n",
939 else if ( (NULL != pos->friend_only_hello) &&
940 (GNUNET_YES == tc->friend_only) )
942 /* Copy friend only HELLO */
943 hs = GNUNET_HELLO_size (pos->friend_only_hello);
944 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
945 sizeof (struct InfoMessage));
946 env = GNUNET_MQ_msg_extra (im,
948 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
949 GNUNET_memcpy (&im[1],
950 pos->friend_only_hello,
952 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953 "Sending friend-only HELLO with size %u for peer `%s'\n",
959 env = GNUNET_MQ_msg (im,
960 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 "Adding no HELLO for peer `%s'\n",
965 im->peer = pos->identity;
966 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
973 * @brief delete expired HELLO entries in directory
975 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
976 * @param fn filename to test to see if the HELLO expired
977 * @return #GNUNET_OK (continue iteration)
980 discard_hosts_helper (void *cls,
983 struct GNUNET_TIME_Absolute *now = cls;
984 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
985 const struct GNUNET_HELLO_Message *hello;
986 struct GNUNET_HELLO_Message *new_hello;
988 unsigned int cur_hello_size;
989 unsigned int new_hello_size;
997 GNUNET_DISK_file_size (fn,
1002 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1003 GNUNET_ERROR_TYPE_BULK,
1008 read_size = GNUNET_DISK_fn_read (fn,
1012 if ( (read_size < (int) sizeof (struct GNUNET_MessageHeader)) ||
1013 (fsize > GNUNET_MAX_MESSAGE_SIZE) )
1015 if (0 != UNLINK (fn))
1016 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1017 GNUNET_ERROR_TYPE_BULK,
1023 writebuffer = GNUNET_malloc (read_size);
1026 while (read_pos < read_size)
1028 /* Check each HELLO */
1029 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1030 cur_hello_size = GNUNET_HELLO_size (hello);
1031 if (0 == cur_hello_size)
1033 /* Invalid data, discard */
1034 if (0 != UNLINK (fn))
1035 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1036 GNUNET_ERROR_TYPE_BULK,
1039 GNUNET_free (writebuffer);
1042 new_hello = GNUNET_HELLO_iterate_addresses (hello,
1047 if (NULL != new_hello)
1048 (void) GNUNET_HELLO_iterate_addresses (hello,
1052 if ( (NULL != new_hello) && (0 < cnt) )
1054 /* Store new HELLO to write it when done */
1055 new_hello_size = GNUNET_HELLO_size (new_hello);
1056 GNUNET_memcpy (&writebuffer[write_pos],
1059 write_pos += new_hello_size;
1061 read_pos += cur_hello_size;
1062 GNUNET_free_non_null (new_hello);
1067 GNUNET_DISK_fn_write (fn,
1070 GNUNET_DISK_PERM_USER_READ |
1071 GNUNET_DISK_PERM_USER_WRITE |
1072 GNUNET_DISK_PERM_GROUP_READ |
1073 GNUNET_DISK_PERM_OTHER_READ);
1075 else if (0 != UNLINK (fn))
1076 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1077 GNUNET_ERROR_TYPE_BULK,
1080 GNUNET_free (writebuffer);
1086 * Call this method periodically to scan peerinfo/ for ancient
1092 cron_clean_data_hosts (void *cls)
1094 struct GNUNET_TIME_Absolute now;
1098 now = GNUNET_TIME_absolute_get ();
1099 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1100 _("Cleaning up directory `%s'\n"),
1101 networkIdDirectory);
1102 GNUNET_DISK_directory_scan (networkIdDirectory,
1103 &discard_hosts_helper,
1105 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1106 &cron_clean_data_hosts,
1112 * Check HELLO-message.
1114 * @param cls identification of the client
1115 * @param hello the actual message
1116 * @return #GNUNET_OK if @a hello is well-formed
1119 check_hello (void *cls,
1120 const struct GNUNET_HELLO_Message *hello)
1122 struct GNUNET_PeerIdentity pid;
1126 GNUNET_HELLO_get_id (hello,
1130 return GNUNET_SYSERR;
1137 * Handle HELLO-message.
1139 * @param cls identification of the client
1140 * @param hello the actual message
1143 handle_hello (void *cls,
1144 const struct GNUNET_HELLO_Message *hello)
1146 struct GNUNET_SERVICE_Client *client = cls;
1147 struct GNUNET_PeerIdentity pid;
1149 GNUNET_assert (GNUNET_OK ==
1150 GNUNET_HELLO_get_id (hello,
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "HELLO message received for peer `%s'\n",
1155 add_host_to_known_hosts (&pid);
1158 GNUNET_SERVICE_client_continue (client);
1163 * Handle GET-message.
1165 * @param cls identification of the client
1166 * @param lpm the actual message
1169 handle_get (void *cls,
1170 const struct ListPeerMessage *lpm)
1172 struct GNUNET_SERVICE_Client *client = cls;
1173 struct TransmitContext tcx;
1174 struct GNUNET_MessageHeader *msg;
1175 struct GNUNET_MQ_Envelope *env;
1177 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1178 "GET message received for peer `%s'\n",
1179 GNUNET_i2s (&lpm->peer));
1180 tcx.friend_only = ntohl (lpm->include_friend_only);
1181 tcx.client = client;
1182 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1186 env = GNUNET_MQ_msg (msg,
1187 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1188 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1190 GNUNET_SERVICE_client_continue (client);
1195 * Handle GET-ALL-message.
1197 * @param cls identification of the client
1198 * @param lapm the actual message
1201 handle_get_all (void *cls,
1202 const struct ListAllPeersMessage *lapm)
1204 struct GNUNET_SERVICE_Client *client = cls;
1205 struct TransmitContext tcx;
1206 struct GNUNET_MQ_Envelope *env;
1207 struct GNUNET_MessageHeader *msg;
1209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210 "GET_ALL message received\n");
1211 tcx.friend_only = ntohl (lapm->include_friend_only);
1212 tcx.client = client;
1213 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1216 env = GNUNET_MQ_msg (msg,
1217 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1218 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1220 GNUNET_SERVICE_client_continue (client);
1225 * Handle NOTIFY-message.
1227 * @param cls identification of the client
1228 * @param nm the actual message
1231 handle_notify (void *cls,
1232 const struct NotifyMessage *nm)
1234 struct GNUNET_SERVICE_Client *client = cls;
1235 struct GNUNET_MQ_Handle *mq;
1236 struct TransmitContext tcx;
1237 struct GNUNET_MQ_Envelope *env;
1238 struct GNUNET_MessageHeader *msg;
1240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1241 "NOTIFY message received\n");
1242 mq = GNUNET_SERVICE_client_get_mq (client);
1243 GNUNET_SERVICE_client_mark_monitor (client);
1244 if (ntohl (nm->include_friend_only))
1245 GNUNET_notification_context_add (notify_friend_only_list,
1248 GNUNET_notification_context_add (notify_list,
1250 tcx.friend_only = ntohl (nm->include_friend_only);
1251 tcx.client = client;
1252 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1255 env = GNUNET_MQ_msg (msg,
1256 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1257 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1259 GNUNET_SERVICE_client_continue (client);
1264 * Client connect callback
1267 * @param client server client
1268 * @param mq for @a client
1272 client_connect_cb (void *cls,
1273 struct GNUNET_SERVICE_Client *client,
1274 struct GNUNET_MQ_Handle *mq)
1283 * Client disconnect callback
1286 * @param client server client
1287 * @param app_ctx should be @a client
1290 client_disconnect_cb (void *cls,
1291 struct GNUNET_SERVICE_Client *client,
1295 GNUNET_assert (app_ctx == client);
1300 * Release memory taken by a host entry.
1303 * @param key key of the host entry
1304 * @param value the `struct HostEntry` to free
1305 * @return #GNUNET_YES (continue to iterate)
1308 free_host_entry (void *cls,
1309 const struct GNUNET_PeerIdentity *key,
1312 struct HostEntry *he = value;
1316 GNUNET_free_non_null (he->hello);
1317 GNUNET_free_non_null (he->friend_only_hello);
1324 * Clean up our state. Called during shutdown.
1329 shutdown_task (void *cls)
1332 GNUNET_notification_context_destroy (notify_list);
1334 GNUNET_notification_context_destroy (notify_friend_only_list);
1335 notify_friend_only_list = NULL;
1337 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1340 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1343 GNUNET_STATISTICS_destroy (stats,
1347 if (NULL != cron_clean)
1349 GNUNET_SCHEDULER_cancel (cron_clean);
1352 if (NULL != cron_scan)
1354 GNUNET_SCHEDULER_cancel (cron_scan);
1357 if (NULL != networkIdDirectory)
1359 GNUNET_free (networkIdDirectory);
1360 networkIdDirectory = NULL;
1366 * Start up peerinfo service.
1368 * @param cls closure
1369 * @param cfg configuration to use
1370 * @param service the initialized service
1374 const struct GNUNET_CONFIGURATION_Handle *cfg,
1375 struct GNUNET_SERVICE_Handle *service)
1379 struct DirScanContext dsc;
1386 = GNUNET_CONTAINER_multipeermap_create (1024,
1389 = GNUNET_STATISTICS_create ("peerinfo",
1392 = GNUNET_notification_context_create (0);
1393 notify_friend_only_list
1394 = GNUNET_notification_context_create (0);
1395 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1399 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1401 "USE_INCLUDED_HELLOS");
1402 if (GNUNET_SYSERR == use_included)
1403 use_included = GNUNET_NO;
1404 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1406 if (GNUNET_YES != noio)
1408 GNUNET_assert (GNUNET_OK ==
1409 GNUNET_CONFIGURATION_get_value_filename (cfg,
1412 &networkIdDirectory));
1414 GNUNET_DISK_directory_create (networkIdDirectory))
1416 GNUNET_SCHEDULER_shutdown ();
1421 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1422 &cron_scan_directory_data_hosts,
1426 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1427 &cron_clean_data_hosts,
1429 if (GNUNET_YES == use_included)
1431 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1432 GNUNET_asprintf (&peerdir,
1437 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1438 _("Importing HELLOs from `%s'\n"),
1441 dsc.remove_files = GNUNET_NO;
1443 GNUNET_DISK_directory_scan (peerdir,
1444 &hosts_directory_scan_callback,
1446 GNUNET_free (peerdir);
1450 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1451 _("Skipping import of included HELLOs\n"));
1458 * Define "main" method using service macro.
1462 GNUNET_SERVICE_OPTION_NONE,
1465 &client_disconnect_cb,
1467 GNUNET_MQ_hd_var_size (hello,
1468 GNUNET_MESSAGE_TYPE_HELLO,
1469 struct GNUNET_HELLO_Message,
1471 GNUNET_MQ_hd_fixed_size (get,
1472 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1473 struct ListPeerMessage,
1475 GNUNET_MQ_hd_fixed_size (get_all,
1476 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1477 struct ListAllPeersMessage,
1479 GNUNET_MQ_hd_fixed_size (notify,
1480 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1481 struct NotifyMessage,
1483 GNUNET_MQ_handler_end ());
1486 /* end of gnunet-service-peerinfo.c */