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