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