- coverity 10080
[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   pnc = GNUNET_PEERINFO_notify (GST_cfg, &process_peerinfo_hello, NULL);
701 }
702
703
704 /**
705  * Stop the validation subsystem.
706  */
707 void
708 GST_validation_stop ()
709 {
710   struct CheckHelloValidatedContext *chvc;
711
712   GNUNET_CONTAINER_multihashmap_iterate (validation_map,
713                                          &cleanup_validation_entry, NULL);
714   GNUNET_CONTAINER_multihashmap_destroy (validation_map);
715   validation_map = NULL;
716   while (NULL != (chvc = chvc_head))
717   {
718     GNUNET_CONTAINER_DLL_remove (chvc_head, chvc_tail, chvc);
719     GNUNET_free (chvc);
720   }
721   GNUNET_PEERINFO_notify_cancel (pnc);
722 }
723
724
725 /**
726  * Send the given PONG to the given address.
727  *
728  * @param cls the PONG message
729  * @param public_key public key for the peer, never NULL
730  * @param valid_until is ZERO if we never validated the address,
731  *                    otherwise a time up to when we consider it (or was) valid
732  * @param validation_block  is FOREVER if the address is for an unsupported plugin (from PEERINFO)
733  *                          is ZERO if the address is considered valid (no validation needed)
734  *                          otherwise a time in the future if we're currently denying re-validation
735  * @param address target address
736  */
737 static void
738 multicast_pong (void *cls,
739                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
740                 *public_key, struct GNUNET_TIME_Absolute valid_until,
741                 struct GNUNET_TIME_Absolute validation_block,
742                 const struct GNUNET_HELLO_Address *address)
743 {
744   struct TransportPongMessage *pong = cls;
745   struct GNUNET_TRANSPORT_PluginFunctions *papi;
746
747   papi = GST_plugins_find (address->transport_name);
748   if (papi == NULL)
749     return;
750
751   GNUNET_assert (papi->send != NULL);
752   GNUNET_assert (papi->get_session != NULL);
753
754   struct Session * session = papi->get_session(papi->cls, address);
755   if (session == NULL)
756   {
757      GNUNET_break (0);
758      return;
759   }
760
761   papi->send (papi->cls, session,
762               (const char *) pong, ntohs (pong->header.size),
763               PONG_PRIORITY, ACCEPTABLE_PING_DELAY,
764               NULL, NULL);
765 }
766
767
768 /**
769  * We've received a PING.  If appropriate, generate a PONG.
770  *
771  * @param sender peer sending the PING
772  * @param hdr the PING
773  * @param sender_address the sender address as we got it
774  * @param session session we got the PING from
775  */
776 void
777 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
778                             const struct GNUNET_MessageHeader *hdr,
779                             const struct GNUNET_HELLO_Address *sender_address,
780                             struct Session *session)
781 {
782   const struct TransportPingMessage *ping;
783   struct TransportPongMessage *pong;
784   struct GNUNET_TRANSPORT_PluginFunctions *papi;
785   struct GNUNET_CRYPTO_RsaSignature *sig_cache;
786   struct GNUNET_TIME_Absolute *sig_cache_exp;
787   const char *addr;
788   const char *addrend;
789   size_t alen;
790   size_t slen;
791   ssize_t ret;
792   int buggy = GNUNET_NO;
793   struct GNUNET_HELLO_Address address;
794
795   if (ntohs (hdr->size) < sizeof (struct TransportPingMessage))
796   {
797     GNUNET_break_op (0);
798     return;
799   }
800   ping = (const struct TransportPingMessage *) hdr;
801   if (0 !=
802       memcmp (&ping->target, &GST_my_identity,
803               sizeof (struct GNUNET_PeerIdentity)))
804   {
805     GNUNET_STATISTICS_update (GST_stats,
806                               gettext_noop
807                               ("# PING message for different peer received"), 1,
808                               GNUNET_NO);
809     return;
810   }
811   GNUNET_STATISTICS_update (GST_stats,
812                             gettext_noop ("# PING messages received"), 1,
813                             GNUNET_NO);
814   addr = (const char *) &ping[1];
815   alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage);
816   /* peer wants to confirm that this is one of our addresses, this is what is
817    * used for address validation */
818
819   sig_cache = NULL;
820   sig_cache_exp = NULL;
821
822   if (0 < alen)
823   {
824     addrend = memchr (addr, '\0', alen);
825     if (NULL == addrend)
826     {
827       GNUNET_break_op (0);
828       return;
829     }
830     addrend++;
831     slen = strlen (addr) + 1;
832     alen -= slen;
833     address.address = addrend;
834     address.address_length = alen;
835     address.transport_name = addr;
836     address.peer = GST_my_identity;
837
838
839     if (GNUNET_YES != GST_hello_test_address (&address, &sig_cache, &sig_cache_exp))
840     {
841 #if KEEP_093_COMPATIBILITY
842       int idsize = sizeof (GST_my_identity);
843       if (alen <= idsize)
844       {
845         if (0 == memcmp (address.address, &GST_my_identity, alen))
846           buggy = GNUNET_YES;
847       }
848       else if (alen <= (idsize + strlen (address.transport_name)))
849       {
850         char *achar = (char *) &address.address;
851         if ((0 == memcmp (address.address, &GST_my_identity, idsize)) &&
852             (0 == memcmp (&achar[idsize], address.transport_name, alen - idsize)))
853           buggy = GNUNET_YES;
854       }
855       else
856       {
857         /* Not predicatable */
858         return;
859       }
860 #endif
861       if (GNUNET_NO == buggy)
862       {
863         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864                     "Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n",
865                     GNUNET_i2s (sender),
866                     GST_plugins_a2s (&address));
867         return;
868       }
869       else
870       {
871         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872                     _("Received a PING message with validation bug from `%s'\n"),
873                     GNUNET_i2s (sender));
874       }
875     }
876   }
877   else
878   {
879     addrend = NULL;             /* make gcc happy */
880     slen = 0;
881     static struct GNUNET_CRYPTO_RsaSignature no_address_signature;
882     static struct GNUNET_TIME_Absolute no_address_signature_expiration;
883
884     sig_cache = &no_address_signature;
885     sig_cache_exp = &no_address_signature_expiration;
886   }
887
888   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
889               "I am `%s', sending PONG to peer `%s'\n",
890               GNUNET_h2s (&GST_my_identity.hashPubKey),
891               GNUNET_i2s (sender));
892
893   pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
894   pong->header.size =
895       htons (sizeof (struct TransportPongMessage) + alen + slen);
896   pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
897   pong->purpose.size =
898       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
899              sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
900              alen + slen);
901   pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
902   pong->challenge = ping->challenge;
903   pong->addrlen = htonl (alen + slen);
904   memcpy (&pong[1], addr, slen);
905 #if KEEP_093_COMPATIBILITY
906   if (GNUNET_YES == buggy)
907   {
908     int idsize = sizeof (GST_my_identity);
909     if (alen <= idsize)
910     {
911       memcpy (&((char *) &pong[1])[slen], &GST_my_identity, alen);
912     }
913     else if (alen <= (idsize + strlen (address.transport_name) + 1))
914     {
915       memcpy (&((char *) &pong[1])[slen], &GST_my_identity, idsize);
916       memcpy (&((char *) &pong[1])[slen + idsize], address.transport_name, alen-idsize);
917     }
918     else
919     {
920       /* If this would happen, we would have a inconsistent PING we cannot reproduce */
921       GNUNET_free (pong);
922       return;
923     }
924
925     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating buggy PONG signature to indicate ownership.\n");
926     pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
927     GNUNET_assert (GNUNET_OK ==
928                    GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
929                                            &pong->signature));
930   }
931   else
932   {
933 #endif
934     memcpy (&((char *) &pong[1])[slen], addrend, alen);
935     if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value <
936         PONG_SIGNATURE_LIFETIME.rel_value / 4)
937     {
938       /* create / update cached sig */
939       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
940                   "Creating PONG signature to indicate ownership.\n");
941       *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
942       pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
943       GNUNET_assert (GNUNET_OK ==
944                      GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose,
945                                              sig_cache));
946     }
947     else
948     {
949       pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
950     }
951     pong->signature = *sig_cache;
952
953 #if KEEP_093_COMPATIBILITY
954   }
955 #endif
956
957   GNUNET_assert (sender_address != NULL);
958
959   /* first see if the session we got this PING from can be used to transmit
960    * a response reliably */
961   papi = GST_plugins_find (sender_address->transport_name);
962   if (papi == NULL)
963     ret = -1;
964   else
965   {
966     GNUNET_assert (papi->send != NULL);
967     GNUNET_assert (papi->get_session != NULL);
968
969     if (session == NULL)
970     {
971       session = papi->get_session (papi->cls, sender_address);
972     }
973     if (session == NULL)
974     {
975       GNUNET_break (0);
976       ret = -1;
977     }
978     else
979     {
980       ret = papi->send (papi->cls, session,
981                         (const char *) pong, ntohs (pong->header.size),
982                         PONG_PRIORITY, ACCEPTABLE_PING_DELAY,
983                         NULL, NULL);
984     }
985   }
986   if (ret != -1)
987   {
988     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
989                 "Transmitted PONG to `%s' via reliable mechanism\n",
990                 GNUNET_i2s (sender));
991     /* done! */
992     GNUNET_STATISTICS_update (GST_stats,
993                               gettext_noop
994                               ("# PONGs unicast via reliable transport"), 1,
995                               GNUNET_NO);
996     GNUNET_free (pong);
997     return;
998   }
999
1000   /* no reliable method found, try transmission via all known addresses */
1001   GNUNET_STATISTICS_update (GST_stats,
1002                             gettext_noop
1003                             ("# PONGs multicast to all available addresses"), 1,
1004                             GNUNET_NO);
1005   GST_validation_get_addresses (sender, &multicast_pong, pong);
1006   GNUNET_free (pong);
1007 }
1008
1009
1010 /**
1011  * Context for the 'validate_address' function
1012  */
1013 struct ValidateAddressContext
1014 {
1015   /**
1016    * Hash of the public key of the peer whose address is being validated.
1017    */
1018   struct GNUNET_PeerIdentity pid;
1019
1020   /**
1021    * Public key of the peer whose address is being validated.
1022    */
1023   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
1024 };
1025
1026
1027 /**
1028  * Iterator callback to go over all addresses and try to validate them
1029  * (unless blocked or already validated).
1030  *
1031  * @param cls pointer to a 'struct ValidateAddressContext'
1032  * @param address the address
1033  * @param expiration expiration time
1034  * @return GNUNET_OK (keep the address)
1035  */
1036 static int
1037 validate_address_iterator (void *cls,
1038                            const struct GNUNET_HELLO_Address *address,
1039                            struct GNUNET_TIME_Absolute expiration)
1040 {
1041   const struct ValidateAddressContext *vac = cls;
1042   struct ValidationEntry *ve;
1043
1044   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
1045     return GNUNET_OK;           /* expired */
1046   ve = find_validation_entry (&vac->public_key, address);
1047   if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task)
1048     ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
1049   return GNUNET_OK;
1050 }
1051
1052
1053 /**
1054  * Add the validated peer address to the HELLO.
1055  *
1056  * @param cls the 'struct ValidationEntry' with the validated address
1057  * @param max space in buf
1058  * @param buf where to add the address
1059  * @return number of bytes written, 0 to signal the
1060  *         end of the iteration.
1061  */
1062 static size_t
1063 add_valid_peer_address (void *cls, size_t max, void *buf)
1064 {
1065   struct ValidationEntry *ve = cls;
1066
1067   if (GNUNET_YES == ve->copied)
1068     return 0;                   /* terminate */
1069   ve->copied = GNUNET_YES;
1070   return GNUNET_HELLO_add_address (ve->address, ve->valid_until, buf, max);
1071 }
1072
1073
1074 /**
1075  * We've received a PONG.  Check if it matches a pending PING and
1076  * mark the respective address as confirmed.
1077  *
1078  * @param sender peer sending the PONG
1079  * @param hdr the PONG
1080  */
1081 void
1082 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
1083                             const struct GNUNET_MessageHeader *hdr)
1084 {
1085   const struct TransportPongMessage *pong;
1086   struct ValidationEntry *ve;
1087   const char *tname;
1088   const char *addr;
1089   size_t addrlen;
1090   size_t slen;
1091   size_t size;
1092   struct GNUNET_HELLO_Message *hello;
1093   struct GNUNET_HELLO_Address address;
1094
1095   if (ntohs (hdr->size) < sizeof (struct TransportPongMessage))
1096   {
1097     GNUNET_break_op (0);
1098     return;
1099   }
1100   GNUNET_STATISTICS_update (GST_stats,
1101                             gettext_noop ("# PONG messages received"), 1,
1102                             GNUNET_NO);
1103
1104   pong = (const struct TransportPongMessage *) hdr;
1105   tname = (const char *) &pong[1];
1106   size = ntohs (hdr->size) - sizeof (struct TransportPongMessage);
1107   addr = memchr (tname, '\0', size);
1108   if (NULL == addr)
1109   {
1110     GNUNET_break_op (0);
1111     return;
1112   }
1113   addr++;
1114   slen = strlen (tname) + 1;
1115   addrlen = size - slen;
1116   address.peer = *sender;
1117   address.address = addr;
1118   address.address_length = addrlen;
1119   address.transport_name = tname;
1120   ve = find_validation_entry (NULL, &address);
1121   if ((NULL == ve) || (ve->expecting_pong == GNUNET_NO))
1122   {
1123     GNUNET_STATISTICS_update (GST_stats,
1124                               gettext_noop
1125                               ("# PONGs dropped, no matching pending validation"),
1126                               1, GNUNET_NO);
1127     return;
1128   }
1129   /* now check that PONG is well-formed */
1130   if (0 != memcmp (&ve->pid, sender, sizeof (struct GNUNET_PeerIdentity)))
1131   {
1132     GNUNET_break_op (0);
1133     return;
1134   }
1135
1136   if (GNUNET_OK !=
1137       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
1138                                 &pong->purpose, &pong->signature,
1139                                 &ve->public_key))
1140   {
1141     GNUNET_break_op (0);
1142     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1143                 "Invalid signature on address %s:%s from peer `%s'\n",
1144                 tname, GST_plugins_a2s (ve->address),
1145                 GNUNET_i2s (sender));
1146     return;
1147   }
1148
1149   if (GNUNET_TIME_absolute_get_remaining
1150       (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0)
1151   {
1152     GNUNET_STATISTICS_update (GST_stats,
1153                               gettext_noop
1154                               ("# PONGs dropped, signature expired"), 1,
1155                               GNUNET_NO);
1156     return;
1157   }
1158   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1159               "Address validated for peer `%s' with plugin `%s': `%s'\n",
1160               GNUNET_i2s (sender), tname, GST_plugins_a2s (ve->address));
1161   /* validity achieved, remember it! */
1162   ve->expecting_pong = GNUNET_NO;
1163   ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1164   ve->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
1165   {
1166     struct GNUNET_ATS_Information ats;
1167     ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
1168     ats.value = htonl ((uint32_t) ve->latency.rel_value);
1169     GNUNET_ATS_address_add (GST_ats, ve->address, NULL, &ats, 1);
1170   }
1171   /* build HELLO to store in PEERINFO */
1172   ve->copied = GNUNET_NO;
1173   hello = GNUNET_HELLO_create (&ve->public_key, &add_valid_peer_address, ve);
1174   GNUNET_PEERINFO_add_peer (GST_peerinfo, hello, NULL, NULL);
1175   GNUNET_free (hello);
1176 }
1177
1178
1179 /**
1180  * We've received a HELLO, check which addresses are new and trigger
1181  * validation.
1182  *
1183  * @param hello the HELLO we received
1184  */
1185 void
1186 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
1187 {
1188   const struct GNUNET_HELLO_Message *hm =
1189       (const struct GNUNET_HELLO_Message *) hello;
1190   struct ValidateAddressContext vac;
1191   struct GNUNET_HELLO_Message *h;
1192
1193   if ((GNUNET_OK != GNUNET_HELLO_get_id (hm, &vac.pid)) ||
1194       (GNUNET_OK != GNUNET_HELLO_get_key (hm, &vac.public_key)))
1195   {
1196     /* malformed HELLO */
1197     GNUNET_break (0);
1198     return;
1199   }
1200   if (0 ==
1201       memcmp (&GST_my_identity, &vac.pid, sizeof (struct GNUNET_PeerIdentity)))
1202     return;
1203   /* Add peer identity without addresses to peerinfo service */
1204   h = GNUNET_HELLO_create (&vac.public_key, NULL, NULL);
1205   GNUNET_PEERINFO_add_peer (GST_peerinfo, h, NULL, NULL);
1206
1207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208               _("Adding `%s' without addresses for peer `%s'\n"), "HELLO",
1209               GNUNET_i2s (&vac.pid));
1210
1211   GNUNET_free (h);
1212   GNUNET_assert (NULL ==
1213                  GNUNET_HELLO_iterate_addresses (hm, GNUNET_NO,
1214                                                  &validate_address_iterator,
1215                                                  &vac));
1216 }
1217
1218
1219 /**
1220  * Closure for 'iterate_addresses'
1221  */
1222 struct IteratorContext
1223 {
1224   /**
1225    * Function to call on each address.
1226    */
1227   GST_ValidationAddressCallback cb;
1228
1229   /**
1230    * Closure for 'cb'.
1231    */
1232   void *cb_cls;
1233
1234 };
1235
1236
1237 /**
1238  * Call the callback in the closure for each validation entry.
1239  *
1240  * @param cls the 'struct GST_ValidationIteratorContext'
1241  * @param key the peer's identity
1242  * @param value the 'struct ValidationEntry'
1243  * @return GNUNET_OK (continue to iterate)
1244  */
1245 static int
1246 iterate_addresses (void *cls, const struct GNUNET_HashCode * key, void *value)
1247 {
1248   struct IteratorContext *ic = cls;
1249   struct ValidationEntry *ve = value;
1250
1251   ic->cb (ic->cb_cls, &ve->public_key, ve->valid_until, ve->revalidation_block,
1252           ve->address);
1253   return GNUNET_OK;
1254 }
1255
1256
1257 /**
1258  * Call the given function for each address for the given target.
1259  * Can either give a snapshot (synchronous API) or be continuous.
1260  *
1261  * @param target peer information is requested for
1262  * @param cb function to call; will not be called after this function returns
1263  * @param cb_cls closure for 'cb'
1264  */
1265 void
1266 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
1267                               GST_ValidationAddressCallback cb, void *cb_cls)
1268 {
1269   struct IteratorContext ic;
1270
1271   ic.cb = cb;
1272   ic.cb_cls = cb_cls;
1273   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
1274                                               &target->hashPubKey,
1275                                               &iterate_addresses, &ic);
1276 }
1277
1278
1279 /**
1280  * Update if we are using an address for a connection actively right now.
1281  * Based on this, the validation module will measure latency for the
1282  * address more or less often.
1283  *
1284  * @param address the address
1285  * @param session the session
1286  * @param in_use GNUNET_YES if we are now using the address for a connection,
1287  *               GNUNET_NO if we are no longer using the address for a connection
1288  * @param line line of caller just for DEBUGGING!
1289  */
1290 void
1291 GST_validation_set_address_use (const struct GNUNET_HELLO_Address *address,
1292                                 struct Session *session,
1293                                 int in_use,
1294                                 int line)
1295 {
1296   struct ValidationEntry *ve;
1297
1298   if (NULL != address)
1299     ve = find_validation_entry (NULL, address);
1300   else
1301     ve = NULL;                  /* FIXME: lookup based on session... */
1302   if (NULL == ve)
1303   {
1304     /* this can happen for inbound connections (sender_address_len == 0); */
1305     return;
1306   }
1307   if (ve->in_use == in_use)
1308   {
1309
1310     if (GNUNET_YES == in_use)
1311     {
1312       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1313                   "Error setting address in use for peer `%s' `%s' to USED: set last time by %i, called now by %i\n",
1314                   GNUNET_i2s (&address->peer), GST_plugins_a2s (address),
1315                   ve->last_line_set_to_yes, line);
1316     }
1317     if (GNUNET_NO == in_use)
1318     {
1319       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1320                   "Error setting address in use for peer `%s' `%s' to NOT_USED: set last time by %i, called now by %i\n",
1321                   GNUNET_i2s (&address->peer), GST_plugins_a2s (address),
1322                   ve->last_line_set_to_no, line);
1323     }
1324   }
1325
1326   if (GNUNET_YES == in_use)
1327   {
1328     ve->last_line_set_to_yes = line;
1329   }
1330   if (GNUNET_NO == in_use)
1331   {
1332     ve->last_line_set_to_no = line;
1333   }
1334
1335   GNUNET_break (ve->in_use != in_use);  /* should be different... */
1336   ve->in_use = in_use;
1337   if (in_use == GNUNET_YES)
1338   {
1339     /* from now on, higher frequeny, so reschedule now */
1340     GNUNET_SCHEDULER_cancel (ve->revalidation_task);
1341     ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
1342   }
1343 }
1344
1345
1346 /**
1347  * Query validation about the latest observed latency on a given
1348  * address.
1349  *
1350  * @param sender peer
1351  * @param address the address
1352  * @param session session
1353  * @return observed latency of the address, FOREVER if the address was
1354  *         never successfully validated
1355  */
1356 struct GNUNET_TIME_Relative
1357 GST_validation_get_address_latency (const struct GNUNET_PeerIdentity *sender,
1358                                     const struct GNUNET_HELLO_Address *address,
1359                                     struct Session *session)
1360 {
1361   struct ValidationEntry *ve;
1362
1363   if (NULL == address)
1364   {
1365     GNUNET_break (0);           // FIXME: support having latency only with session...
1366     return GNUNET_TIME_UNIT_FOREVER_REL;
1367   }
1368   ve = find_validation_entry (NULL, address);
1369   if (NULL == ve)
1370     return GNUNET_TIME_UNIT_FOREVER_REL;
1371   return ve->latency;
1372 }
1373
1374
1375 /* end of file gnunet-service-transport_validation.c */