5d64d8f5ae763bc2cc83bb2df584ec4d7fff65eb
[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, 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     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 =
249         it (it_cls, inptr, GNUNET_TIME_absolute_ntoh (expire),
250             &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, const char *tname,
284                struct GNUNET_TIME_Absolute expiration, const void *addr,
285                uint16_t addrlen)
286 {
287   struct ExpireContext *ec = cls;
288
289   if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen)) &&
290       (0 == strcmp (tname, ec->tname)))
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, const char *tname,
315              struct GNUNET_TIME_Absolute expiration, const void *addr,
316              uint16_t addrlen)
317 {
318   struct MergeContext *mc = cls;
319   struct ExpireContext ec;
320
321   ec.addr = addr;
322   ec.addrlen = addrlen;
323   ec.found = GNUNET_NO;
324   ec.tname = tname;
325   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
326   if ((ec.found == GNUNET_NO) ||
327       (ec.expiration.abs_value < expiration.abs_value) ||
328       ((ec.expiration.abs_value == expiration.abs_value) &&
329        (mc->take_equal == GNUNET_YES)))
330   {
331     mc->ret +=
332         GNUNET_HELLO_add_address (tname, expiration, addr, addrlen,
333                                   &mc->buf[mc->ret], mc->max - mc->ret);
334   }
335   return GNUNET_OK;
336 }
337
338
339 static size_t
340 merge_addr (void *cls, size_t max, void *buf)
341 {
342   struct MergeContext *mc = cls;
343
344   if (mc->h1 == NULL)
345     return 0;
346   mc->ret = 0;
347   mc->max = max;
348   mc->buf = buf;
349   mc->take_equal = GNUNET_NO;
350   mc->other = mc->h2;
351   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
352   mc->take_equal = GNUNET_YES;
353   mc->other = mc->h1;
354   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
355   mc->h1 = NULL;
356   return mc->ret;
357 }
358
359
360 /**
361  * Construct a HELLO message by merging the
362  * addresses in two existing HELLOs (which
363  * must be for the same peer).
364  *
365  * @param h1 first HELLO message
366  * @param h2 the second HELLO message
367  * @return the combined hello message
368  */
369 struct GNUNET_HELLO_Message *
370 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
371                     const struct GNUNET_HELLO_Message *h2)
372 {
373   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
374
375   return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
376 }
377
378
379 struct DeltaContext
380 {
381   struct GNUNET_TIME_Absolute expiration_limit;
382
383   GNUNET_HELLO_AddressIterator it;
384
385   void *it_cls;
386
387   const struct GNUNET_HELLO_Message *old_hello;
388 };
389
390
391 static int
392 delta_match (void *cls, const char *tname,
393              struct GNUNET_TIME_Absolute expiration, const void *addr,
394              uint16_t addrlen)
395 {
396   struct DeltaContext *dc = cls;
397   int ret;
398   struct ExpireContext ec;
399
400   ec.addr = addr;
401   ec.addrlen = addrlen;
402   ec.found = GNUNET_NO;
403   ec.tname = tname;
404   GNUNET_HELLO_iterate_addresses (dc->old_hello, GNUNET_NO, &get_match_exp,
405                                   &ec);
406   if ((ec.found == GNUNET_YES) &&
407       ((ec.expiration.abs_value > expiration.abs_value) ||
408        (ec.expiration.abs_value >= dc->expiration_limit.abs_value)))
409     return GNUNET_YES;          /* skip */
410   ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen);
411   return ret;
412 }
413
414
415 /**
416  * Iterate over addresses in "new_hello" that
417  * are NOT already present in "old_hello".
418  *
419  * @param new_hello a HELLO message
420  * @param old_hello a HELLO message
421  * @param expiration_limit ignore addresses in old_hello
422  *        that expired before the given time stamp
423  * @param it iterator to call on each address
424  * @param it_cls closure for it
425  */
426 void
427 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
428                                     *new_hello,
429                                     const struct GNUNET_HELLO_Message
430                                     *old_hello,
431                                     struct GNUNET_TIME_Absolute
432                                     expiration_limit,
433                                     GNUNET_HELLO_AddressIterator it,
434                                     void *it_cls)
435 {
436   struct DeltaContext dc;
437
438   dc.expiration_limit = expiration_limit;
439   dc.it = it;
440   dc.it_cls = it_cls;
441   dc.old_hello = old_hello;
442   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
443 }
444
445
446 /**
447  * Return the size of the given HELLO message.
448  * @param hello to inspect
449  * @return the size, 0 if HELLO is invalid
450  */
451 uint16_t
452 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
453 {
454   uint16_t ret = ntohs (hello->header.size);
455
456   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
457       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
458     return 0;
459   return ret;
460 }
461
462
463 /**
464  * Get the public key from a HELLO message.
465  *
466  * @param hello the hello message
467  * @param publicKey where to copy the public key information, can be NULL
468  * @return GNUNET_SYSERR if the HELLO was malformed
469  */
470 int
471 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
472                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
473 {
474   uint16_t ret = ntohs (hello->header.size);
475
476   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
477       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
478     return GNUNET_SYSERR;
479   *publicKey = hello->publicKey;
480   return GNUNET_OK;
481 }
482
483
484 /**
485  * Get the peer identity from a HELLO message.
486  *
487  * @param hello the hello message
488  * @param peer where to store the peer's identity
489  * @return GNUNET_SYSERR if the HELLO was malformed
490  */
491 int
492 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
493                      struct GNUNET_PeerIdentity *peer)
494 {
495   uint16_t ret = ntohs (hello->header.size);
496
497   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
498       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
499     return GNUNET_SYSERR;
500   GNUNET_CRYPTO_hash (&hello->publicKey,
501                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
502                       &peer->hashPubKey);
503   return GNUNET_OK;
504 }
505
506
507 /**
508  * Get the header from a HELLO message, used so other code
509  * can correctly send HELLO messages.
510  *
511  * @param hello the hello message
512  *
513  * @return header or NULL if the HELLO was malformed
514  */
515 struct GNUNET_MessageHeader *
516 GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
517 {
518   uint16_t ret = ntohs (hello->header.size);
519
520   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
521       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
522     return NULL;
523
524   return &hello->header;
525 }
526
527
528 struct EqualsContext
529 {
530   struct GNUNET_TIME_Absolute expiration_limit;
531
532   struct GNUNET_TIME_Absolute result;
533
534   const struct GNUNET_HELLO_Message *h2;
535
536   const char *tname;
537
538   const void *addr;
539
540   struct GNUNET_TIME_Absolute expiration;
541
542   int found;
543
544   uint16_t addrlen;
545
546 };
547
548
549 static int
550 find_other_matching (void *cls, const char *tname,
551                      struct GNUNET_TIME_Absolute expiration, const void *addr,
552                      uint16_t addrlen)
553 {
554   struct EqualsContext *ec = cls;
555
556   if (expiration.abs_value < ec->expiration_limit.abs_value)
557     return GNUNET_YES;
558   if ((addrlen == ec->addrlen) && (0 == strcmp (tname, ec->tname)) &&
559       (0 == memcmp (addr, ec->addr, addrlen)))
560   {
561     ec->found = GNUNET_YES;
562     if (expiration.abs_value < ec->expiration.abs_value)
563       ec->result = GNUNET_TIME_absolute_min (expiration, ec->result);
564     return GNUNET_SYSERR;
565   }
566   return GNUNET_YES;
567 }
568
569
570 static int
571 find_matching (void *cls, const char *tname,
572                struct GNUNET_TIME_Absolute expiration, const void *addr,
573                uint16_t addrlen)
574 {
575   struct EqualsContext *ec = cls;
576
577   if (expiration.abs_value < ec->expiration_limit.abs_value)
578     return GNUNET_YES;
579   ec->tname = tname;
580   ec->expiration = expiration;
581   ec->addr = addr;
582   ec->addrlen = addrlen;
583   ec->found = GNUNET_NO;
584   GNUNET_HELLO_iterate_addresses (ec->h2, GNUNET_NO, &find_other_matching, ec);
585   if (ec->found == GNUNET_NO)
586   {
587     ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
588     return GNUNET_SYSERR;
589   }
590   return GNUNET_OK;
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 /* end of hello.c */