passes
[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 2, 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 (uint32_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                           size_t addr_len, char *target, size_t max)
82 {
83   uint32_t alen;
84   size_t slen;
85   struct GNUNET_TIME_AbsoluteNBO exp;
86
87   slen = strlen (tname) + 1;
88   if (slen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
89       addr_len > max)
90     return 0;
91   exp = GNUNET_TIME_absolute_hton (expiration);
92   alen = htonl ((uint32_t) addr_len);
93   memcpy (target, tname, slen);
94   memcpy (&target[slen], &alen, sizeof (uint32_t));
95   slen += sizeof (uint32_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, uint32_t * ralen)
114 {
115   const char *pos;
116   uint32_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 ('\0' != *pos)
130     {
131       /* 0-termination not found */
132       GNUNET_break_op (0);
133       abort ();
134       return 0;
135     }
136   pos++;
137   if (left < sizeof (uint32_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 (uint32_t));
144   alen = ntohl (alen);
145   *ralen = alen;
146   slen += alen + sizeof (uint32_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 - 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   while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
180     {
181       max -= ret;
182       used += ret;
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   uint16_t msize;
209   struct GNUNET_HELLO_Message *ret;
210   const char *inptr;
211   size_t insize;
212   size_t esize;
213   size_t wpos;
214   char *woff;
215   uint32_t alen;
216   struct GNUNET_TIME_AbsoluteNBO expire;
217   int iret;
218
219   msize = GNUNET_HELLO_size (msg);
220   if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
221       (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
222     return NULL;
223   ret = NULL;
224   if (return_modified)
225     {
226       ret = GNUNET_malloc (msize);
227       memcpy (ret, msg, msize);
228     }
229   inptr = (const char *) &msg[1];
230   insize = msize - sizeof (struct GNUNET_HELLO_Message);
231   wpos = 0;
232   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
233   while (insize > 0)
234     {
235       esize = get_hello_address_size (inptr, insize, &alen);
236       if (esize == 0)
237         {
238           GNUNET_break (0);
239           GNUNET_free_non_null (ret);
240           return NULL;
241         }
242       memcpy (&expire,
243               &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
244               sizeof (struct GNUNET_TIME_AbsoluteNBO));
245       iret = it (it_cls,
246                  inptr,
247                  GNUNET_TIME_absolute_ntoh (expire),
248                  &inptr[esize - alen], alen);
249       if (iret == GNUNET_SYSERR)
250         {
251           if (ret != NULL)
252             ret->header.size =
253               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   size_t addrlen;
275   int found;
276   struct GNUNET_TIME_Absolute expiration;
277 };
278
279
280 static int
281 get_match_exp (void *cls,
282                const char *tname,
283                struct GNUNET_TIME_Absolute expiration,
284                const void *addr, size_t addrlen)
285 {
286   struct ExpireContext *ec = cls;
287
288   if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen)))
289     {
290       ec->found = GNUNET_YES;
291       ec->expiration = expiration;
292       return GNUNET_SYSERR;     /* done here */
293     }
294   return GNUNET_OK;
295 }
296
297
298 struct MergeContext
299 {
300   const struct GNUNET_HELLO_Message *h1;
301   const struct GNUNET_HELLO_Message *h2;
302   const struct GNUNET_HELLO_Message *other;
303   char *buf;
304   size_t max;
305   size_t ret;
306   int take_equal;
307
308 };
309
310
311 static int
312 copy_latest (void *cls,
313              const char *tname,
314              struct GNUNET_TIME_Absolute expiration,
315              const void *addr, size_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   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
324   if ((ec.found == GNUNET_NO) ||
325       ((ec.expiration.value < expiration.value) ||
326        ((ec.expiration.value == expiration.value) &&
327         (mc->take_equal == GNUNET_YES))))
328     mc->ret += GNUNET_HELLO_add_address (tname,
329                                          expiration,
330                                          addr,
331                                          addrlen,
332                                          &mc->buf[mc->ret],
333                                          mc->max - mc->ret);
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 char *tname,
393              struct GNUNET_TIME_Absolute expiration,
394              const void *addr, size_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   GNUNET_HELLO_iterate_addresses (dc->old_hello,
404                                   GNUNET_NO, &get_match_exp, &ec);
405   if ((ec.found == GNUNET_YES) &&
406       ((ec.expiration.value > expiration.value) ||
407        (ec.expiration.value >= dc->expiration_limit.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   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
455       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
456     return 0;
457   return ret;
458 }
459
460
461 /**
462  * Get the public key from a HELLO message.
463  *
464  * @param hello the hello message
465  * @param publicKey where to copy the public key information, can be NULL
466  * @return GNUNET_SYSERR if the HELLO was malformed
467  */
468 int
469 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
470                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
471                       *publicKey)
472 {
473   uint16_t ret = ntohs (hello->header.size);
474   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
475       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
476     return GNUNET_SYSERR;
477   *publicKey = hello->publicKey;
478   return GNUNET_OK;
479 }
480
481
482 /**
483  * Get the peer identity from a HELLO message.
484  *
485  * @param hello the hello message
486  * @param peer where to store the peer's identity
487  * @return GNUNET_SYSERR if the HELLO was malformed
488  */
489 int
490 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
491                      struct GNUNET_PeerIdentity *peer)
492 {
493   uint16_t ret = ntohs (hello->header.size);
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 /* end of hello.c */