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