added ATS addresstype information to unix
[oweals/gnunet.git] / src / hello / hello.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 hello/hello.c
23  * @brief helper library for handling HELLOs
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_hello_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_util_lib.h"
30
31 /**
32  * A HELLO message is used to exchange information about
33  * transports with other peers.  This struct is always
34  * followed by the actual network addresses which have
35  * the format:
36  *
37  * 1) transport-name (0-terminated)
38  * 2) address-length (uint16_t, network byte order; possibly
39  *    unaligned!)
40  * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
41  *    unaligned!)
42  * 4) address (address-length bytes; possibly unaligned!)
43  */
44 struct GNUNET_HELLO_Message
45 {
46   /**
47    * Type will be GNUNET_MESSAGE_TYPE_HELLO.
48    */
49   struct GNUNET_MessageHeader header;
50
51   /**
52    * Always zero (for alignment).
53    */
54   uint32_t reserved GNUNET_PACKED;
55
56   /**
57    * The public key of the peer.
58    */
59   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
60
61 };
62
63
64 /**
65  * Copy the given address information into
66  * the given buffer using the format of HELLOs.
67  *
68  * @param address the address
69  * @param expiration expiration for the address
70  * @param target where to copy the address
71  * @param max maximum number of bytes to copy to target
72  * @return number of bytes copied, 0 if
73  *         the target buffer was not big enough.
74  */
75 size_t
76 GNUNET_HELLO_add_address (const struct GNUNET_HELLO_Address *address,
77                           struct GNUNET_TIME_Absolute expiration, char *target,
78                           size_t max)
79 {
80   uint16_t alen;
81   size_t slen;
82   struct GNUNET_TIME_AbsoluteNBO exp;
83
84   slen = strlen (address->transport_name) + 1;
85   if (slen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
86       address->address_length > max)
87     return 0;
88   exp = GNUNET_TIME_absolute_hton (expiration);
89   alen = htons ((uint16_t) address->address_length);
90   memcpy (target, address->transport_name, slen);
91   memcpy (&target[slen], &alen, sizeof (uint16_t));
92   slen += sizeof (uint16_t);
93   memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO));
94   slen += sizeof (struct GNUNET_TIME_AbsoluteNBO);
95   memcpy (&target[slen], address->address, address->address_length);
96   slen += address->address_length;
97   return slen;
98 }
99
100
101 /**
102  * Get the size of an address entry in a HELLO message.
103  *
104  * @param buf pointer to the start of the address entry
105  * @param max maximum size of the entry (end of buf)
106  * @param ralen set to the address length
107  * @return size of the entry, or 0 if max is not large enough
108  */
109 static size_t
110 get_hello_address_size (const char *buf, size_t max, uint16_t * ralen)
111 {
112   const char *pos;
113   uint16_t alen;
114   size_t left;
115   size_t slen;
116
117   left = max;
118   pos = buf;
119   slen = 1;
120   while ((left > 0) && ('\0' != *pos))
121   {
122     left--;
123     pos++;
124     slen++;
125   }
126   if (left == 0)
127   {
128     /* 0-termination not found */
129     GNUNET_break_op (0);
130     return 0;
131   }
132   pos++;
133   if (left < sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO))
134   {
135     /* not enough space for addrlen */
136     GNUNET_break_op (0);
137     return 0;
138   }
139   memcpy (&alen, pos, sizeof (uint16_t));
140   alen = ntohs (alen);
141   *ralen = alen;
142   slen += alen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO);
143   if (max < slen)
144   {
145     /* not enough space for addr */
146     GNUNET_break_op (0);
147     return 0;
148   }
149   return slen;
150 }
151
152
153 /**
154  * Construct a HELLO message given the public key,
155  * expiration time and an iterator that spews the
156  * transport addresses.
157  *
158  * @return the hello message
159  */
160 struct GNUNET_HELLO_Message *
161 GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
162                      *publicKey,
163                      GNUNET_HELLO_GenerateAddressListCallback addrgen,
164                      void *addrgen_cls)
165 {
166   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - 256 -
167               sizeof (struct GNUNET_HELLO_Message)];
168   size_t max;
169   size_t used;
170   size_t ret;
171   struct GNUNET_HELLO_Message *hello;
172
173   max = sizeof (buffer);
174   used = 0;
175   if (addrgen != NULL)
176   {
177     while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
178     {
179       max -= ret;
180       used += ret;
181     }
182   }
183   hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used);
184   hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
185   hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
186   memcpy (&hello->publicKey, publicKey,
187           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
188   memcpy (&hello[1], buffer, used);
189   return hello;
190 }
191
192
193 /**
194  * Iterate over all of the addresses in the HELLO.
195  *
196  * @param msg HELLO to iterate over
197  * @param return_modified if a modified copy should be returned,
198  *         otherwise NULL will be returned
199  * @param it iterator to call on each address
200  * @param it_cls closure for it
201  */
202 struct GNUNET_HELLO_Message *
203 GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
204                                 int return_modified,
205                                 GNUNET_HELLO_AddressIterator it, void *it_cls)
206 {
207   struct GNUNET_HELLO_Address address;
208   uint16_t msize;
209   struct GNUNET_HELLO_Message *ret;
210   const char *inptr;
211   size_t insize;
212   size_t esize;
213   size_t wpos;
214   char *woff;
215   uint16_t alen;
216   struct GNUNET_TIME_AbsoluteNBO expire;
217   int iret;
218
219   msize = GNUNET_HELLO_size (msg);
220   if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
221       (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
222     return NULL;
223   ret = NULL;
224   if (return_modified)
225   {
226     ret = GNUNET_malloc (msize);
227     memcpy (ret, msg, msize);
228   }
229   inptr = (const char *) &msg[1];
230   insize = msize - sizeof (struct GNUNET_HELLO_Message);
231   wpos = 0;
232   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
233   GNUNET_CRYPTO_hash (&msg->publicKey,
234                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
235                       &address.peer.hashPubKey);
236   while (insize > 0)
237   {
238     esize = get_hello_address_size (inptr, insize, &alen);
239     if (esize == 0)
240     {
241       GNUNET_break (0);
242       GNUNET_free_non_null (ret);
243       return NULL;
244     }
245     memcpy (&expire,
246             &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
247             sizeof (struct GNUNET_TIME_AbsoluteNBO));
248     address.address = &inptr[esize - alen];
249     address.address_length = alen;
250     address.transport_name = inptr;
251     iret = it (it_cls, &address, GNUNET_TIME_absolute_ntoh (expire));
252     if (iret == GNUNET_SYSERR)
253     {
254       if (ret != NULL)
255         ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
256       return ret;
257     }
258     if ((iret == GNUNET_OK) && (ret != NULL))
259     {
260       memcpy (woff, inptr, esize);
261       woff += esize;
262       wpos += esize;
263     }
264     insize -= esize;
265     inptr += esize;
266   }
267   if (ret != NULL)
268     ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
269   return ret;
270 }
271
272
273 struct ExpireContext
274 {
275   const struct GNUNET_HELLO_Address *address;
276   int found;
277   struct GNUNET_TIME_Absolute expiration;
278 };
279
280
281 static int
282 get_match_exp (void *cls, const struct GNUNET_HELLO_Address *address,
283                struct GNUNET_TIME_Absolute expiration)
284 {
285   struct ExpireContext *ec = cls;
286
287   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
288   {
289     ec->found = GNUNET_YES;
290     ec->expiration = expiration;
291     return GNUNET_SYSERR;       /* done here */
292   }
293   return GNUNET_OK;
294 }
295
296
297 struct MergeContext
298 {
299   const struct GNUNET_HELLO_Message *h1;
300   const struct GNUNET_HELLO_Message *h2;
301   const struct GNUNET_HELLO_Message *other;
302   char *buf;
303   size_t max;
304   size_t ret;
305   int take_equal;
306
307 };
308
309
310 static int
311 copy_latest (void *cls, const struct GNUNET_HELLO_Address *address,
312              struct GNUNET_TIME_Absolute expiration)
313 {
314   struct MergeContext *mc = cls;
315   struct ExpireContext ec;
316
317   ec.address = address;
318   ec.found = GNUNET_NO;
319   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
320   if ((ec.found == GNUNET_NO) ||
321       (ec.expiration.abs_value < expiration.abs_value) ||
322       ((ec.expiration.abs_value == expiration.abs_value) &&
323        (mc->take_equal == GNUNET_YES)))
324   {
325     mc->ret +=
326         GNUNET_HELLO_add_address (address, expiration, &mc->buf[mc->ret],
327                                   mc->max - mc->ret);
328   }
329   return GNUNET_OK;
330 }
331
332
333 static size_t
334 merge_addr (void *cls, size_t max, void *buf)
335 {
336   struct MergeContext *mc = cls;
337
338   if (mc->h1 == NULL)
339     return 0;
340   mc->ret = 0;
341   mc->max = max;
342   mc->buf = buf;
343   mc->take_equal = GNUNET_NO;
344   mc->other = mc->h2;
345   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
346   mc->take_equal = GNUNET_YES;
347   mc->other = mc->h1;
348   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
349   mc->h1 = NULL;
350   return mc->ret;
351 }
352
353
354 /**
355  * Construct a HELLO message by merging the
356  * addresses in two existing HELLOs (which
357  * must be for the same peer).
358  *
359  * @param h1 first HELLO message
360  * @param h2 the second HELLO message
361  * @return the combined hello message
362  */
363 struct GNUNET_HELLO_Message *
364 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
365                     const struct GNUNET_HELLO_Message *h2)
366 {
367   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
368
369   return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
370 }
371
372
373 struct DeltaContext
374 {
375   struct GNUNET_TIME_Absolute expiration_limit;
376
377   GNUNET_HELLO_AddressIterator it;
378
379   void *it_cls;
380
381   const struct GNUNET_HELLO_Message *old_hello;
382 };
383
384
385 static int
386 delta_match (void *cls, const struct GNUNET_HELLO_Address *address,
387              struct GNUNET_TIME_Absolute expiration)
388 {
389   struct DeltaContext *dc = cls;
390   int ret;
391   struct ExpireContext ec;
392
393   ec.address = address;
394   ec.found = GNUNET_NO;
395   GNUNET_HELLO_iterate_addresses (dc->old_hello, GNUNET_NO, &get_match_exp,
396                                   &ec);
397   if ((ec.found == GNUNET_YES) &&
398       ((ec.expiration.abs_value > expiration.abs_value) ||
399        (ec.expiration.abs_value >= dc->expiration_limit.abs_value)))
400     return GNUNET_YES;          /* skip */
401   ret = dc->it (dc->it_cls, address, expiration);
402   return ret;
403 }
404
405
406 /**
407  * Iterate over addresses in "new_hello" that
408  * are NOT already present in "old_hello".
409  *
410  * @param new_hello a HELLO message
411  * @param old_hello a HELLO message
412  * @param expiration_limit ignore addresses in old_hello
413  *        that expired before the given time stamp
414  * @param it iterator to call on each address
415  * @param it_cls closure for it
416  */
417 void
418 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
419                                     *new_hello,
420                                     const struct GNUNET_HELLO_Message
421                                     *old_hello,
422                                     struct GNUNET_TIME_Absolute
423                                     expiration_limit,
424                                     GNUNET_HELLO_AddressIterator it,
425                                     void *it_cls)
426 {
427   struct DeltaContext dc;
428
429   dc.expiration_limit = expiration_limit;
430   dc.it = it;
431   dc.it_cls = it_cls;
432   dc.old_hello = old_hello;
433   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
434 }
435
436
437 /**
438  * Return the size of the given HELLO message.
439  * @param hello to inspect
440  * @return the size, 0 if HELLO is invalid
441  */
442 uint16_t
443 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
444 {
445   uint16_t ret = ntohs (hello->header.size);
446
447   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
448       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
449     return 0;
450   return ret;
451 }
452
453
454 /**
455  * Get the public key from a HELLO message.
456  *
457  * @param hello the hello message
458  * @param publicKey where to copy the public key information, can be NULL
459  * @return GNUNET_SYSERR if the HELLO was malformed
460  */
461 int
462 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
463                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
464 {
465   uint16_t ret = ntohs (hello->header.size);
466
467   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
468       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
469     return GNUNET_SYSERR;
470   *publicKey = hello->publicKey;
471   return GNUNET_OK;
472 }
473
474
475 /**
476  * Get the peer identity from a HELLO message.
477  *
478  * @param hello the hello message
479  * @param peer where to store the peer's identity
480  * @return GNUNET_SYSERR if the HELLO was malformed
481  */
482 int
483 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
484                      struct GNUNET_PeerIdentity *peer)
485 {
486   uint16_t ret = ntohs (hello->header.size);
487
488   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
489       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
490     return GNUNET_SYSERR;
491   GNUNET_CRYPTO_hash (&hello->publicKey,
492                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
493                       &peer->hashPubKey);
494   return GNUNET_OK;
495 }
496
497
498 /**
499  * Get the header from a HELLO message, used so other code
500  * can correctly send HELLO messages.
501  *
502  * @param hello the hello message
503  *
504  * @return header or NULL if the HELLO was malformed
505  */
506 struct GNUNET_MessageHeader *
507 GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
508 {
509   uint16_t ret = ntohs (hello->header.size);
510
511   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
512       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
513     return NULL;
514
515   return &hello->header;
516 }
517
518
519 struct EqualsContext
520 {
521   struct GNUNET_TIME_Absolute expiration_limit;
522
523   struct GNUNET_TIME_Absolute result;
524
525   const struct GNUNET_HELLO_Message *h2;
526
527   const struct GNUNET_HELLO_Address *address;
528
529   struct GNUNET_TIME_Absolute expiration;
530
531   int found;
532
533 };
534
535
536 static int
537 find_other_matching (void *cls, const struct GNUNET_HELLO_Address *address,
538                      struct GNUNET_TIME_Absolute expiration)
539 {
540   struct EqualsContext *ec = cls;
541
542   if (expiration.abs_value < ec->expiration_limit.abs_value)
543     return GNUNET_YES;
544   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
545   {
546     ec->found = GNUNET_YES;
547     if (expiration.abs_value < ec->expiration.abs_value)
548       ec->result = GNUNET_TIME_absolute_min (expiration, ec->result);
549     return GNUNET_SYSERR;
550   }
551   return GNUNET_YES;
552 }
553
554
555 static int
556 find_matching (void *cls, const struct GNUNET_HELLO_Address *address,
557                struct GNUNET_TIME_Absolute expiration)
558 {
559   struct EqualsContext *ec = cls;
560
561   if (expiration.abs_value < ec->expiration_limit.abs_value)
562     return GNUNET_YES;
563   ec->address = address;
564   ec->expiration = expiration;
565   ec->found = GNUNET_NO;
566   GNUNET_HELLO_iterate_addresses (ec->h2, GNUNET_NO, &find_other_matching, ec);
567   if (ec->found == GNUNET_NO)
568   {
569     ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
570     return GNUNET_SYSERR;
571   }
572   return GNUNET_OK;
573 }
574
575
576 /**
577  * Test if two HELLO messages contain the same addresses.
578  * If they only differ in expiration time, the lowest
579  * expiration time larger than 'now' where they differ
580  * is returned.
581  *
582  * @param h1 first HELLO message
583  * @param h2 the second HELLO message
584  * @param now time to use for deciding which addresses have
585  *            expired and should not be considered at all
586  * @return absolute time forever if the two HELLOs are
587  *         totally identical; smallest timestamp >= now if
588  *         they only differ in timestamps;
589  *         zero if the some addresses with expirations >= now
590  *         do not match at all
591  */
592 struct GNUNET_TIME_Absolute
593 GNUNET_HELLO_equals (const struct GNUNET_HELLO_Message *h1,
594                      const struct GNUNET_HELLO_Message *h2,
595                      struct GNUNET_TIME_Absolute now)
596 {
597   struct EqualsContext ec;
598
599   if (0 !=
600       memcmp (&h1->publicKey, &h2->publicKey,
601               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
602     return GNUNET_TIME_UNIT_ZERO_ABS;
603   ec.expiration_limit = now;
604   ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
605   ec.h2 = h2;
606   GNUNET_HELLO_iterate_addresses (h1, GNUNET_NO, &find_matching, &ec);
607   if (ec.result.abs_value == GNUNET_TIME_UNIT_ZERO.rel_value)
608     return ec.result;
609   ec.h2 = h1;
610   GNUNET_HELLO_iterate_addresses (h2, GNUNET_NO, &find_matching, &ec);
611   return ec.result;
612 }
613
614
615 static int
616 find_min_expire (void *cls, const struct GNUNET_HELLO_Address *address,
617                  struct GNUNET_TIME_Absolute expiration)
618 {
619   struct GNUNET_TIME_Absolute *min = cls;
620
621   *min = GNUNET_TIME_absolute_min (*min, expiration);
622   return GNUNET_OK;
623 }
624
625
626 /**
627  * When does the last address in the given HELLO expire?
628  *
629  * @param msg HELLO to inspect
630  * @return time the last address expires, 0 if there are no addresses in the HELLO
631  */
632 struct GNUNET_TIME_Absolute
633 GNUNET_HELLO_get_last_expiration (const struct GNUNET_HELLO_Message *msg)
634 {
635   struct GNUNET_TIME_Absolute ret;
636
637   ret.abs_value = 0;
638   GNUNET_HELLO_iterate_addresses (msg, GNUNET_NO, &find_min_expire, &ret);
639   return ret;
640 }
641
642
643 /* end of hello.c */