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