aboutsummaryrefslogtreecommitdiff
path: root/src/peerinfo/gnunet-service-peerinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/peerinfo/gnunet-service-peerinfo.c')
-rw-r--r--src/peerinfo/gnunet-service-peerinfo.c1370
1 files changed, 0 insertions, 1370 deletions
diff --git a/src/peerinfo/gnunet-service-peerinfo.c b/src/peerinfo/gnunet-service-peerinfo.c
deleted file mode 100644
index 1b1232ecb..000000000
--- a/src/peerinfo/gnunet-service-peerinfo.c
+++ /dev/null
@@ -1,1370 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2016 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file peerinfo/gnunet-service-peerinfo.c
23 * @brief maintains list of known peers
24 *
25 * Code to maintain the list of currently known hosts (in memory
26 * structure of data/hosts/).
27 *
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_hello_lib.h"
34#include "gnunet_protocols.h"
35#include "gnunet_statistics_service.h"
36#include "peerinfo.h"
37
38/**
39 * How often do we scan the HOST_DIR for new entries?
40 */
41#define DATA_HOST_FREQ \
42 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
43
44/**
45 * How often do we discard old entries in data/hosts/?
46 */
47#define DATA_HOST_CLEAN_FREQ \
48 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
49
50
51/**
52 * In-memory cache of known hosts.
53 */
54struct HostEntry
55{
56 /**
57 * Identity of the peer.
58 */
59 struct GNUNET_PeerIdentity identity;
60
61 /**
62 * Hello for the peer (can be NULL)
63 */
64 struct GNUNET_HELLO_Message *hello;
65
66 /**
67 * Friend only hello for the peer (can be NULL)
68 */
69 struct GNUNET_HELLO_Message *friend_only_hello;
70};
71
72
73/**
74 * Result of reading a file
75 */
76struct ReadHostFileContext
77{
78 /**
79 * Hello for the peer (can be NULL)
80 */
81 struct GNUNET_HELLO_Message *hello;
82
83 /**
84 * Friend only hello for the peer (can be NULL)
85 */
86 struct GNUNET_HELLO_Message *friend_only_hello;
87};
88
89
90/**
91 * The in-memory list of known hosts, mapping of
92 * host IDs to 'struct HostEntry*' values.
93 */
94static struct GNUNET_CONTAINER_MultiPeerMap *hostmap;
95
96/**
97 * Clients to immediately notify about all changes.
98 */
99static struct GNUNET_NotificationContext *notify_list;
100
101/**
102 * Clients to immediately notify about all changes,
103 * even for friend-only HELLOs.
104 */
105static struct GNUNET_NotificationContext *notify_friend_only_list;
106
107/**
108 * Directory where the hellos are stored in (peerinfo/)
109 */
110static char *networkIdDirectory;
111
112/**
113 * Handle for reporting statistics.
114 */
115static struct GNUNET_STATISTICS_Handle *stats;
116
117/**
118 * Handle for task to run #cron_clean_data_hosts()
119 */
120static struct GNUNET_SCHEDULER_Task *cron_clean;
121
122/**
123 * Handle for task to run #cron_scan_directory_data_hosts()
124 */
125static struct GNUNET_SCHEDULER_Task *cron_scan;
126
127
128/**
129 * Notify all clients in the notify list about the
130 * given host entry changing.
131 *
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
135 */
136static struct InfoMessage *
137make_info_message (const struct HostEntry *he, int include_friend_only)
138{
139 struct InfoMessage *im;
140 struct GNUNET_HELLO_Message *src;
141 size_t hs;
142
143 if (GNUNET_YES == include_friend_only)
144 src = he->friend_only_hello;
145 else
146 src = he->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);
153 return im;
154}
155
156
157/**
158 * Address iterator that causes expired entries to be discarded.
159 *
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
164 */
165static int
166discard_expired (void *cls,
167 const struct GNUNET_HELLO_Address *address,
168 struct GNUNET_TIME_Absolute expiration)
169{
170 const struct GNUNET_TIME_Absolute *now = cls;
171
172 if (now->abs_value_us > expiration.abs_value_us)
173 {
174 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
175 _ ("Removing expired address of transport `%s'\n"),
176 address->transport_name);
177 return GNUNET_NO;
178 }
179 return GNUNET_OK;
180}
181
182
183/**
184 * Address iterator that counts the remaining addresses.
185 *
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)
190 */
191static int
192count_addresses (void *cls,
193 const struct GNUNET_HELLO_Address *address,
194 struct GNUNET_TIME_Absolute expiration)
195{
196 unsigned int *cnt = cls;
197
198 (void) address;
199 (void) expiration;
200 (*cnt)++;
201 return GNUNET_OK;
202}
203
204
205/**
206 * Get the filename under which we would store the GNUNET_HELLO_Message
207 * for the given host and protocol.
208 *
209 * @param id peer for which we need the filename for the HELLO
210 * @return filename of the form DIRECTORY/HOSTID
211 */
212static char *
213get_host_filename (const struct GNUNET_PeerIdentity *id)
214{
215 char *fn;
216
217 if (NULL == networkIdDirectory)
218 return NULL;
219 GNUNET_asprintf (&fn,
220 "%s%s%s",
221 networkIdDirectory,
222 DIR_SEPARATOR_STR,
223 GNUNET_i2s_full (id));
224 return fn;
225}
226
227
228/**
229 * Broadcast information about the given entry to all
230 * clients that care.
231 *
232 * @param entry entry to broadcast about
233 */
234static void
235notify_all (struct HostEntry *entry)
236{
237 struct InfoMessage *msg_pub;
238 struct InfoMessage *msg_friend;
239
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,
245 &msg_pub->header,
246 GNUNET_NO);
247 GNUNET_free (msg_pub);
248 msg_friend = make_info_message (entry, GNUNET_YES);
249 GNUNET_notification_context_broadcast (notify_friend_only_list,
250 &msg_friend->header,
251 GNUNET_NO);
252 GNUNET_free (msg_friend);
253}
254
255
256/**
257 * Bind a host address (hello) to a hostId.
258 *
259 * @param peer the peer for which this is a hello
260 * @param hello the verified (!) hello message
261 */
262static void
263update_hello (const struct GNUNET_PeerIdentity *peer,
264 const struct GNUNET_HELLO_Message *hello);
265
266
267/**
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.
273 *
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
277 */
278static void
279read_host_file (const char *fn,
280 int unlink_garbage,
281 struct ReadHostFileContext *r)
282{
283 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
284 ssize_t size_total;
285 struct GNUNET_TIME_Absolute now;
286 unsigned int left;
287 const struct GNUNET_HELLO_Message *hello;
288 struct GNUNET_HELLO_Message *hello_clean;
289 size_t read_pos;
290 uint16_t size_hello;
291
292 r->friend_only_hello = NULL;
293 r->hello = NULL;
294
295 if (GNUNET_YES != GNUNET_DISK_file_test (fn))
296 return;
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",
300 (int) size_total,
301 fn);
302 if ((size_total < 0) ||
303 (((size_t) size_total) < sizeof(struct GNUNET_MessageHeader)))
304 {
305 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
306 _ ("Failed to parse HELLO in file `%s': %s\n"),
307 fn,
308 "File has invalid size");
309 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
310 (ENOENT != errno))
311 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
312 return;
313 }
314
315 read_pos = 0;
316 while (read_pos < (size_t) size_total)
317 {
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))
321 {
322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
323 _ ("Failed to parse HELLO in file `%s'\n"),
324 fn);
325 if (0 == read_pos)
326 {
327 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
328 (ENOENT != errno))
329 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
330 }
331 else
332 {
333 if ((GNUNET_YES == unlink_garbage) && (0 != truncate (fn, read_pos)) &&
334 (ENOENT != errno))
335 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "truncate", fn);
336 }
337 return;
338 }
339
340 now = GNUNET_TIME_absolute_get ();
341 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
342 GNUNET_YES,
343 &discard_expired,
344 &now);
345 if (NULL == hello_clean)
346 {
347 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
348 _ ("Failed to parse HELLO in file `%s'\n"),
349 fn);
350 if ((GNUNET_YES == unlink_garbage) && (0 != unlink (fn)) &&
351 (ENOENT != errno))
352 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
353 return;
354 }
355 left = 0;
356 (void) GNUNET_HELLO_iterate_addresses (hello_clean,
357 GNUNET_NO,
358 &count_addresses,
359 &left);
360
361 if (0 == left)
362 {
363 GNUNET_free (hello_clean);
364 break;
365 }
366
367 if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean))
368 {
369 if (NULL == r->hello)
370 r->hello = hello_clean;
371 else
372 {
373 GNUNET_break (0);
374 GNUNET_free (r->hello);
375 r->hello = hello_clean;
376 }
377 }
378 else
379 {
380 if (NULL == r->friend_only_hello)
381 r->friend_only_hello = hello_clean;
382 else
383 {
384 GNUNET_break (0);
385 GNUNET_free (r->friend_only_hello);
386 r->friend_only_hello = hello_clean;
387 }
388 }
389 read_pos += size_hello;
390 }
391
392 if (0 == left)
393 {
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);
397 }
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"
402 : "NO friend only");
403}
404
405
406/**
407 * Add a host to the list and notify clients about this event
408 *
409 * @param identity the identity of the host
410 * @return the HostEntry
411 */
412static struct HostEntry *
413add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
414{
415 struct HostEntry *entry;
416 struct ReadHostFileContext r;
417 char *fn;
418
419 entry = GNUNET_CONTAINER_multipeermap_get (hostmap, identity);
420 if (NULL == entry)
421 {
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"),
427 1,
428 GNUNET_NO);
429 entry = GNUNET_new (struct HostEntry);
430 entry->identity = *identity;
431 GNUNET_assert (GNUNET_OK ==
432 GNUNET_CONTAINER_multipeermap_put (
433 hostmap,
434 &entry->identity,
435 entry,
436 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
437 notify_all (entry);
438 fn = get_host_filename (identity);
439 if (NULL != fn)
440 {
441 read_host_file (fn, GNUNET_YES, &r);
442 if (NULL != r.hello)
443 update_hello (identity, r.hello);
444 if (NULL != r.friend_only_hello)
445 update_hello (identity, r.friend_only_hello);
446 GNUNET_free (r.hello);
447 GNUNET_free (r.friend_only_hello);
448 GNUNET_free (fn);
449 }
450 }
451 return entry;
452}
453
454
455/**
456 * Remove a file that should not be there. LOG
457 * success or failure.
458 *
459 * @param fullname name of the file to remove
460 */
461static void
462remove_garbage (const char *fullname)
463{
464 if (0 == unlink (fullname))
465 GNUNET_log (
466 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
467 _ (
468 "File `%s' in directory `%s' does not match naming convention. Removed.\n"),
469 fullname,
470 networkIdDirectory);
471 else
472 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
473 "unlink",
474 fullname);
475}
476
477
478/**
479 * Closure for #hosts_directory_scan_callback().
480 */
481struct DirScanContext
482{
483 /**
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.
487 */
488 int remove_files;
489
490 /**
491 * Counter for the number of (valid) entries found, incremented
492 * by one for each match.
493 */
494 unsigned int matched;
495};
496
497
498/**
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.
501 *
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)
506 */
507static int
508hosts_directory_scan_callback (void *cls, const char *fullname)
509{
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;
517
518 if (GNUNET_YES != GNUNET_DISK_file_test (fullname))
519 return GNUNET_OK; /* ignore non-files */
520
521 filename = strrchr (fullname, DIR_SEPARATOR);
522 if ((NULL == filename) || (1 > strlen (filename)))
523 filename = fullname;
524 else
525 filename++;
526
527 read_host_file (fullname, dsc->remove_files, &r);
528 if ((NULL == r.hello) && (NULL == r.friend_only_hello))
529 return GNUNET_OK;
530 if (NULL != r.friend_only_hello)
531 {
532 if (GNUNET_OK != GNUNET_HELLO_get_id (r.friend_only_hello, &id_friend))
533 {
534 if (GNUNET_YES == dsc->remove_files)
535 remove_garbage (fullname);
536 return GNUNET_OK;
537 }
538 id = id_friend;
539 }
540 if (NULL != r.hello)
541 {
542 if (GNUNET_OK != GNUNET_HELLO_get_id (r.hello, &id_public))
543 {
544 if (GNUNET_YES == dsc->remove_files)
545 remove_garbage (fullname);
546 return GNUNET_OK;
547 }
548 id = id_public;
549 }
550
551 if ((NULL != r.hello) && (NULL != r.friend_only_hello) &&
552 (0 != GNUNET_memcmp (&id_friend, &id_public)))
553 {
554 /* HELLOs are not for the same peer */
555 GNUNET_break (0);
556 if (GNUNET_YES == dsc->remove_files)
557 remove_garbage (fullname);
558 return GNUNET_OK;
559 }
560 if (GNUNET_OK ==
561 GNUNET_CRYPTO_eddsa_public_key_from_string (filename,
562 strlen (filename),
563 &identity.public_key))
564 {
565 if (0 != GNUNET_memcmp (&id, &identity))
566 {
567 /* HELLOs are not for the same peer */
568 GNUNET_break (0);
569 if (GNUNET_YES == dsc->remove_files)
570 remove_garbage (fullname);
571 return GNUNET_OK;
572 }
573 }
574
575 /* ok, found something valid, remember HELLO */
576 add_host_to_known_hosts (&id);
577 if (NULL != r.hello)
578 {
579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 "Updating peer `%s' public HELLO \n",
581 GNUNET_i2s (&id));
582 update_hello (&id, r.hello);
583 GNUNET_free (r.hello);
584 }
585 if (NULL != r.friend_only_hello)
586 {
587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
588 "Updating peer `%s' friend only HELLO \n",
589 GNUNET_i2s (&id));
590 update_hello (&id, r.friend_only_hello);
591 GNUNET_free (r.friend_only_hello);
592 }
593 dsc->matched++;
594 return GNUNET_OK;
595}
596
597
598/**
599 * Call this method periodically to scan data/hosts for new hosts.
600 *
601 * @param cls unused
602 */
603static void
604cron_scan_directory_data_hosts (void *cls)
605{
606 static unsigned int retries;
607 struct DirScanContext dsc;
608
609 (void) cls;
610 cron_scan = NULL;
611 if (GNUNET_SYSERR == GNUNET_DISK_directory_create (networkIdDirectory))
612 {
613 cron_scan =
614 GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
615 GNUNET_SCHEDULER_PRIORITY_IDLE,
616 &
617 cron_scan_directory_data_hosts,
618 NULL);
619 return;
620 }
621 dsc.matched = 0;
622 dsc.remove_files = GNUNET_YES;
623 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
624 _ ("Scanning directory `%s'\n"),
625 networkIdDirectory);
626 GNUNET_DISK_directory_scan (networkIdDirectory,
627 &hosts_directory_scan_callback,
628 &dsc);
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"),
632 networkIdDirectory);
633 cron_scan =
634 GNUNET_SCHEDULER_add_delayed_with_priority (DATA_HOST_FREQ,
635 GNUNET_SCHEDULER_PRIORITY_IDLE,
636 &cron_scan_directory_data_hosts,
637 NULL);
638}
639
640
641/**
642 * Update the HELLO of a friend by merging the addresses.
643 *
644 * @param hello original hello
645 * @param friend_hello hello with additional addresses
646 * @return merged HELLO
647 */
648static struct GNUNET_HELLO_Message *
649update_friend_hello (const struct GNUNET_HELLO_Message *hello,
650 const struct GNUNET_HELLO_Message *friend_hello)
651{
652 struct GNUNET_HELLO_Message *res;
653 struct GNUNET_HELLO_Message *tmp;
654 struct GNUNET_PeerIdentity pid;
655
656 if (NULL != friend_hello)
657 {
658 res = GNUNET_HELLO_merge (hello, friend_hello);
659 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
660 return res;
661 }
662
663 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
664 {
665 GNUNET_break (0);
666 return NULL;
667 }
668 tmp = GNUNET_HELLO_create (&pid.public_key, NULL, NULL, GNUNET_YES);
669 res = GNUNET_HELLO_merge (hello, tmp);
670 GNUNET_free (tmp);
671 GNUNET_assert (GNUNET_YES == GNUNET_HELLO_is_friend_only (res));
672 return res;
673}
674
675
676/**
677 * Bind a host address (hello) to a hostId.
678 *
679 * @param peer the peer for which this is a hello
680 * @param hello the verified (!) hello message
681 */
682static void
683update_hello (const struct GNUNET_PeerIdentity *peer,
684 const struct GNUNET_HELLO_Message *hello)
685{
686 char *fn;
687 struct HostEntry *host;
688 struct GNUNET_HELLO_Message *mrg;
689 struct GNUNET_HELLO_Message **dest;
690 struct GNUNET_TIME_Absolute delta;
691 unsigned int cnt;
692 unsigned int size;
693 int friend_hello_type;
694 int store_hello;
695 int store_friend_hello;
696 unsigned int pos;
697 char *buffer;
698
699 host = GNUNET_CONTAINER_multipeermap_get (hostmap, peer);
700 GNUNET_assert (NULL != host);
701
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",
706 GNUNET_i2s (peer));
707
708 dest = NULL;
709 if (GNUNET_YES == friend_hello_type)
710 {
711 dest = &host->friend_only_hello;
712 }
713 else
714 {
715 dest = &host->hello;
716 }
717
718 if (NULL == (*dest))
719 {
720 (*dest) = GNUNET_malloc (GNUNET_HELLO_size (hello));
721 GNUNET_memcpy ((*dest), hello, GNUNET_HELLO_size (hello));
722 }
723 else
724 {
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)
728 {
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",
733 GNUNET_i2s (peer));
734 GNUNET_free (mrg);
735 return;
736 }
737 GNUNET_free ((*dest));
738 (*dest) = mrg;
739 }
740
741 if ((NULL != (host->hello)) && (GNUNET_NO == friend_hello_type))
742 {
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;
748 }
749
750 if (NULL != host->hello)
751 GNUNET_assert ((GNUNET_NO == GNUNET_HELLO_is_friend_only (host->hello)));
752 if (NULL != host->friend_only_hello)
753 GNUNET_assert (
754 (GNUNET_YES == GNUNET_HELLO_is_friend_only (host->friend_only_hello)));
755
756 fn = get_host_filename (peer);
757 if ((NULL != fn) && (GNUNET_OK == GNUNET_DISK_directory_create_for_file (fn)))
758 {
759 store_hello = GNUNET_NO;
760 size = 0;
761 cnt = 0;
762 if (NULL != host->hello)
763 (void) GNUNET_HELLO_iterate_addresses (host->hello,
764 GNUNET_NO,
765 &count_addresses,
766 &cnt);
767 if (cnt > 0)
768 {
769 store_hello = GNUNET_YES;
770 size += GNUNET_HELLO_size (host->hello);
771 }
772 cnt = 0;
773 if (NULL != host->friend_only_hello)
774 (void) GNUNET_HELLO_iterate_addresses (host->friend_only_hello,
775 GNUNET_NO,
776 &count_addresses,
777 &cnt);
778 store_friend_hello = GNUNET_NO;
779 if (0 < cnt)
780 {
781 store_friend_hello = GNUNET_YES;
782 size += GNUNET_HELLO_size (host->friend_only_hello);
783 }
784
785 if ((GNUNET_NO == store_hello) && (GNUNET_NO == store_friend_hello))
786 {
787 /* no valid addresses, don't put HELLO on disk; in fact,
788 if one exists on disk, remove it */
789 (void) unlink (fn);
790 }
791 else
792 {
793 buffer = GNUNET_malloc (size);
794 pos = 0;
795
796 if (GNUNET_YES == store_hello)
797 {
798 GNUNET_memcpy (buffer, host->hello, GNUNET_HELLO_size (host->hello));
799 pos += GNUNET_HELLO_size (host->hello);
800 }
801 if (GNUNET_YES == store_friend_hello)
802 {
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);
807 }
808 GNUNET_assert (pos == size);
809
810 if (GNUNET_SYSERR ==
811 GNUNET_DISK_fn_write (fn,
812 buffer,
813 size,
814 GNUNET_DISK_PERM_USER_READ
815 | GNUNET_DISK_PERM_USER_WRITE
816 | GNUNET_DISK_PERM_GROUP_READ
817 | GNUNET_DISK_PERM_OTHER_READ))
818 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
819 else
820 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821 "Stored %s %s HELLO in %s with total size %u\n",
822 (GNUNET_YES == store_friend_hello) ? "friend-only" : "",
823 (GNUNET_YES == store_hello) ? "public" : "",
824 fn,
825 size);
826 GNUNET_free (buffer);
827 }
828 }
829 GNUNET_free (fn);
830 notify_all (host);
831}
832
833
834/**
835 * Closure for #add_to_tc()
836 */
837struct TransmitContext
838{
839 /**
840 * Client to transmit to
841 */
842 struct GNUNET_SERVICE_Client *client;
843
844 /**
845 * Include friend only HELLOs #GNUNET_YES or #GNUNET_NO
846 */
847 int friend_only;
848};
849
850
851/**
852 * Do transmit info about peer to given host.
853 *
854 * @param cls NULL to hit all hosts, otherwise specifies a particular target
855 * @param key hostID
856 * @param value information to transmit
857 * @return #GNUNET_YES (continue to iterate)
858 */
859static int
860add_to_tc (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
861{
862 struct TransmitContext *tc = cls;
863 struct HostEntry *pos = value;
864 struct InfoMessage *im;
865 uint16_t hs;
866 struct GNUNET_MQ_Envelope *env;
867
868 hs = 0;
869
870 if ((NULL != pos->hello) && (GNUNET_NO == tc->friend_only))
871 {
872 /* Copy public HELLO */
873 hs = GNUNET_HELLO_size (pos->hello);
874 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InfoMessage));
875 env = GNUNET_MQ_msg_extra (im, hs, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
876 GNUNET_memcpy (&im[1], pos->hello, hs);
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
878 "Sending public HELLO with size %u for peer `%s'\n",
879 hs,
880 GNUNET_i2s (key));
881 }
882 else if ((NULL != pos->friend_only_hello) && (GNUNET_YES == tc->friend_only))
883 {
884 /* Copy friend only HELLO */
885 hs = GNUNET_HELLO_size (pos->friend_only_hello);
886 GNUNET_assert (hs < GNUNET_MAX_MESSAGE_SIZE - sizeof(struct InfoMessage));
887 env = GNUNET_MQ_msg_extra (im, hs, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
888 GNUNET_memcpy (&im[1], pos->friend_only_hello, hs);
889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
890 "Sending friend-only HELLO with size %u for peer `%s'\n",
891 hs,
892 GNUNET_i2s (key));
893 }
894 else
895 {
896 env = GNUNET_MQ_msg (im, GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
897 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
898 "Adding no HELLO for peer `%s'\n",
899 GNUNET_i2s (key));
900 }
901 im->peer = pos->identity;
902 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (tc->client), env);
903 return GNUNET_YES;
904}
905
906
907/**
908 * @brief delete expired HELLO entries in directory
909 *
910 * @param cls pointer to current time (`struct GNUNET_TIME_Absolute *`)
911 * @param fn filename to test to see if the HELLO expired
912 * @return #GNUNET_OK (continue iteration)
913 */
914static int
915discard_hosts_helper (void *cls, const char *fn)
916{
917 struct GNUNET_TIME_Absolute *now = cls;
918 char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
919 const struct GNUNET_HELLO_Message *hello;
920 struct GNUNET_HELLO_Message *new_hello;
921 int read_size;
922 unsigned int cur_hello_size;
923 unsigned int new_hello_size;
924 int read_pos;
925 int write_pos;
926 unsigned int cnt;
927 char *writebuffer;
928 uint64_t fsize;
929
930 if (GNUNET_OK != GNUNET_DISK_file_size (fn, &fsize, GNUNET_YES, GNUNET_YES))
931 {
932 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
933 | GNUNET_ERROR_TYPE_BULK,
934 "fstat",
935 fn);
936 return GNUNET_OK;
937 }
938 read_size = GNUNET_DISK_fn_read (fn, buffer, sizeof(buffer));
939
940 if ((read_size < (int) sizeof(struct GNUNET_MessageHeader)) ||
941 (fsize > GNUNET_MAX_MESSAGE_SIZE))
942 {
943 if (0 != unlink (fn))
944 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
945 | GNUNET_ERROR_TYPE_BULK,
946 "unlink",
947 fn);
948 return GNUNET_OK;
949 }
950
951 writebuffer = GNUNET_malloc (read_size);
952 read_pos = 0;
953 write_pos = 0;
954 while (read_pos < read_size)
955 {
956 /* Check each HELLO */
957 hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos];
958 cur_hello_size = GNUNET_HELLO_size (hello);
959 if (0 == cur_hello_size)
960 {
961 /* Invalid data, discard */
962 if (0 != unlink (fn))
963 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
964 | GNUNET_ERROR_TYPE_BULK,
965 "unlink",
966 fn);
967 GNUNET_free (writebuffer);
968 return GNUNET_OK;
969 }
970 new_hello =
971 GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES, &discard_expired, now);
972 cnt = 0;
973 if (NULL != new_hello)
974 (void) GNUNET_HELLO_iterate_addresses (hello,
975 GNUNET_NO,
976 &count_addresses,
977 &cnt);
978 if ((NULL != new_hello) && (0 < cnt))
979 {
980 /* Store new HELLO to write it when done */
981 new_hello_size = GNUNET_HELLO_size (new_hello);
982 GNUNET_memcpy (&writebuffer[write_pos], new_hello, new_hello_size);
983 write_pos += new_hello_size;
984 }
985 read_pos += cur_hello_size;
986 GNUNET_free (new_hello);
987 }
988
989 if (0 < write_pos)
990 {
991 (void) GNUNET_DISK_directory_remove (fn);
992 GNUNET_assert (GNUNET_OK ==
993 GNUNET_DISK_fn_write (fn,
994 writebuffer,
995 write_pos,
996 GNUNET_DISK_PERM_USER_READ
997 | GNUNET_DISK_PERM_USER_WRITE
998 | GNUNET_DISK_PERM_GROUP_READ
999 | GNUNET_DISK_PERM_OTHER_READ));
1000 }
1001 else if (0 != unlink (fn))
1002 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING
1003 | GNUNET_ERROR_TYPE_BULK,
1004 "unlink",
1005 fn);
1006
1007 GNUNET_free (writebuffer);
1008 return GNUNET_OK;
1009}
1010
1011
1012/**
1013 * Call this method periodically to scan peerinfo/ for ancient
1014 * HELLOs to expire.
1015 *
1016 * @param cls unused
1017 */
1018static void
1019cron_clean_data_hosts (void *cls)
1020{
1021 struct GNUNET_TIME_Absolute now;
1022
1023 (void) cls;
1024 cron_clean = NULL;
1025 now = GNUNET_TIME_absolute_get ();
1026 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1027 _ ("Cleaning up directory `%s'\n"),
1028 networkIdDirectory);
1029 GNUNET_DISK_directory_scan (networkIdDirectory, &discard_hosts_helper, &now);
1030 cron_clean = GNUNET_SCHEDULER_add_delayed (DATA_HOST_CLEAN_FREQ,
1031 &cron_clean_data_hosts,
1032 NULL);
1033}
1034
1035
1036/**
1037 * Check HELLO-message.
1038 *
1039 * @param cls identification of the client
1040 * @param hello the actual message
1041 * @return #GNUNET_OK if @a hello is well-formed
1042 */
1043static int
1044check_hello (void *cls, const struct GNUNET_HELLO_Message *hello)
1045{
1046 struct GNUNET_PeerIdentity pid;
1047
1048 (void) cls;
1049 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
1050 {
1051 GNUNET_break (0);
1052 return GNUNET_SYSERR;
1053 }
1054 return GNUNET_OK;
1055}
1056
1057
1058/**
1059 * Handle HELLO-message.
1060 *
1061 * @param cls identification of the client
1062 * @param hello the actual message
1063 */
1064static void
1065handle_hello (void *cls, const struct GNUNET_HELLO_Message *hello)
1066{
1067 struct GNUNET_SERVICE_Client *client = cls;
1068 struct GNUNET_PeerIdentity pid;
1069
1070 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
1071 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1072 "HELLO message received for peer `%s'\n",
1073 GNUNET_i2s (&pid));
1074 add_host_to_known_hosts (&pid);
1075 update_hello (&pid, hello);
1076 GNUNET_SERVICE_client_continue (client);
1077}
1078
1079
1080/**
1081 * Handle GET-message.
1082 *
1083 * @param cls identification of the client
1084 * @param lpm the actual message
1085 */
1086static void
1087handle_get (void *cls, const struct ListPeerMessage *lpm)
1088{
1089 struct GNUNET_SERVICE_Client *client = cls;
1090 struct TransmitContext tcx;
1091 struct GNUNET_MessageHeader *msg;
1092 struct GNUNET_MQ_Envelope *env;
1093
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095 "GET message received for peer `%s'\n",
1096 GNUNET_i2s (&lpm->peer));
1097 tcx.friend_only = ntohl (lpm->include_friend_only);
1098 tcx.client = client;
1099 GNUNET_CONTAINER_multipeermap_get_multiple (hostmap,
1100 &lpm->peer,
1101 &add_to_tc,
1102 &tcx);
1103 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1104 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1105 GNUNET_SERVICE_client_continue (client);
1106}
1107
1108
1109/**
1110 * Handle GET-ALL-message.
1111 *
1112 * @param cls identification of the client
1113 * @param lapm the actual message
1114 */
1115static void
1116handle_get_all (void *cls, const struct ListAllPeersMessage *lapm)
1117{
1118 struct GNUNET_SERVICE_Client *client = cls;
1119 struct TransmitContext tcx;
1120 struct GNUNET_MQ_Envelope *env;
1121 struct GNUNET_MessageHeader *msg;
1122
1123 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GET_ALL message received\n");
1124 tcx.friend_only = ntohl (lapm->include_friend_only);
1125 tcx.client = client;
1126 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &add_to_tc, &tcx);
1127 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1128 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1129 GNUNET_SERVICE_client_continue (client);
1130}
1131
1132
1133/**
1134 * Handle NOTIFY-message.
1135 *
1136 * @param cls identification of the client
1137 * @param nm the actual message
1138 */
1139static void
1140handle_notify (void *cls, const struct NotifyMessage *nm)
1141{
1142 struct GNUNET_SERVICE_Client *client = cls;
1143 struct GNUNET_MQ_Handle *mq;
1144 struct TransmitContext tcx;
1145 struct GNUNET_MQ_Envelope *env;
1146 struct GNUNET_MessageHeader *msg;
1147
1148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "NOTIFY message received\n");
1149 mq = GNUNET_SERVICE_client_get_mq (client);
1150 GNUNET_SERVICE_client_mark_monitor (client);
1151 if (ntohl (nm->include_friend_only))
1152 GNUNET_notification_context_add (notify_friend_only_list, mq);
1153 else
1154 GNUNET_notification_context_add (notify_list, mq);
1155 tcx.friend_only = ntohl (nm->include_friend_only);
1156 tcx.client = client;
1157 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &add_to_tc, &tcx);
1158 env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
1159 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
1160 GNUNET_SERVICE_client_continue (client);
1161}
1162
1163
1164/**
1165 * Client connect callback
1166 *
1167 * @param cls unused
1168 * @param client server client
1169 * @param mq for @a client
1170 * @return @a client
1171 */
1172static void *
1173client_connect_cb (void *cls,
1174 struct GNUNET_SERVICE_Client *client,
1175 struct GNUNET_MQ_Handle *mq)
1176{
1177 (void) cls;
1178 (void) mq;
1179 return client;
1180}
1181
1182
1183/**
1184 * Client disconnect callback
1185 *
1186 * @param cls unused
1187 * @param client server client
1188 * @param app_ctx should be @a client
1189 */
1190static void
1191client_disconnect_cb (void *cls,
1192 struct GNUNET_SERVICE_Client *client,
1193 void *app_ctx)
1194{
1195 (void) cls;
1196 GNUNET_assert (app_ctx == client);
1197}
1198
1199
1200/**
1201 * Release memory taken by a host entry.
1202 *
1203 * @param cls NULL
1204 * @param key key of the host entry
1205 * @param value the `struct HostEntry` to free
1206 * @return #GNUNET_YES (continue to iterate)
1207 */
1208static int
1209free_host_entry (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
1210{
1211 struct HostEntry *he = value;
1212
1213 (void) cls;
1214 (void) key;
1215 GNUNET_free (he->hello);
1216 GNUNET_free (he->friend_only_hello);
1217 GNUNET_free (he);
1218 return GNUNET_YES;
1219}
1220
1221
1222/**
1223 * Clean up our state. Called during shutdown.
1224 *
1225 * @param cls unused
1226 */
1227static void
1228shutdown_task (void *cls)
1229{
1230 (void) cls;
1231 GNUNET_notification_context_destroy (notify_list);
1232 notify_list = NULL;
1233 GNUNET_notification_context_destroy (notify_friend_only_list);
1234 notify_friend_only_list = NULL;
1235
1236 GNUNET_CONTAINER_multipeermap_iterate (hostmap, &free_host_entry, NULL);
1237 GNUNET_CONTAINER_multipeermap_destroy (hostmap);
1238 if (NULL != stats)
1239 {
1240 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1241 stats = NULL;
1242 }
1243 if (NULL != cron_clean)
1244 {
1245 GNUNET_SCHEDULER_cancel (cron_clean);
1246 cron_clean = NULL;
1247 }
1248 if (NULL != cron_scan)
1249 {
1250 GNUNET_SCHEDULER_cancel (cron_scan);
1251 cron_scan = NULL;
1252 }
1253 if (NULL != networkIdDirectory)
1254 {
1255 GNUNET_free (networkIdDirectory);
1256 networkIdDirectory = NULL;
1257 }
1258}
1259
1260
1261/**
1262 * Start up peerinfo service.
1263 *
1264 * @param cls closure
1265 * @param cfg configuration to use
1266 * @param service the initialized service
1267 */
1268static void
1269run (void *cls,
1270 const struct GNUNET_CONFIGURATION_Handle *cfg,
1271 struct GNUNET_SERVICE_Handle *service)
1272{
1273 char *peerdir;
1274 char *ip;
1275 struct DirScanContext dsc;
1276 int noio;
1277 int use_included;
1278
1279 (void) cls;
1280 (void) service;
1281 hostmap = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES);
1282 stats = GNUNET_STATISTICS_create ("peerinfo", cfg);
1283 notify_list = GNUNET_notification_context_create (0);
1284 notify_friend_only_list = GNUNET_notification_context_create (0);
1285 noio = GNUNET_CONFIGURATION_get_value_yesno (cfg, "peerinfo", "NO_IO");
1286 use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1287 "peerinfo",
1288 "USE_INCLUDED_HELLOS");
1289 if (GNUNET_SYSERR == use_included)
1290 use_included = GNUNET_NO;
1291 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1292 if (GNUNET_YES != noio)
1293 {
1294 GNUNET_assert (
1295 GNUNET_OK ==
1296 GNUNET_CONFIGURATION_get_value_filename (cfg,
1297 "peerinfo",
1298 "HOSTS",
1299 &networkIdDirectory));
1300 if (GNUNET_OK != GNUNET_DISK_directory_create (networkIdDirectory))
1301 {
1302 GNUNET_SCHEDULER_shutdown ();
1303 return;
1304 }
1305
1306 cron_scan =
1307 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1308 &cron_scan_directory_data_hosts,
1309 NULL);
1310
1311 cron_clean =
1312 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1313 &cron_clean_data_hosts,
1314 NULL);
1315 if (GNUNET_YES == use_included)
1316 {
1317 ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1318 GNUNET_asprintf (&peerdir, "%shellos", ip);
1319 GNUNET_free (ip);
1320
1321 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1322 _ ("Importing HELLOs from `%s'\n"),
1323 peerdir);
1324 dsc.matched = 0;
1325 dsc.remove_files = GNUNET_NO;
1326
1327 GNUNET_DISK_directory_scan (peerdir,
1328 &hosts_directory_scan_callback,
1329 &dsc);
1330 GNUNET_free (peerdir);
1331 }
1332 else
1333 {
1334 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1335 _ ("Skipping import of included HELLOs\n"));
1336 }
1337 }
1338}
1339
1340
1341/**
1342 * Define "main" method using service macro.
1343 */
1344GNUNET_SERVICE_MAIN (
1345 "peerinfo",
1346 GNUNET_SERVICE_OPTION_NONE,
1347 &run,
1348 &client_connect_cb,
1349 &client_disconnect_cb,
1350 NULL,
1351 GNUNET_MQ_hd_var_size (hello,
1352 GNUNET_MESSAGE_TYPE_HELLO,
1353 struct GNUNET_HELLO_Message,
1354 NULL),
1355 GNUNET_MQ_hd_fixed_size (get,
1356 GNUNET_MESSAGE_TYPE_PEERINFO_GET,
1357 struct ListPeerMessage,
1358 NULL),
1359 GNUNET_MQ_hd_fixed_size (get_all,
1360 GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
1361 struct ListAllPeersMessage,
1362 NULL),
1363 GNUNET_MQ_hd_fixed_size (notify,
1364 GNUNET_MESSAGE_TYPE_PEERINFO_NOTIFY,
1365 struct NotifyMessage,
1366 NULL),
1367 GNUNET_MQ_handler_end ());
1368
1369
1370/* end of gnunet-service-peerinfo.c */