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