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 SessionHeader *session_header;
616   const char *addr;
617   const char *addrend;
618   size_t alen;
619   size_t slen;
620   ssize_t ret;
621
622   if (ntohs (hdr->size) < sizeof (struct TransportPingMessage))
623     {
624       GNUNET_break_op (0);
625       return;
626     }
627   ping = (const struct TransportPingMessage *) hdr;
628   if (0 != memcmp (&ping->target,
629                    &GST_my_identity,
630                    sizeof (struct GNUNET_PeerIdentity)))
631     {
632 #if DEBUG_TRANSPORT
633       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634                   _("Received `%s' message from `%s' destined for `%s' which is not me!\n"),
635                   "PING",
636                   (sender_address != NULL)
637                   ? GST_plugin_a2s (plugin_name,
638                                     sender_address,
639                                     sender_address_len)
640                   : "<inbound>",
641                   GNUNET_i2s (&ping->target));
642 #endif
643       return;
644     }
645 #if DEBUG_TRANSPORT
646   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
647               "Processing `%s' from `%s'\n",
648               "PING",
649               (sender_address != NULL)
650               ? GST_plugin_a2s (plugin_name,
651                                 sender_address,
652                                 sender_address_len)
653               : "<inbound>");
654 #endif
655   GNUNET_STATISTICS_update (GST_stats,
656                             gettext_noop ("# PING messages received"),
657                             1,
658                             GNUNET_NO);
659   addr = (const char*) &ping[1];
660   alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage);
661   if (alen == 0)
662     {
663       /* peer wants to confirm that we have an outbound connection to him; 
664          we handle this case here even though it has nothing to do with
665          address validation (!) */
666       if ( (sender_address == NULL) || (session == NULL) )
667         {
668           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
669                       _("Refusing to create PONG since I do initiate the session with `%s'.\n"),
670                       GNUNET_i2s (sender));
671           return;
672         }
673       session_header = (struct SessionHeader *)session;
674 #if DEBUG_TRANSPORT
675       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676                   "Creating PONG indicating that we initiated a connection to peer `%s' using address `%s' \n",
677                   GNUNET_i2s (peer),
678                   GST_plugin_a2s (plugin_name,
679                                   sender_address,
680                                   sender_address_len));
681 #endif
682       slen = strlen (plugin_name) + 1;
683       pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + sender_address_len + slen);
684       pong->header.size = htons (sizeof (struct TransportPongMessage) + sender_address_len + 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) + sender_address_len + slen);
691       pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING);
692       pong->challenge = ping->challenge;
693       pong->addrlen = htonl(sender_address_len + slen);
694       pong->pid = *sender;
695       memcpy (&pong[1],
696               plugin_name,
697               slen);
698       memcpy (&((char*)&pong[1])[slen],
699               sender_address,
700               sender_address_len);
701       if (GNUNET_TIME_absolute_get_remaining (session_header->pong_sig_expires).rel_value < 
702           PONG_SIGNATURE_LIFETIME.rel_value / 4)
703         {
704           /* create / update cached sig */
705 #if DEBUG_TRANSPORT
706           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707                       "Creating PONG signature to indicate active connection.\n");
708 #endif
709           session_header->pong_sig_expires = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
710           pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
711           GNUNET_assert (GNUNET_OK ==
712                          GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
713                                                  &pong->purpose,
714                                                  &session_header->pong_signature));
715         }
716       else
717         {
718           pong->expiration = GNUNET_TIME_absolute_hton (session_header->pong_sig_expires);
719         }
720       pong->signature = session_header->pong_signature;
721     }
722   else
723     {
724       /* peer wants to confirm that this is one of our addresses, this is what is
725          used for address validation */
726       struct GNUNET_CRYPTO_RsaSignature *sig_cache;
727       struct GNUNET_TIME_Absolute *sig_cache_exp;
728
729       addrend = memchr (addr, '\0', alen);
730       if (NULL == addrend)
731         {
732           GNUNET_break_op (0);
733           return;
734         }
735       addrend++;
736       slen = strlen(addr);
737       alen -= slen;
738
739       if (GNUNET_YES !=
740           GST_hello_test_address (addr,
741                                   addrend,
742                                   alen,
743                                   &sig_cache,
744                                   &sig_cache_exp))
745         {
746           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
747                       _("Not confirming PING with address `%s' since I cannot confirm having this address.\n"),
748                       GST_plugins_a2s (addr,
749                                        addrend,
750                                        alen));
751           return;
752         }
753
754       pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen);
755       pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen);
756       pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
757       pong->purpose.size =
758         htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
759                sizeof (uint32_t) +
760                sizeof (struct GNUNET_TIME_AbsoluteNBO) +
761                sizeof (struct GNUNET_PeerIdentity) + alen + slen);
762       pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
763       pong->challenge = ping->challenge;
764       pong->addrlen = htonl(alen + slen);
765       pong->pid = GST_my_identity;
766       memcpy (&pong[1], addr, slen);
767       memcpy (&((char*)&pong[1])[slen], addrend, alen);
768       if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value < PONG_SIGNATURE_LIFETIME.rel_value / 4)
769         {
770           /* create / update cached sig */
771 #if DEBUG_TRANSPORT
772           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
773                       "Creating PONG signature to indicate ownership.\n");
774 #endif
775           *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
776           pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
777           GNUNET_assert (GNUNET_OK ==
778                          GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
779                                                  &pong->purpose,
780                                                  sig_cache));
781         }
782       else
783         {
784           pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
785         }
786       pong->signature = *sig_cache;
787     }
788
789   /* first see if the session we got this PING from can be used to transmit
790      a response reliably */
791   papi = GST_plugins_find (plugin_name);
792   if (papi == NULL)
793     ret = -1;
794   else
795     ret = papi->send (papi->cls,
796                       sender,
797                       (const char*) pong,
798                       ntohs (pong->header.size),
799                       PONG_PRIORITY,
800                       HELLO_REVALIDATION_START_TIME,
801                       session,
802                       sender_address,
803                       sender_address_len,
804                       GNUNET_SYSERR,
805                       NULL, NULL);
806   if (ret != -1)
807     {
808       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
809                   "Transmitted PONG to `%s' via reliable mechanism\n",
810                   GNUNET_i2s (sender));
811       /* done! */
812       GNUNET_STATISTICS_update (GST_stats,
813                                 gettext_noop ("# PONGs unicast via reliable transport"),
814                                 1,
815                                 GNUNET_NO);
816       GNUNET_free (pong);
817       return;
818     }
819   
820   /* no reliable method found, try transmission via all known addresses */
821   GNUNET_STATISTICS_update (GST_stats,
822                             gettext_noop ("# PONGs multicast to all available addresses"),
823                             1,
824                             GNUNET_NO);
825   (void) GST_validation_get_addresses (sender,
826                                        GNUNET_YES,
827                                        &multicast_pong,
828                                        pong);
829   GNUNET_free (pong);
830 }
831
832
833 /**
834  * Context for the 'validate_address' function
835  */
836 struct ValidateAddressContext
837 {
838   /**
839    * Hash of the public key of the peer whose address is being validated.
840    */ 
841   struct GNUNET_PeerIdentity pid;
842
843   /**
844    * Public key of the peer whose address is being validated.
845    */
846   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
847 };
848
849
850 /**
851  * Iterator callback to go over all addresses and try to validate them
852  * (unless blocked or already validated).
853  *
854  * @param cls pointer to a 'struct ValidateAddressContext'
855  * @param tname name of the transport
856  * @param expiration expiration time
857  * @param addr the address
858  * @param addrlen length of the address
859  * @return GNUNET_OK (keep the address)
860  */
861 static int
862 validate_address (void *cls,
863                   const char *tname,
864                   struct GNUNET_TIME_Absolute expiration,
865                   const void *addr, 
866                   uint16_t addrlen)
867 {
868   const struct ValidateAddressContext *vac = cls;
869   const struct GNUNET_PeerIdentity *pid = &vac->pid;
870   struct ValidationEntry *ve;
871   struct TransportPingMessage ping;
872   struct GNUNET_TRANSPORT_PluginFunctions *papi;
873   ssize_t ret;
874   size_t tsize;
875   size_t slen;
876
877   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
878     return GNUNET_OK; /* expired */
879   ve = find_validation_entry (&vac->public_key, pid, tname, addr, addrlen);
880   if (GNUNET_TIME_absolute_get_remaining (ve->validation_block).rel_value > 0)
881     return GNUNET_OK; /* blocked */
882   if ( (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) &&
883        (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > 0) )
884     return GNUNET_OK; /* revalidation task already scheduled & still  valid */
885   ve->validation_block = GNUNET_TIME_relative_to_absolute (HELLO_REVALIDATION_START_TIME);
886   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
887     GNUNET_SCHEDULER_cancel (ve->timeout_task);
888   ve->timeout_task = GNUNET_SCHEDULER_add_delayed (HELLO_REVALIDATION_START_TIME,
889                                                    &timeout_hello_validation,
890                                                    ve);
891   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
892               "Transmitting plain PING to `%s'\n",
893               GNUNET_i2s (pid));  
894   ping.header.size = htons(sizeof(struct TransportPingMessage));
895   ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
896   ping.challenge = htonl(ve->challenge);
897   ping.target = *pid;
898   
899   slen = strlen(ve->transport_name) + 1;
900   tsize = sizeof(struct TransportPingMessage) + ve->addrlen + slen;
901   {
902     char message_buf[tsize];
903
904     memcpy(message_buf, &ping, sizeof (struct TransportPingMessage));
905     memcpy(&message_buf[sizeof (struct TransportPingMessage)],
906            ve->transport_name,
907            slen);
908     memcpy(&message_buf[sizeof (struct TransportPingMessage) + slen],
909            ve->addr,
910            ve->addrlen);
911     papi = GST_plugins_find (ve->transport_name);
912     if (papi == NULL)
913       ret = -1;
914     else
915       ret = papi->send (papi->cls,
916                         pid,
917                         message_buf,
918                         tsize,
919                         PING_PRIORITY,
920                         HELLO_REVALIDATION_START_TIME,
921                         NULL /* no session */,
922                         ve->addr,
923                         ve->addrlen,
924                         GNUNET_YES,
925                         NULL, NULL);
926   }
927   if (-1 != ret)
928     {
929       ve->send_time = GNUNET_TIME_absolute_get ();
930       GNUNET_STATISTICS_update (GST_stats,
931                                 gettext_noop ("# PING without HELLO messages sent"),
932                                 1,
933                                 GNUNET_NO);
934     }
935   return GNUNET_OK;
936 }
937
938
939 /**
940  * Do address validation again to keep address valid.
941  *
942  * @param cls the 'struct ValidationEntry'
943  * @param tc scheduler context (unused)
944  */
945 static void
946 revalidate_address (void *cls, 
947                     const struct GNUNET_SCHEDULER_TaskContext *tc)
948 {
949   struct ValidationEntry *ve = cls;
950   struct GNUNET_TIME_Relative delay;
951   struct ValidateAddressContext vac;
952
953   ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
954   delay = GNUNET_TIME_absolute_get_remaining (ve->validation_block);
955   if (delay.rel_value > 0)
956     {
957       /* should wait a bit longer */
958       ve->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
959                                                        &revalidate_address,
960                                                        ve);
961       return;
962     }
963   GNUNET_STATISTICS_update (GST_stats,
964                             gettext_noop ("# address revalidations started"),
965                             1,
966                             GNUNET_NO);
967   vac.pid = ve->pid;
968   vac.public_key = ve->public_key;
969   validate_address (&vac,
970                     ve->transport_name,
971                     ve->valid_until,
972                     ve->addr,
973                     (uint16_t) ve->addrlen);
974 }
975
976
977 /**
978  * Add the validated peer address to the HELLO.
979  *
980  * @param cls the 'struct ValidationEntry' with the validated address
981  * @param max space in buf
982  * @param buf where to add the address
983  */
984 static size_t
985 add_valid_peer_address (void *cls,
986                         size_t max,
987                         void *buf)
988 {
989   struct ValidationEntry *ve = cls;
990
991   return GNUNET_HELLO_add_address (ve->transport_name,
992                                    ve->valid_until,
993                                    ve->addr,
994                                    ve->addrlen,
995                                    buf,
996                                    max);
997 }
998
999
1000 /**
1001  * We've received a PONG.  Check if it matches a pending PING and
1002  * mark the respective address as confirmed.
1003  *
1004  * @param sender peer sending the PONG
1005  * @param hdr the PONG
1006  * @param plugin_name name of plugin that received the PONG
1007  * @param sender_address address of the sender as known to the plugin, NULL
1008  *                       if we did not initiate the connection
1009  * @param sender_address_len number of bytes in sender_address
1010  */
1011 void
1012 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
1013                             const struct GNUNET_MessageHeader *hdr,
1014                             const char *plugin_name,
1015                             const void *sender_address,
1016                             size_t sender_address_len)
1017 {
1018   const struct TransportPongMessage *pong;
1019   struct ValidationEntry *ve;
1020   const char *addr;
1021   const char *addrend;
1022   size_t alen;
1023   size_t slen;
1024   uint32_t rdelay;
1025   struct GNUNET_TIME_Relative delay;
1026   struct GNUNET_HELLO_Message *hello;
1027
1028   if (ntohs (hdr->size) < sizeof (struct TransportPongMessage))
1029     {
1030       GNUNET_break_op (0);
1031       return;
1032     }
1033   GNUNET_STATISTICS_update (GST_stats,
1034                             gettext_noop ("# PONG messages received"),
1035                             1,
1036                             GNUNET_NO);
1037   pong = (const struct TransportPongMessage *) hdr;
1038   if (0 != memcmp (&pong->pid,
1039                    sender,
1040                    sizeof (struct GNUNET_PeerIdentity)))
1041     {
1042       /* PONG is validating inbound session, not an address, not the case
1043          used for address validation, ignore here! */
1044       return;
1045     }
1046 #if DEBUG_TRANSPORT
1047   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
1048               "Processing `%s' from `%s'\n",
1049               "PONG",
1050               (sender_address != NULL)
1051               ? GST_plugin_a2s (plugin_name,
1052                                 sender_address,
1053                                 sender_address_len)
1054               : "<inbound>");
1055 #endif
1056   addr = (const char*) &pong[1];
1057   alen = ntohs (hdr->size) - sizeof (struct TransportPongMessage);
1058   addrend = memchr (addr, '\0', alen);
1059   if (NULL == addrend)
1060     {
1061       GNUNET_break_op (0);
1062       return;
1063     }
1064   addrend++;
1065   slen = strlen(addr);
1066   alen -= slen;
1067   ve = find_validation_entry (NULL,
1068                               sender,
1069                               addr,
1070                               addrend,
1071                               alen);
1072   if (NULL == ve)
1073     {
1074       GNUNET_STATISTICS_update (GST_stats,
1075                                 gettext_noop ("# PONGs dropped, no matching pending validation"),
1076                                 1,
1077                                 GNUNET_NO);
1078       return;
1079     }
1080   /* now check that PONG is well-formed */
1081   if (GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0)
1082     {
1083       GNUNET_STATISTICS_update (GST_stats,
1084                                 gettext_noop ("# PONGs dropped, signature expired"),
1085                                 1,
1086                                 GNUNET_NO);
1087       return;
1088     }
1089   if (GNUNET_OK !=
1090       GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
1091                                 &pong->purpose,
1092                                 &pong->signature,
1093                                 &ve->public_key))
1094     {
1095       GNUNET_break_op (0);
1096       return;
1097     }
1098   
1099   /* validity achieved, remember it! */
1100   ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
1101
1102   /* build HELLO to store in PEERINFO */
1103   hello = GNUNET_HELLO_create (&ve->public_key,
1104                                &add_valid_peer_address,
1105                                ve);
1106   GNUNET_PEERINFO_add_peer (GST_peerinfo,
1107                             hello);
1108   GNUNET_free (hello);
1109
1110   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
1111     GNUNET_SCHEDULER_cancel (ve->timeout_task);
1112
1113   /* randomly delay by up to 1h to avoid synchronous validations */
1114   rdelay = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1115                                      60 * 60);
1116   delay = GNUNET_TIME_relative_add (HELLO_REVALIDATION_START_TIME,
1117                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1118                                                                    rdelay));
1119   ve->timeout_task = GNUNET_SCHEDULER_add_delayed (delay,
1120                                                    &revalidate_address,
1121                                                    ve);
1122 }
1123
1124
1125 /**
1126  * We've received a HELLO, check which addresses are new and trigger
1127  * validation.
1128  *
1129  * @param hello the HELLO we received
1130  */
1131 void
1132 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
1133 {
1134   const struct GNUNET_HELLO_Message* hm = (const struct GNUNET_HELLO_Message*) hello;
1135   struct ValidateAddressContext vac;
1136
1137   if ( (GNUNET_OK !=
1138         GNUNET_HELLO_get_id (hm, &vac.pid)) ||
1139        (GNUNET_OK !=
1140         GNUNET_HELLO_get_key (hm, &vac.public_key)) )
1141     {
1142       /* malformed HELLO */
1143       GNUNET_break (0);
1144       return; 
1145     }
1146   GNUNET_assert (NULL ==
1147                  GNUNET_HELLO_iterate_addresses (hm,
1148                                                  GNUNET_NO,
1149                                                  &validate_address,
1150                                                  &vac));
1151 }
1152
1153
1154 /**
1155  * Opaque handle to stop incremental validation address callbacks.
1156  */
1157 struct GST_ValidationIteratorContext
1158 {
1159   /**
1160    * Function to call on each address.
1161    */
1162   GST_ValidationAddressCallback cb;
1163
1164   /**
1165    * Closure for 'cb'.
1166    */
1167   void *cb_cls;
1168
1169   /**
1170    * Which peer are we monitoring?
1171    */   
1172   struct GNUNET_PeerIdentity target;
1173 };
1174
1175
1176 /**
1177  * Call the callback in the closure for each validation entry.
1178  *
1179  * @param cls the 'struct GST_ValidationIteratorContext'
1180  * @param key the peer's identity
1181  * @param value the 'struct ValidationEntry'
1182  * @return GNUNET_OK (continue to iterate)
1183  */
1184 static int
1185 iterate_addresses (void *cls,
1186                    const GNUNET_HashCode *key,
1187                    void *value)
1188 {
1189   struct GST_ValidationIteratorContext *vic = cls;
1190   struct ValidationEntry *ve = value;
1191
1192   vic->cb (vic->cb_cls,
1193            &ve->public_key,
1194            &ve->pid,
1195            ve->valid_until,
1196            ve->validation_block,
1197            ve->transport_name,
1198            ve->addr,
1199            ve->addrlen);
1200   return GNUNET_OK;
1201 }
1202
1203
1204 /**
1205  * Call the given function for each address for the given target.
1206  * Can either give a snapshot (synchronous API) or be continuous.
1207  *
1208  * @param target peer information is requested for
1209  * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to
1210  *                      continue to give information about addresses as it evolves
1211  * @param cb function to call; will not be called after this function returns
1212  *                             if snapshot_only is GNUNET_YES
1213  * @param cb_cls closure for 'cb'
1214  * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES
1215  */
1216 struct GST_ValidationIteratorContext *
1217 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
1218                               int snapshot_only,
1219                               GST_ValidationAddressCallback cb,
1220                               void *cb_cls)
1221 {
1222   struct GST_ValidationIteratorContext *vic;
1223
1224   vic = GNUNET_malloc (sizeof (struct GST_ValidationIteratorContext));
1225   vic->cb = cb;
1226   vic->cb_cls = cb_cls;
1227   vic->target = *target;
1228   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
1229                                               &target->hashPubKey,
1230                                               &iterate_addresses,
1231                                               vic);
1232   if (GNUNET_YES == snapshot_only)
1233     {
1234       GNUNET_free (vic);
1235       return NULL;
1236     }
1237   GNUNET_CONTAINER_multihashmap_put (notify_map,
1238                                      &target->hashPubKey,
1239                                      vic,
1240                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1241   return vic;
1242 }
1243
1244
1245 /**
1246  * Cancel an active validation address iteration.
1247  *
1248  * @param ctx the context of the operation that is cancelled
1249  */
1250 void
1251 GST_validation_get_addresses_cancel (struct GST_ValidationIteratorContext *ctx)
1252 {
1253   GNUNET_assert (GNUNET_OK ==
1254                  GNUNET_CONTAINER_multihashmap_remove (notify_map,
1255                                                        &ctx->target.hashPubKey,
1256                                                        ctx));
1257   GNUNET_free (ctx);
1258 }
1259
1260
1261 /* end of file gnunet-service-transport_validation.c */