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.c708
1 files changed, 708 insertions, 0 deletions
diff --git a/src/peerinfo/gnunet-service-peerinfo.c b/src/peerinfo/gnunet-service-peerinfo.c
new file mode 100644
index 000000000..b81c7b6ee
--- /dev/null
+++ b/src/peerinfo/gnunet-service-peerinfo.c
@@ -0,0 +1,708 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2004, 2005, 2007, 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
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/ and data/credit/).
27 *
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_crypto_lib.h"
33#include "gnunet_disk_lib.h"
34#include "gnunet_hello_lib.h"
35#include "gnunet_protocols.h"
36#include "gnunet_service_lib.h"
37#include "peerinfo.h"
38
39/**
40 * How often do we scan the HOST_DIR for new entries?
41 */
42#define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
43
44/**
45 * How often do we flush trust values to disk?
46 */
47#define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
48
49/**
50 * How often do we discard old entries in data/hosts/?
51 */
52#define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60)
53
54/**
55 * In-memory cache of known hosts.
56 */
57struct HostEntry
58{
59
60 /**
61 * This is a linked list.
62 */
63 struct HostEntry *next;
64
65 /**
66 * Identity of the peer.
67 */
68 struct GNUNET_PeerIdentity identity;
69
70 /**
71 * Hello for the peer (can be NULL)
72 */
73 struct GNUNET_HELLO_Message *hello;
74
75 /**
76 * Trust rating for this peer
77 */
78 uint32_t trust;
79
80 /**
81 * Trust rating for this peer on disk.
82 */
83 uint32_t disk_trust;
84
85};
86
87/**
88 * The in-memory list of known hosts.
89 */
90static struct HostEntry *hosts;
91
92/**
93 * Directory where the hellos are stored in (data/hosts)
94 */
95static char *networkIdDirectory;
96
97/**
98 * Where do we store trust information?
99 */
100static char *trustDirectory;
101
102
103/**
104 * Address iterator that causes expired entries to be discarded.
105 *
106 * @param cls pointer to the current time
107 * @return GNUNET_NO if expiration smaller than the current time
108 */
109static int
110discard_expired (void *cls,
111 const char *tname,
112 struct GNUNET_TIME_Absolute expiration,
113 const void *addr, size_t addrlen)
114{
115 const struct GNUNET_TIME_Absolute *now = cls;
116 if (now->value > expiration.value)
117 return GNUNET_NO;
118 return GNUNET_OK;
119}
120
121
122/**
123 * Get the filename under which we would store the GNUNET_HELLO_Message
124 * for the given host and protocol.
125 * @return filename of the form DIRECTORY/HOSTID
126 */
127static char *
128get_host_filename (const struct GNUNET_PeerIdentity *id)
129{
130 struct GNUNET_CRYPTO_HashAsciiEncoded fil;
131 char *fn;
132
133 GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
134 GNUNET_asprintf (&fn,
135 "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil);
136 return fn;
137}
138
139
140/**
141 * Get the filename under which we would store the GNUNET_HELLO_Message
142 * for the given host and protocol.
143 * @return filename of the form DIRECTORY/HOSTID
144 */
145static char *
146get_trust_filename (const struct GNUNET_PeerIdentity *id)
147{
148 struct GNUNET_CRYPTO_HashAsciiEncoded fil;
149 char *fn;
150
151 GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil);
152 GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil);
153 return fn;
154}
155
156/**
157 * Find the host entry for the given peer. Call
158 * only when synchronized!
159 * @return NULL if not found
160 */
161static struct HostEntry *
162lookup_host_entry (const struct GNUNET_PeerIdentity *id)
163{
164 struct HostEntry *pos;
165
166 pos = hosts;
167 while ((pos != NULL) &&
168 (0 !=
169 memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity))))
170 pos = pos->next;
171 return pos;
172}
173
174
175/**
176 * Add a host to the list.
177 *
178 * @param identity the identity of the host
179 * @param protocol the protocol for the host
180 */
181static void
182add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity)
183{
184 struct HostEntry *entry;
185 char *fn;
186 uint32_t trust;
187 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
188 const struct GNUNET_HELLO_Message *hello;
189 struct GNUNET_HELLO_Message *hello_clean;
190 int size;
191 struct GNUNET_TIME_Absolute now;
192
193 entry = lookup_host_entry (identity);
194 if (entry != NULL)
195 return;
196 entry = GNUNET_malloc (sizeof (struct HostEntry));
197 entry->identity = *identity;
198 fn = get_trust_filename (identity);
199 if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) &&
200 (sizeof (trust) == GNUNET_DISK_file_read (fn, sizeof (trust), &trust)))
201 entry->disk_trust = entry->trust = ntohl (trust);
202 GNUNET_free (fn);
203
204 fn = get_host_filename (identity);
205 if (GNUNET_DISK_file_test (fn) == GNUNET_YES)
206 {
207 size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer);
208 hello = (const struct GNUNET_HELLO_Message *) buffer;
209 now = GNUNET_TIME_absolute_get ();
210 hello_clean = GNUNET_HELLO_iterate_addresses (hello,
211 GNUNET_YES,
212 &discard_expired, &now);
213 entry->hello = hello_clean;
214 }
215 GNUNET_free (fn);
216 entry->next = hosts;
217 hosts = entry;
218}
219
220
221/**
222 * Increase the host credit by a value.
223 *
224 * @param hostId is the identity of the host
225 * @param value is the int value by which the
226 * host credit is to be increased or decreased
227 * @returns the actual change in trust (positive or negative)
228 */
229static int
230change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value)
231{
232 struct HostEntry *host;
233
234 if (value == 0)
235 return 0;
236 host = lookup_host_entry (hostId);
237 if (host == NULL)
238 {
239 add_host_to_known_hosts (hostId);
240 host = lookup_host_entry (hostId);
241 }
242 GNUNET_assert (host != NULL);
243 if (value > 0)
244 {
245 if (host->trust + value < host->trust)
246 {
247 value = ((uint32_t) - 1) - host->trust;
248 host->trust = (uint32_t) - 1; /* maximized */
249 }
250 else
251 host->trust += value;
252 }
253 else
254 {
255 if (host->trust < -value)
256 {
257 value = -host->trust;
258 host->trust = 0;
259 }
260 else
261 host->trust += value;
262 }
263 return value;
264}
265
266
267/**
268 * Remove a file that should not be there. LOG
269 * success or failure.
270 */
271static void
272remove_garbage (const char *fullname)
273{
274 if (0 == UNLINK (fullname))
275 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
276 _
277 ("File `%s' in directory `%s' does not match naming convention. "
278 "Removed.\n"), fullname, networkIdDirectory);
279 else
280 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR |
281 GNUNET_ERROR_TYPE_BULK, "unlink", fullname);
282}
283
284
285static int
286hosts_directory_scan_callback (void *cls, const char *fullname)
287{
288 unsigned int *matched = cls;
289 struct GNUNET_PeerIdentity identity;
290 const char *filename;
291
292 if (GNUNET_DISK_file_test (fullname) != GNUNET_YES)
293 return GNUNET_OK; /* ignore non-files */
294 if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
295 {
296 remove_garbage (fullname);
297 return GNUNET_OK;
298 }
299 filename =
300 &fullname[strlen (fullname) -
301 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1];
302 if (filename[-1] != DIR_SEPARATOR)
303 {
304 remove_garbage (fullname);
305 return GNUNET_OK;
306 }
307 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename,
308 &identity.hashPubKey))
309 {
310 remove_garbage (fullname);
311 return GNUNET_OK;
312 }
313 (*matched)++;
314 add_host_to_known_hosts (&identity);
315 return GNUNET_OK;
316}
317
318
319/**
320 * Call this method periodically to scan data/hosts for new hosts.
321 */
322static void
323cron_scan_directory_data_hosts (void *cls,
324 const struct GNUNET_SCHEDULER_TaskContext *tc)
325{
326 static unsigned int retries;
327 unsigned int count;
328
329 count = 0;
330 GNUNET_DISK_directory_scan (networkIdDirectory,
331 &hosts_directory_scan_callback, &count);
332 if ((0 == count) && (0 == (++retries & 31)))
333 GNUNET_log (GNUNET_ERROR_TYPE_WARNING |
334 GNUNET_ERROR_TYPE_BULK,
335 _("Still no peers found in `%s'!\n"), networkIdDirectory);
336 GNUNET_SCHEDULER_add_delayed (tc->sched,
337 GNUNET_NO,
338 GNUNET_SCHEDULER_PRIORITY_KEEP,
339 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
340 DATA_HOST_FREQ,
341 &cron_scan_directory_data_hosts, NULL);
342}
343
344
345/**
346 * Bind a host address (hello) to a hostId.
347 *
348 * @param peer the peer for which this is a hello
349 * @param hello the verified (!) hello message
350 */
351static void
352bind_address (const struct GNUNET_PeerIdentity *peer,
353 const struct GNUNET_HELLO_Message *hello)
354{
355 char *fn;
356 struct HostEntry *host;
357 struct GNUNET_HELLO_Message *mrg;
358
359 add_host_to_known_hosts (peer);
360 host = lookup_host_entry (peer);
361 GNUNET_assert (host != NULL);
362 if (host->hello == NULL)
363 {
364 host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
365 memcpy (host->hello, hello, GNUNET_HELLO_size (hello));
366 }
367 else
368 {
369 mrg = GNUNET_HELLO_merge (host->hello, hello);
370 GNUNET_free (host->hello);
371 host->hello = mrg;
372 }
373 fn = get_host_filename (peer);
374 GNUNET_DISK_file_write (fn, host->hello, GNUNET_HELLO_size (hello), "644");
375 GNUNET_free (fn);
376}
377
378
379/**
380 * Do transmit info either for only the host matching the given
381 * argument or for all known hosts and change their trust values by
382 * the given delta.
383 *
384 * @param only NULL to hit all hosts
385 */
386static void
387send_to_each_host (const struct GNUNET_PeerIdentity *only,
388 int trust_change,
389 struct GNUNET_SERVER_Client *client,
390 struct GNUNET_SERVER_Handle *server)
391{
392 struct HostEntry *pos;
393 struct InfoMessage *im;
394 const struct GNUNET_MessageHeader *end;
395 uint16_t hs;
396 char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
397 struct GNUNET_SERVER_TransmitContext *tc;
398
399 tc = GNUNET_SERVER_transmit_context_create (client);
400 pos = hosts;
401 while (pos != NULL)
402 {
403 if ((only == NULL) ||
404 (0 ==
405 memcmp (only, &pos->identity,
406 sizeof (struct GNUNET_PeerIdentity))))
407 {
408 change_host_trust (&pos->identity, trust_change);
409 hs = 0;
410 im = (struct InfoMessage *) buf;
411 if (pos->hello != NULL)
412 {
413 hs = GNUNET_HELLO_size (pos->hello);
414 GNUNET_assert (hs <
415 GNUNET_SERVER_MAX_MESSAGE_SIZE -
416 sizeof (struct InfoMessage));
417 memcpy (&im[1], pos->hello, hs);
418 }
419 im->trust = htonl (pos->trust);
420 im->peer = pos->identity;
421 end = &im->header;
422 GNUNET_SERVER_transmit_context_append (tc,
423 &end[1],
424 hs +
425 sizeof (struct InfoMessage) -
426 sizeof (struct
427 GNUNET_MessageHeader),
428 GNUNET_MESSAGE_TYPE_PEERINFO_INFO);
429 }
430 pos = pos->next;
431 }
432 GNUNET_SERVER_transmit_context_append (tc, NULL, 0,
433 GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END);
434 GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
435}
436
437
438/**
439 * Write host-trust information to a file - flush the buffer entry!
440 * Assumes synchronized access.
441 */
442static void
443flush_trust (struct HostEntry *host)
444{
445 char *fn;
446 uint32_t trust;
447
448 if (host->trust == host->disk_trust)
449 return; /* unchanged */
450 fn = get_trust_filename (&host->identity);
451 if (host->trust == 0)
452 {
453 if ((0 != UNLINK (fn)) && (errno != ENOENT))
454 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
455 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
456 }
457 else
458 {
459 trust = htonl (host->trust);
460 if (GNUNET_OK ==
461 GNUNET_DISK_file_write (fn, &trust, sizeof (uint32_t), "644"))
462 host->disk_trust = host->trust;
463 }
464 GNUNET_free (fn);
465}
466
467/**
468 * Call this method periodically to scan data/hosts for new hosts.
469 */
470static void
471cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
472{
473 struct HostEntry *pos;
474
475 pos = hosts;
476 while (pos != NULL)
477 {
478 flush_trust (pos);
479 pos = pos->next;
480 }
481 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
482 GNUNET_SCHEDULER_add_delayed (tc->sched,
483 GNUNET_YES,
484 GNUNET_SCHEDULER_PRIORITY_KEEP,
485 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
486 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
487}
488
489
490/**
491 * @brief delete expired HELLO entries in data/hosts/
492 */
493static int
494discard_hosts_helper (void *cls, const char *fn)
495{
496 struct GNUNET_TIME_Absolute *now = cls;
497 char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
498 const struct GNUNET_HELLO_Message *hello;
499 struct GNUNET_HELLO_Message *new_hello;
500 int size;
501
502 size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer);
503 if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn)))
504 {
505 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
506 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
507 return GNUNET_OK;
508 }
509 hello = (const struct GNUNET_HELLO_Message *) buffer;
510 new_hello = GNUNET_HELLO_iterate_addresses (hello,
511 GNUNET_YES,
512 &discard_expired, now);
513 if ((new_hello == NULL) && (0 != UNLINK (fn)))
514 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING |
515 GNUNET_ERROR_TYPE_BULK, "unlink", fn);
516 if (new_hello != NULL)
517 {
518 GNUNET_DISK_file_write (fn,
519 new_hello,
520 GNUNET_HELLO_size (new_hello), "644");
521 GNUNET_free (new_hello);
522 }
523 return GNUNET_OK;
524}
525
526
527/**
528 * Call this method periodically to scan data/hosts for new hosts.
529 */
530static void
531cron_clean_data_hosts (void *cls,
532 const struct GNUNET_SCHEDULER_TaskContext *tc)
533{
534 struct GNUNET_TIME_Absolute now;
535
536 now = GNUNET_TIME_absolute_get ();
537 GNUNET_DISK_directory_scan (networkIdDirectory,
538 &discard_hosts_helper, &now);
539
540 GNUNET_SCHEDULER_add_delayed (tc->sched,
541 GNUNET_NO,
542 GNUNET_SCHEDULER_PRIORITY_KEEP,
543 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
544 DATA_HOST_CLEAN_FREQ,
545 &cron_clean_data_hosts, NULL);
546}
547
548
549/**
550 * Handle ADD-message.
551 *
552 * @param cls closure
553 * @param server the server handling the message
554 * @param client identification of the client
555 * @param message the actual message
556 */
557static void
558handle_add (void *cls,
559 struct GNUNET_SERVER_Handle *server,
560 struct GNUNET_SERVER_Client *client,
561 const struct GNUNET_MessageHeader *message)
562{
563 const struct PeerAddMessage *pam;
564 const struct GNUNET_MessageHeader *hello;
565 uint16_t size;
566
567 size = ntohs (message->size);
568 if (size <
569 sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader))
570 {
571 GNUNET_break (0);
572 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
573 return;
574 }
575 pam = (const struct PeerAddMessage *) message;
576 hello = (const struct GNUNET_MessageHeader *) &pam[1];
577 if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size))
578 {
579 GNUNET_break (0);
580 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
581 return;
582 }
583 bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello);
584 GNUNET_SERVER_receive_done (client, GNUNET_OK);
585}
586
587
588/**
589 * Handle GET-message.
590 *
591 * @param cls closure
592 * @param server the server handling the message
593 * @param client identification of the client
594 * @param message the actual message
595 */
596static void
597handle_get (void *cls,
598 struct GNUNET_SERVER_Handle *server,
599 struct GNUNET_SERVER_Client *client,
600 const struct GNUNET_MessageHeader *message)
601{
602 const struct ListPeerMessage *lpm;
603
604 lpm = (const struct ListPeerMessage *) message;
605 send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client, server);
606}
607
608
609/**
610 * Handle GET-ALL-message.
611 *
612 * @param cls closure
613 * @param server the server handling the message
614 * @param client identification of the client
615 * @param message the actual message
616 */
617static void
618handle_get_all (void *cls,
619 struct GNUNET_SERVER_Handle *server,
620 struct GNUNET_SERVER_Client *client,
621 const struct GNUNET_MessageHeader *message)
622{
623 const struct ListAllPeersMessage *lpm;
624
625 lpm = (const struct ListAllPeersMessage *) message;
626 send_to_each_host (NULL, ntohl (lpm->trust_change), client, server);
627}
628
629
630/**
631 * List of handlers for the messages understood by this
632 * service.
633 */
634static struct GNUNET_SERVER_MessageHandler handlers[] = {
635 {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0},
636 {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET,
637 sizeof (struct ListPeerMessage)},
638 {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL,
639 sizeof (struct ListAllPeersMessage)},
640 {NULL, NULL, 0, 0}
641};
642
643
644/**
645 * Process statistics requests.
646 *
647 * @param cls closure
648 * @param sched scheduler to use
649 * @param server the initialized server
650 * @param cfg configuration to use
651 */
652static void
653run (void *cls,
654 struct GNUNET_SCHEDULER_Handle *sched,
655 struct GNUNET_SERVER_Handle *server,
656 struct GNUNET_CONFIGURATION_Handle *cfg)
657{
658 GNUNET_assert (GNUNET_OK ==
659 GNUNET_CONFIGURATION_get_value_filename (cfg,
660 "peerinfo",
661 "HOSTS",
662 &networkIdDirectory));
663 GNUNET_assert (GNUNET_OK ==
664 GNUNET_CONFIGURATION_get_value_filename (cfg,
665 "peerinfo",
666 "TRUST",
667 &trustDirectory));
668 GNUNET_DISK_directory_create (networkIdDirectory);
669 GNUNET_DISK_directory_create (trustDirectory);
670 GNUNET_SCHEDULER_add_delayed (sched,
671 GNUNET_NO,
672 GNUNET_SCHEDULER_PRIORITY_IDLE,
673 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
674 GNUNET_TIME_UNIT_MILLISECONDS,
675 &cron_scan_directory_data_hosts, NULL);
676 GNUNET_SCHEDULER_add_delayed (sched,
677 GNUNET_YES,
678 GNUNET_SCHEDULER_PRIORITY_HIGH,
679 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
680 TRUST_FLUSH_FREQ, &cron_flush_trust, NULL);
681 GNUNET_SCHEDULER_add_delayed (sched,
682 GNUNET_NO,
683 GNUNET_SCHEDULER_PRIORITY_IDLE,
684 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
685 DATA_HOST_CLEAN_FREQ,
686 &cron_clean_data_hosts, NULL);
687 GNUNET_SERVER_add_handlers (server, handlers);
688}
689
690
691/**
692 * The main function for the statistics service.
693 *
694 * @param argc number of arguments from the command line
695 * @param argv command line arguments
696 * @return 0 ok, 1 on error
697 */
698int
699main (int argc, char *const *argv)
700{
701 return (GNUNET_OK ==
702 GNUNET_SERVICE_run (argc,
703 argv,
704 "peerinfo", &run, NULL, NULL, NULL)) ? 0 : 1;
705}
706
707
708/* end of gnunet-service-peerinfo.c */