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