5a21bd41f584483a7583bb38f130291e4dfee83a
[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       return 0;
134     }
135   pos++;
136   if (left < sizeof (uint32_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 (uint32_t));
143   alen = ntohl (alen);
144   *ralen = alen;
145   slen += alen + sizeof (uint32_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 - 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   while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
179     {
180       max -= ret;
181       used += ret;
182     }
183   hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used);
184   hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
185   hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
186   memcpy (&hello->publicKey, publicKey,
187           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
188   memcpy (&hello[1], buffer, used);
189   return hello;
190 }
191
192
193 /**
194  * Iterate over all of the addresses in the HELLO.
195  *
196  * @param msg HELLO to iterate over
197  * @param return_modified if a modified copy should be returned,
198  *         otherwise NULL will be returned
199  * @param it iterator to call on each address
200  * @param it_cls closure for it
201  */
202 struct GNUNET_HELLO_Message *
203 GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
204                                 int return_modified,
205                                 GNUNET_HELLO_AddressIterator it, void *it_cls)
206 {
207   uint16_t msize;
208   struct GNUNET_HELLO_Message *ret;
209   const char *inptr;
210   size_t insize;
211   size_t esize;
212   size_t wpos;
213   char *woff;
214   uint32_t alen;
215   struct GNUNET_TIME_AbsoluteNBO expire;
216   int iret;
217
218   msize = GNUNET_HELLO_size (msg);
219   if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
220       (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
221     return NULL;
222   ret = NULL;
223   if (return_modified)
224     {
225       ret = GNUNET_malloc (msize);
226       memcpy (ret, msg, msize);
227     }
228   inptr = (const char *) &msg[1];
229   insize = msize - sizeof (struct GNUNET_HELLO_Message);
230   wpos = 0;
231   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
232   while (insize > 0)
233     {
234       esize = get_hello_address_size (inptr, insize, &alen);
235       if (esize == 0)
236         {
237           GNUNET_break (0);
238           GNUNET_free_non_null (ret);
239           return NULL;
240         }
241       memcpy (&expire,
242               &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
243               sizeof (struct GNUNET_TIME_AbsoluteNBO));
244       iret = it (it_cls,
245                  inptr,
246                  GNUNET_TIME_absolute_ntoh (expire),
247                  &inptr[esize - alen], alen);
248       if (iret == GNUNET_SYSERR)
249         {
250           if (ret != NULL)
251             ret->header.size =
252               ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
253           return ret;
254         }
255       if ((iret == GNUNET_OK) && (ret != NULL))
256         {
257           memcpy (woff, inptr, esize);
258           woff += esize;
259           wpos += esize;
260         }
261       insize -= esize;
262       inptr += esize;
263     }
264   if (ret != NULL)
265     ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
266   return ret;
267 }
268
269
270 struct ExpireContext
271 {
272   const void *addr;
273   size_t addrlen;
274   int found;
275   struct GNUNET_TIME_Absolute expiration;
276 };
277
278
279 static int
280 get_match_exp (void *cls,
281                const char *tname,
282                struct GNUNET_TIME_Absolute expiration,
283                const void *addr, size_t addrlen)
284 {
285   struct ExpireContext *ec = cls;
286
287   if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen)))
288     {
289       ec->found = GNUNET_YES;
290       ec->expiration = expiration;
291       return GNUNET_SYSERR;     /* done here */
292     }
293   return GNUNET_OK;
294 }
295
296
297 struct MergeContext
298 {
299   const struct GNUNET_HELLO_Message *h1;
300   const struct GNUNET_HELLO_Message *h2;
301   const struct GNUNET_HELLO_Message *other;
302   char *buf;
303   size_t max;
304   size_t ret;
305   int take_equal;
306
307 };
308
309
310 static int
311 copy_latest (void *cls,
312              const char *tname,
313              struct GNUNET_TIME_Absolute expiration,
314              const void *addr, size_t addrlen)
315 {
316   struct MergeContext *mc = cls;
317   struct ExpireContext ec;
318
319   ec.addr = addr;
320   ec.addrlen = addrlen;
321   ec.found = GNUNET_NO;
322   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
323   if ((ec.found == GNUNET_NO) ||
324       ((ec.expiration.value < expiration.value) ||
325        ((ec.expiration.value == expiration.value) &&
326         (mc->take_equal == GNUNET_YES))))
327     mc->ret += GNUNET_HELLO_add_address (tname,
328                                          expiration,
329                                          addr,
330                                          addrlen,
331                                          &mc->buf[mc->ret],
332                                          mc->max - mc->ret);
333   return GNUNET_OK;
334 }
335
336
337 static size_t
338 merge_addr (void *cls, size_t max, void *buf)
339 {
340   struct MergeContext *mc = cls;
341
342   if (mc->h1 == NULL)
343     return 0;
344   mc->ret = 0;
345   mc->max = max;
346   mc->buf = buf;
347   mc->take_equal = GNUNET_NO;
348   mc->other = mc->h2;
349   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
350   mc->take_equal = GNUNET_YES;
351   mc->other = mc->h1;
352   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
353   mc->h1 = NULL;
354   return mc->ret;
355 }
356
357
358 /**
359  * Construct a HELLO message by merging the
360  * addresses in two existing HELLOs (which
361  * must be for the same peer).
362  *
363  * @param h1 first HELLO message
364  * @param h2 the second HELLO message
365  * @return the combined hello message
366  */
367 struct GNUNET_HELLO_Message *
368 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
369                     const struct GNUNET_HELLO_Message *h2)
370 {
371   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
372
373   return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
374 }
375
376
377 struct DeltaContext
378 {
379   struct GNUNET_TIME_Absolute expiration_limit;
380
381   GNUNET_HELLO_AddressIterator it;
382
383   void *it_cls;
384
385   const struct GNUNET_HELLO_Message *old_hello;
386 };
387
388
389 static int
390 delta_match (void *cls,
391              const char *tname,
392              struct GNUNET_TIME_Absolute expiration,
393              const void *addr, size_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   GNUNET_HELLO_iterate_addresses (dc->old_hello,
403                                   GNUNET_NO, &get_match_exp, &ec);
404   if ((ec.found == GNUNET_YES) &&
405       ((ec.expiration.value > expiration.value) ||
406        (ec.expiration.value >= dc->expiration_limit.value)))
407     return GNUNET_YES;          /* skip */
408   ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen);
409   return ret;
410 }
411
412
413 /**
414  * Iterate over addresses in "new_hello" that
415  * are NOT already present in "old_hello".
416  *
417  * @param new_hello a HELLO message
418  * @param old_hello a HELLO message
419  * @param expiration_limit ignore addresses in old_hello
420  *        that expired before the given time stamp
421  * @param it iterator to call on each address
422  * @param it_cls closure for it
423  */
424 void
425 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
426                                     *new_hello,
427                                     const struct GNUNET_HELLO_Message
428                                     *old_hello,
429                                     struct GNUNET_TIME_Absolute
430                                     expiration_limit,
431                                     GNUNET_HELLO_AddressIterator it,
432                                     void *it_cls)
433 {
434   struct DeltaContext dc;
435
436   dc.expiration_limit = expiration_limit;
437   dc.it = it;
438   dc.it_cls = it_cls;
439   dc.old_hello = old_hello;
440   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
441 }
442
443
444 /**
445  * Return the size of the given HELLO message.
446  * @param hello to inspect
447  * @return the size, 0 if HELLO is invalid
448  */
449 uint16_t
450 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
451 {
452   uint16_t ret = ntohs (hello->header.size);
453   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
454       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
455     return 0;
456   return ret;
457 }
458
459
460 /**
461  * Get the public key from a HELLO message.
462  *
463  * @param hello the hello message
464  * @param publicKey where to copy the public key information, can be NULL
465  * @return GNUNET_SYSERR if the HELLO was malformed
466  */
467 int
468 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
469                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
470                       *publicKey)
471 {
472   uint16_t ret = ntohs (hello->header.size);
473   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
474       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
475     return GNUNET_SYSERR;
476   *publicKey = hello->publicKey;
477   return GNUNET_OK;
478 }
479
480
481
482 /* end of hello.c */