9583fc1a790dadb5c4252d1f70d66d83cc775f13
[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
33
34 /**
35  * How long until a HELLO verification attempt should time out?
36  * Must be rather small, otherwise a partially successful HELLO
37  * validation (some addresses working) might not be available
38  * before a client's request for a connection fails for good.
39  * Besides, if a single request to an address takes a long time,
40  * then the peer is unlikely worthwhile anyway.
41  */
42 #define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
43
44 /**
45  * How long is a PONG signature valid?  We'll recycle a signature until
46  * 1/4 of this time is remaining.  PONGs should expire so that if our
47  * external addresses change an adversary cannot replay them indefinitely.
48  * OTOH, we don't want to spend too much time generating PONG signatures,
49  * so they must have some lifetime to reduce our CPU usage.
50  */
51 #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
52
53 /**
54  * After how long do we expire an address in a HELLO that we just
55  * validated?  This value is also used for our own addresses when we
56  * create a HELLO.
57  */
58 #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
59
60 /**
61  * How long before an existing address expires should we again try to
62  * validate it?  Must be (significantly) smaller than
63  * HELLO_ADDRESS_EXPIRATION.
64  */
65 #define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
66
67 /**
68  * How long before we try to check an address again (if it turned out to
69  * be invalid the first time)?
70  */
71 #define MAX_REVALIDATION_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
72
73 /**
74  * Size of the validation map hashmap.
75  */
76 #define VALIDATION_MAP_SIZE 256
77
78 /**
79  * Priority to use for PINGs and PONGs
80  */ 
81 #define PING_PRIORITY 1
82
83 /**
84  * Message used to ask a peer to validate receipt (to check an address
85  * from a HELLO).  Followed by the address we are trying to validate,
86  * or an empty address if we are just sending a PING to confirm that a
87  * connection which the receiver (of the PING) initiated is still valid.
88  */
89 struct TransportPingMessage
90 {
91
92   /**
93    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING
94    */
95   struct GNUNET_MessageHeader header;
96
97   /**
98    * Challenge code (to ensure fresh reply).
99    */
100   uint32_t challenge GNUNET_PACKED;
101
102   /**
103    * Who is the intended recipient?
104    */
105   struct GNUNET_PeerIdentity target;
106
107 };
108
109
110 /**
111  * Message used to validate a HELLO.  The challenge is included in the
112  * confirmation to make matching of replies to requests possible.  The
113  * signature signs our public key, an expiration time and our address.<p>
114  *
115  * This message is followed by our transport address that the PING tried
116  * to confirm (if we liked it).  The address can be empty (zero bytes)
117  * if the PING had not address either (and we received the request via
118  * a connection that we initiated).
119  */
120 struct TransportPongMessage
121 {
122
123   /**
124    * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
125    */
126   struct GNUNET_MessageHeader header;
127
128   /**
129    * Challenge code from PING (showing freshness).  Not part of what
130    * is signed so that we can re-use signatures.
131    */
132   uint32_t challenge GNUNET_PACKED;
133
134   /**
135    * Signature.
136    */
137   struct GNUNET_CRYPTO_RsaSignature signature;
138
139   /**
140    * What are we signing and why?  Two possible reason codes can be here:
141    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
142    * plausible address for this peer (pid is set to identity of signer); or
143    * GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_USING to confirm that this is
144    * an address we used to connect to the peer with the given pid.
145    */
146   struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
147
148   /**
149    * When does this signature expire?
150    */
151   struct GNUNET_TIME_AbsoluteNBO expiration;
152
153   /**
154    * Either the identity of the peer Who signed this message, or the
155    * identity of the peer that we're connected to using the given
156    * address (depending on purpose.type).
157    */
158   struct GNUNET_PeerIdentity pid;
159
160   /**
161    * Size of address appended to this message (part of what is
162    * being signed, hence not redundant).
163    */
164   uint32_t addrlen;
165
166 };
167
168
169 /**
170  * Information about an address under validation
171  */
172 struct ValidationEntry 
173 {
174
175   /**
176    * Name of the transport.
177    */
178   char *transport_name;
179
180   /**
181    * The address, actually a pointer to the end
182    * of this struct.  Do not free!
183    */
184   const void *addr;
185
186   /**
187    * The identity of the peer.
188    */
189   struct GNUNET_PeerIdentity pid;
190
191   /**
192    * ID of task that will clean up this entry if nothing happens.
193    */
194   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
195
196   /**
197    * At what time did we send the latest validation request?
198    */
199   struct GNUNET_TIME_Absolute send_time;
200
201   /**
202    * Until when is this address valid?
203    * ZERO if it is not currently considered valid.
204    */
205   struct GNUNET_TIME_Absolute valid_until;
206
207   /**
208    * How long until we can try to validate this address again?
209    * FOREVER if the address is for an unsupported plugin (from PEERINFO)
210    * ZERO if the address is considered valid (no validation needed)
211    * otherwise a time in the future if we're currently denying re-validation
212    */
213   struct GNUNET_TIME_Absolute validation_block;
214                                             
215   /**
216    * Challenge number we used.
217    */
218   uint32_t challenge;
219
220   /**
221    * Length of addr.
222    */
223   size_t addrlen;
224
225 };
226
227
228 /**
229  * Context of currently active requests to peerinfo
230  * for validation of HELLOs.
231  */
232 struct CheckHelloValidatedContext
233 {
234
235   /**
236    * This is a doubly-linked list.
237    */
238   struct CheckHelloValidatedContext *next;
239
240   /**
241    * This is a doubly-linked list.
242    */
243   struct CheckHelloValidatedContext *prev;
244
245   /**
246    * Hello that we are validating.
247    */
248   const struct GNUNET_HELLO_Message *hello;
249
250   /**
251    * Context for peerinfo iteration.
252    */
253   struct GNUNET_PEERINFO_IteratorContext *piter;
254
255 };
256
257
258 /**
259  * Head of linked list of HELLOs awaiting validation.
260  */
261 static struct CheckHelloValidatedContext *chvc_head;
262
263 /**
264  * Tail of linked list of HELLOs awaiting validation
265  */
266 static struct CheckHelloValidatedContext *chvc_tail;
267
268 /**
269  * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
270  * of the given peer that we are currently validating, have validated
271  * or are blocked from re-validation for a while).
272  */
273 static struct GNUNET_CONTAINER_MultiHashMap *validation_map;
274
275 /**
276  * Map of PeerIdentities to 'struct GST_ValidationIteratorContext's.
277  */
278 static struct GNUNET_CONTAINER_MultiHashMap *notify_map;
279
280
281 /**
282  * Start the validation subsystem.
283  */
284 void 
285 GST_validation_start ()
286 {
287   validation_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
288   notify_map = GNUNET_CONTAINER_multihashmap_create (VALIDATION_MAP_SIZE);
289 }
290
291
292 /**
293  * Iterate over validation entries and free them.
294  *
295  * @param cls (unused)
296  * @param key peer identity (unused)
297  * @param value a 'struct ValidationEntry' to clean up
298  * @return GNUNET_YES (continue to iterate)
299  */
300 static int
301 cleanup_validation_entry (void *cls,
302                           const GNUNET_HashCode *key,
303                           void *value)
304 {
305   struct ValidationEntry *ve = value;
306     
307   GNUNET_free (ve->transport_name);
308   if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
309     {
310       GNUNET_SCHEDULER_cancel (ve->timeout_task);
311       ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
312     }
313   GNUNET_free (ve);
314   return GNUNET_OK;
315 }
316
317
318 /**
319  * Stop the validation subsystem.
320  */
321 void
322 GST_validation_stop ()
323 {
324   struct CheckHelloValidatedContext *chvc;
325
326   GNUNET_CONTAINER_multihashmap_iterate (validation_map,
327                                          &cleanup_validation_entry,
328                                          NULL);
329   GNUNET_CONTAINER_multihashmap_destroy (validation_map);
330   validation_map = NULL;
331   GNUNET_assert (GNUNET_CONTAINER_multihashmap_size (notify_map) == 0);
332   GNUNET_CONTAINER_multihashmap_destroy (notify_map);
333   notify_map = NULL;
334   while (NULL != (chvc = chvc_head))
335     {
336       GNUNET_CONTAINER_DLL_remove (chvc_head,
337                                    chvc_tail,
338                                    chvc);
339       GNUNET_PEERINFO_iterate_cancel (chvc->piter);      
340       GNUNET_free (chvc);
341     }
342 }
343
344
345 #if 0
346 /**
347  * Address validation cleanup task (record no longer needed).
348  *
349  * @param cls the 'struct ValidationEntry'
350  * @param tc scheduler context (unused)
351  */
352 static void
353 timeout_hello_validation (void *cls, 
354                           const struct GNUNET_SCHEDULER_TaskContext *tc)
355 {
356   struct ValidationEntry *va = cls;
357
358   va->timeout_task = GNUNET_SCHEDULER_NO_TASK;
359   GNUNET_STATISTICS_update (GST_stats,
360                             gettext_noop ("# address records discarded"),
361                             1,
362                             GNUNET_NO);
363   GNUNET_break (GNUNET_OK ==
364                 GNUNET_CONTAINER_multihashmap_remove (validation_map,
365                                                       &va->pid.hashPubKey,
366                                                       va));
367   GNUNET_free (va->transport_name);
368   GNUNET_free (va);
369 }
370 #endif
371
372
373 /**
374  * Context for the validation entry match function.
375  */
376 struct ValidationEntryMatchContext
377 {
378   /**
379    * Where to store the result?
380    */
381   struct ValidationEntry *ve;
382   
383   /**
384    * Transport name we're looking for.
385    */
386   const char *transport_name;
387
388   /**
389    * Address we're interested in.
390    */
391   const char *addr;
392
393   /**
394    * Number of bytes in 'addr'.
395    */
396   size_t addrlen;
397 };
398
399
400 /**
401  * Iterate over validation entries until a matching one is found.
402  *
403  * @param cls the 'struct ValidationEntryMatchContext'
404  * @param key peer identity (unused)
405  * @param value a 'struct ValidationEntry' to match
406  * @return GNUNET_YES if the entry does not match,
407  *         GNUNET_NO if the entry does match
408  */
409 static int
410 validation_entry_match (void *cls,
411                         const GNUNET_HashCode *key,
412                         void *value)
413 {
414   struct ValidationEntryMatchContext *vemc = cls;
415   struct ValidationEntry *ve = value;
416
417   if ( (ve->addrlen == vemc->addrlen) &&
418        (0 == memcmp (ve->addr, vemc->addr, ve->addrlen)) &&
419        (0 == strcmp (ve->transport_name, vemc->transport_name)) )
420     {
421       vemc->ve = ve;
422       return GNUNET_NO;
423     }
424   return GNUNET_YES;
425 }
426
427
428 /**
429  * Find a ValidationEntry entry for the given neighbour that matches
430  * the given address and transport.  If none exists, create one (but
431  * without starting any validation).
432  *
433  * @param neighbour which peer we care about
434  * @param tname name of the transport plugin
435  * @param session session to look for, NULL for 'any'; otherwise
436  *        can be used for the service to "learn" this session ID
437  *        if 'addr' matches
438  * @param addr binary address
439  * @param addrlen length of addr
440  * @return validation entry matching the given specifications
441  */
442 static struct ValidationEntry *
443 find_validation_entry (struct GNUNET_PeerIdentity *neighbour,
444                        const char *tname,
445                        const char *addr,
446                        size_t addrlen)
447 {
448   struct ValidationEntryMatchContext vemc;
449   struct ValidationEntry *ve;
450
451   vemc.ve = NULL;
452   vemc.transport_name = tname;
453   vemc.addr = addr;
454   vemc.addrlen = addrlen;
455   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
456                                               &neighbour->hashPubKey,
457                                               &validation_entry_match,
458                                               &vemc);
459   if (NULL != (ve = vemc.ve))
460     return ve;
461   ve = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen);
462   ve->transport_name = GNUNET_strdup (tname);
463   ve->addr = (void*) &ve[1];
464   ve->pid = *neighbour;
465   ve->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
466                                             UINT32_MAX);                                            
467   memcpy (&ve[1], addr, addrlen);
468   ve->addrlen = addrlen;
469   GNUNET_CONTAINER_multihashmap_put (validation_map,
470                                      &neighbour->hashPubKey,
471                                      ve,
472                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
473   return ve;
474 }
475
476
477 /**
478  * We've received a PING.  If appropriate, generate a PONG.
479  *
480  * @param sender peer sending the PING
481  * @param hdr the PING
482  * @param plugin_name name of plugin that received the PING
483  * @param sender_address address of the sender as known to the plugin, NULL
484  *                       if we did not initiate the connection
485  * @param sender_address_len number of bytes in sender_address
486  */
487 void
488 GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
489                             const struct GNUNET_MessageHeader *hdr,
490                             const char *plugin_name,
491                             const void *sender_address,
492                             size_t sender_address_len)
493 {
494 }
495
496
497 /**
498  * We've received a PONG.  Check if it matches a pending PING and
499  * mark the respective address as confirmed.
500  *
501  * @param sender peer sending the PONG
502  * @param hdr the PONG
503  * @param plugin_name name of plugin that received the PONG
504  * @param sender_address address of the sender as known to the plugin, NULL
505  *                       if we did not initiate the connection
506  * @param sender_address_len number of bytes in sender_address
507  */
508 void
509 GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
510                             const struct GNUNET_MessageHeader *hdr,
511                             const char *plugin_name,
512                             const void *sender_address,
513                             size_t sender_address_len)
514 {
515 }
516
517
518 /**
519  * Iterator callback to go over all addresses and try to validate them
520  * (unless blocked or already validated).
521  *
522  * @param cls pointer to the 'struct PeerIdentity' of the peer
523  * @param tname name of the transport
524  * @param expiration expiration time
525  * @param addr the address
526  * @param addrlen length of the address
527  * @return GNUNET_OK (keep the address)
528  */
529 static int
530 validate_address (void *cls,
531                   const char *tname,
532                   struct GNUNET_TIME_Absolute expiration,
533                   const void *addr, 
534                   uint16_t addrlen)
535 {
536   struct GNUNET_PeerIdentity *pid = cls;
537   struct ValidationEntry *ve;
538   struct TransportPingMessage ping;
539   struct GNUNET_TRANSPORT_PluginFunctions *papi;
540   ssize_t ret;
541   size_t tsize;
542   size_t slen;
543
544   if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0)
545     return GNUNET_OK; /* expired */
546   ve = find_validation_entry (pid, tname, addr, addrlen);
547   if (GNUNET_TIME_absolute_get_remaining (ve->validation_block).rel_value > 0)
548     return GNUNET_OK; /* blocked */
549   if (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > 0)
550     return GNUNET_OK; /* valid */
551   ve->validation_block = GNUNET_TIME_relative_to_absolute (MAX_REVALIDATION_FREQUENCY);
552
553   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554               "Transmitting plain PING to `%s'\n",
555               GNUNET_i2s (pid));  
556   ping.header.size = htons(sizeof(struct TransportPingMessage));
557   ping.header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
558   ping.challenge = htonl(ve->challenge);
559   ping.target = *pid;
560   
561   slen = strlen(ve->transport_name) + 1;
562   tsize = sizeof(struct TransportPingMessage) + ve->addrlen + slen;
563   {
564     char message_buf[tsize];
565
566     memcpy(message_buf, &ping, sizeof (struct TransportPingMessage));
567     memcpy(&message_buf[sizeof (struct TransportPingMessage)],
568            ve->transport_name,
569            slen);
570     memcpy(&message_buf[sizeof (struct TransportPingMessage) + slen],
571            ve->addr,
572            ve->addrlen);
573     papi = GST_plugins_find (ve->transport_name);
574     ret = papi->send (papi->cls,
575                       pid,
576                       message_buf,
577                       tsize,
578                       PING_PRIORITY,
579                       HELLO_VERIFICATION_TIMEOUT,
580                       NULL /* no session */,
581                       ve->addr,
582                       ve->addrlen,
583                       GNUNET_YES,
584                       NULL, NULL);
585   }
586   if (-1 != ret)
587     {
588       ve->send_time = GNUNET_TIME_absolute_get ();
589       GNUNET_STATISTICS_update (GST_stats,
590                                 gettext_noop ("# PING without HELLO messages sent"),
591                                 1,
592                                 GNUNET_NO);
593     }
594   return GNUNET_OK;
595 }
596
597
598
599 /**
600  * We've received a HELLO, check which addresses are new and trigger
601  * validation.
602  *
603  * @param hello the HELLO we received
604  */
605 void
606 GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
607 {
608   const struct GNUNET_HELLO_Message* hm = (const struct GNUNET_HELLO_Message*) hello;
609   struct GNUNET_PeerIdentity pid;
610
611   if (GNUNET_OK !=
612       GNUNET_HELLO_get_id (hm, &pid))    
613     {
614       /* malformed HELLO */
615       GNUNET_break (0);
616       return; 
617     }
618   GNUNET_assert (NULL ==
619                  GNUNET_HELLO_iterate_addresses (hm,
620                                                  GNUNET_NO,
621                                                  &validate_address,
622                                                  &pid));
623 }
624
625
626 /**
627  * Opaque handle to stop incremental validation address callbacks.
628  */
629 struct GST_ValidationIteratorContext
630 {
631   /**
632    * Function to call on each address.
633    */
634   GST_ValidationAddressCallback cb;
635
636   /**
637    * Closure for 'cb'.
638    */
639   void *cb_cls;
640
641   /**
642    * Which peer are we monitoring?
643    */   
644   struct GNUNET_PeerIdentity target;
645 };
646
647
648 /**
649  * Call the callback in the closure for each validation entry.
650  *
651  * @param cls the 'struct GST_ValidationIteratorContext'
652  * @param key the peer's identity
653  * @param value the 'struct ValidationEntry'
654  * @return GNUNET_OK (continue to iterate)
655  */
656 static int
657 iterate_addresses (void *cls,
658                    const GNUNET_HashCode *key,
659                    void *value)
660 {
661   struct GST_ValidationIteratorContext *vic = cls;
662   struct ValidationEntry *ve = value;
663
664   vic->cb (vic->cb_cls,
665            &ve->pid,
666            ve->valid_until,
667            ve->validation_block,
668            ve->transport_name,
669            ve->addr,
670            ve->addrlen);
671   return GNUNET_OK;
672 }
673
674
675 /**
676  * Call the given function for each address for the given target.
677  * Can either give a snapshot (synchronous API) or be continuous.
678  *
679  * @param target peer information is requested for
680  * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to
681  *                      continue to give information about addresses as it evolves
682  * @param cb function to call; will not be called after this function returns
683  *                             if snapshot_only is GNUNET_YES
684  * @param cb_cls closure for 'cb'
685  * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES
686  */
687 struct GST_ValidationIteratorContext *
688 GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
689                               int snapshot_only,
690                               GST_ValidationAddressCallback cb,
691                               void *cb_cls)
692 {
693   struct GST_ValidationIteratorContext *vic;
694
695   vic = GNUNET_malloc (sizeof (struct GST_ValidationIteratorContext));
696   vic->cb = cb;
697   vic->cb_cls = cb_cls;
698   vic->target = *target;
699   GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
700                                               &target->hashPubKey,
701                                               &iterate_addresses,
702                                               vic);
703   if (GNUNET_YES == snapshot_only)
704     {
705       GNUNET_free (vic);
706       return NULL;
707     }
708   GNUNET_CONTAINER_multihashmap_put (notify_map,
709                                      &target->hashPubKey,
710                                      vic,
711                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
712   return vic;
713 }
714
715
716 /**
717  * Cancel an active validation address iteration.
718  *
719  * @param ctx the context of the operation that is cancelled
720  */
721 void
722 GST_validation_get_addresses_cancel (struct GST_ValidationIteratorContext *ctx)
723 {
724   GNUNET_assert (GNUNET_OK ==
725                  GNUNET_CONTAINER_multihashmap_remove (notify_map,
726                                                        &ctx->target.hashPubKey,
727                                                        ctx));
728   GNUNET_free (ctx);
729 }
730
731
732 /* end of file gnunet-service-transport_validation.c */