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