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/>.
20 * @file peerinfo/gnunet-service-peerinfo.c
21 * @brief maintains list of known peers
23 * Code to maintain the list of currently known hosts (in memory
24 * structure of data/hosts/).
26 * @author Christian Grothoff
30 #include "gnunet_util_lib.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_statistics_service.h"
37 * How often do we scan the HOST_DIR for new entries?
39 #define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
42 * How often do we discard old entries in data/hosts/?
44 #define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
48 * In-memory cache of known hosts.
54 * Identity of the peer.
56 struct GNUNET_PeerIdentity identity;
59 * Hello for the peer (can be NULL)
61 struct GNUNET_HELLO_Message *hello;
64 * Friend only hello for the peer (can be NULL)
66 struct GNUNET_HELLO_Message *friend_only_hello;
72 * Result of reading a file
74 struct ReadHostFileContext
77 * Hello for the peer (can be NULL)
79 struct GNUNET_HELLO_Message *hello;
82 * Friend only hello for the peer (can be NULL)
84 struct GNUNET_HELLO_Message *friend_only_hello;
89 * The in-memory list of known hosts, mapping of
90 * host IDs to 'struct HostEntry*' values.
92 static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
95 * Clients to immediately notify about all changes.
97 static struct GNUNET_NotificationContext *notify_list;
100 * Clients to immediately notify about all changes,
101 * even for friend-only HELLOs.
103 static struct GNUNET_NotificationContext *notify_friend_only_list;
106 * Directory where the hellos are stored in (peerinfo/)
108 static char *networkIdDirectory;
111 * Handle for reporting statistics.
113 static struct GNUNET_STATISTICS_Handle *stats;
116 * Handle for task to run #cron_clean_data_hosts()
118 static struct GNUNET_SCHEDULER_Task *cron_clean;
121 * Handle for task to run #cron_scan_directory_data_hosts()
123 static struct GNUNET_SCHEDULER_Task *cron_scan;
127 * Notify all clients in the notify list about the
128 * given host entry changing.
130 * @param he entry of the host for which we generate a notification
131 * @param include_friend_only create public of friend-only message
132 * @return generated notification message
134 static struct InfoMessage *
135 make_info_message (const struct HostEntry *he,
136 int include_friend_only)
138 struct InfoMessage *im;
139 struct GNUNET_HELLO_Message *src;
142 if (GNUNET_YES == include_friend_only)
143 src = he->friend_only_hello;
146 hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src);
147 im = GNUNET_malloc (sizeof (struct InfoMessage) + hs);
148 im->header.size = htons (hs + sizeof (struct InfoMessage));
149 im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
150 im->peer = he->identity;
151 GNUNET_memcpy (&im[1],
159 * Address iterator that causes expired entries to be discarded.
161 * @param cls pointer to the current time
162 * @param address the address
163 * @param expiration expiration time for the address
164 * @return #GNUNET_NO if expiration smaller than the current time
167 discard_expired (void *cls,
168 const struct GNUNET_HELLO_Address *address,
169 struct GNUNET_TIME_Absolute expiration)
171 const struct GNUNET_TIME_Absolute *now = cls;
173 if (now->abs_value_us > expiration.abs_value_us)
175 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
176 _("Removing expired address of transport `%s'\n"),
177 address->transport_name);
185 * Address iterator that counts the remaining addresses.
187 * @param cls pointer to the counter
188 * @param address the address
189 * @param expiration expiration time for the address
190 * @return #GNUNET_OK (always)
193 count_addresses (void *cls,
194 const struct GNUNET_HELLO_Address *address,
195 struct GNUNET_TIME_Absolute expiration)
197 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 < 0) ||
308 (((size_t) size_total) < sizeof (struct GNUNET_MessageHeader)) )
310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311 _("Failed to parse HELLO in file `%s': %s\n"),
313 "File has invalid size");
314 if ( (GNUNET_YES == unlink_garbage) &&
315 (0 != UNLINK (fn)) &&
317 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
324 while (read_pos < (size_t) size_total)
326 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
327 size_hello = GNUNET_HELLO_size (hello);
328 if ( (0 == size_hello) ||
329 (((size_t) size_total) - read_pos < size_hello) )
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332 _("Failed to parse HELLO in file `%s'\n"),
336 if ((GNUNET_YES == unlink_garbage) &&
337 (0 != UNLINK (fn)) &&
339 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
345 if ( (GNUNET_YES == unlink_garbage) &&
346 (0 != TRUNCATE (fn, read_pos)) &&
348 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
355 now = GNUNET_TIME_absolute_get ();
356 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
360 if (NULL == hello_clean)
362 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
363 _("Failed to parse HELLO in file `%s'\n"),
365 if ((GNUNET_YES == unlink_garbage) &&
366 (0 != UNLINK (fn)) &&
368 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
374 (void) GNUNET_HELLO_iterate_addresses (hello_clean,
381 GNUNET_free (hello_clean);
385 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
387 if (NULL == r->hello)
388 r->hello = hello_clean;
392 GNUNET_free (r->hello);
393 r->hello = hello_clean;
398 if (NULL == r->friend_only_hello)
399 r->friend_only_hello = hello_clean;
403 GNUNET_free (r->friend_only_hello);
404 r->friend_only_hello = hello_clean;
407 read_pos += size_hello;
412 /* no addresses left, remove from disk */
413 if ( (GNUNET_YES == unlink_garbage) &&
415 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420 "Found `%s' and `%s' HELLO message in file\n",
421 (NULL != r->hello) ? "public" : "NON-public",
422 (NULL != r->friend_only_hello) ? "friend only" : "NO friend only");
427 * Add a host to the list and notify clients about this event
429 * @param identity the identity of the host
430 * @return the HostEntry
432 static struct HostEntry *
433 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
435 struct HostEntry *entry;
436 struct ReadHostFileContext r;
439 entry = GNUNET_CONTAINER_multipeermap_get (hostmap,
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Adding new peer `%s'\n",
445 GNUNET_i2s (identity));
446 GNUNET_STATISTICS_update (stats,
447 gettext_noop ("# peers known"),
450 entry = GNUNET_new (struct HostEntry);
451 entry->identity = *identity;
452 GNUNET_assert (GNUNET_OK ==
453 GNUNET_CONTAINER_multipeermap_put (hostmap,
456 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
458 fn = get_host_filename (identity);
465 update_hello (identity,
467 if (NULL != r.friend_only_hello)
468 update_hello (identity,
469 r.friend_only_hello);
470 GNUNET_free_non_null (r.hello);
471 GNUNET_free_non_null (r.friend_only_hello);
480 * Remove a file that should not be there. LOG
481 * success or failure.
483 * @param fullname name of the file to remove
486 remove_garbage (const char *fullname)
488 if (0 == UNLINK (fullname))
489 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
490 _("File `%s' in directory `%s' does not match naming convention. Removed.\n"),
494 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
501 * Closure for #hosts_directory_scan_callback().
503 struct DirScanContext
506 * #GNUNET_YES if we should remove files that are broken,
507 * #GNUNET_NO if the directory we are iterating over should
508 * be treated as read-only by us.
513 * Counter for the number of (valid) entries found, incremented
514 * by one for each match.
516 unsigned int matched;
521 * Function that is called on each HELLO file in a particular directory.
522 * Try to parse the file and add the HELLO to our list.
524 * @param cls pointer to 'unsigned int' to increment for each file, or NULL
525 * if the file is from a read-only, read-once resource directory
526 * @param fullname name of the file to parse
527 * @return #GNUNET_OK (continue iteration)
530 hosts_directory_scan_callback (void *cls,
531 const char *fullname)
533 struct DirScanContext *dsc = cls;
534 struct GNUNET_PeerIdentity identity;
535 struct ReadHostFileContext r;
536 const char *filename;
537 struct GNUNET_PeerIdentity id_public;
538 struct GNUNET_PeerIdentity id_friend;
539 struct GNUNET_PeerIdentity id;
541 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
542 return GNUNET_OK; /* ignore non-files */
544 filename = strrchr (fullname,
546 if ( (NULL == filename) ||
547 (1 > strlen (filename)) )
552 read_host_file (fullname,
555 if ( (NULL == r.hello) &&
556 (NULL == r.friend_only_hello))
558 if (NULL != r.friend_only_hello)
561 GNUNET_HELLO_get_id (r.friend_only_hello,
564 if (GNUNET_YES == dsc->remove_files)
565 remove_garbage (fullname);
573 GNUNET_HELLO_get_id (r.hello,
576 if (GNUNET_YES == dsc->remove_files)
577 remove_garbage (fullname);
583 if ( (NULL != r.hello) &&
584 (NULL != r.friend_only_hello) &&
585 (0 != memcmp (&id_friend,
587 sizeof (id_friend))) )
589 /* HELLOs are not for the same peer */
591 if (GNUNET_YES == dsc->remove_files)
592 remove_garbage (fullname);
596 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
598 &identity.public_key))
600 if (0 != memcmp (&id, &identity, sizeof (id_friend)))
602 /* HELLOs are not for the same peer */
604 if (GNUNET_YES == dsc->remove_files)
605 remove_garbage (fullname);
610 /* ok, found something valid, remember HELLO */
611 add_host_to_known_hosts (&id);
614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
615 "Updating peer `%s' public HELLO \n",
617 update_hello (&id, r.hello);
618 GNUNET_free (r.hello);
620 if (NULL != r.friend_only_hello)
622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623 "Updating peer `%s' friend only HELLO \n",
625 update_hello (&id, r.friend_only_hello);
626 GNUNET_free (r.friend_only_hello);
634 * Call this method periodically to scan data/hosts for new hosts.
639 cron_scan_directory_data_hosts (void *cls)
641 static unsigned int retries;
642 struct DirScanContext dsc;
647 GNUNET_DISK_directory_create (networkIdDirectory))
649 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
650 GNUNET_SCHEDULER_PRIORITY_IDLE,
651 &cron_scan_directory_data_hosts, NULL);
655 dsc.remove_files = GNUNET_YES;
656 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
657 _("Scanning directory `%s'\n"),
659 GNUNET_DISK_directory_scan (networkIdDirectory,
660 &hosts_directory_scan_callback,
662 if ( (0 == dsc.matched) &&
663 (0 == (++retries & 31)) )
664 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
665 _("Still no peers found in `%s'!\n"),
667 cron_scan = GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
668 GNUNET_SCHEDULER_PRIORITY_IDLE,
669 &cron_scan_directory_data_hosts,
675 * Update the HELLO of a friend by merging the addresses.
677 * @param hello original hello
678 * @param friend_hello hello with additional addresses
679 * @return merged HELLO
681 static struct GNUNET_HELLO_Message *
682 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
683 const struct GNUNET_HELLO_Message *friend_hello)
685 struct GNUNET_HELLO_Message * res;
686 struct GNUNET_HELLO_Message * tmp;
687 struct GNUNET_PeerIdentity pid;
689 if (NULL != friend_hello)
691 res = GNUNET_HELLO_merge (hello,
693 GNUNET_assert (GNUNET_YES ==
694 GNUNET_HELLO_is_friend_only (res));
699 GNUNET_HELLO_get_id (hello, &pid))
704 tmp = GNUNET_HELLO_create (&pid.public_key,
708 res = GNUNET_HELLO_merge (hello, tmp);
710 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
716 * Bind a host address (hello) to a hostId.
718 * @param peer the peer for which this is a hello
719 * @param hello the verified (!) hello message
722 update_hello (const struct GNUNET_PeerIdentity *peer,
723 const struct GNUNET_HELLO_Message *hello)
726 struct HostEntry *host;
727 struct GNUNET_HELLO_Message *mrg;
728 struct GNUNET_HELLO_Message **dest;
729 struct GNUNET_TIME_Absolute delta;
732 int friend_hello_type;
734 int store_friend_hello;
738 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
739 GNUNET_assert (NULL != host);
741 friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743 "Updating %s HELLO for `%s'\n",
744 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
748 if (GNUNET_YES == friend_hello_type)
750 dest = &host->friend_only_hello;
759 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
760 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
764 mrg = GNUNET_HELLO_merge ((*dest),
766 delta = GNUNET_HELLO_equals (mrg,
768 GNUNET_TIME_absolute_get ());
769 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
771 /* no differences, just ignore the update */
772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
773 "No change in %s HELLO for `%s'\n",
774 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
779 GNUNET_free ((*dest));
783 if ( (NULL != (host->hello)) &&
784 (GNUNET_NO == friend_hello_type) )
786 /* Update friend only hello */
787 mrg = update_friend_hello (host->hello,
788 host->friend_only_hello);
789 if (NULL != host->friend_only_hello)
790 GNUNET_free (host->friend_only_hello);
791 host->friend_only_hello = mrg;
794 if (NULL != host->hello)
795 GNUNET_assert ((GNUNET_NO ==
796 GNUNET_HELLO_is_friend_only (host->hello)));
797 if (NULL != host->friend_only_hello)
798 GNUNET_assert ((GNUNET_YES ==
799 GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
801 fn = get_host_filename (peer);
804 GNUNET_DISK_directory_create_for_file (fn)) )
806 store_hello = GNUNET_NO;
809 if (NULL != host->hello)
810 (void) GNUNET_HELLO_iterate_addresses (host->hello,
816 store_hello = GNUNET_YES;
817 size += GNUNET_HELLO_size (host->hello);
820 if (NULL != host->friend_only_hello)
821 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
825 store_friend_hello = GNUNET_NO;
828 store_friend_hello = GNUNET_YES;
829 size += GNUNET_HELLO_size (host->friend_only_hello);
832 if ( (GNUNET_NO == store_hello) &&
833 (GNUNET_NO == store_friend_hello) )
835 /* no valid addresses, don't put HELLO on disk; in fact,
836 if one exists on disk, remove it */
841 buffer = GNUNET_malloc (size);
844 if (GNUNET_YES == store_hello)
846 GNUNET_memcpy (buffer,
848 GNUNET_HELLO_size (host->hello));
849 pos += GNUNET_HELLO_size (host->hello);
851 if (GNUNET_YES == store_friend_hello)
853 GNUNET_memcpy (&buffer[pos],
854 host->friend_only_hello,
855 GNUNET_HELLO_size (host->friend_only_hello));
856 pos += GNUNET_HELLO_size (host->friend_only_hello);
858 GNUNET_assert (pos == size);
860 if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, buffer, size,
861 GNUNET_DISK_PERM_USER_READ |
862 GNUNET_DISK_PERM_USER_WRITE |
863 GNUNET_DISK_PERM_GROUP_READ |
864 GNUNET_DISK_PERM_OTHER_READ))
865 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
867 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
868 "Stored %s %s HELLO in %s with total size %u\n",
869 (GNUNET_YES == store_friend_hello) ? "friend-only": "",
870 (GNUNET_YES == store_hello) ? "public": "",
873 GNUNET_free (buffer);
876 GNUNET_free_non_null (fn);
882 * Closure for #add_to_tc()
884 struct TransmitContext
887 * Client to transmit to
889 struct GNUNET_SERVICE_Client *client;
892 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
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 struct GNUNET_MQ_Envelope *env;
919 if ( (NULL != pos->hello) &&
920 (GNUNET_NO == tc->friend_only) )
922 /* Copy public HELLO */
923 hs = GNUNET_HELLO_size (pos->hello);
924 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
925 sizeof (struct InfoMessage));
926 env = GNUNET_MQ_msg_extra (im,
928 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
929 GNUNET_memcpy (&im[1],
932 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
933 "Sending public HELLO with size %u for peer `%s'\n",
937 else if ( (NULL != pos->friend_only_hello) &&
938 (GNUNET_YES == tc->friend_only) )
940 /* Copy friend only HELLO */
941 hs = GNUNET_HELLO_size (pos->friend_only_hello);
942 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE -
943 sizeof (struct InfoMessage));
944 env = GNUNET_MQ_msg_extra (im,
946 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
947 GNUNET_memcpy (&im[1],
948 pos->friend_only_hello,
950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
951 "Sending friend-only HELLO with size %u for peer `%s'\n",
957 env = GNUNET_MQ_msg (im,
958 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
960 "Adding no HELLO for peer `%s'\n",
963 im->peer = pos->identity;
964 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client),
971 * @brief delete expired HELLO entries in directory
973 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
974 * @param fn filename to test to see if the HELLO expired
975 * @return #GNUNET_OK (continue iteration)
978 discard_hosts_helper (void *cls,
981 struct GNUNET_TIME_Absolute *now = cls;
982 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
983 const struct GNUNET_HELLO_Message *hello;
984 struct GNUNET_HELLO_Message *new_hello;
986 unsigned int cur_hello_size;
987 unsigned int new_hello_size;
995 GNUNET_DISK_file_size (fn,
1000 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1001 GNUNET_ERROR_TYPE_BULK,
1006 read_size = GNUNET_DISK_fn_read (fn,
1010 if ( (read_size < (int) sizeof (struct GNUNET_MessageHeader)) ||
1011 (fsize > GNUNET_MAX_MESSAGE_SIZE) )
1013 if (0 != UNLINK (fn))
1014 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1015 GNUNET_ERROR_TYPE_BULK,
1021 writebuffer = GNUNET_malloc (read_size);
1024 while (read_pos < read_size)
1026 /* Check each HELLO */
1027 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
1028 cur_hello_size = GNUNET_HELLO_size (hello);
1029 if (0 == cur_hello_size)
1031 /* Invalid data, discard */
1032 if (0 != UNLINK (fn))
1033 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1034 GNUNET_ERROR_TYPE_BULK,
1037 GNUNET_free (writebuffer);
1040 new_hello = GNUNET_HELLO_iterate_addresses (hello,
1045 if (NULL != new_hello)
1046 (void) GNUNET_HELLO_iterate_addresses (hello,
1050 if ( (NULL != new_hello) && (0 < cnt) )
1052 /* Store new HELLO to write it when done */
1053 new_hello_size = GNUNET_HELLO_size (new_hello);
1054 GNUNET_memcpy (&writebuffer[write_pos],
1057 write_pos += new_hello_size;
1059 read_pos += cur_hello_size;
1060 GNUNET_free_non_null (new_hello);
1065 GNUNET_DISK_fn_write (fn,
1068 GNUNET_DISK_PERM_USER_READ |
1069 GNUNET_DISK_PERM_USER_WRITE |
1070 GNUNET_DISK_PERM_GROUP_READ |
1071 GNUNET_DISK_PERM_OTHER_READ);
1073 else if (0 != UNLINK (fn))
1074 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
1075 GNUNET_ERROR_TYPE_BULK,
1078 GNUNET_free (writebuffer);
1084 * Call this method periodically to scan peerinfo/ for ancient
1090 cron_clean_data_hosts (void *cls)
1092 struct GNUNET_TIME_Absolute now;
1096 now = GNUNET_TIME_absolute_get ();
1097 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1098 _("Cleaning up directory `%s'\n"),
1099 networkIdDirectory);
1100 GNUNET_DISK_directory_scan (networkIdDirectory,
1101 &discard_hosts_helper,
1103 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1104 &cron_clean_data_hosts,
1110 * Check HELLO-message.
1112 * @param cls identification of the client
1113 * @param hello the actual message
1114 * @return #GNUNET_OK if @a hello is well-formed
1117 check_hello (void *cls,
1118 const struct GNUNET_HELLO_Message *hello)
1120 struct GNUNET_PeerIdentity pid;
1124 GNUNET_HELLO_get_id (hello,
1128 return GNUNET_SYSERR;
1135 * Handle HELLO-message.
1137 * @param cls identification of the client
1138 * @param hello the actual message
1141 handle_hello (void *cls,
1142 const struct GNUNET_HELLO_Message *hello)
1144 struct GNUNET_SERVICE_Client *client = cls;
1145 struct GNUNET_PeerIdentity pid;
1147 GNUNET_assert (GNUNET_OK ==
1148 GNUNET_HELLO_get_id (hello,
1150 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1151 "HELLO message received for peer `%s'\n",
1153 add_host_to_known_hosts (&pid);
1156 GNUNET_SERVICE_client_continue (client);
1161 * Handle GET-message.
1163 * @param cls identification of the client
1164 * @param lpm the actual message
1167 handle_get (void *cls,
1168 const struct ListPeerMessage *lpm)
1170 struct GNUNET_SERVICE_Client *client = cls;
1171 struct TransmitContext tcx;
1172 struct GNUNET_MessageHeader *msg;
1173 struct GNUNET_MQ_Envelope *env;
1175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176 "GET message received for peer `%s'\n",
1177 GNUNET_i2s (&lpm->peer));
1178 tcx.friend_only = ntohl (lpm->include_friend_only);
1179 tcx.client = client;
1180 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1184 env = GNUNET_MQ_msg (msg,
1185 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1186 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1188 GNUNET_SERVICE_client_continue (client);
1193 * Handle GET-ALL-message.
1195 * @param cls identification of the client
1196 * @param lapm the actual message
1199 handle_get_all (void *cls,
1200 const struct ListAllPeersMessage *lapm)
1202 struct GNUNET_SERVICE_Client *client = cls;
1203 struct TransmitContext tcx;
1204 struct GNUNET_MQ_Envelope *env;
1205 struct GNUNET_MessageHeader *msg;
1207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208 "GET_ALL message received\n");
1209 tcx.friend_only = ntohl (lapm->include_friend_only);
1210 tcx.client = client;
1211 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1214 env = GNUNET_MQ_msg (msg,
1215 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1216 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1218 GNUNET_SERVICE_client_continue (client);
1223 * Handle NOTIFY-message.
1225 * @param cls identification of the client
1226 * @param nm the actual message
1229 handle_notify (void *cls,
1230 const struct NotifyMessage *nm)
1232 struct GNUNET_SERVICE_Client *client = cls;
1233 struct GNUNET_MQ_Handle *mq;
1234 struct TransmitContext tcx;
1235 struct GNUNET_MQ_Envelope *env;
1236 struct GNUNET_MessageHeader *msg;
1238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1239 "NOTIFY message received\n");
1240 mq = GNUNET_SERVICE_client_get_mq (client);
1241 GNUNET_SERVICE_client_mark_monitor (client);
1242 if (ntohl (nm->include_friend_only))
1243 GNUNET_notification_context_add (notify_friend_only_list,
1246 GNUNET_notification_context_add (notify_list,
1248 tcx.friend_only = ntohl (nm->include_friend_only);
1249 tcx.client = client;
1250 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1253 env = GNUNET_MQ_msg (msg,
1254 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1255 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1257 GNUNET_SERVICE_client_continue (client);
1262 * Client connect callback
1265 * @param client server client
1266 * @param mq for @a client
1270 client_connect_cb (void *cls,
1271 struct GNUNET_SERVICE_Client *client,
1272 struct GNUNET_MQ_Handle *mq)
1281 * Client disconnect callback
1284 * @param client server client
1285 * @param app_ctx should be @a client
1288 client_disconnect_cb (void *cls,
1289 struct GNUNET_SERVICE_Client *client,
1293 GNUNET_assert (app_ctx == client);
1298 * Release memory taken by a host entry.
1301 * @param key key of the host entry
1302 * @param value the `struct HostEntry` to free
1303 * @return #GNUNET_YES (continue to iterate)
1306 free_host_entry (void *cls,
1307 const struct GNUNET_PeerIdentity *key,
1310 struct HostEntry *he = value;
1314 GNUNET_free_non_null (he->hello);
1315 GNUNET_free_non_null (he->friend_only_hello);
1322 * Clean up our state. Called during shutdown.
1327 shutdown_task (void *cls)
1330 GNUNET_notification_context_destroy (notify_list);
1332 GNUNET_notification_context_destroy (notify_friend_only_list);
1333 notify_friend_only_list = NULL;
1335 GNUNET_CONTAINER_multipeermap_iterate (hostmap,
1338 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1341 GNUNET_STATISTICS_destroy (stats,
1345 if (NULL != cron_clean)
1347 GNUNET_SCHEDULER_cancel (cron_clean);
1350 if (NULL != cron_scan)
1352 GNUNET_SCHEDULER_cancel (cron_scan);
1355 if (NULL != networkIdDirectory)
1357 GNUNET_free (networkIdDirectory);
1358 networkIdDirectory = NULL;
1364 * Start up peerinfo service.
1366 * @param cls closure
1367 * @param cfg configuration to use
1368 * @param service the initialized service
1372 const struct GNUNET_CONFIGURATION_Handle *cfg,
1373 struct GNUNET_SERVICE_Handle *service)
1377 struct DirScanContext dsc;
1384 = GNUNET_CONTAINER_multipeermap_create (1024,
1387 = GNUNET_STATISTICS_create ("peerinfo",
1390 = GNUNET_notification_context_create (0);
1391 notify_friend_only_list
1392 = GNUNET_notification_context_create (0);
1393 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1397 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1399 "USE_INCLUDED_HELLOS");
1400 if (GNUNET_SYSERR == use_included)
1401 use_included = GNUNET_NO;
1402 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1404 if (GNUNET_YES != noio)
1406 GNUNET_assert (GNUNET_OK ==
1407 GNUNET_CONFIGURATION_get_value_filename (cfg,
1410 &networkIdDirectory));
1412 GNUNET_DISK_directory_create (networkIdDirectory))
1414 GNUNET_SCHEDULER_shutdown ();
1419 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1420 &cron_scan_directory_data_hosts,
1424 = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1425 &cron_clean_data_hosts,
1427 if (GNUNET_YES == use_included)
1429 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1430 GNUNET_asprintf (&peerdir,
1435 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1436 _("Importing HELLOs from `%s'\n"),
1439 dsc.remove_files = GNUNET_NO;
1441 GNUNET_DISK_directory_scan (peerdir,
1442 &hosts_directory_scan_callback,
1444 GNUNET_free (peerdir);
1448 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1449 _("Skipping import of included HELLOs\n"));
1456 * Define "main" method using service macro.
1460 GNUNET_SERVICE_OPTION_NONE,
1463 &client_disconnect_cb,
1465 GNUNET_MQ_hd_var_size (hello,
1466 GNUNET_MESSAGE_TYPE_HELLO,
1467 struct GNUNET_HELLO_Message,
1469 GNUNET_MQ_hd_fixed_size (get,
1470 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1471 struct ListPeerMessage,
1473 GNUNET_MQ_hd_fixed_size (get_all,
1474 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1475 struct ListAllPeersMessage,
1477 GNUNET_MQ_hd_fixed_size (notify,
1478 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1479 struct NotifyMessage,
1481 GNUNET_MQ_handler_end ());
1484 /* end of gnunet-service-peerinfo.c */