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 \
42 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
45 * How often do we discard old entries in data/hosts/?
47 #define DATA_HOST_CLEAN_FREQ \
48 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
52 * In-memory cache of known hosts.
57 * Identity of the peer.
59 struct GNUNET_PeerIdentity identity;
62 * Hello for the peer (can be NULL)
64 struct GNUNET_HELLO_Message *hello;
67 * Friend only hello for the peer (can be NULL)
69 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, int include_friend_only)
139 struct InfoMessage *im;
140 struct GNUNET_HELLO_Message *src;
143 if (GNUNET_YES == include_friend_only)
144 src = he->friend_only_hello;
147 hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src);
148 im = GNUNET_malloc (sizeof(struct InfoMessage) + hs);
149 im->header.size = htons (hs + sizeof(struct InfoMessage));
150 im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
151 im->peer = he->identity;
152 GNUNET_memcpy (&im[1], src, hs);
158 * Address iterator that causes expired entries to be discarded.
160 * @param cls pointer to the current time
161 * @param address the address
162 * @param expiration expiration time for the address
163 * @return #GNUNET_NO if expiration smaller than the current time
166 discard_expired (void *cls,
167 const struct GNUNET_HELLO_Address *address,
168 struct GNUNET_TIME_Absolute expiration)
170 const struct GNUNET_TIME_Absolute *now = cls;
172 if (now->abs_value_us > expiration.abs_value_us)
174 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
175 _ ("Removing expired address of transport `%s'\n"),
176 address->transport_name);
184 * Address iterator that counts the remaining addresses.
186 * @param cls pointer to the counter
187 * @param address the address
188 * @param expiration expiration time for the address
189 * @return #GNUNET_OK (always)
192 count_addresses (void *cls,
193 const struct GNUNET_HELLO_Address *address,
194 struct GNUNET_TIME_Absolute expiration)
196 unsigned int *cnt = cls;
206 * Get the filename under which we would store the GNUNET_HELLO_Message
207 * for the given host and protocol.
209 * @param id peer for which we need the filename for the HELLO
210 * @return filename of the form DIRECTORY/HOSTID
213 get_host_filename (const struct GNUNET_PeerIdentity *id)
217 if (NULL == networkIdDirectory)
219 GNUNET_asprintf (&fn,
223 GNUNET_i2s_full (id));
229 * Broadcast information about the given entry to all
232 * @param entry entry to broadcast about
235 notify_all (struct HostEntry *entry)
237 struct InfoMessage *msg_pub;
238 struct InfoMessage *msg_friend;
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
241 "Notifying all clients about peer `%s'\n",
242 GNUNET_i2s (&entry->identity));
243 msg_pub = make_info_message (entry, GNUNET_NO);
244 GNUNET_notification_context_broadcast (notify_list,
247 GNUNET_free (msg_pub);
248 msg_friend = make_info_message (entry, GNUNET_YES);
249 GNUNET_notification_context_broadcast (notify_friend_only_list,
252 GNUNET_free (msg_friend);
257 * Bind a host address (hello) to a hostId.
259 * @param peer the peer for which this is a hello
260 * @param hello the verified (!) hello message
263 update_hello (const struct GNUNET_PeerIdentity *peer,
264 const struct GNUNET_HELLO_Message *hello);
268 * Try to read the HELLOs in the given filename and discard expired
269 * addresses. Removes the file if one the HELLO is malformed. If all
270 * addresses are expired, the HELLO is also removed (but the HELLO
271 * with the public key is still returned if it was found and valid).
272 * The file can contain multiple HELLO messages.
274 * @param fn name of the file
275 * @param unlink_garbage if #GNUNET_YES, try to remove useless files
276 * @param r ReadHostFileContext to store the resutl
279 read_host_file (const char *fn,
281 struct ReadHostFileContext *r)
283 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
285 struct GNUNET_TIME_Absolute now;
287 const struct GNUNET_HELLO_Message *hello;
288 struct GNUNET_HELLO_Message *hello_clean;
292 r->friend_only_hello = NULL;
295 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
297 size_total = GNUNET_DISK_fn_read (fn, buffer, sizeof(buffer));
298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299 "Read %d bytes from `%s'\n",
302 if ((size_total < 0) ||
303 (((size_t) size_total) < sizeof(struct GNUNET_MessageHeader)))
305 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
306 _ ("Failed to parse HELLO in file `%s': %s\n"),
308 "File has invalid size");
309 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
311 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
316 while (read_pos < (size_t) size_total)
318 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
319 size_hello = GNUNET_HELLO_size (hello);
320 if ((0 == size_hello) || (((size_t) size_total) - read_pos < size_hello))
322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323 _ ("Failed to parse HELLO in file `%s'\n"),
327 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
329 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
333 if ((GNUNET_YES == unlink_garbage) && (0 != truncate (fn, read_pos)) &&
335 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "truncate", fn);
340 now = GNUNET_TIME_absolute_get ();
341 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
345 if (NULL == hello_clean)
347 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
348 _ ("Failed to parse HELLO in file `%s'\n"),
350 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
352 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
356 (void) GNUNET_HELLO_iterate_addresses (hello_clean,
363 GNUNET_free (hello_clean);
367 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
369 if (NULL == r->hello)
370 r->hello = hello_clean;
374 GNUNET_free (r->hello);
375 r->hello = hello_clean;
380 if (NULL == r->friend_only_hello)
381 r->friend_only_hello = hello_clean;
385 GNUNET_free (r->friend_only_hello);
386 r->friend_only_hello = hello_clean;
389 read_pos += size_hello;
394 /* no addresses left, remove from disk */
395 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)))
396 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
398 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
399 "Found `%s' and `%s' HELLO message in file\n",
400 (NULL != r->hello) ? "public" : "NON-public",
401 (NULL != r->friend_only_hello) ? "friend only"
407 * Add a host to the list and notify clients about this event
409 * @param identity the identity of the host
410 * @return the HostEntry
412 static struct HostEntry *
413 add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
415 struct HostEntry *entry;
416 struct ReadHostFileContext r;
419 entry = GNUNET_CONTAINER_multipeermap_get (hostmap, identity);
422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
423 "Adding new peer `%s'\n",
424 GNUNET_i2s (identity));
425 GNUNET_STATISTICS_update (stats,
426 gettext_noop ("# peers known"),
429 entry = GNUNET_new (struct HostEntry);
430 entry->identity = *identity;
431 GNUNET_assert (GNUNET_OK ==
432 GNUNET_CONTAINER_multipeermap_put (
436 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
438 fn = get_host_filename (identity);
441 read_host_file (fn, GNUNET_YES, &r);
443 update_hello (identity, r.hello);
444 if (NULL != r.friend_only_hello)
445 update_hello (identity, r.friend_only_hello);
446 GNUNET_free_non_null (r.hello);
447 GNUNET_free_non_null (r.friend_only_hello);
456 * Remove a file that should not be there. LOG
457 * success or failure.
459 * @param fullname name of the file to remove
462 remove_garbage (const char *fullname)
464 if (0 == unlink (fullname))
466 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
468 "File `%s' in directory `%s' does not match naming convention. Removed.\n"),
472 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
479 * Closure for #hosts_directory_scan_callback().
481 struct DirScanContext
484 * #GNUNET_YES if we should remove files that are broken,
485 * #GNUNET_NO if the directory we are iterating over should
486 * be treated as read-only by us.
491 * Counter for the number of (valid) entries found, incremented
492 * by one for each match.
494 unsigned int matched;
499 * Function that is called on each HELLO file in a particular directory.
500 * Try to parse the file and add the HELLO to our list.
502 * @param cls pointer to 'unsigned int' to increment for each file, or NULL
503 * if the file is from a read-only, read-once resource directory
504 * @param fullname name of the file to parse
505 * @return #GNUNET_OK (continue iteration)
508 hosts_directory_scan_callback (void *cls, const char *fullname)
510 struct DirScanContext *dsc = cls;
511 struct GNUNET_PeerIdentity identity;
512 struct ReadHostFileContext r;
513 const char *filename;
514 struct GNUNET_PeerIdentity id_public;
515 struct GNUNET_PeerIdentity id_friend;
516 struct GNUNET_PeerIdentity id;
518 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
519 return GNUNET_OK; /* ignore non-files */
521 filename = strrchr (fullname, DIR_SEPARATOR);
522 if ((NULL == filename) || (1 > strlen (filename)))
527 read_host_file (fullname, dsc->remove_files, &r);
528 if ((NULL == r.hello) && (NULL == r.friend_only_hello))
530 if (NULL != r.friend_only_hello)
532 if (GNUNET_OK != GNUNET_HELLO_get_id (r.friend_only_hello, &id_friend))
534 if (GNUNET_YES == dsc->remove_files)
535 remove_garbage (fullname);
542 if (GNUNET_OK != GNUNET_HELLO_get_id (r.hello, &id_public))
544 if (GNUNET_YES == dsc->remove_files)
545 remove_garbage (fullname);
551 if ((NULL != r.hello) && (NULL != r.friend_only_hello) &&
552 (0 != GNUNET_memcmp (&id_friend, &id_public)))
554 /* HELLOs are not for the same peer */
556 if (GNUNET_YES == dsc->remove_files)
557 remove_garbage (fullname);
561 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
563 &identity.public_key))
565 if (0 != GNUNET_memcmp (&id, &identity))
567 /* HELLOs are not for the same peer */
569 if (GNUNET_YES == dsc->remove_files)
570 remove_garbage (fullname);
575 /* ok, found something valid, remember HELLO */
576 add_host_to_known_hosts (&id);
579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 "Updating peer `%s' public HELLO \n",
582 update_hello (&id, r.hello);
583 GNUNET_free (r.hello);
585 if (NULL != r.friend_only_hello)
587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
588 "Updating peer `%s' friend only HELLO \n",
590 update_hello (&id, r.friend_only_hello);
591 GNUNET_free (r.friend_only_hello);
599 * Call this method periodically to scan data/hosts for new hosts.
604 cron_scan_directory_data_hosts (void *cls)
606 static unsigned int retries;
607 struct DirScanContext dsc;
611 if (GNUNET_SYSERR == GNUNET_DISK_directory_create (networkIdDirectory))
614 GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
615 GNUNET_SCHEDULER_PRIORITY_IDLE,
617 cron_scan_directory_data_hosts,
622 dsc.remove_files = GNUNET_YES;
623 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
624 _ ("Scanning directory `%s'\n"),
626 GNUNET_DISK_directory_scan (networkIdDirectory,
627 &hosts_directory_scan_callback,
629 if ((0 == dsc.matched) && (0 == (++retries & 31)))
630 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
631 _ ("Still no peers found in `%s'!\n"),
634 GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
635 GNUNET_SCHEDULER_PRIORITY_IDLE,
636 &cron_scan_directory_data_hosts,
642 * Update the HELLO of a friend by merging the addresses.
644 * @param hello original hello
645 * @param friend_hello hello with additional addresses
646 * @return merged HELLO
648 static struct GNUNET_HELLO_Message *
649 update_friend_hello (const struct GNUNET_HELLO_Message *hello,
650 const struct GNUNET_HELLO_Message *friend_hello)
652 struct GNUNET_HELLO_Message *res;
653 struct GNUNET_HELLO_Message *tmp;
654 struct GNUNET_PeerIdentity pid;
656 if (NULL != friend_hello)
658 res = GNUNET_HELLO_merge (hello, friend_hello);
659 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
663 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
668 tmp = GNUNET_HELLO_create (&pid.public_key, NULL, NULL, GNUNET_YES);
669 res = GNUNET_HELLO_merge (hello, tmp);
671 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
677 * Bind a host address (hello) to a hostId.
679 * @param peer the peer for which this is a hello
680 * @param hello the verified (!) hello message
683 update_hello (const struct GNUNET_PeerIdentity *peer,
684 const struct GNUNET_HELLO_Message *hello)
687 struct HostEntry *host;
688 struct GNUNET_HELLO_Message *mrg;
689 struct GNUNET_HELLO_Message **dest;
690 struct GNUNET_TIME_Absolute delta;
693 int friend_hello_type;
695 int store_friend_hello;
699 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
700 GNUNET_assert (NULL != host);
702 friend_hello_type = GNUNET_HELLO_is_friend_only (hello);
703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
704 "Updating %s HELLO for `%s'\n",
705 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
709 if (GNUNET_YES == friend_hello_type)
711 dest = &host->friend_only_hello;
720 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
721 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
725 mrg = GNUNET_HELLO_merge ((*dest), hello);
726 delta = GNUNET_HELLO_equals (mrg, (*dest), GNUNET_TIME_absolute_get ());
727 if (delta.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
729 /* no differences, just ignore the update */
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
731 "No change in %s HELLO for `%s'\n",
732 (GNUNET_YES == friend_hello_type) ? "friend-only" : "public",
737 GNUNET_free ((*dest));
741 if ((NULL != (host->hello)) && (GNUNET_NO == friend_hello_type))
743 /* Update friend only hello */
744 mrg = update_friend_hello (host->hello, host->friend_only_hello);
745 if (NULL != host->friend_only_hello)
746 GNUNET_free (host->friend_only_hello);
747 host->friend_only_hello = mrg;
750 if (NULL != host->hello)
751 GNUNET_assert ((GNUNET_NO == GNUNET_HELLO_is_friend_only (host->hello)));
752 if (NULL != host->friend_only_hello)
754 (GNUNET_YES == GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
756 fn = get_host_filename (peer);
757 if ((NULL != fn) && (GNUNET_OK == GNUNET_DISK_directory_create_for_file (fn)))
759 store_hello = GNUNET_NO;
762 if (NULL != host->hello)
763 (void) GNUNET_HELLO_iterate_addresses (host->hello,
769 store_hello = GNUNET_YES;
770 size += GNUNET_HELLO_size (host->hello);
773 if (NULL != host->friend_only_hello)
774 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
778 store_friend_hello = GNUNET_NO;
781 store_friend_hello = GNUNET_YES;
782 size += GNUNET_HELLO_size (host->friend_only_hello);
785 if ((GNUNET_NO == store_hello) && (GNUNET_NO == store_friend_hello))
787 /* no valid addresses, don't put HELLO on disk; in fact,
788 if one exists on disk, remove it */
793 buffer = GNUNET_malloc (size);
796 if (GNUNET_YES == store_hello)
798 GNUNET_memcpy (buffer, host->hello, GNUNET_HELLO_size (host->hello));
799 pos += GNUNET_HELLO_size (host->hello);
801 if (GNUNET_YES == store_friend_hello)
803 GNUNET_memcpy (&buffer[pos],
804 host->friend_only_hello,
805 GNUNET_HELLO_size (host->friend_only_hello));
806 pos += GNUNET_HELLO_size (host->friend_only_hello);
808 GNUNET_assert (pos == size);
810 if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn,
813 GNUNET_DISK_PERM_USER_READ
814 | GNUNET_DISK_PERM_USER_WRITE
815 | GNUNET_DISK_PERM_GROUP_READ
816 | GNUNET_DISK_PERM_OTHER_READ))
817 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
819 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820 "Stored %s %s HELLO in %s with total size %u\n",
821 (GNUNET_YES == store_friend_hello) ? "friend-only" : "",
822 (GNUNET_YES == store_hello) ? "public" : "",
825 GNUNET_free (buffer);
828 GNUNET_free_non_null (fn);
834 * Closure for #add_to_tc()
836 struct TransmitContext
839 * Client to transmit to
841 struct GNUNET_SERVICE_Client *client;
844 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
851 * Do transmit info about peer to given host.
853 * @param cls NULL to hit all hosts, otherwise specifies a particular target
855 * @param value information to transmit
856 * @return #GNUNET_YES (continue to iterate)
859 add_to_tc (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
861 struct TransmitContext *tc = cls;
862 struct HostEntry *pos = value;
863 struct InfoMessage *im;
865 struct GNUNET_MQ_Envelope *env;
869 if ((NULL != pos->hello) && (GNUNET_NO == tc->friend_only))
871 /* Copy public HELLO */
872 hs = GNUNET_HELLO_size (pos->hello);
873 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InfoMessage));
874 env = GNUNET_MQ_msg_extra (im, hs, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
875 GNUNET_memcpy (&im[1], pos->hello, hs);
876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877 "Sending public HELLO with size %u for peer `%s'\n",
881 else if ((NULL != pos->friend_only_hello) && (GNUNET_YES == tc->friend_only))
883 /* Copy friend only HELLO */
884 hs = GNUNET_HELLO_size (pos->friend_only_hello);
885 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InfoMessage));
886 env = GNUNET_MQ_msg_extra (im, hs, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
887 GNUNET_memcpy (&im[1], pos->friend_only_hello, hs);
888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889 "Sending friend-only HELLO with size %u for peer `%s'\n",
895 env = GNUNET_MQ_msg (im, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
896 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897 "Adding no HELLO for peer `%s'\n",
900 im->peer = pos->identity;
901 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client), env);
907 * @brief delete expired HELLO entries in directory
909 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
910 * @param fn filename to test to see if the HELLO expired
911 * @return #GNUNET_OK (continue iteration)
914 discard_hosts_helper (void *cls, const char *fn)
916 struct GNUNET_TIME_Absolute *now = cls;
917 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
918 const struct GNUNET_HELLO_Message *hello;
919 struct GNUNET_HELLO_Message *new_hello;
921 unsigned int cur_hello_size;
922 unsigned int new_hello_size;
929 if (GNUNET_OK != GNUNET_DISK_file_size (fn, &fsize, GNUNET_YES, GNUNET_YES))
931 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
932 | GNUNET_ERROR_TYPE_BULK,
937 read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof(buffer));
939 if ((read_size < (int) sizeof(struct GNUNET_MessageHeader)) ||
940 (fsize > GNUNET_MAX_MESSAGE_SIZE))
942 if (0 != unlink (fn))
943 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
944 | GNUNET_ERROR_TYPE_BULK,
950 writebuffer = GNUNET_malloc (read_size);
953 while (read_pos < read_size)
955 /* Check each HELLO */
956 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
957 cur_hello_size = GNUNET_HELLO_size (hello);
958 if (0 == cur_hello_size)
960 /* Invalid data, discard */
961 if (0 != unlink (fn))
962 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
963 | GNUNET_ERROR_TYPE_BULK,
966 GNUNET_free (writebuffer);
970 GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES, &discard_expired, now);
972 if (NULL != new_hello)
973 (void) GNUNET_HELLO_iterate_addresses (hello,
977 if ((NULL != new_hello) && (0 < cnt))
979 /* Store new HELLO to write it when done */
980 new_hello_size = GNUNET_HELLO_size (new_hello);
981 GNUNET_memcpy (&writebuffer[write_pos], new_hello, new_hello_size);
982 write_pos += new_hello_size;
984 read_pos += cur_hello_size;
985 GNUNET_free_non_null (new_hello);
990 GNUNET_DISK_fn_write (fn,
993 GNUNET_DISK_PERM_USER_READ
994 | GNUNET_DISK_PERM_USER_WRITE
995 | GNUNET_DISK_PERM_GROUP_READ
996 | GNUNET_DISK_PERM_OTHER_READ);
998 else if (0 != unlink (fn))
999 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
1000 | GNUNET_ERROR_TYPE_BULK,
1004 GNUNET_free (writebuffer);
1010 * Call this method periodically to scan peerinfo/ for ancient
1016 cron_clean_data_hosts (void *cls)
1018 struct GNUNET_TIME_Absolute now;
1022 now = GNUNET_TIME_absolute_get ();
1023 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1024 _ ("Cleaning up directory `%s'\n"),
1025 networkIdDirectory);
1026 GNUNET_DISK_directory_scan (networkIdDirectory, &discard_hosts_helper, &now);
1027 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1028 &cron_clean_data_hosts,
1034 * Check HELLO-message.
1036 * @param cls identification of the client
1037 * @param hello the actual message
1038 * @return #GNUNET_OK if @a hello is well-formed
1041 check_hello (void *cls, const struct GNUNET_HELLO_Message *hello)
1043 struct GNUNET_PeerIdentity pid;
1046 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
1049 return GNUNET_SYSERR;
1056 * Handle HELLO-message.
1058 * @param cls identification of the client
1059 * @param hello the actual message
1062 handle_hello (void *cls, const struct GNUNET_HELLO_Message *hello)
1064 struct GNUNET_SERVICE_Client *client = cls;
1065 struct GNUNET_PeerIdentity pid;
1067 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
1068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069 "HELLO message received for peer `%s'\n",
1071 add_host_to_known_hosts (&pid);
1072 update_hello (&pid, hello);
1073 GNUNET_SERVICE_client_continue (client);
1078 * Handle GET-message.
1080 * @param cls identification of the client
1081 * @param lpm the actual message
1084 handle_get (void *cls, const struct ListPeerMessage *lpm)
1086 struct GNUNET_SERVICE_Client *client = cls;
1087 struct TransmitContext tcx;
1088 struct GNUNET_MessageHeader *msg;
1089 struct GNUNET_MQ_Envelope *env;
1091 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1092 "GET message received for peer `%s'\n",
1093 GNUNET_i2s (&lpm->peer));
1094 tcx.friend_only = ntohl (lpm->include_friend_only);
1095 tcx.client = client;
1096 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1100 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1101 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1102 GNUNET_SERVICE_client_continue (client);
1107 * Handle GET-ALL-message.
1109 * @param cls identification of the client
1110 * @param lapm the actual message
1113 handle_get_all (void *cls, const struct ListAllPeersMessage *lapm)
1115 struct GNUNET_SERVICE_Client *client = cls;
1116 struct TransmitContext tcx;
1117 struct GNUNET_MQ_Envelope *env;
1118 struct GNUNET_MessageHeader *msg;
1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GET_ALL message received\n");
1121 tcx.friend_only = ntohl (lapm->include_friend_only);
1122 tcx.client = client;
1123 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &add_to_tc, &tcx);
1124 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1125 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1126 GNUNET_SERVICE_client_continue (client);
1131 * Handle NOTIFY-message.
1133 * @param cls identification of the client
1134 * @param nm the actual message
1137 handle_notify (void *cls, const struct NotifyMessage *nm)
1139 struct GNUNET_SERVICE_Client *client = cls;
1140 struct GNUNET_MQ_Handle *mq;
1141 struct TransmitContext tcx;
1142 struct GNUNET_MQ_Envelope *env;
1143 struct GNUNET_MessageHeader *msg;
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "NOTIFY message received\n");
1146 mq = GNUNET_SERVICE_client_get_mq (client);
1147 GNUNET_SERVICE_client_mark_monitor (client);
1148 if (ntohl (nm->include_friend_only))
1149 GNUNET_notification_context_add (notify_friend_only_list, mq);
1151 GNUNET_notification_context_add (notify_list, mq);
1152 tcx.friend_only = ntohl (nm->include_friend_only);
1153 tcx.client = client;
1154 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &add_to_tc, &tcx);
1155 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1156 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1157 GNUNET_SERVICE_client_continue (client);
1162 * Client connect callback
1165 * @param client server client
1166 * @param mq for @a client
1170 client_connect_cb (void *cls,
1171 struct GNUNET_SERVICE_Client *client,
1172 struct GNUNET_MQ_Handle *mq)
1181 * Client disconnect callback
1184 * @param client server client
1185 * @param app_ctx should be @a client
1188 client_disconnect_cb (void *cls,
1189 struct GNUNET_SERVICE_Client *client,
1193 GNUNET_assert (app_ctx == client);
1198 * Release memory taken by a host entry.
1201 * @param key key of the host entry
1202 * @param value the `struct HostEntry` to free
1203 * @return #GNUNET_YES (continue to iterate)
1206 free_host_entry (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1208 struct HostEntry *he = value;
1212 GNUNET_free_non_null (he->hello);
1213 GNUNET_free_non_null (he->friend_only_hello);
1220 * Clean up our state. Called during shutdown.
1225 shutdown_task (void *cls)
1228 GNUNET_notification_context_destroy (notify_list);
1230 GNUNET_notification_context_destroy (notify_friend_only_list);
1231 notify_friend_only_list = NULL;
1233 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &free_host_entry, NULL);
1234 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1237 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1240 if (NULL != cron_clean)
1242 GNUNET_SCHEDULER_cancel (cron_clean);
1245 if (NULL != cron_scan)
1247 GNUNET_SCHEDULER_cancel (cron_scan);
1250 if (NULL != networkIdDirectory)
1252 GNUNET_free (networkIdDirectory);
1253 networkIdDirectory = NULL;
1259 * Start up peerinfo service.
1261 * @param cls closure
1262 * @param cfg configuration to use
1263 * @param service the initialized service
1267 const struct GNUNET_CONFIGURATION_Handle *cfg,
1268 struct GNUNET_SERVICE_Handle *service)
1272 struct DirScanContext dsc;
1278 hostmap = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES);
1279 stats = GNUNET_STATISTICS_create ("peerinfo", cfg);
1280 notify_list = GNUNET_notification_context_create (0);
1281 notify_friend_only_list = GNUNET_notification_context_create (0);
1282 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg, "peerinfo", "NO_IO");
1283 use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1285 "USE_INCLUDED_HELLOS");
1286 if (GNUNET_SYSERR == use_included)
1287 use_included = GNUNET_NO;
1288 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1289 if (GNUNET_YES != noio)
1293 GNUNET_CONFIGURATION_get_value_filename (cfg,
1296 &networkIdDirectory));
1297 if (GNUNET_OK != GNUNET_DISK_directory_create (networkIdDirectory))
1299 GNUNET_SCHEDULER_shutdown ();
1304 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1305 &cron_scan_directory_data_hosts,
1309 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1310 &cron_clean_data_hosts,
1312 if (GNUNET_YES == use_included)
1314 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1315 GNUNET_asprintf (&peerdir, "%shellos", ip);
1318 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1319 _ ("Importing HELLOs from `%s'\n"),
1322 dsc.remove_files = GNUNET_NO;
1324 GNUNET_DISK_directory_scan (peerdir,
1325 &hosts_directory_scan_callback,
1327 GNUNET_free (peerdir);
1331 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1332 _ ("Skipping import of included HELLOs\n"));
1339 * Define "main" method using service macro.
1341 GNUNET_SERVICE_MAIN (
1343 GNUNET_SERVICE_OPTION_NONE,
1346 &client_disconnect_cb,
1348 GNUNET_MQ_hd_var_size (hello,
1349 GNUNET_MESSAGE_TYPE_HELLO,
1350 struct GNUNET_HELLO_Message,
1352 GNUNET_MQ_hd_fixed_size (get,
1353 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1354 struct ListPeerMessage,
1356 GNUNET_MQ_hd_fixed_size (get_all,
1357 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1358 struct ListAllPeersMessage,
1360 GNUNET_MQ_hd_fixed_size (notify,
1361 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1362 struct NotifyMessage,
1364 GNUNET_MQ_handler_end ());
1367 /* end of gnunet-service-peerinfo.c */