handle PONGs
[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.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_signatures.h"
33
34
35 /**
36  * How long is a PONG signature valid?  We'll recycle a signature until
37  * 1/4 of this time is remaining.  PONGs should expire so that if our
38  * external addresses change an adversary cannot replay them indefinitely.
39  * OTOH, we don't want to spend too much time generating PONG signatures,
40  * so they must have some lifetime to reduce our CPU usage.
41  */
42 #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
43
44 /**
45  * After how long do we expire an address in a HELLO that we just
46  * validated?  This value is also used for our own addresses when we
47  * create a HELLO.
48  */
49 #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
50
51 /**
52  * How long before an existing address expires should we again try to
53  * validate it?  Must be (significantly) smaller than
54  * HELLO_ADDRESS_EXPIRATION.
55  */
56 #define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
57
58 /**
59  * Size of the validation map hashmap.
60  */
61 #define VALIDATION_MAP_SIZE 256
62
63 /**
64  * Priority to use for PINGs
65  */ 
66 #define PING_PRIORITY 2
67
68 /**
69  * Priority to use for PONGs
70  */ 
71 #define PONG_PRIORITY 4
72
73
74 /**
75  * Message used to ask a peer to validate receipt (to check an address
76  * from a HELLO).  Followed by the address we are trying to validate,
77  * or an empty address if we are just sending a PING to confirm that a
78  * connection which the receiver (of the PING) initiated is still valid.
79  */
80 struct TransportPingMessage
81 {
82
83   /**
84    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
85    */
86   struct GNUNET_MessageHeader header;
87
88   /**
89    * Challenge code (to ensure fresh reply).
90    */
91   uint32_t challenge GNUNET_PACKED;
92
93   /**
94    * Who is the intended recipient?
95    */
96   struct GNUNET_PeerIdentity target;
97
98 };
99
100
101 /**
102  * Message used to validate a HELLO.  The challenge is included in the
103  * confirmation to make matching of replies to requests possible.  The
104  * signature signs our public key, an expiration time and our address.<p>
105  *
106  * This message is followed by our transport address that the PING tried
107  * to confirm (if we liked it).  The address can be empty (zero bytes)
108  * if the PING had not address either (and we received the request via
109  * a connection that we initiated).
110  */
111 struct TransportPongMessage
112 {
113
114   /**
115    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
116    */
117   struct GNUNET_MessageHeader header;
118
119   /**
120    * Challenge code from PING (showing freshness).  Not part of what
121    * is signed so that we can re-use signatures.
122    */
123   uint32_t challenge GNUNET_PACKED;
124
125   /**
126    * Signature.
127    */
128   struct GNUNET_CRYPTO_RsaSignature signature;
129
130   /**
131    * What are we signing and why?  Two possible reason codes can be here:
132    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
133    * plausible address for this peer (pid is set to identity of signer); or
134    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
135    * an address we used to connect to the peer with the given pid.
136    */
137   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
138
139   /**
140    * When does this signature expire?
141    */
142   struct GNUNET_TIME_AbsoluteNBO expiration;
143
144   /**
145    * Either the identity of the peer Who signed this message, or the
146    * identity of the peer that we're connected to using the given
147    * address (depending on purpose.type).
148    */
149   struct GNUNET_PeerIdentity pid;
150
151   /**
152    * Size of address appended to this message (part of what is
153    * being signed, hence not redundant).
154    */
155   uint32_t addrlen;
156
157 };
158
159
160 /**
161  * Information about an address under validation
162  */
163 struct ValidationEntry 
164 {
165
166   /**
167    * Name of the transport.
168    */
169   char *transport_name;
170
171   /**
172    * The address, actually a pointer to the end
173    * of this struct.  Do not free!
174    */
175   const void *addr;
176
177   /**
178    * Public key of the peer.
179    */
180   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;                                                
181
182   /**
183    * The identity of the peer.
184    */
185   struct GNUNET_PeerIdentity pid;
186
187   /**
188    * ID of task that will clean up this entry if nothing happens.
189    */
190   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
191
192   /**
193    * At what time did we send the latest validation request?
194    */
195   struct GNUNET_TIME_Absolute send_time;
196
197   /**
198    * Until when is this address valid?
199    * ZERO if it is not currently considered valid.
200    */
201   struct GNUNET_TIME_Absolute valid_until;
202
203   /**
204    * How long until we can try to validate this address again?
205    * FOREVER if the address is for an unsupported plugin (from PEERINFO)
206    * ZERO if the address is considered valid (no validation needed)
207    * otherwise a time in the future if we're currently denying re-validation
208    */
209   struct GNUNET_TIME_Absolute validation_block;
210                                             
211   /**
212    * Challenge number we used.
213    */
214   uint32_t challenge;
215
216   /**
217    * Length of addr.
218    */
219   size_t addrlen;
220
221 };
222
223
224 /**
225  * Context of currently active requests to peerinfo
226  * for validation of HELLOs.
227  */
228 struct CheckHelloValidatedContext
229 {
230
231   /**
232    * This is a doubly-linked list.
233    */
234   struct CheckHelloValidatedContext *next;
235
236   /**
237    * This is a doubly-linked list.
238    */
239   struct CheckHelloValidatedContext *prev;
240
241   /**
242    * Hello that we are validating.
243    */
244   const struct GNUNET_HELLO_Message *hello;
245
246   /**
247    * Context for peerinfo iteration.
248    */
249   struct GNUNET_PEERINFO_IteratorContext *piter;
250
251 };
252
253
254 /**
255  * Head of linked list of HELLOs awaiting validation.
256  */
257 static struct CheckHelloValidatedContext *chvc_head;
258
259 /**
260  * Tail of linked list of HELLOs awaiting validation
261  */
262 static struct CheckHelloValidatedContext *chvc_tail;
263
264 /**
265  * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
266  * of the given peer that we are currently validating, have validated
267  * or are blocked from re-validation for a while).
268  */
269 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
270
271 /**
272  * Map of PeerIdentities to 'struct GST_ValidationIteratorContext's.
273  */
274 static struct GNUNET_CONTAINER_MultiHashMap *notify_map;
275
276
277 /**
278  * Start the validation subsystem.
279  */
280 void 
281 GST_validation_start ()
282 {
283   validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
284   notify_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
285 }
286
287
288 /**
289  * Iterate over validation entries and free them.
290  *
291  * @param cls (unused)
292  * @param key peer identity (unused)
293  * @param value a 'struct ValidationEntry' to clean up
294  * @return GNUNET_YES (continue to iterate)
295  */
296 static int
297 cleanup_validation_entry (void *cls,
298                           const GNUNET_HashCode *key,
299                           void *value)
300 {
301   struct ValidationEntry *ve = value;
302     
303   GNUNET_free (ve->transport_name);
304   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
305     {
306       GNUNET_SCHEDULER_cancel (ve->timeout_task);
307       ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
308     }
309   GNUNET_free (ve);
310   return GNUNET_OK;
311 }
312
313
314 /**
315  * Stop the validation subsystem.
316  */
317 void
318 GST_validation_stop ()
319 {
320   struct CheckHelloValidatedContext *chvc;
321
322   GNUNET_CONTAINER_multihashmap_iterate (validation_map,
323                                          &cleanup_validation_entry,
324                                          NULL);
325   GNUNET_CONTAINER_multihashmap_destroy (validation_map);
326   validation_map = NULL;
327   GNUNET_assert (GNUNET_CONTAINER_multihashmap_size (notify_map) == 0);
328   GNUNET_CONTAINER_multihashmap_destroy (notify_map);
329   notify_map = NULL;
330   while (NULL != (chvc = chvc_head))
331     {
332       GNUNET_CONTAINER_DLL_remove (chvc_head,
333                                    chvc_tail,
334                                    chvc);
335       GNUNET_PEERINFO_iterate_cancel (chvc->piter);      
336       GNUNET_free (chvc);
337     }
338 }
339
340
341 /**
342  * Address validation cleanup task (record no longer needed).
343  *
344  * @param cls the 'struct ValidationEntry'
345  * @param tc scheduler context (unused)
346  */
347 static void
348 timeout_hello_validation (void *cls, 
349                           const struct GNUNET_SCHEDULER_TaskContext *tc)
350 {
351   struct ValidationEntry *va = cls;
352
353   va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
354   GNUNET_STATISTICS_update (GST_stats,
355                             gettext_noop ("# address records discarded"),
356                             1,
357                             GNUNET_NO);
358   GNUNET_break (GNUNET_OK ==
359                 GNUNET_CONTAINER_multihashmap_remove (validation_map,
360                                                       &va->pid.hashPubKey,
361                                                       va));
362   GNUNET_free (va->transport_name);
363   GNUNET_free (va);
364 }
365
366
367 /**
368  * Context for the validation entry match function.
369  */
370 struct ValidationEntryMatchContext
371 {
372   /**
373    * Where to store the result?
374    */
375   struct ValidationEntry *ve;
376   
377   /**
378    * Transport name we're looking for.
379    */
380   const char *transport_name;
381
382   /**
383    * Address we're interested in.
384    */
385   const char *addr;
386
387   /**
388    * Number of bytes in 'addr'.
389    */
390   size_t addrlen;
391 };
392
393
394 /**
395  * Iterate over validation entries until a matching one is found.
396  *
397  * @param cls the 'struct ValidationEntryMatchContext'
398  * @param key peer identity (unused)
399  * @param value a 'struct ValidationEntry' to match
400  * @return GNUNET_YES if the entry does not match,
401  *         GNUNET_NO if the entry does match
402  */
403 static int
404 validation_entry_match (void *cls,
405                         const GNUNET_HashCode *key,
406                         void *value)
407 {
408   struct ValidationEntryMatchContext *vemc = cls;
409   struct ValidationEntry *ve = value;
410
411   if ( (ve->addrlen == vemc->addrlen) &&
412        (0 == memcmp (ve->addr, vemc->addr, ve->addrlen)) &&
413        (0 == strcmp (ve->transport_name, vemc->transport_name)) )
414     {
415       vemc->ve = ve;
416       return GNUNET_NO;
417     }
418   return GNUNET_YES;
419 }
420
421
422 /**
423  * Find a ValidationEntry entry for the given neighbour that matches
424  * the given address and transport.  If none exists, create one (but
425  * without starting any validation).
426  *
427  * @param public_key public key of the peer, NULL for unknown
428  * @param neighbour which peer we care about
429  * @param tname name of the transport plugin
430  * @param session session to look for, NULL for 'any'; otherwise
431  *        can be used for the service to "learn" this session ID
432  *        if 'addr' matches
433  * @param addr binary address
434  * @param addrlen length of addr
435  * @return validation entry matching the given specifications, NULL
436  *         if we don't have an existing entry and no public key was given
437  */
438 static struct ValidationEntry *
439 find_validation_entry (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
440                        const struct GNUNET_PeerIdentity *neighbour,
441                        const char *tname,
442                        const char *addr,
443                        size_t addrlen)
444 {
445   struct ValidationEntryMatchContext vemc;
446   struct ValidationEntry *ve;
447
448   vemc.ve = NULL;
449   vemc.transport_name = tname;
450   vemc.addr = addr;
451   vemc.addrlen = addrlen;
452   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
453                                               &neighbour->hashPubKey,
454                                               &validation_entry_match,
455                                               &vemc);
456   if (NULL != (ve = vemc.ve))
457     return ve;
458   if (public_key == NULL)
459     return NULL;
460   ve = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
461   ve->transport_name = GNUNET_strdup (tname);
462   ve->addr = (void*) &ve[1];
463   ve->public_key = *public_key;
464   ve->pid = *neighbour;
465   ve->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
466                                             UINT32_MAX);
467   memcpy (&ve[1], addr, addrlen);
468   ve->addrlen = addrlen;
469   GNUNET_CONTAINER_multihashmap_put (validation_map,
470                                      &neighbour->hashPubKey,
471                                      ve,
472                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
473   return ve;
474 }
475
476
477 /**
478  * Send the given PONG to the given address.
479  *
480  * @param cls the PONG message
481  * @param target peer this change is about, never NULL
482  * @param valid_until is ZERO if we never validated the address,
483  *                    otherwise a time up to when we consider it (or was) valid
484  * @param validation_block  is FOREVER if the address is for an unsupported plugin (from PEERINFO)
485  *                          is ZERO if the address is considered valid (no validation needed)
486  *                          otherwise a time in the future if we're currently denying re-validation
487  * @param plugin_name name of the plugin
488  * @param plugin_address binary address
489  * @param plugin_address_len length of address
490  */
491 static void
492 multicast_pong (void *cls,
493                 const struct GNUNET_PeerIdentity *target,
494                 struct GNUNET_TIME_Absolute valid_until,
495                 struct GNUNET_TIME_Absolute validation_block,
496                 const char *plugin_name,
497                 const void *plugin_address,
498                 size_t plugin_address_len)
499 {
500   struct TransportPongMessage *pong = cls;
501   struct GNUNET_TRANSPORT_PluginFunctions *papi;
502
503   papi = GST_plugins_find (plugin_name);
504   if (papi == NULL)
505     return;
506   (void) papi->send (papi->cls,
507                      target,
508                      (const char*) pong,
509                      ntohs (pong->header.size),
510                      PONG_PRIORITY,
511                      HELLO_REVALIDATION_START_TIME,
512                      NULL,
513                      plugin_address,
514                      plugin_address_len,
515                      GNUNET_YES,
516                      NULL, NULL);
517 }
518
519
520 /**
521  * We've received a PING.  If appropriate, generate a PONG.
522  *
523  * @param sender peer sending the PING
524  * @param hdr the PING
525  * @param session session we got the PING from
526  * @param plugin_name name of plugin that received the PING
527  * @param sender_address address of the sender as known to the plugin, NULL
528  *                       if we did not initiate the connection
529  * @param sender_address_len number of bytes in sender_address
530  */
531 void
532 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
533                             const struct GNUNET_MessageHeader *hdr,
534                             const char *plugin_name,
535                             struct Session *session,
536                             const void *sender_address,
537                             size_t sender_address_len)
538 {
539
540   const struct TransportPingMessage *ping;
541   struct TransportPongMessage *pong;
542   struct GNUNET_TRANSPORT_PluginFunctions *papi;
543   const char *addr;
544   const char *addrend;
545   size_t alen;
546   size_t slen;
547   ssize_t ret;
548
549   if (ntohs (hdr->size) < sizeof (struct TransportPingMessage))
550     {
551       GNUNET_break_op (0);
552       return;
553     }
554   ping = (const struct TransportPingMessage *) hdr;
555   if (0 != memcmp (&ping->target,
556                    &GST_my_identity,
557                    sizeof (struct GNUNET_PeerIdentity)))
558     {
559 #if DEBUG_TRANSPORT
560       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561                   _("Received `%s' message from `%s' destined for `%s' which is not me!\n"),
562                   "PING",
563                   (sender_address != NULL)
564                   ? GST_plugin_a2s (plugin_name,
565                                     sender_address,
566                                     sender_address_len)
567                   : "<inbound>",
568                   GNUNET_i2s (&ping->target));
569 #endif
570       return;
571     }
572 #if DEBUG_TRANSPORT
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
574               "Processing `%s' from `%s'\n",
575               "PING",
576               (sender_address != NULL)
577               ? GST_plugin_a2s (plugin_name,
578                                 sender_address,
579                                 sender_address_len)
580               : "<inbound>");
581 #endif
582   GNUNET_STATISTICS_update (GST_stats,
583                             gettext_noop ("# PING messages received"),
584                             1,
585                             GNUNET_NO);
586   addr = (const char*) &ping[1];
587   alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage);
588   if (alen == 0)
589     {
590       /* peer wants to confirm that we have an outbound connection to him; 
591          we handle this case here even though it has nothing to do with
592          address validation (!) */
593       if (sender_address == NULL)
594         {
595           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
596                       _("Refusing to create PONG since I do initiate the session with `%s'.\n"),
597                       GNUNET_i2s (sender));
598           return;
599         }
600 #if DEBUG_TRANSPORT
601       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602                   "Creating PONG indicating that we initiated a connection to peer `%s' using address `%s' \n",
603                   GNUNET_i2s (peer),
604                   GST_plugin_a2s (plugin_name,
605                                   sender_address,
606                                   sender_address_len));
607 #endif
608       slen = strlen (plugin_name) + 1;
609       pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len + slen);
610       pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len + slen);
611       pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
612       pong->purpose.size =
613         htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
614                sizeof (uint32_t) +
615                sizeof (struct GNUNET_TIME_AbsoluteNBO) +
616                sizeof (struct GNUNET_PeerIdentity) + sender_address_len + slen);
617       pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING);
618       pong->challenge = ping->challenge;
619       pong->addrlen = htonl(sender_address_len + slen);
620       pong->pid = *sender;
621       memcpy (&pong[1],
622               plugin_name,
623               slen);
624       memcpy (&((char*)&pong[1])[slen],
625               sender_address,
626               sender_address_len);
627 #if 0
628       /* FIXME: lookup signature! */
629       if (GNUNET_TIME_absolute_get_remaining (session_header->pong_sig_expires).rel_value < 
630           PONG_SIGNATURE_LIFETIME.rel_value / 4)
631         {
632           /* create / update cached sig */
633 #if DEBUG_TRANSPORT
634           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
635                       "Creating PONG signature to indicate active connection.\n");
636 #endif
637           session_header->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
638           pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
639           GNUNET_assert (GNUNET_OK ==
640                          GNUNET_CRYPTO_rsa_sign (my_private_key,
641                                                  &pong->purpose,
642                                                  &session_header->pong_signature));
643         }
644       else
645         {
646           pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
647         }
648       memcpy (&pong->signature,
649               &session_header->pong_signature,
650               sizeof (struct GNUNET_CRYPTO_RsaSignature));
651 #else
652       pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
653       GNUNET_assert (GNUNET_OK ==
654                      GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
655                                              &pong->purpose,
656                                              &pong->signature));
657 #endif
658     }
659   else
660     {
661       /* peer wants to confirm that this is one of our addresses, this is what is
662          used for address validation */
663       addrend = memchr (addr, '\0', alen);
664       if (NULL == addrend)
665         {
666           GNUNET_break_op (0);
667           return;
668         }
669       addrend++;
670       slen = strlen(addr);
671       alen -= slen;
672       papi = GST_plugins_find (addr);
673       
674       if ( (NULL == papi) ||
675            (GNUNET_OK !=
676             papi->check_address (papi->cls,
677                                  addrend,
678                                  alen)) )
679         {
680           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
681                       _("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
682                       GST_plugins_a2s (addr,
683                                        addrend,
684                                        alen));
685           return;
686         }
687
688       pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
689       pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen);
690       pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
691       pong->purpose.size =
692         htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
693                sizeof (uint32_t) +
694                sizeof (struct GNUNET_TIME_AbsoluteNBO) +
695                sizeof (struct GNUNET_PeerIdentity) + alen + slen);
696       pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
697       pong->challenge = ping->challenge;
698       pong->addrlen = htonl(alen + slen);
699       pong->pid = GST_my_identity;
700       memcpy (&pong[1], addr, slen);
701       memcpy (&((char*)&pong[1])[slen], addrend, alen);
702 #if 0
703       if ( (oal != NULL) &&
704            (GNUNET_TIME_absolute_get_remaining (oal->pong_sig_expires).rel_value < PONG_SIGNATURE_LIFETIME.rel_value / 4) )
705         {
706           /* create / update cached sig */
707 #if DEBUG_TRANSPORT
708           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709                       "Creating PONG signature to indicate ownership.\n");
710 #endif
711           oal->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
712           pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
713           GNUNET_assert (GNUNET_OK ==
714                          GNUNET_CRYPTO_rsa_sign (my_private_key,
715                                                  &pong->purpose,
716                                                  &oal->pong_signature));
717           memcpy (&pong->signature,
718                   &oal->pong_signature,
719                   sizeof (struct GNUNET_CRYPTO_RsaSignature));
720         }
721       else if (oal == NULL)
722         {
723 #else
724           /* not using cache (typically DV-only) */
725           pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME));
726           GNUNET_assert (GNUNET_OK ==
727                          GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
728                                                  &pong->purpose,
729                                                  &pong->signature));
730 #endif
731 #if 0
732         }
733       else
734         {
735           /* can used cached version */
736           pong->expiration = GNUNET_TIME_absolute_hton (oal->pong_sig_expires);
737           memcpy (&pong->signature,
738                   &oal->pong_signature,
739                   sizeof (struct GNUNET_CRYPTO_RsaSignature));
740         }
741 #endif
742     }
743
744   /* first see if the session we got this PING from can be used to transmit
745      a response reliably */
746   papi = GST_plugins_find (plugin_name);
747   if (papi == NULL)
748     ret = -1;
749   else
750     ret = papi->send (papi->cls,
751                       sender,
752                       (const char*) pong,
753                       ntohs (pong->header.size),
754                       PONG_PRIORITY,
755                       HELLO_REVALIDATION_START_TIME,
756                       session,
757                       sender_address,
758                       sender_address_len,
759                       GNUNET_SYSERR,
760                       NULL, NULL);
761   if (ret != -1)
762     {
763       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764                   "Transmitted PONG to `%s' via reliable mechanism\n",
765                   GNUNET_i2s (sender));
766       /* done! */
767       GNUNET_STATISTICS_update (GST_stats,
768                                 gettext_noop ("# PONGs unicast via reliable transport"),
769                                 1,
770                                 GNUNET_NO);
771       GNUNET_free (pong);
772       return;
773     }
774   
775   /* no reliable method found, try transmission via all known addresses */
776   GNUNET_STATISTICS_update (GST_stats,
777                             gettext_noop ("# PONGs multicast to all available addresses"),
778                             1,
779                             GNUNET_NO);
780   (void) GST_validation_get_addresses (sender,
781                                        GNUNET_YES,
782                                        &multicast_pong,
783                                        pong);
784   GNUNET_free (pong);
785 }
786
787
788 /**
789  * Context for the 'validate_address' function
790  */
791 struct ValidateAddressContext
792 {
793   /**
794    * Hash of the public key of the peer whose address is being validated.
795    */ 
796   struct GNUNET_PeerIdentity pid;
797
798   /**
799    * Public key of the peer whose address is being validated.
800    */
801   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
802 };
803
804
805 /**
806  * Iterator callback to go over all addresses and try to validate them
807  * (unless blocked or already validated).
808  *
809  * @param cls pointer to a 'struct ValidateAddressContext'
810  * @param tname name of the transport
811  * @param expiration expiration time
812  * @param addr the address
813  * @param addrlen length of the address
814  * @return GNUNET_OK (keep the address)
815  */
816 static int
817 validate_address (void *cls,
818                   const char *tname,
819                   struct GNUNET_TIME_Absolute expiration,
820                   const void *addr, 
821                   uint16_t addrlen)
822 {
823   const struct ValidateAddressContext *vac = cls;
824   const struct GNUNET_PeerIdentity *pid = &vac->pid;
825   struct ValidationEntry *ve;
826   struct TransportPingMessage ping;
827   struct GNUNET_TRANSPORT_PluginFunctions *papi;
828   ssize_t ret;
829   size_t tsize;
830   size_t slen;
831
832   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
833     return GNUNET_OK; /* expired */
834   ve = find_validation_entry (&vac->public_key, pid, tname, addr, addrlen);
835   if (GNUNET_TIME_absolute_get_remaining (ve->validation_block).rel_value > 0)
836     return GNUNET_OK; /* blocked */
837   if ( (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) &&
838        (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > 0) )
839     return GNUNET_OK; /* revalidation task already scheduled & still  valid */
840   ve->validation_block = GNUNET_TIME_relative_to_absolute (HELLO_REVALIDATION_START_TIME);
841   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
842     GNUNET_SCHEDULER_cancel (ve->timeout_task);
843   ve->timeout_task = GNUNET_SCHEDULER_add_delayed (HELLO_REVALIDATION_START_TIME,
844                                                    &timeout_hello_validation,
845                                                    ve);
846   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847               "Transmitting plain PING to `%s'\n",
848               GNUNET_i2s (pid));  
849   ping.header.size = htons(sizeof(struct TransportPingMessage));
850   ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
851   ping.challenge = htonl(ve->challenge);
852   ping.target = *pid;
853   
854   slen = strlen(ve->transport_name) + 1;
855   tsize = sizeof(struct TransportPingMessage) + ve->addrlen + slen;
856   {
857     char message_buf[tsize];
858
859     memcpy(message_buf, &ping, sizeof (struct TransportPingMessage));
860     memcpy(&message_buf[sizeof (struct TransportPingMessage)],
861            ve->transport_name,
862            slen);
863     memcpy(&message_buf[sizeof (struct TransportPingMessage) + slen],
864            ve->addr,
865            ve->addrlen);
866     papi = GST_plugins_find (ve->transport_name);
867     if (papi == NULL)
868       ret = -1;
869     else
870       ret = papi->send (papi->cls,
871                         pid,
872                         message_buf,
873                         tsize,
874                         PING_PRIORITY,
875                         HELLO_REVALIDATION_START_TIME,
876                         NULL /* no session */,
877                         ve->addr,
878                         ve->addrlen,
879                         GNUNET_YES,
880                         NULL, NULL);
881   }
882   if (-1 != ret)
883     {
884       ve->send_time = GNUNET_TIME_absolute_get ();
885       GNUNET_STATISTICS_update (GST_stats,
886                                 gettext_noop ("# PING without HELLO messages sent"),
887                                 1,
888                                 GNUNET_NO);
889     }
890   return GNUNET_OK;
891 }
892
893
894 /**
895  * Do address validation again to keep address valid.
896  *
897  * @param cls the 'struct ValidationEntry'
898  * @param tc scheduler context (unused)
899  */
900 static void
901 revalidate_address (void *cls, 
902                     const struct GNUNET_SCHEDULER_TaskContext *tc)
903 {
904   struct ValidationEntry *ve = cls;
905   struct GNUNET_TIME_Relative delay;
906   struct ValidateAddressContext vac;
907
908   ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
909   delay = GNUNET_TIME_absolute_get_remaining (ve->validation_block);
910   if (delay.rel_value > 0)
911     {
912       /* should wait a bit longer */
913       ve->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
914                                                        &revalidate_address,
915                                                        ve);
916       return;
917     }
918   GNUNET_STATISTICS_update (GST_stats,
919                             gettext_noop ("# address revalidations started"),
920                             1,
921                             GNUNET_NO);
922   vac.pid = ve->pid;
923   vac.public_key = ve->public_key;
924   validate_address (&vac,
925                     ve->transport_name,
926                     ve->valid_until,
927                     ve->addr,
928                     (uint16_t) ve->addrlen);
929 }
930
931
932 /**
933  * Add the validated peer address to the HELLO.
934  *
935  * @param cls the 'struct ValidationEntry' with the validated address
936  * @param max space in buf
937  * @param buf where to add the address
938  */
939 static size_t
940 add_valid_peer_address (void *cls,
941                         size_t max,
942                         void *buf)
943 {
944   struct ValidationEntry *ve = cls;
945
946   return GNUNET_HELLO_add_address (ve->transport_name,
947                                    ve->valid_until,
948                                    ve->addr,
949                                    ve->addrlen,
950                                    buf,
951                                    max);
952 }
953
954
955 /**
956  * We've received a PONG.  Check if it matches a pending PING and
957  * mark the respective address as confirmed.
958  *
959  * @param sender peer sending the PONG
960  * @param hdr the PONG
961  * @param plugin_name name of plugin that received the PONG
962  * @param sender_address address of the sender as known to the plugin, NULL
963  *                       if we did not initiate the connection
964  * @param sender_address_len number of bytes in sender_address
965  */
966 void
967 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
968                             const struct GNUNET_MessageHeader *hdr,
969                             const char *plugin_name,
970                             const void *sender_address,
971                             size_t sender_address_len)
972 {
973   const struct TransportPongMessage *pong;
974   struct ValidationEntry *ve;
975   const char *addr;
976   const char *addrend;
977   size_t alen;
978   size_t slen;
979   uint32_t rdelay;
980   struct GNUNET_TIME_Relative delay;
981   struct GNUNET_HELLO_Message *hello;
982
983   if (ntohs (hdr->size) < sizeof (struct TransportPongMessage))
984     {
985       GNUNET_break_op (0);
986       return;
987     }
988   GNUNET_STATISTICS_update (GST_stats,
989                             gettext_noop ("# PONG messages received"),
990                             1,
991                             GNUNET_NO);
992   pong = (const struct TransportPongMessage *) hdr;
993   if (0 != memcmp (&pong->pid,
994                    sender,
995                    sizeof (struct GNUNET_PeerIdentity)))
996     {
997       /* PONG is validating inbound session, not an address, not the case
998          used for address validation, ignore here! */
999       return;
1000     }
1001 #if DEBUG_TRANSPORT
1002   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1003               "Processing `%s' from `%s'\n",
1004               "PONG",
1005               (sender_address != NULL)
1006               ? GST_plugin_a2s (plugin_name,
1007                                 sender_address,
1008                                 sender_address_len)
1009               : "<inbound>");
1010 #endif
1011   addr = (const char*) &pong[1];
1012   alen = ntohs (hdr->size) - sizeof (struct TransportPongMessage);
1013   addrend = memchr (addr, '\0', alen);
1014   if (NULL == addrend)
1015     {
1016       GNUNET_break_op (0);
1017       return;
1018     }
1019   addrend++;
1020   slen = strlen(addr);
1021   alen -= slen;
1022   ve = find_validation_entry (NULL,
1023                               sender,
1024                               addr,
1025                               addrend,
1026                               alen);
1027   if (NULL == ve)
1028     {
1029       GNUNET_STATISTICS_update (GST_stats,
1030                                 gettext_noop ("# PONGs dropped, no matching pending validation"),
1031                                 1,
1032                                 GNUNET_NO);
1033       return;
1034     }
1035   /* now check that PONG is well-formed */
1036   if (GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0)
1037     {
1038       GNUNET_STATISTICS_update (GST_stats,
1039                                 gettext_noop ("# PONGs dropped, signature expired"),
1040                                 1,
1041                                 GNUNET_NO);
1042       return;
1043     }
1044   if (GNUNET_OK !=
1045       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
1046                                 &pong->purpose,
1047                                 &pong->signature,
1048                                 &ve->public_key))
1049     {
1050       GNUNET_break_op (0);
1051       return;
1052     }
1053   
1054   /* validity achieved, remember it! */
1055   ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1056
1057   /* build HELLO to store in PEERINFO */
1058   hello = GNUNET_HELLO_create (&ve->public_key,
1059                                &add_valid_peer_address,
1060                                ve);
1061   GNUNET_PEERINFO_add_peer (GST_peerinfo,
1062                             hello);
1063   GNUNET_free (hello);
1064
1065   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
1066     GNUNET_SCHEDULER_cancel (ve->timeout_task);
1067
1068   /* randomly delay by up to 1h to avoid synchronous validations */
1069   rdelay = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1070                                      60 * 60);
1071   delay = GNUNET_TIME_relative_add (HELLO_REVALIDATION_START_TIME,
1072                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1073                                                                    rdelay));
1074   ve->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
1075                                                    &revalidate_address,
1076                                                    ve);
1077 }
1078
1079
1080 /**
1081  * We've received a HELLO, check which addresses are new and trigger
1082  * validation.
1083  *
1084  * @param hello the HELLO we received
1085  */
1086 void
1087 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
1088 {
1089   const struct GNUNET_HELLO_Message* hm = (const struct GNUNET_HELLO_Message*) hello;
1090   struct ValidateAddressContext vac;
1091
1092   if ( (GNUNET_OK !=
1093         GNUNET_HELLO_get_id (hm, &vac.pid)) ||
1094        (GNUNET_OK !=
1095         GNUNET_HELLO_get_key (hm, &vac.public_key)) )
1096     {
1097       /* malformed HELLO */
1098       GNUNET_break (0);
1099       return; 
1100     }
1101   GNUNET_assert (NULL ==
1102                  GNUNET_HELLO_iterate_addresses (hm,
1103                                                  GNUNET_NO,
1104                                                  &validate_address,
1105                                                  &vac));
1106 }
1107
1108
1109 /**
1110  * Opaque handle to stop incremental validation address callbacks.
1111  */
1112 struct GST_ValidationIteratorContext
1113 {
1114   /**
1115    * Function to call on each address.
1116    */
1117   GST_ValidationAddressCallback cb;
1118
1119   /**
1120    * Closure for 'cb'.
1121    */
1122   void *cb_cls;
1123
1124   /**
1125    * Which peer are we monitoring?
1126    */   
1127   struct GNUNET_PeerIdentity target;
1128 };
1129
1130
1131 /**
1132  * Call the callback in the closure for each validation entry.
1133  *
1134  * @param cls the 'struct GST_ValidationIteratorContext'
1135  * @param key the peer's identity
1136  * @param value the 'struct ValidationEntry'
1137  * @return GNUNET_OK (continue to iterate)
1138  */
1139 static int
1140 iterate_addresses (void *cls,
1141                    const GNUNET_HashCode *key,
1142                    void *value)
1143 {
1144   struct GST_ValidationIteratorContext *vic = cls;
1145   struct ValidationEntry *ve = value;
1146
1147   vic->cb (vic->cb_cls,
1148            &ve->pid,
1149            ve->valid_until,
1150            ve->validation_block,
1151            ve->transport_name,
1152            ve->addr,
1153            ve->addrlen);
1154   return GNUNET_OK;
1155 }
1156
1157
1158 /**
1159  * Call the given function for each address for the given target.
1160  * Can either give a snapshot (synchronous API) or be continuous.
1161  *
1162  * @param target peer information is requested for
1163  * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to
1164  *                      continue to give information about addresses as it evolves
1165  * @param cb function to call; will not be called after this function returns
1166  *                             if snapshot_only is GNUNET_YES
1167  * @param cb_cls closure for 'cb'
1168  * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES
1169  */
1170 struct GST_ValidationIteratorContext *
1171 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
1172                               int snapshot_only,
1173                               GST_ValidationAddressCallback cb,
1174                               void *cb_cls)
1175 {
1176   struct GST_ValidationIteratorContext *vic;
1177
1178   vic = GNUNET_malloc (sizeof (struct GST_ValidationIteratorContext));
1179   vic->cb = cb;
1180   vic->cb_cls = cb_cls;
1181   vic->target = *target;
1182   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
1183                                               &target->hashPubKey,
1184                                               &iterate_addresses,
1185                                               vic);
1186   if (GNUNET_YES == snapshot_only)
1187     {
1188       GNUNET_free (vic);
1189       return NULL;
1190     }
1191   GNUNET_CONTAINER_multihashmap_put (notify_map,
1192                                      &target->hashPubKey,
1193                                      vic,
1194                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1195   return vic;
1196 }
1197
1198
1199 /**
1200  * Cancel an active validation address iteration.
1201  *
1202  * @param ctx the context of the operation that is cancelled
1203  */
1204 void
1205 GST_validation_get_addresses_cancel (struct GST_ValidationIteratorContext *ctx)
1206 {
1207   GNUNET_assert (GNUNET_OK ==
1208                  GNUNET_CONTAINER_multihashmap_remove (notify_map,
1209                                                        &ctx->target.hashPubKey,
1210                                                        ctx));
1211   GNUNET_free (ctx);
1212 }
1213
1214
1215 /* end of file gnunet-service-transport_validation.c */