ab334013dce5d698f559b3cb10206cb61d1ab12c
[oweals/gnunet.git] / src / transport / gnunet-service-transport_validation.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 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 3, 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 transport/gnunet-service-transport_validation.c
23  * @brief address validation subsystem
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-transport_validation.h"
28 #include "gnunet-service-transport_plugins.h"
29 #include "gnunet-service-transport_hello.h"
30 #include "gnunet-service-transport_blacklist.h"
31 #include "gnunet-service-transport.h"
32 #include "gnunet_hello_lib.h"
33 #include "gnunet_ats_service.h"
34 #include "gnunet_peerinfo_service.h"
35 #include "gnunet_signatures.h"
36
37 #define KEEP_093_COMPATIBILITY GNUNET_NO
38
39 /**
40  * How long is a PONG signature valid?  We'll recycle a signature until
41  * 1/4 of this time is remaining.  PONGs should expire so that if our
42  * external addresses change an adversary cannot replay them indefinitely.
43  * OTOH, we don't want to spend too much time generating PONG signatures,
44  * so they must have some lifetime to reduce our CPU usage.
45  */
46 #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
47
48 /**
49  * After how long do we expire an address in a HELLO that we just
50  * validated?  This value is also used for our own addresses when we
51  * create a HELLO.
52  */
53 #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
54
55 /**
56  * How often do we allow PINGing an address that we have not yet
57  * validated?  This also determines how long we track an address that
58  * we cannot validate (because after this time we can destroy the
59  * validation record).
60  */
61 #define UNVALIDATED_PING_KEEPALIVE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
62
63 /**
64  * How often do we PING an address that we have successfully validated
65  * in the past but are not actively using?  Should be (significantly)
66  * smaller than HELLO_ADDRESS_EXPIRATION.
67  */
68 #define VALIDATED_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
69
70 /**
71  * How often do we PING an address that we are currently using?
72  */
73 #define CONNECTED_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
74
75 /**
76  * How much delay is acceptable for sending the PING or PONG?
77  */
78 #define ACCEPTABLE_PING_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
79
80 /**
81  * Size of the validation map hashmap.
82  */
83 #define VALIDATION_MAP_SIZE 256
84
85 /**
86  * Priority to use for PINGs
87  */
88 #define PING_PRIORITY 2
89
90 /**
91  * Priority to use for PONGs
92  */
93 #define PONG_PRIORITY 4
94
95
96 GNUNET_NETWORK_STRUCT_BEGIN
97
98 /**
99  * Message used to ask a peer to validate receipt (to check an address
100  * from a HELLO).  Followed by the address we are trying to validate,
101  * or an empty address if we are just sending a PING to confirm that a
102  * connection which the receiver (of the PING) initiated is still valid.
103  */
104 struct TransportPingMessage
105 {
106
107   /**
108    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
109    */
110   struct GNUNET_MessageHeader header;
111
112   /**
113    * Challenge code (to ensure fresh reply).
114    */
115   uint32_t challenge GNUNET_PACKED;
116
117   /**
118    * Who is the intended recipient?
119    */
120   struct GNUNET_PeerIdentity target;
121
122 };
123
124
125 /**
126  * Message used to validate a HELLO.  The challenge is included in the
127  * confirmation to make matching of replies to requests possible.  The
128  * signature signs our public key, an expiration time and our address.<p>
129  *
130  * This message is followed by our transport address that the PING tried
131  * to confirm (if we liked it).  The address can be empty (zero bytes)
132  * if the PING had not address either (and we received the request via
133  * a connection that we initiated).
134  */
135 struct TransportPongMessage
136 {
137
138   /**
139    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
140    */
141   struct GNUNET_MessageHeader header;
142
143   /**
144    * Challenge code from PING (showing freshness).  Not part of what
145    * is signed so that we can re-use signatures.
146    */
147   uint32_t challenge GNUNET_PACKED;
148
149   /**
150    * Signature.
151    */
152   struct GNUNET_CRYPTO_RsaSignature signature;
153
154   /**
155    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
156    * plausible address for the signing peer.
157    */
158   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
159
160   /**
161    * When does this signature expire?
162    */
163   struct GNUNET_TIME_AbsoluteNBO expiration;
164
165   /**
166    * Size of address appended to this message (part of what is
167    * being signed, hence not redundant).
168    */
169   uint32_t addrlen GNUNET_PACKED;
170
171 };
172 GNUNET_NETWORK_STRUCT_END
173
174 /**
175  * Information about an address under validation
176  */
177 struct ValidationEntry
178 {
179
180   /**
181    * The address.
182    */
183   struct GNUNET_HELLO_Address *address;
184
185   /**
186    * Handle to the blacklist check (if we're currently in it).
187    */
188   struct GST_BlacklistCheck *bc;
189
190   /**
191    * Public key of the peer.
192    */
193   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
194
195   /**
196    * The identity of the peer. FIXME: duplicated (also in 'address')
197    */
198   struct GNUNET_PeerIdentity pid;
199
200   /**
201    * ID of task that will clean up this entry if nothing happens.
202    */
203   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
204
205   /**
206    * ID of task that will trigger address revalidation.
207    */
208   GNUNET_SCHEDULER_TaskIdentifier revalidation_task;
209
210   /**
211    * At what time did we send the latest validation request (PING)?
212    */
213   struct GNUNET_TIME_Absolute send_time;
214
215   /**
216    * Until when is this address valid?
217    * ZERO if it is not currently considered valid.
218    */
219   struct GNUNET_TIME_Absolute valid_until;
220
221   /**
222    * How long until we can try to validate this address again?
223    * FOREVER if the address is for an unsupported plugin (from PEERINFO)
224    * ZERO if the address is considered valid (no validation needed)
225    * otherwise a time in the future if we're currently denying re-validation
226    */
227   struct GNUNET_TIME_Absolute revalidation_block;
228
229   /**
230    * Last observed latency for this address (round-trip), delay between
231    * last PING sent and PONG received; FOREVER if we never got a PONG.
232    */
233   struct GNUNET_TIME_Relative latency;
234
235   /**
236    * Challenge number we used.
237    */
238   uint32_t challenge;
239
240   /**
241    * When passing the address in 'add_valid_peer_address', did we
242    * copy the address to the HELLO yet?
243    */
244   int copied;
245
246   /**
247    * Are we currently using this address for a connection?
248    */
249   int in_use;
250
251   /**
252    * Are we expecting a PONG message for this validation entry?
253    */
254   int expecting_pong;
255
256   /* FIXME: DEBUGGING */
257   int last_line_set_to_no;
258   int last_line_set_to_yes;
259 };
260
261
262 /**
263  * Context of currently active requests to peerinfo
264  * for validation of HELLOs.
265  */
266 struct CheckHelloValidatedContext
267 {
268
269   /**
270    * This is a doubly-linked list.
271    */
272   struct CheckHelloValidatedContext *next;
273
274   /**
275    * This is a doubly-linked list.
276    */
277   struct CheckHelloValidatedContext *prev;
278
279   /**
280    * Hello that we are validating.
281    */
282   const struct GNUNET_HELLO_Message *hello;
283
284 };
285
286
287 /**
288  * Head of linked list of HELLOs awaiting validation.
289  */
290 static struct CheckHelloValidatedContext *chvc_head;
291
292 /**
293  * Tail of linked list of HELLOs awaiting validation
294  */
295 static struct CheckHelloValidatedContext *chvc_tail;
296
297 /**
298  * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
299  * of the given peer that we are currently validating, have validated
300  * or are blocked from re-validation for a while).
301  */
302 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
303
304 /**
305  * Context for peerinfo iteration.
306  */
307 static struct GNUNET_PEERINFO_NotifyContext *pnc;
308
309
310 /**
311  * Context for the validation entry match function.
312  */
313 struct ValidationEntryMatchContext
314 {
315   /**
316    * Where to store the result?
317    */
318   struct ValidationEntry *ve;
319
320   /**
321    * Address we're interested in.
322    */
323   const struct GNUNET_HELLO_Address *address;
324
325 };
326
327
328 /**
329  * Iterate over validation entries until a matching one is found.
330  *
331  * @param cls the 'struct ValidationEntryMatchContext'
332  * @param key peer identity (unused)
333  * @param value a 'struct ValidationEntry' to match
334  * @return GNUNET_YES if the entry does not match,
335  *         GNUNET_NO if the entry does match
336  */
337 static int
338 validation_entry_match (void *cls, const struct GNUNET_HashCode * key, void *value)
339 {
340   struct ValidationEntryMatchContext *vemc = cls;
341   struct ValidationEntry *ve = value;
342
343   if (0 == GNUNET_HELLO_address_cmp (ve->address, vemc->address))
344   {
345     vemc->ve = ve;
346     return GNUNET_NO;
347   }
348   return GNUNET_YES;
349 }
350
351
352 /**
353  * Iterate over validation entries and free them.
354  *
355  * @param cls (unused)
356  * @param key peer identity (unused)
357  * @param value a 'struct ValidationEntry' to clean up
358  * @return GNUNET_YES (continue to iterate)
359  */
360 static int
361 cleanup_validation_entry (void *cls, const struct GNUNET_HashCode * key, void *value)
362 {
363   struct ValidationEntry *ve = value;
364
365   if (NULL != ve->bc)
366   {
367     GST_blacklist_test_cancel (ve->bc);
368     ve->bc = NULL;
369   }
370   GNUNET_break (GNUNET_OK ==
371                 GNUNET_CONTAINER_multihashmap_remove (validation_map,
372                                                       &ve->pid.hashPubKey, ve));
373   GNUNET_HELLO_address_free (ve->address);
374   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
375   {
376     GNUNET_SCHEDULER_cancel (ve->timeout_task);
377     ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
378   }
379   if (GNUNET_SCHEDULER_NO_TASK != ve->revalidation_task)
380   {
381     GNUNET_SCHEDULER_cancel (ve->revalidation_task);
382     ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK;
383   }
384   GNUNET_free (ve);
385   return GNUNET_OK;
386 }
387
388
389 /**
390  * Address validation cleanup task.  Assesses if the record is no
391  * longer valid and then possibly triggers its removal.
392  *
393  * @param cls the 'struct ValidationEntry'
394  * @param tc scheduler context (unused)
395  */
396 static void
397 timeout_hello_validation (void *cls,
398                           const struct GNUNET_SCHEDULER_TaskContext *tc)
399 {
400   struct ValidationEntry *ve = cls;
401   struct GNUNET_TIME_Absolute max;
402   struct GNUNET_TIME_Relative left;
403
404   ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
405   max = GNUNET_TIME_absolute_max (ve->valid_until, ve->revalidation_block);
406   left = GNUNET_TIME_absolute_get_remaining (max);
407   if (left.rel_value > 0)
408   {
409     /* should wait a bit longer */
410     ve->timeout_task =
411         GNUNET_SCHEDULER_add_delayed (left, &timeout_hello_validation, ve);
412     return;
413   }
414   GNUNET_STATISTICS_update (GST_stats,
415                             gettext_noop ("# address records discarded"), 1,
416                             GNUNET_NO);
417   cleanup_validation_entry (NULL, &ve->pid.hashPubKey, ve);
418 }
419
420
421 /**
422  * Function called with the result from blacklisting.
423  * Send a PING to the other peer if a communication is allowed.
424  *
425  * @param cls our 'struct ValidationEntry'
426  * @param pid identity of the other peer
427  * @param result GNUNET_OK if the connection is allowed, GNUNET_NO if not
428  */
429 static void
430 transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid,
431                           int result)
432 {
433   struct ValidationEntry *ve = cls;
434   struct TransportPingMessage ping;
435   struct GNUNET_TRANSPORT_PluginFunctions *papi;
436   const struct GNUNET_MessageHeader *hello;
437   ssize_t ret;
438   size_t tsize;
439   size_t slen;
440   uint16_t hsize;
441
442   ve->bc = NULL;
443   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s' %s %s\n",
444               GNUNET_i2s (pid), GST_plugins_a2s (ve->address), ve->address->transport_name);
445
446   slen = strlen (ve->address->transport_name) + 1;
447   hello = GST_hello_get ();
448   hsize = ntohs (hello->size);
449   tsize =
450       sizeof (struct TransportPingMessage) + ve->address->address_length +
451       slen + hsize;
452
453   ping.header.size =
454       htons (sizeof (struct TransportPingMessage) +
455              ve->address->address_length + slen);
456   ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
457   ping.challenge = htonl (ve->challenge);
458   ping.target = *pid;
459
460   if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
461   {
462     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463                 _
464                 ("Not transmitting `%s' with `%s', message too big (%u bytes!). This should not happen.\n"),
465                 "HELLO", "PING", (unsigned int) tsize);
466     /* message too big (!?), get rid of HELLO */
467     hsize = 0;
468     tsize =
469         sizeof (struct TransportPingMessage) + ve->address->address_length +
470         slen + hsize;
471   }
472   {
473     char message_buf[tsize];
474
475     /* build message with structure:
476      *  [HELLO][TransportPingMessage][Transport name][Address] */
477     memcpy (message_buf, hello, hsize);
478     memcpy (&message_buf[hsize], &ping, sizeof (struct TransportPingMessage));
479     memcpy (&message_buf[sizeof (struct TransportPingMessage) + hsize],
480             ve->address->transport_name, slen);
481     memcpy (&message_buf[sizeof (struct TransportPingMessage) + slen + hsize],
482             ve->address->address, ve->address->address_length);
483     papi = GST_plugins_find (ve->address->transport_name);
484     if (papi == NULL)
485       ret = -1;
486     else
487     {
488       GNUNET_assert (papi->send != NULL);
489       GNUNET_assert (papi->get_session != NULL);
490       struct Session * session = papi->get_session(papi->cls, ve->address);
491
492       if (session != NULL)
493       {
494         ret = papi->send (papi->cls, session,
495                           message_buf, tsize,
496                           PING_PRIORITY, ACCEPTABLE_PING_DELAY,
497                           NULL, NULL);
498       }
499       else
500       {
501         /* Could not get a valid session */
502         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not get a valid session for `%s' %s\n",
503                     GNUNET_i2s (pid), GST_plugins_a2s (ve->address));
504         ret = -1;
505       }
506     }
507   }
508   if (-1 != ret)
509   {
510     ve->send_time = GNUNET_TIME_absolute_get ();
511     GNUNET_STATISTICS_update (GST_stats,
512                               gettext_noop
513                               ("# PING without HELLO messages sent"), 1,
514                               GNUNET_NO);
515     ve->expecting_pong = GNUNET_YES;
516   }
517 }
518
519
520 /**
521  * Do address validation again to keep address valid.
522  *
523  * @param cls the 'struct ValidationEntry'
524  * @param tc scheduler context (unused)
525  */
526 static void
527 revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
528 {
529   struct ValidationEntry *ve = cls;
530   struct GNUNET_TIME_Relative canonical_delay;
531   struct GNUNET_TIME_Relative delay;
532   struct GST_BlacklistCheck *bc;
533   uint32_t rdelay;
534
535   ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK;
536   delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block);
537   /* How long until we can possibly permit the next PING? */
538   canonical_delay =
539       (ve->in_use ==
540        GNUNET_YES) ? CONNECTED_PING_FREQUENCY
541       : ((GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value >
542           0) ? VALIDATED_PING_FREQUENCY : UNVALIDATED_PING_KEEPALIVE);
543   if (delay.rel_value > canonical_delay.rel_value * 2)
544   {
545     /* situation changed, recalculate delay */
546     delay = canonical_delay;
547     ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay);
548   }
549   if (delay.rel_value > 0)
550   {
551     /* should wait a bit longer */
552     ve->revalidation_task =
553         GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);
554     return;
555   }
556   ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay);
557
558   /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */
559   rdelay =
560       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
561                                 canonical_delay.rel_value);
562   delay =
563       GNUNET_TIME_relative_add (canonical_delay,
564                                 GNUNET_TIME_relative_multiply
565                                 (GNUNET_TIME_UNIT_MILLISECONDS, rdelay));
566   ve->revalidation_task =
567       GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);
568
569   /* start PINGing by checking blacklist */
570   GNUNET_STATISTICS_update (GST_stats,
571                             gettext_noop ("# address revalidations started"), 1,
572                             GNUNET_NO);
573   bc = GST_blacklist_test_allowed (&ve->pid, ve->address->transport_name,
574                                    &transmit_ping_if_allowed, ve);
575   if (NULL != bc)
576     ve->bc = bc;                /* only set 'bc' if 'transmit_ping_if_allowed' was not already
577                                  * called... */
578 }
579
580
581 /**
582  * Find a ValidationEntry entry for the given neighbour that matches
583  * the given address and transport.  If none exists, create one (but
584  * without starting any validation).
585  *
586  * @param public_key public key of the peer, NULL for unknown
587  * @param address address to find
588  * @return validation entry matching the given specifications, NULL
589  *         if we don't have an existing entry and no public key was given
590  */
591 static struct ValidationEntry *
592 find_validation_entry (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
593                        *public_key, const struct GNUNET_HELLO_Address *address)
594 {
595   struct ValidationEntryMatchContext vemc;
596   struct ValidationEntry *ve;
597
598   vemc.ve = NULL;
599   vemc.address = address;
600   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
601                                               &address->peer.hashPubKey,
602                                               &validation_entry_match, &vemc);
603   if (NULL != (ve = vemc.ve))
604     return ve;
605   if (public_key == NULL)
606     return NULL;
607   ve = GNUNET_malloc (sizeof (struct ValidationEntry));
608   ve->in_use = GNUNET_SYSERR; /* not defined */
609   ve->last_line_set_to_no  = 0;
610   ve->last_line_set_to_yes  = 0;
611   ve->address = GNUNET_HELLO_address_copy (address);
612   ve->public_key = *public_key;
613   ve->pid = address->peer;
614   ve->latency = GNUNET_TIME_UNIT_FOREVER_REL;
615   ve->challenge =
616       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
617   ve->timeout_task =
618       GNUNET_SCHEDULER_add_delayed (UNVALIDATED_PING_KEEPALIVE,
619                                     &timeout_hello_validation, ve);
620   GNUNET_CONTAINER_multihashmap_put (validation_map, &address->peer.hashPubKey,
621                                      ve,
622                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
623   ve->expecting_pong = GNUNET_NO;
624   return ve;
625 }
626
627
628 /**
629  * Iterator which adds the given address to the set of validated
630  * addresses.
631  *
632  * @param cls original HELLO message
633  * @param address the address
634  * @param expiration expiration time
635  * @return GNUNET_OK (keep the address)
636  */
637 static int
638 add_valid_address (void *cls, const struct GNUNET_HELLO_Address *address,
639                    struct GNUNET_TIME_Absolute expiration)
640 {
641   const struct GNUNET_HELLO_Message *hello = cls;
642   struct ValidationEntry *ve;
643   struct GNUNET_PeerIdentity pid;
644   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
645
646   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
647     return GNUNET_OK;           /* expired */
648   if ((GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid)) ||
649       (GNUNET_OK != GNUNET_HELLO_get_key (hello, &public_key)))
650   {
651     GNUNET_break (0);
652     return GNUNET_OK;           /* invalid HELLO !? */
653   }
654   if (0 == memcmp (&GST_my_identity, &pid, sizeof (struct GNUNET_PeerIdentity)))
655   {
656     /* Peerinfo returned own identity, skip validation */
657     return GNUNET_OK;
658   }
659
660   ve = find_validation_entry (&public_key, address);
661   ve->valid_until = GNUNET_TIME_absolute_max (ve->valid_until, expiration);
662
663   if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task)
664     ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
665   GNUNET_ATS_address_add (GST_ats, address, NULL, NULL, 0);
666   return GNUNET_OK;
667 }
668
669
670 /**
671  * Function called for any HELLO known to PEERINFO.
672  *
673  * @param cls unused
674  * @param peer id of the peer, NULL for last call
675  * @param hello hello message for the peer (can be NULL)
676  * @param err_msg error message
677  */
678 static void
679 process_peerinfo_hello (void *cls, const struct GNUNET_PeerIdentity *peer,
680                         const struct GNUNET_HELLO_Message *hello,
681                         const char *err_msg)
682 {
683   GNUNET_assert (NULL != peer);
684   if (NULL == hello)
685     return;
686   GNUNET_assert (NULL ==
687                  GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO,
688                                                  &add_valid_address,
689                                                  (void *) hello));
690 }
691
692
693 /**
694  * Start the validation subsystem.
695  */
696 void
697 GST_validation_start ()
698 {
699   validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE,
700                                                          GNUNET_NO);
701   pnc = GNUNET_PEERINFO_notify (GST_cfg, &process_peerinfo_hello, NULL);
702 }
703
704
705 /**
706  * Stop the validation subsystem.
707  */
708 void
709 GST_validation_stop ()
710 {
711   struct CheckHelloValidatedContext *chvc;
712
713   GNUNET_CONTAINER_multihashmap_iterate (validation_map,
714                                          &cleanup_validation_entry, NULL);
715   GNUNET_CONTAINER_multihashmap_destroy (validation_map);
716   validation_map = NULL;
717   while (NULL != (chvc = chvc_head))
718   {
719     GNUNET_CONTAINER_DLL_remove (chvc_head, chvc_tail, chvc);
720     GNUNET_free (chvc);
721   }
722   GNUNET_PEERINFO_notify_cancel (pnc);
723 }
724
725
726 /**
727  * Send the given PONG to the given address.
728  *
729  * @param cls the PONG message
730  * @param public_key public key for the peer, never NULL
731  * @param valid_until is ZERO if we never validated the address,
732  *                    otherwise a time up to when we consider it (or was) valid
733  * @param validation_block  is FOREVER if the address is for an unsupported plugin (from PEERINFO)
734  *                          is ZERO if the address is considered valid (no validation needed)
735  *                          otherwise a time in the future if we're currently denying re-validation
736  * @param address target address
737  */
738 static void
739 multicast_pong (void *cls,
740                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
741                 *public_key, struct GNUNET_TIME_Absolute valid_until,
742                 struct GNUNET_TIME_Absolute validation_block,
743                 const struct GNUNET_HELLO_Address *address)
744 {
745   struct TransportPongMessage *pong = cls;
746   struct GNUNET_TRANSPORT_PluginFunctions *papi;
747
748   papi = GST_plugins_find (address->transport_name);
749   if (papi == NULL)
750     return;
751
752   GNUNET_assert (papi->send != NULL);
753   GNUNET_assert (papi->get_session != NULL);
754
755   struct Session * session = papi->get_session(papi->cls, address);
756   if (session == NULL)
757   {
758      GNUNET_break (0);
759      return;
760   }
761
762   papi->send (papi->cls, session,
763               (const char *) pong, ntohs (pong->header.size),
764               PONG_PRIORITY, ACCEPTABLE_PING_DELAY,
765               NULL, NULL);
766 }
767
768
769 /**
770  * We've received a PING.  If appropriate, generate a PONG.
771  *
772  * @param sender peer sending the PING
773  * @param hdr the PING
774  * @param sender_address the sender address as we got it
775  * @param session session we got the PING from
776  */
777 void
778 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
779                             const struct GNUNET_MessageHeader *hdr,
780                             const struct GNUNET_HELLO_Address *sender_address,
781                             struct Session *session)
782 {
783   const struct TransportPingMessage *ping;
784   struct TransportPongMessage *pong;
785   struct GNUNET_TRANSPORT_PluginFunctions *papi;
786   struct GNUNET_CRYPTO_RsaSignature *sig_cache;
787   struct GNUNET_TIME_Absolute *sig_cache_exp;
788   const char *addr;
789   const char *addrend;
790   size_t alen;
791   size_t slen;
792   ssize_t ret;
793   int buggy = GNUNET_NO;
794   struct GNUNET_HELLO_Address address;
795
796   if (ntohs (hdr->size) < sizeof (struct TransportPingMessage))
797   {
798     GNUNET_break_op (0);
799     return;
800   }
801   ping = (const struct TransportPingMessage *) hdr;
802   if (0 !=
803       memcmp (&ping->target, &GST_my_identity,
804               sizeof (struct GNUNET_PeerIdentity)))
805   {
806     GNUNET_STATISTICS_update (GST_stats,
807                               gettext_noop
808                               ("# PING message for different peer received"), 1,
809                               GNUNET_NO);
810     return;
811   }
812   GNUNET_STATISTICS_update (GST_stats,
813                             gettext_noop ("# PING messages received"), 1,
814                             GNUNET_NO);
815   addr = (const char *) &ping[1];
816   alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage);
817   /* peer wants to confirm that this is one of our addresses, this is what is
818    * used for address validation */
819
820   sig_cache = NULL;
821   sig_cache_exp = NULL;
822
823   if (alen > 0)
824   {
825     addrend = memchr (addr, '\0', alen);
826     if (NULL == addrend)
827     {
828       GNUNET_break_op (0);
829       return;
830     }
831     addrend++;
832     slen = strlen (addr) + 1;
833     alen -= slen;
834     address.address = addrend;
835     address.address_length = alen;
836     address.transport_name = addr;
837     address.peer = GST_my_identity;
838
839
840     if (GNUNET_YES != GST_hello_test_address (&address, &sig_cache, &sig_cache_exp))
841     {
842 #if KEEP_093_COMPATIBILITY
843       int idsize = sizeof (GST_my_identity);
844       if (alen <= idsize)
845       {
846         if (0 == memcmp (address.address, &GST_my_identity, alen))
847           buggy = GNUNET_YES;
848       }
849       else if (alen <= (idsize + strlen (address.transport_name)))
850       {
851         char *achar = (char *) &address.address;
852         if ((0 == memcmp (address.address, &GST_my_identity, idsize)) &&
853             (0 == memcmp (&achar[idsize], address.transport_name, alen - idsize)))
854           buggy = GNUNET_YES;
855       }
856       else
857       {
858         /* Not predicatable */
859         return;
860       }
861 #endif
862       if (GNUNET_NO == buggy)
863       {
864         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865                     "Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n",
866                     GNUNET_i2s (sender),
867                     GST_plugins_a2s (&address));
868         return;
869       }
870       else
871       {
872         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
873                     _("Received a PING message with validation bug from `%s'\n"),
874                     GNUNET_i2s (sender));
875       }
876     }
877   }
878   else
879   {
880     addrend = NULL;             /* make gcc happy */
881     slen = 0;
882     static struct GNUNET_CRYPTO_RsaSignature no_address_signature;
883     static struct GNUNET_TIME_Absolute no_address_signature_expiration;
884
885     sig_cache = &no_address_signature;
886     sig_cache_exp = &no_address_signature_expiration;
887   }
888
889   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
890               "I am `%s', sending PONG to peer `%s'\n",
891               GNUNET_h2s (&GST_my_identity.hashPubKey),
892               GNUNET_i2s (sender));
893
894   /* message with structure:
895    * [TransportPongMessage][Transport name][Address] */
896
897   pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
898   pong->header.size =
899       htons (sizeof (struct TransportPongMessage) + alen + slen);
900   pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
901   pong->purpose.size =
902       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
903              sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
904              alen + slen);
905   pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
906   memcpy (&pong->challenge, &ping->challenge, sizeof (ping->challenge));
907   pong->addrlen = htonl (alen + slen);
908   memcpy (&pong[1], addr, slen);   /* Copy transport plugin */
909 #if KEEP_093_COMPATIBILITY
910   if (GNUNET_YES == buggy)
911   {
912     int idsize = sizeof (GST_my_identity);
913     if (alen <= idsize)
914     {
915       memcpy (&((char *) &pong[1])[slen], &GST_my_identity, alen);
916     }
917     else if (alen <= (idsize + strlen (address.transport_name) + 1))
918     {
919       memcpy (&((char *) &pong[1])[slen], &GST_my_identity, idsize);
920       memcpy (&((char *) &pong[1])[slen + idsize], address.transport_name, alen-idsize);
921     }
922     else
923     {
924       /* If this would happen, we would have a inconsistent PING we cannot reproduce */
925       GNUNET_free (pong);
926       return;
927     }
928
929     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating buggy PONG signature to indicate ownership.\n");
930     pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
931     GNUNET_assert (GNUNET_OK ==
932                    GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
933                                            &pong->signature));
934   }
935   else
936   {
937 #endif
938     if (alen > 0)
939     {
940         GNUNET_assert (NULL != addrend);
941         memcpy (&((char *) &pong[1])[slen], addrend, alen);
942     }
943     if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value <
944         PONG_SIGNATURE_LIFETIME.rel_value / 4)
945     {
946       /* create / update cached sig */
947       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948                   "Creating PONG signature to indicate ownership.\n");
949       *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
950       pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
951       GNUNET_assert (GNUNET_OK ==
952                      GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
953                                              sig_cache));
954     }
955     else
956     {
957       pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
958     }
959     pong->signature = *sig_cache;
960
961 #if KEEP_093_COMPATIBILITY
962   }
963 #endif
964
965   GNUNET_assert (sender_address != NULL);
966
967   /* first see if the session we got this PING from can be used to transmit
968    * a response reliably */
969   papi = GST_plugins_find (sender_address->transport_name);
970   if (papi == NULL)
971     ret = -1;
972   else
973   {
974     GNUNET_assert (papi->send != NULL);
975     GNUNET_assert (papi->get_session != NULL);
976
977     if (session == NULL)
978     {
979       session = papi->get_session (papi->cls, sender_address);
980     }
981     if (session == NULL)
982     {
983       GNUNET_break (0);
984       ret = -1;
985     }
986     else
987     {
988       ret = papi->send (papi->cls, session,
989                         (const char *) pong, ntohs (pong->header.size),
990                         PONG_PRIORITY, ACCEPTABLE_PING_DELAY,
991                         NULL, NULL);
992     }
993   }
994   if (ret != -1)
995   {
996     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
997                 "Transmitted PONG to `%s' via reliable mechanism\n",
998                 GNUNET_i2s (sender));
999     /* done! */
1000     GNUNET_STATISTICS_update (GST_stats,
1001                               gettext_noop
1002                               ("# PONGs unicast via reliable transport"), 1,
1003                               GNUNET_NO);
1004     GNUNET_free (pong);
1005     return;
1006   }
1007
1008   /* no reliable method found, try transmission via all known addresses */
1009   GNUNET_STATISTICS_update (GST_stats,
1010                             gettext_noop
1011                             ("# PONGs multicast to all available addresses"), 1,
1012                             GNUNET_NO);
1013   GST_validation_get_addresses (sender, &multicast_pong, pong);
1014   GNUNET_free (pong);
1015 }
1016
1017
1018 /**
1019  * Context for the 'validate_address' function
1020  */
1021 struct ValidateAddressContext
1022 {
1023   /**
1024    * Hash of the public key of the peer whose address is being validated.
1025    */
1026   struct GNUNET_PeerIdentity pid;
1027
1028   /**
1029    * Public key of the peer whose address is being validated.
1030    */
1031   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
1032 };
1033
1034
1035 /**
1036  * Iterator callback to go over all addresses and try to validate them
1037  * (unless blocked or already validated).
1038  *
1039  * @param cls pointer to a 'struct ValidateAddressContext'
1040  * @param address the address
1041  * @param expiration expiration time
1042  * @return GNUNET_OK (keep the address)
1043  */
1044 static int
1045 validate_address_iterator (void *cls,
1046                            const struct GNUNET_HELLO_Address *address,
1047                            struct GNUNET_TIME_Absolute expiration)
1048 {
1049   const struct ValidateAddressContext *vac = cls;
1050   struct ValidationEntry *ve;
1051
1052   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
1053     return GNUNET_OK;           /* expired */
1054   ve = find_validation_entry (&vac->public_key, address);
1055   if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task)
1056     ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
1057   return GNUNET_OK;
1058 }
1059
1060
1061 /**
1062  * Add the validated peer address to the HELLO.
1063  *
1064  * @param cls the 'struct ValidationEntry' with the validated address
1065  * @param max space in buf
1066  * @param buf where to add the address
1067  * @return number of bytes written, 0 to signal the
1068  *         end of the iteration.
1069  */
1070 static size_t
1071 add_valid_peer_address (void *cls, size_t max, void *buf)
1072 {
1073   struct ValidationEntry *ve = cls;
1074
1075   if (GNUNET_YES == ve->copied)
1076     return 0;                   /* terminate */
1077   ve->copied = GNUNET_YES;
1078   return GNUNET_HELLO_add_address (ve->address, ve->valid_until, buf, max);
1079 }
1080
1081
1082 /**
1083  * We've received a PONG.  Check if it matches a pending PING and
1084  * mark the respective address as confirmed.
1085  *
1086  * @param sender peer sending the PONG
1087  * @param hdr the PONG
1088  */
1089 void
1090 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
1091                             const struct GNUNET_MessageHeader *hdr)
1092 {
1093   const struct TransportPongMessage *pong;
1094   struct ValidationEntry *ve;
1095   const char *tname;
1096   const char *addr;
1097   size_t addrlen;
1098   size_t slen;
1099   size_t size;
1100   struct GNUNET_HELLO_Message *hello;
1101   struct GNUNET_HELLO_Address address;
1102
1103   if (ntohs (hdr->size) < sizeof (struct TransportPongMessage))
1104   {
1105     GNUNET_break_op (0);
1106     return;
1107   }
1108   GNUNET_STATISTICS_update (GST_stats,
1109                             gettext_noop ("# PONG messages received"), 1,
1110                             GNUNET_NO);
1111
1112   /* message with structure:
1113    * [TransportPongMessage][Transport name][Address] */
1114
1115   pong = (const struct TransportPongMessage *) hdr;
1116   tname = (const char *) &pong[1];
1117   size = ntohs (hdr->size) - sizeof (struct TransportPongMessage);
1118   addr = memchr (tname, '\0', size);
1119   if (NULL == addr)
1120   {
1121     GNUNET_break_op (0);
1122     return;
1123   }
1124   addr++;
1125   slen = strlen (tname) + 1;
1126   addrlen = size - slen;
1127   address.peer = *sender;
1128   address.address = addr;
1129   address.address_length = addrlen;
1130   address.transport_name = tname;
1131   ve = find_validation_entry (NULL, &address);
1132   if ((NULL == ve) || (ve->expecting_pong == GNUNET_NO))
1133   {
1134     GNUNET_STATISTICS_update (GST_stats,
1135                               gettext_noop
1136                               ("# PONGs dropped, no matching pending validation"),
1137                               1, GNUNET_NO);
1138     return;
1139   }
1140   /* now check that PONG is well-formed */
1141   if (0 != memcmp (&ve->pid, sender, sizeof (struct GNUNET_PeerIdentity)))
1142   {
1143     GNUNET_break_op (0);
1144     return;
1145   }
1146
1147   if (GNUNET_OK !=
1148       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
1149                                 &pong->purpose, &pong->signature,
1150                                 &ve->public_key))
1151   {
1152     GNUNET_break_op (0);
1153     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1154                 "Invalid signature on address %s:%s from peer `%s'\n",
1155                 tname, GST_plugins_a2s (ve->address),
1156                 GNUNET_i2s (sender));
1157     return;
1158   }
1159
1160   if (GNUNET_TIME_absolute_get_remaining
1161       (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0)
1162   {
1163     GNUNET_STATISTICS_update (GST_stats,
1164                               gettext_noop
1165                               ("# PONGs dropped, signature expired"), 1,
1166                               GNUNET_NO);
1167     return;
1168   }
1169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1170               "Address validated for peer `%s' with plugin `%s': `%s'\n",
1171               GNUNET_i2s (sender), tname, GST_plugins_a2s (ve->address));
1172   /* validity achieved, remember it! */
1173   ve->expecting_pong = GNUNET_NO;
1174   ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1175   ve->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
1176   {
1177     struct GNUNET_ATS_Information ats;
1178     ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1179     ats.value = htonl ((uint32_t) ve->latency.rel_value);
1180     GNUNET_ATS_address_add (GST_ats, ve->address, NULL, &ats, 1);
1181   }
1182   /* build HELLO to store in PEERINFO */
1183   ve->copied = GNUNET_NO;
1184   hello = GNUNET_HELLO_create (&ve->public_key, &add_valid_peer_address, ve);
1185   GNUNET_PEERINFO_add_peer (GST_peerinfo, hello, NULL, NULL);
1186   GNUNET_free (hello);
1187 }
1188
1189
1190 /**
1191  * We've received a HELLO, check which addresses are new and trigger
1192  * validation.
1193  *
1194  * @param hello the HELLO we received
1195  */
1196 void
1197 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
1198 {
1199   const struct GNUNET_HELLO_Message *hm =
1200       (const struct GNUNET_HELLO_Message *) hello;
1201   struct ValidateAddressContext vac;
1202   struct GNUNET_HELLO_Message *h;
1203
1204   if ((GNUNET_OK != GNUNET_HELLO_get_id (hm, &vac.pid)) ||
1205       (GNUNET_OK != GNUNET_HELLO_get_key (hm, &vac.public_key)))
1206   {
1207     /* malformed HELLO */
1208     GNUNET_break (0);
1209     return;
1210   }
1211   if (0 ==
1212       memcmp (&GST_my_identity, &vac.pid, sizeof (struct GNUNET_PeerIdentity)))
1213     return;
1214   /* Add peer identity without addresses to peerinfo service */
1215   h = GNUNET_HELLO_create (&vac.public_key, NULL, NULL);
1216   GNUNET_PEERINFO_add_peer (GST_peerinfo, h, NULL, NULL);
1217
1218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1219               _("Adding `%s' without addresses for peer `%s'\n"), "HELLO",
1220               GNUNET_i2s (&vac.pid));
1221
1222   GNUNET_free (h);
1223   GNUNET_assert (NULL ==
1224                  GNUNET_HELLO_iterate_addresses (hm, GNUNET_NO,
1225                                                  &validate_address_iterator,
1226                                                  &vac));
1227 }
1228
1229
1230 /**
1231  * Closure for 'iterate_addresses'
1232  */
1233 struct IteratorContext
1234 {
1235   /**
1236    * Function to call on each address.
1237    */
1238   GST_ValidationAddressCallback cb;
1239
1240   /**
1241    * Closure for 'cb'.
1242    */
1243   void *cb_cls;
1244
1245 };
1246
1247
1248 /**
1249  * Call the callback in the closure for each validation entry.
1250  *
1251  * @param cls the 'struct GST_ValidationIteratorContext'
1252  * @param key the peer's identity
1253  * @param value the 'struct ValidationEntry'
1254  * @return GNUNET_OK (continue to iterate)
1255  */
1256 static int
1257 iterate_addresses (void *cls, const struct GNUNET_HashCode * key, void *value)
1258 {
1259   struct IteratorContext *ic = cls;
1260   struct ValidationEntry *ve = value;
1261
1262   ic->cb (ic->cb_cls, &ve->public_key, ve->valid_until, ve->revalidation_block,
1263           ve->address);
1264   return GNUNET_OK;
1265 }
1266
1267
1268 /**
1269  * Call the given function for each address for the given target.
1270  * Can either give a snapshot (synchronous API) or be continuous.
1271  *
1272  * @param target peer information is requested for
1273  * @param cb function to call; will not be called after this function returns
1274  * @param cb_cls closure for 'cb'
1275  */
1276 void
1277 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
1278                               GST_ValidationAddressCallback cb, void *cb_cls)
1279 {
1280   struct IteratorContext ic;
1281
1282   ic.cb = cb;
1283   ic.cb_cls = cb_cls;
1284   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
1285                                               &target->hashPubKey,
1286                                               &iterate_addresses, &ic);
1287 }
1288
1289
1290 /**
1291  * Update if we are using an address for a connection actively right now.
1292  * Based on this, the validation module will measure latency for the
1293  * address more or less often.
1294  *
1295  * @param address the address
1296  * @param session the session
1297  * @param in_use GNUNET_YES if we are now using the address for a connection,
1298  *               GNUNET_NO if we are no longer using the address for a connection
1299  * @param line line of caller just for DEBUGGING!
1300  */
1301 void
1302 GST_validation_set_address_use (const struct GNUNET_HELLO_Address *address,
1303                                 struct Session *session,
1304                                 int in_use,
1305                                 int line)
1306 {
1307   struct ValidationEntry *ve;
1308
1309   if (NULL != address)
1310     ve = find_validation_entry (NULL, address);
1311   else
1312     ve = NULL;                  /* FIXME: lookup based on session... */
1313   if (NULL == ve)
1314   {
1315     /* this can happen for inbound connections (sender_address_len == 0); */
1316     return;
1317   }
1318   if (ve->in_use == in_use)
1319   {
1320
1321     if (GNUNET_YES == in_use)
1322     {
1323       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1324                   "Error setting address in use for peer `%s' `%s' to USED: set last time by %i, called now by %i\n",
1325                   GNUNET_i2s (&address->peer), GST_plugins_a2s (address),
1326                   ve->last_line_set_to_yes, line);
1327     }
1328     if (GNUNET_NO == in_use)
1329     {
1330       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1331                   "Error setting address in use for peer `%s' `%s' to NOT_USED: set last time by %i, called now by %i\n",
1332                   GNUNET_i2s (&address->peer), GST_plugins_a2s (address),
1333                   ve->last_line_set_to_no, line);
1334     }
1335   }
1336
1337   if (GNUNET_YES == in_use)
1338   {
1339     ve->last_line_set_to_yes = line;
1340   }
1341   if (GNUNET_NO == in_use)
1342   {
1343     ve->last_line_set_to_no = line;
1344   }
1345
1346   GNUNET_break (ve->in_use != in_use);  /* should be different... */
1347   ve->in_use = in_use;
1348   if (in_use == GNUNET_YES)
1349   {
1350     /* from now on, higher frequeny, so reschedule now */
1351     GNUNET_SCHEDULER_cancel (ve->revalidation_task);
1352     ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
1353   }
1354 }
1355
1356
1357 /**
1358  * Query validation about the latest observed latency on a given
1359  * address.
1360  *
1361  * @param sender peer
1362  * @param address the address
1363  * @param session session
1364  * @return observed latency of the address, FOREVER if the address was
1365  *         never successfully validated
1366  */
1367 struct GNUNET_TIME_Relative
1368 GST_validation_get_address_latency (const struct GNUNET_PeerIdentity *sender,
1369                                     const struct GNUNET_HELLO_Address *address,
1370                                     struct Session *session)
1371 {
1372   struct ValidationEntry *ve;
1373
1374   if (NULL == address)
1375   {
1376     GNUNET_break (0);           // FIXME: support having latency only with session...
1377     return GNUNET_TIME_UNIT_FOREVER_REL;
1378   }
1379   ve = find_validation_entry (NULL, address);
1380   if (NULL == ve)
1381     return GNUNET_TIME_UNIT_FOREVER_REL;
1382   return ve->latency;
1383 }
1384
1385
1386 /* end of file gnunet-service-transport_validation.c */