missing
[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),
251                  &inptr[esize - alen], alen);
252       if (iret == GNUNET_SYSERR)
253         {
254           if (ret != NULL)
255             ret->header.size =
256               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 void *addr;
277   const char *tname;
278   uint16_t addrlen;
279   int found;
280   struct GNUNET_TIME_Absolute expiration;
281 };
282
283
284 static int
285 get_match_exp (void *cls,
286                const char *tname,
287                struct GNUNET_TIME_Absolute expiration,
288                const void *addr, uint16_t addrlen)
289 {
290   struct ExpireContext *ec = cls;
291
292   if ( (addrlen == ec->addrlen) && 
293        (0 == memcmp (addr, ec->addr, addrlen)) &&
294        (0 == strcmp (tname, ec->tname)) )
295     {
296       ec->found = GNUNET_YES;
297       ec->expiration = expiration;
298       return GNUNET_SYSERR;     /* done here */
299     }
300   return GNUNET_OK;
301 }
302
303
304 struct MergeContext
305 {
306   const struct GNUNET_HELLO_Message *h1;
307   const struct GNUNET_HELLO_Message *h2;
308   const struct GNUNET_HELLO_Message *other;
309   char *buf;
310   size_t max;
311   size_t ret;
312   int take_equal;
313
314 };
315
316
317 static int
318 copy_latest (void *cls,
319              const char *tname,
320              struct GNUNET_TIME_Absolute expiration,
321              const void *addr, uint16_t addrlen)
322 {
323   struct MergeContext *mc = cls;
324   struct ExpireContext ec;
325
326   ec.addr = addr;
327   ec.addrlen = addrlen;
328   ec.found = GNUNET_NO;
329   ec.tname = tname;
330   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
331   if ( (ec.found == GNUNET_NO) ||
332        (ec.expiration.value < expiration.value) ||
333        ( (ec.expiration.value == expiration.value) &&
334          (mc->take_equal == GNUNET_YES) ) )
335     {
336       mc->ret += GNUNET_HELLO_add_address (tname,
337                                            expiration,
338                                            addr,
339                                            addrlen,
340                                            &mc->buf[mc->ret],
341                                            mc->max - mc->ret);
342     }
343   return GNUNET_OK;
344 }
345
346
347 static size_t
348 merge_addr (void *cls, size_t max, void *buf)
349 {
350   struct MergeContext *mc = cls;
351
352   if (mc->h1 == NULL)
353     return 0;
354   mc->ret = 0;
355   mc->max = max;
356   mc->buf = buf;
357   mc->take_equal = GNUNET_NO;
358   mc->other = mc->h2;
359   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
360   mc->take_equal = GNUNET_YES;
361   mc->other = mc->h1;
362   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
363   mc->h1 = NULL;
364   return mc->ret;
365 }
366
367
368 /**
369  * Construct a HELLO message by merging the
370  * addresses in two existing HELLOs (which
371  * must be for the same peer).
372  *
373  * @param h1 first HELLO message
374  * @param h2 the second HELLO message
375  * @return the combined hello message
376  */
377 struct GNUNET_HELLO_Message *
378 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
379                     const struct GNUNET_HELLO_Message *h2)
380 {
381   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
382
383   return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
384 }
385
386
387 struct DeltaContext
388 {
389   struct GNUNET_TIME_Absolute expiration_limit;
390
391   GNUNET_HELLO_AddressIterator it;
392
393   void *it_cls;
394
395   const struct GNUNET_HELLO_Message *old_hello;
396 };
397
398
399 static int
400 delta_match (void *cls,
401              const char *tname,
402              struct GNUNET_TIME_Absolute expiration,
403              const void *addr, uint16_t addrlen)
404 {
405   struct DeltaContext *dc = cls;
406   int ret;
407   struct ExpireContext ec;
408
409   ec.addr = addr;
410   ec.addrlen = addrlen;
411   ec.found = GNUNET_NO;
412   ec.tname = tname;
413   GNUNET_HELLO_iterate_addresses (dc->old_hello,
414                                   GNUNET_NO, &get_match_exp, &ec);
415   if ((ec.found == GNUNET_YES) &&
416       ((ec.expiration.value > expiration.value) ||
417        (ec.expiration.value >= dc->expiration_limit.value)))
418     return GNUNET_YES;          /* skip */
419   ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen);
420   return ret;
421 }
422
423
424 /**
425  * Iterate over addresses in "new_hello" that
426  * are NOT already present in "old_hello".
427  *
428  * @param new_hello a HELLO message
429  * @param old_hello a HELLO message
430  * @param expiration_limit ignore addresses in old_hello
431  *        that expired before the given time stamp
432  * @param it iterator to call on each address
433  * @param it_cls closure for it
434  */
435 void
436 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
437                                     *new_hello,
438                                     const struct GNUNET_HELLO_Message
439                                     *old_hello,
440                                     struct GNUNET_TIME_Absolute
441                                     expiration_limit,
442                                     GNUNET_HELLO_AddressIterator it,
443                                     void *it_cls)
444 {
445   struct DeltaContext dc;
446
447   dc.expiration_limit = expiration_limit;
448   dc.it = it;
449   dc.it_cls = it_cls;
450   dc.old_hello = old_hello;
451   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
452 }
453
454
455 /**
456  * Return the size of the given HELLO message.
457  * @param hello to inspect
458  * @return the size, 0 if HELLO is invalid
459  */
460 uint16_t
461 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
462 {
463   uint16_t ret = ntohs (hello->header.size);
464   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
465       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
466     return 0;
467   return ret;
468 }
469
470
471 /**
472  * Get the public key from a HELLO message.
473  *
474  * @param hello the hello message
475  * @param publicKey where to copy the public key information, can be NULL
476  * @return GNUNET_SYSERR if the HELLO was malformed
477  */
478 int
479 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
480                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
481                       *publicKey)
482 {
483   uint16_t ret = ntohs (hello->header.size);
484   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
485       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
486     return GNUNET_SYSERR;
487   *publicKey = hello->publicKey;
488   return GNUNET_OK;
489 }
490
491
492 /**
493  * Get the peer identity from a HELLO message.
494  *
495  * @param hello the hello message
496  * @param peer where to store the peer's identity
497  * @return GNUNET_SYSERR if the HELLO was malformed
498  */
499 int
500 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
501                      struct GNUNET_PeerIdentity *peer)
502 {
503   uint16_t ret = ntohs (hello->header.size);
504   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
505       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
506     return GNUNET_SYSERR;
507   GNUNET_CRYPTO_hash (&hello->publicKey,
508                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
509                       &peer->hashPubKey);
510   return GNUNET_OK;
511 }
512
513
514 /**
515  * Get the header from a HELLO message, used so other code
516  * can correctly send HELLO messages.
517  *
518  * @param hello the hello message
519  *
520  * @return header or NULL if the HELLO was malformed
521  */
522 struct GNUNET_MessageHeader *
523 GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
524 {
525   uint16_t ret = ntohs (hello->header.size);
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.value < ec->expiration_limit.value)
564     return GNUNET_YES;
565   if ( (addrlen == ec->addrlen) && 
566        (0 == strcmp (tname,
567                      ec->tname)) &&
568        (0 == memcmp (addr,
569                      ec->addr,
570                      addrlen)) )
571     {
572       ec->found = GNUNET_YES;
573       if (expiration.value < ec->expiration.value)      
574         ec->result = GNUNET_TIME_absolute_min (expiration,
575                                                ec->result);             
576       return GNUNET_SYSERR;
577     }
578   return GNUNET_YES;
579 }
580
581
582 static int
583 find_matching (void *cls,
584                const char *tname,
585                struct GNUNET_TIME_Absolute expiration,
586                const void *addr, uint16_t addrlen)
587 {
588   struct EqualsContext *ec = cls;
589
590   if (expiration.value < ec->expiration_limit.value)
591     return GNUNET_YES;
592   ec->tname = tname;
593   ec->expiration = expiration;
594   ec->addr = addr;
595   ec->addrlen = addrlen;
596   ec->found = GNUNET_NO;
597   GNUNET_HELLO_iterate_addresses (ec->h2,
598                                   GNUNET_NO,
599                                   &find_other_matching,
600                                   ec);
601   if (ec->found == GNUNET_NO)
602     {
603       ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
604       return GNUNET_SYSERR;
605     }
606   return GNUNET_OK;
607 }
608
609 /**
610  * Test if two HELLO messages contain the same addresses.
611  * If they only differ in expiration time, the lowest
612  * expiration time larger than 'now' where they differ
613  * is returned.
614  *
615  * @param h1 first HELLO message
616  * @param h2 the second HELLO message
617  * @param now time to use for deciding which addresses have
618  *            expired and should not be considered at all
619  * @return absolute time forever if the two HELLOs are 
620  *         totally identical; smallest timestamp >= now if
621  *         they only differ in timestamps; 
622  *         zero if the some addresses with expirations >= now
623  *         do not match at all
624  */
625 struct GNUNET_TIME_Absolute 
626 GNUNET_HELLO_equals (const struct
627                      GNUNET_HELLO_Message *h1,
628                      const struct
629                      GNUNET_HELLO_Message *h2,
630                      struct GNUNET_TIME_Absolute now)
631 {
632   struct EqualsContext ec;
633
634   if (0 != memcmp (&h1->publicKey,
635                    &h2->publicKey,
636                    sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
637     return GNUNET_TIME_UNIT_ZERO_ABS;
638   ec.expiration_limit = now;
639   ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
640   ec.h2 = h2;
641   GNUNET_HELLO_iterate_addresses (h1,
642                                    GNUNET_NO,
643                                    &find_matching,
644                                    &ec);
645   if (ec.result.value ==
646       GNUNET_TIME_UNIT_ZERO.value)
647     return ec.result; 
648   ec.h2 = h1;
649   GNUNET_HELLO_iterate_addresses (h2,
650                                    GNUNET_NO,
651                                    &find_matching,
652                                    &ec);
653   return ec.result;
654 }
655
656
657 /* end of hello.c */