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