2 This file is part of GNUnet.
3 (C) 2010,2011 Christian Grothoff (and other contributing authors)
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 3, or (at your
8 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 General Public License for more details.
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.
22 * @file transport/gnunet-service-transport_validation.c
23 * @brief address validation subsystem
24 * @author Christian Grothoff
27 #include "gnunet-service-transport_validation.h"
28 #include "gnunet-service-transport.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_peerinfo_service.h"
33 * How long until a HELLO verification attempt should time out?
34 * Must be rather small, otherwise a partially successful HELLO
35 * validation (some addresses working) might not be available
36 * before a client's request for a connection fails for good.
37 * Besides, if a single request to an address takes a long time,
38 * then the peer is unlikely worthwhile anyway.
40 #define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
43 * How long is a PONG signature valid? We'll recycle a signature until
44 * 1/4 of this time is remaining. PONGs should expire so that if our
45 * external addresses change an adversary cannot replay them indefinitely.
46 * OTOH, we don't want to spend too much time generating PONG signatures,
47 * so they must have some lifetime to reduce our CPU usage.
49 #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
52 * After how long do we expire an address in a HELLO that we just
53 * validated? This value is also used for our own addresses when we
56 #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
60 * How long before an existing address expires should we again try to
61 * validate it? Must be (significantly) smaller than
62 * HELLO_ADDRESS_EXPIRATION.
64 #define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
67 * Size of the validation map hashmap.
69 #define VALIDATION_MAP_SIZE 256
73 * Information about an address under validation
75 struct ValidationEntry
79 * Name of the transport.
84 * The address, actually a pointer to the end
85 * of this struct. Do not free!
90 * The identity of the peer.
92 struct GNUNET_PeerIdentity pid;
95 * ID of task that will clean up this entry if nothing happens.
97 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
100 * At what time did we send the latest validation request?
102 struct GNUNET_TIME_Absolute send_time;
105 * When did we last succeed with validating this address?
106 * FOREVER if the address has not been validated (we're currently checking)
107 * ZERO if the address was validated a long time ago (from PEERINFO)
108 * otherwise a time in the past if this process validated the address
110 struct GNUNET_TIME_Absolute last_validated_at;
113 * How long until we can try to validate this address again?
114 * FOREVER if the address is for an unsupported plugin (from PEERINFO)
115 * ZERO if the address is considered valid (no validation needed)
116 * otherwise a time in the future if we're currently denying re-validation
118 struct GNUNET_TIME_Absolute validation_block;
121 * Challenge number we used.
134 * Context of currently active requests to peerinfo
135 * for validation of HELLOs.
137 struct CheckHelloValidatedContext
141 * This is a doubly-linked list.
143 struct CheckHelloValidatedContext *next;
146 * This is a doubly-linked list.
148 struct CheckHelloValidatedContext *prev;
151 * Hello that we are validating.
153 const struct GNUNET_HELLO_Message *hello;
156 * Context for peerinfo iteration.
158 struct GNUNET_PEERINFO_IteratorContext *piter;
164 * Head of linked list of HELLOs awaiting validation.
166 static struct CheckHelloValidatedContext *chvc_head;
169 * Tail of linked list of HELLOs awaiting validation
171 static struct CheckHelloValidatedContext *chvc_tail;
174 * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
175 * of the given peer that we are currently validating, have validated
176 * or are blocked from re-validation for a while).
178 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
181 * Map of PeerIdentities to 'struct GST_ValidationIteratorContext's.
183 static struct GNUNET_CONTAINER_MultiHashMap *notify_map;
187 * Start the validation subsystem.
190 GST_validation_start ()
192 validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
193 notify_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
198 * Iterate over validation entries and free them.
200 * @param cls (unused)
201 * @param key peer identity (unused)
202 * @param value a 'struct ValidationEntry' to clean up
203 * @return GNUNET_YES (continue to iterate)
206 cleanup_validation_entry (void *cls,
207 const GNUNET_HashCode *key,
210 struct ValidationEntry *ve = value;
212 GNUNET_free (ve->transport_name);
213 if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
215 GNUNET_SCHEDULER_cancel (ve->timeout_task);
216 ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
224 * Stop the validation subsystem.
227 GST_validation_stop ()
229 struct CheckHelloValidatedContext *chvc;
231 GNUNET_CONTAINER_multihashmap_iterate (validation_map,
232 &cleanup_validation_entry,
234 GNUNET_CONTAINER_multihashmap_destroy (validation_map);
235 validation_map = NULL;
236 GNUNET_assert (GNUNET_CONTAINER_multihashmap_size (notify_map) == 0);
237 GNUNET_CONTAINER_multihashmap_destroy (notify_map);
239 while (NULL != (chvc = chvc_head))
241 GNUNET_CONTAINER_DLL_remove (chvc_head,
244 GNUNET_PEERINFO_iterate_cancel (chvc->piter);
252 * Address validation cleanup task (record no longer needed).
254 * @param cls the 'struct ValidationEntry'
255 * @param tc scheduler context (unused)
258 timeout_hello_validation (void *cls,
259 const struct GNUNET_SCHEDULER_TaskContext *tc)
261 struct ValidationEntry *va = cls;
263 va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
264 GNUNET_STATISTICS_update (GST_stats,
265 gettext_noop ("# address records discarded"),
268 GNUNET_break (GNUNET_OK ==
269 GNUNET_CONTAINER_multihashmap_remove (validation_map,
272 GNUNET_free (va->transport_name);
279 * Context for the validation entry match function.
281 struct ValidationEntryMatchContext
284 * Where to store the result?
286 struct ValidationEntry *ve;
289 * Transport name we're looking for.
291 const char *transport_name;
294 * Address we're interested in.
299 * Number of bytes in 'addr'.
306 * Iterate over validation entries until a matching one is found.
308 * @param cls the 'struct ValidationEntryMatchContext'
309 * @param key peer identity (unused)
310 * @param value a 'struct ValidationEntry' to match
311 * @return GNUNET_YES if the entry does not match,
312 * GNUNET_NO if the entry does match
315 validation_entry_match (void *cls,
316 const GNUNET_HashCode *key,
319 struct ValidationEntryMatchContext *vemc = cls;
320 struct ValidationEntry *ve = value;
322 if ( (ve->addrlen == vemc->addrlen) &&
323 (0 == memcmp (ve->addr, vemc->addr, ve->addrlen)) &&
324 (0 == strcmp (ve->transport_name, vemc->transport_name)) )
334 * Find a ValidationEntry entry for the given neighbour that matches
335 * the given address and transport. If none exists, create one (but
336 * without starting any validation).
338 * @param neighbour which peer we care about
339 * @param tname name of the transport plugin
340 * @param session session to look for, NULL for 'any'; otherwise
341 * can be used for the service to "learn" this session ID
343 * @param addr binary address
344 * @param addrlen length of addr
345 * @return validation entry matching the given specifications
347 static struct ValidationEntry *
348 find_validation_entry (struct GNUNET_PeerIdentity *neighbour,
353 struct ValidationEntryMatchContext vemc;
354 struct ValidationEntry *ve;
357 vemc.transport_name = tname;
359 vemc.addrlen = addrlen;
360 GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
361 &neighbour->hashPubKey,
362 &validation_entry_match,
364 if (NULL != (ve = vemc.ve))
366 ve = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
367 ve->transport_name = GNUNET_strdup (tname);
368 ve->addr = (void*) &ve[1];
369 ve->pid = *neighbour;
370 memcpy (&ve[1], addr, addrlen);
371 ve->addrlen = addrlen;
372 ve->last_validated_at = GNUNET_TIME_UNIT_FOREVER_ABS;
373 GNUNET_CONTAINER_multihashmap_put (validation_map,
374 &neighbour->hashPubKey,
376 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
382 * We've received a PING. If appropriate, generate a PONG.
384 * @param sender peer sending the PING
385 * @param hdr the PING
386 * @param plugin_name name of plugin that received the PING
387 * @param sender_address address of the sender as known to the plugin, NULL
388 * if we did not initiate the connection
389 * @param sender_address_len number of bytes in sender_address
392 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
393 const struct GNUNET_MessageHeader *hdr,
394 const char *plugin_name,
395 const void *sender_address,
396 size_t sender_address_len)
402 * We've received a PONG. Check if it matches a pending PING and
403 * mark the respective address as confirmed.
405 * @param sender peer sending the PONG
406 * @param hdr the PONG
407 * @param plugin_name name of plugin that received the PONG
408 * @param sender_address address of the sender as known to the plugin, NULL
409 * if we did not initiate the connection
410 * @param sender_address_len number of bytes in sender_address
413 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
414 const struct GNUNET_MessageHeader *hdr,
415 const char *plugin_name,
416 const void *sender_address,
417 size_t sender_address_len)
423 * Iterator callback to go over all addresses and try to validate them
424 * (unless blocked or already validated).
426 * @param cls pointer to the 'struct PeerIdentity' of the peer
427 * @param tname name of the transport
428 * @param expiration expiration time
429 * @param addr the address
430 * @param addrlen length of the address
431 * @return GNUNET_OK (keep the address)
434 validate_address (void *cls,
436 struct GNUNET_TIME_Absolute expiration,
440 struct GNUNET_PeerIdentity *pid = cls;
441 struct ValidationEntry *ve;
443 if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
444 return GNUNET_OK; /* expired */
445 ve = find_validation_entry (pid, tname, addr, addrlen);
446 // FIXME: check if validated/blocked, if not start validation...
447 ve++; // make compiler happy
454 * We've received a HELLO, check which addresses are new and trigger
457 * @param hello the HELLO we received
460 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
462 const struct GNUNET_HELLO_Message* hm = (const struct GNUNET_HELLO_Message*) hello;
463 struct GNUNET_PeerIdentity pid;
466 GNUNET_HELLO_get_id (hm, &pid))
468 /* malformed HELLO */
472 GNUNET_assert (NULL ==
473 GNUNET_HELLO_iterate_addresses (hm,
481 * Opaque handle to stop incremental validation address callbacks.
483 struct GST_ValidationIteratorContext
486 * Function to call on each address.
488 GST_ValidationAddressCallback cb;
496 * Which peer are we monitoring?
498 struct GNUNET_PeerIdentity target;
503 * Call the callback in the closure for each validation entry.
505 * @param cls the 'struct GST_ValidationIteratorContext'
506 * @param key the peer's identity
507 * @param value the 'struct ValidationEntry'
508 * @return GNUNET_OK (continue to iterate)
511 iterate_addresses (void *cls,
512 const GNUNET_HashCode *key,
515 struct GST_ValidationIteratorContext *vic = cls;
516 struct ValidationEntry *ve = value;
518 vic->cb (vic->cb_cls,
520 ve->last_validated_at,
521 ve->validation_block,
530 * Call the given function for each address for the given target.
531 * Can either give a snapshot (synchronous API) or be continuous.
533 * @param target peer information is requested for
534 * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to
535 * continue to give information about addresses as it evolves
536 * @param cb function to call; will not be called after this function returns
537 * if snapshot_only is GNUNET_YES
538 * @param cb_cls closure for 'cb'
539 * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES
541 struct GST_ValidationIteratorContext *
542 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
544 GST_ValidationAddressCallback cb,
547 struct GST_ValidationIteratorContext *vic;
549 vic = GNUNET_malloc (sizeof (struct GST_ValidationIteratorContext));
551 vic->cb_cls = cb_cls;
552 vic->target = *target;
553 GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
557 if (GNUNET_YES == snapshot_only)
562 GNUNET_CONTAINER_multihashmap_put (notify_map,
565 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
571 * Cancel an active validation address iteration.
573 * @param ctx the context of the operation that is cancelled
576 GST_validation_get_addresses_cancel (struct GST_ValidationIteratorContext *ctx)
578 GNUNET_assert (GNUNET_OK ==
579 GNUNET_CONTAINER_multihashmap_remove (notify_map,
580 &ctx->target.hashPubKey,
586 /* end of file gnunet-service-transport_validation.c */