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_plugins.h"
29 #include "gnunet-service-transport.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_peerinfo_service.h"
35 * How long until a HELLO verification attempt should time out?
36 * Must be rather small, otherwise a partially successful HELLO
37 * validation (some addresses working) might not be available
38 * before a client's request for a connection fails for good.
39 * Besides, if a single request to an address takes a long time,
40 * then the peer is unlikely worthwhile anyway.
42 #define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
45 * How long is a PONG signature valid? We'll recycle a signature until
46 * 1/4 of this time is remaining. PONGs should expire so that if our
47 * external addresses change an adversary cannot replay them indefinitely.
48 * OTOH, we don't want to spend too much time generating PONG signatures,
49 * so they must have some lifetime to reduce our CPU usage.
51 #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
54 * After how long do we expire an address in a HELLO that we just
55 * validated? This value is also used for our own addresses when we
58 #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
61 * How long before an existing address expires should we again try to
62 * validate it? Must be (significantly) smaller than
63 * HELLO_ADDRESS_EXPIRATION.
65 #define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
68 * How long before we try to check an address again (if it turned out to
69 * be invalid the first time)?
71 #define MAX_REVALIDATION_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
74 * Size of the validation map hashmap.
76 #define VALIDATION_MAP_SIZE 256
79 * Priority to use for PINGs and PONGs
81 #define PING_PRIORITY 1
84 * Message used to ask a peer to validate receipt (to check an address
85 * from a HELLO). Followed by the address we are trying to validate,
86 * or an empty address if we are just sending a PING to confirm that a
87 * connection which the receiver (of the PING) initiated is still valid.
89 struct TransportPingMessage
93 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
95 struct GNUNET_MessageHeader header;
98 * Challenge code (to ensure fresh reply).
100 uint32_t challenge GNUNET_PACKED;
103 * Who is the intended recipient?
105 struct GNUNET_PeerIdentity target;
111 * Message used to validate a HELLO. The challenge is included in the
112 * confirmation to make matching of replies to requests possible. The
113 * signature signs our public key, an expiration time and our address.<p>
115 * This message is followed by our transport address that the PING tried
116 * to confirm (if we liked it). The address can be empty (zero bytes)
117 * if the PING had not address either (and we received the request via
118 * a connection that we initiated).
120 struct TransportPongMessage
124 * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
126 struct GNUNET_MessageHeader header;
129 * Challenge code from PING (showing freshness). Not part of what
130 * is signed so that we can re-use signatures.
132 uint32_t challenge GNUNET_PACKED;
137 struct GNUNET_CRYPTO_RsaSignature signature;
140 * What are we signing and why? Two possible reason codes can be here:
141 * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
142 * plausible address for this peer (pid is set to identity of signer); or
143 * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
144 * an address we used to connect to the peer with the given pid.
146 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
149 * When does this signature expire?
151 struct GNUNET_TIME_AbsoluteNBO expiration;
154 * Either the identity of the peer Who signed this message, or the
155 * identity of the peer that we're connected to using the given
156 * address (depending on purpose.type).
158 struct GNUNET_PeerIdentity pid;
161 * Size of address appended to this message (part of what is
162 * being signed, hence not redundant).
170 * Information about an address under validation
172 struct ValidationEntry
176 * Name of the transport.
178 char *transport_name;
181 * The address, actually a pointer to the end
182 * of this struct. Do not free!
187 * The identity of the peer.
189 struct GNUNET_PeerIdentity pid;
192 * ID of task that will clean up this entry if nothing happens.
194 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
197 * At what time did we send the latest validation request?
199 struct GNUNET_TIME_Absolute send_time;
202 * Until when is this address valid?
203 * ZERO if it is not currently considered valid.
205 struct GNUNET_TIME_Absolute valid_until;
208 * How long until we can try to validate this address again?
209 * FOREVER if the address is for an unsupported plugin (from PEERINFO)
210 * ZERO if the address is considered valid (no validation needed)
211 * otherwise a time in the future if we're currently denying re-validation
213 struct GNUNET_TIME_Absolute validation_block;
216 * Challenge number we used.
229 * Context of currently active requests to peerinfo
230 * for validation of HELLOs.
232 struct CheckHelloValidatedContext
236 * This is a doubly-linked list.
238 struct CheckHelloValidatedContext *next;
241 * This is a doubly-linked list.
243 struct CheckHelloValidatedContext *prev;
246 * Hello that we are validating.
248 const struct GNUNET_HELLO_Message *hello;
251 * Context for peerinfo iteration.
253 struct GNUNET_PEERINFO_IteratorContext *piter;
259 * Head of linked list of HELLOs awaiting validation.
261 static struct CheckHelloValidatedContext *chvc_head;
264 * Tail of linked list of HELLOs awaiting validation
266 static struct CheckHelloValidatedContext *chvc_tail;
269 * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
270 * of the given peer that we are currently validating, have validated
271 * or are blocked from re-validation for a while).
273 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
276 * Map of PeerIdentities to 'struct GST_ValidationIteratorContext's.
278 static struct GNUNET_CONTAINER_MultiHashMap *notify_map;
282 * Start the validation subsystem.
285 GST_validation_start ()
287 validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
288 notify_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
293 * Iterate over validation entries and free them.
295 * @param cls (unused)
296 * @param key peer identity (unused)
297 * @param value a 'struct ValidationEntry' to clean up
298 * @return GNUNET_YES (continue to iterate)
301 cleanup_validation_entry (void *cls,
302 const GNUNET_HashCode *key,
305 struct ValidationEntry *ve = value;
307 GNUNET_free (ve->transport_name);
308 if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
310 GNUNET_SCHEDULER_cancel (ve->timeout_task);
311 ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
319 * Stop the validation subsystem.
322 GST_validation_stop ()
324 struct CheckHelloValidatedContext *chvc;
326 GNUNET_CONTAINER_multihashmap_iterate (validation_map,
327 &cleanup_validation_entry,
329 GNUNET_CONTAINER_multihashmap_destroy (validation_map);
330 validation_map = NULL;
331 GNUNET_assert (GNUNET_CONTAINER_multihashmap_size (notify_map) == 0);
332 GNUNET_CONTAINER_multihashmap_destroy (notify_map);
334 while (NULL != (chvc = chvc_head))
336 GNUNET_CONTAINER_DLL_remove (chvc_head,
339 GNUNET_PEERINFO_iterate_cancel (chvc->piter);
347 * Address validation cleanup task (record no longer needed).
349 * @param cls the 'struct ValidationEntry'
350 * @param tc scheduler context (unused)
353 timeout_hello_validation (void *cls,
354 const struct GNUNET_SCHEDULER_TaskContext *tc)
356 struct ValidationEntry *va = cls;
358 va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
359 GNUNET_STATISTICS_update (GST_stats,
360 gettext_noop ("# address records discarded"),
363 GNUNET_break (GNUNET_OK ==
364 GNUNET_CONTAINER_multihashmap_remove (validation_map,
367 GNUNET_free (va->transport_name);
374 * Context for the validation entry match function.
376 struct ValidationEntryMatchContext
379 * Where to store the result?
381 struct ValidationEntry *ve;
384 * Transport name we're looking for.
386 const char *transport_name;
389 * Address we're interested in.
394 * Number of bytes in 'addr'.
401 * Iterate over validation entries until a matching one is found.
403 * @param cls the 'struct ValidationEntryMatchContext'
404 * @param key peer identity (unused)
405 * @param value a 'struct ValidationEntry' to match
406 * @return GNUNET_YES if the entry does not match,
407 * GNUNET_NO if the entry does match
410 validation_entry_match (void *cls,
411 const GNUNET_HashCode *key,
414 struct ValidationEntryMatchContext *vemc = cls;
415 struct ValidationEntry *ve = value;
417 if ( (ve->addrlen == vemc->addrlen) &&
418 (0 == memcmp (ve->addr, vemc->addr, ve->addrlen)) &&
419 (0 == strcmp (ve->transport_name, vemc->transport_name)) )
429 * Find a ValidationEntry entry for the given neighbour that matches
430 * the given address and transport. If none exists, create one (but
431 * without starting any validation).
433 * @param neighbour which peer we care about
434 * @param tname name of the transport plugin
435 * @param session session to look for, NULL for 'any'; otherwise
436 * can be used for the service to "learn" this session ID
438 * @param addr binary address
439 * @param addrlen length of addr
440 * @return validation entry matching the given specifications
442 static struct ValidationEntry *
443 find_validation_entry (struct GNUNET_PeerIdentity *neighbour,
448 struct ValidationEntryMatchContext vemc;
449 struct ValidationEntry *ve;
452 vemc.transport_name = tname;
454 vemc.addrlen = addrlen;
455 GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
456 &neighbour->hashPubKey,
457 &validation_entry_match,
459 if (NULL != (ve = vemc.ve))
461 ve = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
462 ve->transport_name = GNUNET_strdup (tname);
463 ve->addr = (void*) &ve[1];
464 ve->pid = *neighbour;
465 ve->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
467 memcpy (&ve[1], addr, addrlen);
468 ve->addrlen = addrlen;
469 GNUNET_CONTAINER_multihashmap_put (validation_map,
470 &neighbour->hashPubKey,
472 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
478 * We've received a PING. If appropriate, generate a PONG.
480 * @param sender peer sending the PING
481 * @param hdr the PING
482 * @param plugin_name name of plugin that received the PING
483 * @param sender_address address of the sender as known to the plugin, NULL
484 * if we did not initiate the connection
485 * @param sender_address_len number of bytes in sender_address
488 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
489 const struct GNUNET_MessageHeader *hdr,
490 const char *plugin_name,
491 const void *sender_address,
492 size_t sender_address_len)
498 * We've received a PONG. Check if it matches a pending PING and
499 * mark the respective address as confirmed.
501 * @param sender peer sending the PONG
502 * @param hdr the PONG
503 * @param plugin_name name of plugin that received the PONG
504 * @param sender_address address of the sender as known to the plugin, NULL
505 * if we did not initiate the connection
506 * @param sender_address_len number of bytes in sender_address
509 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
510 const struct GNUNET_MessageHeader *hdr,
511 const char *plugin_name,
512 const void *sender_address,
513 size_t sender_address_len)
519 * Iterator callback to go over all addresses and try to validate them
520 * (unless blocked or already validated).
522 * @param cls pointer to the 'struct PeerIdentity' of the peer
523 * @param tname name of the transport
524 * @param expiration expiration time
525 * @param addr the address
526 * @param addrlen length of the address
527 * @return GNUNET_OK (keep the address)
530 validate_address (void *cls,
532 struct GNUNET_TIME_Absolute expiration,
536 struct GNUNET_PeerIdentity *pid = cls;
537 struct ValidationEntry *ve;
538 struct TransportPingMessage ping;
539 struct GNUNET_TRANSPORT_PluginFunctions *papi;
544 if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
545 return GNUNET_OK; /* expired */
546 ve = find_validation_entry (pid, tname, addr, addrlen);
547 if (GNUNET_TIME_absolute_get_remaining (ve->validation_block).rel_value > 0)
548 return GNUNET_OK; /* blocked */
549 if (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > 0)
550 return GNUNET_OK; /* valid */
551 ve->validation_block = GNUNET_TIME_relative_to_absolute (MAX_REVALIDATION_FREQUENCY);
553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 "Transmitting plain PING to `%s'\n",
556 ping.header.size = htons(sizeof(struct TransportPingMessage));
557 ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
558 ping.challenge = htonl(ve->challenge);
561 slen = strlen(ve->transport_name) + 1;
562 tsize = sizeof(struct TransportPingMessage) + ve->addrlen + slen;
564 char message_buf[tsize];
566 memcpy(message_buf, &ping, sizeof (struct TransportPingMessage));
567 memcpy(&message_buf[sizeof (struct TransportPingMessage)],
570 memcpy(&message_buf[sizeof (struct TransportPingMessage) + slen],
573 papi = GST_plugins_find (ve->transport_name);
574 ret = papi->send (papi->cls,
579 HELLO_VERIFICATION_TIMEOUT,
580 NULL /* no session */,
588 ve->send_time = GNUNET_TIME_absolute_get ();
589 GNUNET_STATISTICS_update (GST_stats,
590 gettext_noop ("# PING without HELLO messages sent"),
600 * We've received a HELLO, check which addresses are new and trigger
603 * @param hello the HELLO we received
606 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
608 const struct GNUNET_HELLO_Message* hm = (const struct GNUNET_HELLO_Message*) hello;
609 struct GNUNET_PeerIdentity pid;
612 GNUNET_HELLO_get_id (hm, &pid))
614 /* malformed HELLO */
618 GNUNET_assert (NULL ==
619 GNUNET_HELLO_iterate_addresses (hm,
627 * Opaque handle to stop incremental validation address callbacks.
629 struct GST_ValidationIteratorContext
632 * Function to call on each address.
634 GST_ValidationAddressCallback cb;
642 * Which peer are we monitoring?
644 struct GNUNET_PeerIdentity target;
649 * Call the callback in the closure for each validation entry.
651 * @param cls the 'struct GST_ValidationIteratorContext'
652 * @param key the peer's identity
653 * @param value the 'struct ValidationEntry'
654 * @return GNUNET_OK (continue to iterate)
657 iterate_addresses (void *cls,
658 const GNUNET_HashCode *key,
661 struct GST_ValidationIteratorContext *vic = cls;
662 struct ValidationEntry *ve = value;
664 vic->cb (vic->cb_cls,
667 ve->validation_block,
676 * Call the given function for each address for the given target.
677 * Can either give a snapshot (synchronous API) or be continuous.
679 * @param target peer information is requested for
680 * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to
681 * continue to give information about addresses as it evolves
682 * @param cb function to call; will not be called after this function returns
683 * if snapshot_only is GNUNET_YES
684 * @param cb_cls closure for 'cb'
685 * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES
687 struct GST_ValidationIteratorContext *
688 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
690 GST_ValidationAddressCallback cb,
693 struct GST_ValidationIteratorContext *vic;
695 vic = GNUNET_malloc (sizeof (struct GST_ValidationIteratorContext));
697 vic->cb_cls = cb_cls;
698 vic->target = *target;
699 GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
703 if (GNUNET_YES == snapshot_only)
708 GNUNET_CONTAINER_multihashmap_put (notify_map,
711 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
717 * Cancel an active validation address iteration.
719 * @param ctx the context of the operation that is cancelled
722 GST_validation_get_addresses_cancel (struct GST_ValidationIteratorContext *ctx)
724 GNUNET_assert (GNUNET_OK ==
725 GNUNET_CONTAINER_multihashmap_remove (notify_map,
726 &ctx->target.hashPubKey,
732 /* end of file gnunet-service-transport_validation.c */