382edf3d9c457ef9148e438099f76fd930cf827f
[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_util_lib.h"
30 #include "gnunet_transport_plugin.h"
31
32 GNUNET_NETWORK_STRUCT_BEGIN
33
34 /**
35  * A HELLO message is used to exchange information about
36  * transports with other peers.  This struct is always
37  * followed by the actual network addresses which have
38  * the format:
39  *
40  * 1) transport-name (0-terminated)
41  * 2) address-length (uint16_t, network byte order; possibly
42  *    unaligned!)
43  * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
44  *    unaligned!)
45  * 4) address (address-length bytes; possibly unaligned!)
46  */
47 struct GNUNET_HELLO_Message
48 {
49   /**
50    * Type will be GNUNET_MESSAGE_TYPE_HELLO.
51    */
52   struct GNUNET_MessageHeader header;
53
54   /**
55    * Always zero (for alignment).
56    */
57   uint32_t reserved GNUNET_PACKED;
58
59   /**
60    * The public key of the peer.
61    */
62   struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded publicKey;
63
64 };
65 GNUNET_NETWORK_STRUCT_END
66
67
68 /**
69  * Context used for building our own URI.
70  */
71 struct GNUNET_HELLO_ComposeUriContext
72 {
73   /**
74    * Final URI.
75    */
76   char *uri;
77
78   /**
79    * Function for finding transport plugins by name.
80    */
81   GNUNET_HELLO_TransportPluginsFind plugins_find;
82 };
83
84
85 /**
86  * Context for 'add_address_to_hello'.
87  */
88 struct GNUNET_HELLO_ParseUriContext
89 {
90   /**
91    * Position in the URI with the next address to parse.
92    */
93   const char *pos;
94
95   /**
96    * Set to GNUNET_SYSERR to indicate parse errors.
97    */
98   int ret;
99
100   /**
101    * Function for finding transport plugins by name.
102    */
103   GNUNET_HELLO_TransportPluginsFind plugins_find;
104 };
105
106
107 /**
108  * Return HELLO type
109  *
110  * @param h HELLO Message to test
111  * @return GNUNET_MESSAGE_TYPE_HELLO or GNUNET_MESSAGE_TYPE_FRIEND_HELLO or 0 on error
112  */
113 uint16_t
114 GNUNET_HELLO_get_type (const struct GNUNET_HELLO_Message *h)
115 {
116   if (GNUNET_MESSAGE_TYPE_HELLO == ntohs(h->header.type))
117         return GNUNET_MESSAGE_TYPE_HELLO;
118   if (GNUNET_MESSAGE_TYPE_FRIEND_HELLO == ntohs(h->header.type))
119         return GNUNET_MESSAGE_TYPE_FRIEND_HELLO;
120   return 0;
121 }
122
123
124 /**
125  * Copy the given address information into
126  * the given buffer using the format of HELLOs.
127  *
128  * @param address the address
129  * @param expiration expiration for the address
130  * @param target where to copy the address
131  * @param max maximum number of bytes to copy to target
132  * @return number of bytes copied, 0 if
133  *         the target buffer was not big enough.
134  */
135 size_t
136 GNUNET_HELLO_add_address (const struct GNUNET_HELLO_Address *address,
137                           struct GNUNET_TIME_Absolute expiration, char *target,
138                           size_t max)
139 {
140   uint16_t alen;
141   size_t slen;
142   struct GNUNET_TIME_AbsoluteNBO exp;
143
144   slen = strlen (address->transport_name) + 1;
145   if (slen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
146       address->address_length > max)
147     return 0;
148   exp = GNUNET_TIME_absolute_hton (expiration);
149   alen = htons ((uint16_t) address->address_length);
150   memcpy (target, address->transport_name, slen);
151   memcpy (&target[slen], &alen, sizeof (uint16_t));
152   slen += sizeof (uint16_t);
153   memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO));
154   slen += sizeof (struct GNUNET_TIME_AbsoluteNBO);
155   memcpy (&target[slen], address->address, address->address_length);
156   slen += address->address_length;
157   return slen;
158 }
159
160
161 /**
162  * Get the size of an address entry in a HELLO message.
163  *
164  * @param buf pointer to the start of the address entry
165  * @param max maximum size of the entry (end of buf)
166  * @param ralen set to the address length
167  * @return size of the entry, or 0 if max is not large enough
168  */
169 static size_t
170 get_hello_address_size (const char *buf, size_t max, uint16_t * ralen)
171 {
172   const char *pos;
173   uint16_t alen;
174   size_t left;
175   size_t slen;
176
177   left = max;
178   pos = buf;
179   slen = 1;
180   while ((left > 0) && ('\0' != *pos))
181   {
182     left--;
183     pos++;
184     slen++;
185   }
186   if (left == 0)
187   {
188     /* 0-termination not found */
189     GNUNET_break_op (0);
190     return 0;
191   }
192   pos++;
193   if (left < sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO))
194   {
195     /* not enough space for addrlen */
196     GNUNET_break_op (0);
197     return 0;
198   }
199   memcpy (&alen, pos, sizeof (uint16_t));
200   alen = ntohs (alen);
201   *ralen = alen;
202   slen += alen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO);
203   if (max < slen)
204   {
205     /* not enough space for addr */
206     GNUNET_break_op (0);
207     return 0;
208   }
209   return slen;
210 }
211
212
213 /**
214  * Construct a HELLO message given the public key,
215  * expiration time and an iterator that spews the
216  * transport addresses.
217  *
218  * @return the hello message
219  */
220 struct GNUNET_HELLO_Message *
221 GNUNET_HELLO_create (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded
222                      *publicKey,
223                      GNUNET_HELLO_GenerateAddressListCallback addrgen,
224                      void *addrgen_cls,
225                      int friend_only)
226 {
227   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - 256 -
228               sizeof (struct GNUNET_HELLO_Message)];
229   size_t max;
230   size_t used;
231   size_t ret;
232   struct GNUNET_HELLO_Message *hello;
233
234   max = sizeof (buffer);
235   used = 0;
236   if (addrgen != NULL)
237   {
238     while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
239     {
240       max -= ret;
241       used += ret;
242     }
243   }
244   hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used);
245   if (GNUNET_NO == friend_only)
246         hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
247   else
248         hello->header.type = htons (GNUNET_MESSAGE_TYPE_FRIEND_HELLO);
249   hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
250   memcpy (&hello->publicKey, publicKey,
251           sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded));
252   memcpy (&hello[1], buffer, used);
253   return hello;
254 }
255
256
257 /**
258  * Iterate over all of the addresses in the HELLO.
259  *
260  * @param msg HELLO to iterate over
261  * @param return_modified if a modified copy should be returned,
262  *         otherwise NULL will be returned
263  * @param it iterator to call on each address
264  * @param it_cls closure for it
265  */
266 struct GNUNET_HELLO_Message *
267 GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
268                                 int return_modified,
269                                 GNUNET_HELLO_AddressIterator it, void *it_cls)
270 {
271   struct GNUNET_HELLO_Address address;
272   uint16_t msize;
273   struct GNUNET_HELLO_Message *ret;
274   const char *inptr;
275   size_t insize;
276   size_t esize;
277   size_t wpos;
278   char *woff;
279   uint16_t alen;
280   struct GNUNET_TIME_AbsoluteNBO expire;
281   int iret;
282
283   msize = GNUNET_HELLO_size (msg);
284   if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
285       ((ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO) &&
286                          (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_FRIEND_HELLO)))
287     return NULL;
288   ret = NULL;
289   if (return_modified)
290   {
291     ret = GNUNET_malloc (msize);
292     memcpy (ret, msg, msize);
293   }
294   inptr = (const char *) &msg[1];
295   insize = msize - sizeof (struct GNUNET_HELLO_Message);
296   wpos = 0;
297   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
298   GNUNET_CRYPTO_hash (&msg->publicKey,
299                       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
300                       &address.peer.hashPubKey);
301   while (insize > 0)
302   {
303     esize = get_hello_address_size (inptr, insize, &alen);
304     if (esize == 0)
305     {
306       GNUNET_break (0);
307       GNUNET_free_non_null (ret);
308       return NULL;
309     }
310     memcpy (&expire,
311             &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
312             sizeof (struct GNUNET_TIME_AbsoluteNBO));
313     address.address = &inptr[esize - alen];
314     address.address_length = alen;
315     address.transport_name = inptr;
316     iret = it (it_cls, &address, GNUNET_TIME_absolute_ntoh (expire));
317     if (iret == GNUNET_SYSERR)
318     {
319       if (ret != NULL)
320         ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
321       return ret;
322     }
323     if ((iret == GNUNET_OK) && (ret != NULL))
324     {
325       memcpy (woff, inptr, esize);
326       woff += esize;
327       wpos += esize;
328     }
329     insize -= esize;
330     inptr += esize;
331   }
332   if (ret != NULL)
333     ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
334   return ret;
335 }
336
337
338 struct ExpireContext
339 {
340   const struct GNUNET_HELLO_Address *address;
341   int found;
342   struct GNUNET_TIME_Absolute expiration;
343 };
344
345
346 static int
347 get_match_exp (void *cls, const struct GNUNET_HELLO_Address *address,
348                struct GNUNET_TIME_Absolute expiration)
349 {
350   struct ExpireContext *ec = cls;
351
352   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
353   {
354     ec->found = GNUNET_YES;
355     ec->expiration = expiration;
356     return GNUNET_SYSERR;       /* done here */
357   }
358   return GNUNET_OK;
359 }
360
361
362 struct MergeContext
363 {
364   const struct GNUNET_HELLO_Message *h1;
365   const struct GNUNET_HELLO_Message *h2;
366   const struct GNUNET_HELLO_Message *other;
367   char *buf;
368   size_t max;
369   size_t ret;
370   int take_equal;
371
372 };
373
374
375 static int
376 copy_latest (void *cls, const struct GNUNET_HELLO_Address *address,
377              struct GNUNET_TIME_Absolute expiration)
378 {
379   struct MergeContext *mc = cls;
380   struct ExpireContext ec;
381
382   ec.address = address;
383   ec.found = GNUNET_NO;
384   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
385   if ((ec.found == GNUNET_NO) ||
386       (ec.expiration.abs_value < expiration.abs_value) ||
387       ((ec.expiration.abs_value == expiration.abs_value) &&
388        (mc->take_equal == GNUNET_YES)))
389   {
390     mc->ret +=
391         GNUNET_HELLO_add_address (address, expiration, &mc->buf[mc->ret],
392                                   mc->max - mc->ret);
393   }
394   return GNUNET_OK;
395 }
396
397
398 static size_t
399 merge_addr (void *cls, size_t max, void *buf)
400 {
401   struct MergeContext *mc = cls;
402
403   if (mc->h1 == NULL)
404     return 0;
405   mc->ret = 0;
406   mc->max = max;
407   mc->buf = buf;
408   mc->take_equal = GNUNET_NO;
409   mc->other = mc->h2;
410   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
411   mc->take_equal = GNUNET_YES;
412   mc->other = mc->h1;
413   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
414   mc->h1 = NULL;
415   return mc->ret;
416 }
417
418
419 /**
420  * Construct a HELLO message by merging the
421  * addresses in two existing HELLOs (which
422  * must be for the same peer).
423  *
424  * @param h1 first HELLO message
425  * @param h2 the second HELLO message
426  * @return the combined hello message
427  */
428 struct GNUNET_HELLO_Message *
429 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
430                     const struct GNUNET_HELLO_Message *h2)
431 {
432   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
433   int friend_only;
434   if (h1->header.type != h2->header.type)
435   {
436                 /* Trying to merge different HELLO types */
437                 GNUNET_break (0);
438                 return NULL;
439   }
440   if (GNUNET_MESSAGE_TYPE_HELLO == (ntohs(h1->header.type)))
441         friend_only = GNUNET_NO;
442   else if (GNUNET_MESSAGE_TYPE_FRIEND_HELLO == (ntohs(h1->header.type)))
443                 friend_only = GNUNET_YES;
444   else
445   {
446                 GNUNET_break (0);
447                 return NULL;
448   }
449
450         return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc, friend_only);
451 }
452
453
454 struct DeltaContext
455 {
456   struct GNUNET_TIME_Absolute expiration_limit;
457
458   GNUNET_HELLO_AddressIterator it;
459
460   void *it_cls;
461
462   const struct GNUNET_HELLO_Message *old_hello;
463 };
464
465
466 static int
467 delta_match (void *cls, const struct GNUNET_HELLO_Address *address,
468              struct GNUNET_TIME_Absolute expiration)
469 {
470   struct DeltaContext *dc = cls;
471   int ret;
472   struct ExpireContext ec;
473
474   ec.address = address;
475   ec.found = GNUNET_NO;
476   GNUNET_HELLO_iterate_addresses (dc->old_hello, GNUNET_NO, &get_match_exp,
477                                   &ec);
478   if ((ec.found == GNUNET_YES) &&
479       ((ec.expiration.abs_value > expiration.abs_value) ||
480        (ec.expiration.abs_value >= dc->expiration_limit.abs_value)))
481     return GNUNET_YES;          /* skip */
482   ret = dc->it (dc->it_cls, address, expiration);
483   return ret;
484 }
485
486
487 /**
488  * Iterate over addresses in "new_hello" that
489  * are NOT already present in "old_hello".
490  *
491  * @param new_hello a HELLO message
492  * @param old_hello a HELLO message
493  * @param expiration_limit ignore addresses in old_hello
494  *        that expired before the given time stamp
495  * @param it iterator to call on each address
496  * @param it_cls closure for it
497  */
498 void
499 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
500                                     *new_hello,
501                                     const struct GNUNET_HELLO_Message
502                                     *old_hello,
503                                     struct GNUNET_TIME_Absolute
504                                     expiration_limit,
505                                     GNUNET_HELLO_AddressIterator it,
506                                     void *it_cls)
507 {
508   struct DeltaContext dc;
509
510   dc.expiration_limit = expiration_limit;
511   dc.it = it;
512   dc.it_cls = it_cls;
513   dc.old_hello = old_hello;
514   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
515 }
516
517
518 /**
519  * Return the size of the given HELLO message.
520  * @param hello to inspect
521  * @return the size, 0 if HELLO is invalid
522  */
523 uint16_t
524 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
525 {
526   uint16_t ret = ntohs (hello->header.size);
527
528   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
529       ((ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO) &&
530                          (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_FRIEND_HELLO)))
531     return 0;
532   return ret;
533 }
534
535
536 /**
537  * Get the public key from a HELLO message.
538  *
539  * @param hello the hello message
540  * @param publicKey where to copy the public key information, can be NULL
541  * @return GNUNET_SYSERR if the HELLO was malformed
542  */
543 int
544 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
545                       struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicKey)
546 {
547   uint16_t ret = ntohs (hello->header.size);
548
549   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
550       ((ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO) &&
551                          (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_FRIEND_HELLO)))
552     return GNUNET_SYSERR;
553   *publicKey = hello->publicKey;
554   return GNUNET_OK;
555 }
556
557
558 /**
559  * Get the peer identity from a HELLO message.
560  *
561  * @param hello the hello message
562  * @param peer where to store the peer's identity
563  * @return GNUNET_SYSERR if the HELLO was malformed
564  */
565 int
566 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
567                      struct GNUNET_PeerIdentity *peer)
568 {
569   uint16_t ret = ntohs (hello->header.size);
570
571   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
572       ((ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO) &&
573                          (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_FRIEND_HELLO)))
574     return GNUNET_SYSERR;
575   GNUNET_CRYPTO_hash (&hello->publicKey,
576                       sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded),
577                       &peer->hashPubKey);
578   return GNUNET_OK;
579 }
580
581
582 /**
583  * Get the header from a HELLO message, used so other code
584  * can correctly send HELLO messages.
585  *
586  * @param hello the hello message
587  *
588  * @return header or NULL if the HELLO was malformed
589  */
590 struct GNUNET_MessageHeader *
591 GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
592 {
593   uint16_t ret = ntohs (hello->header.size);
594
595   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
596       ((ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO) &&
597                          (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_FRIEND_HELLO)))
598     return NULL;
599
600   return &hello->header;
601 }
602
603
604 struct EqualsContext
605 {
606   struct GNUNET_TIME_Absolute expiration_limit;
607
608   struct GNUNET_TIME_Absolute result;
609
610   const struct GNUNET_HELLO_Message *h2;
611
612   const struct GNUNET_HELLO_Address *address;
613
614   struct GNUNET_TIME_Absolute expiration;
615
616   int found;
617
618 };
619
620
621 static int
622 find_other_matching (void *cls, const struct GNUNET_HELLO_Address *address,
623                      struct GNUNET_TIME_Absolute expiration)
624 {
625   struct EqualsContext *ec = cls;
626
627   if (expiration.abs_value < ec->expiration_limit.abs_value)
628     return GNUNET_YES;
629   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
630   {
631     ec->found = GNUNET_YES;
632     if (expiration.abs_value < ec->expiration.abs_value)
633       ec->result = GNUNET_TIME_absolute_min (expiration, ec->result);
634     return GNUNET_SYSERR;
635   }
636   return GNUNET_YES;
637 }
638
639
640 static int
641 find_matching (void *cls, const struct GNUNET_HELLO_Address *address,
642                struct GNUNET_TIME_Absolute expiration)
643 {
644   struct EqualsContext *ec = cls;
645
646   if (expiration.abs_value < ec->expiration_limit.abs_value)
647     return GNUNET_YES;
648   ec->address = address;
649   ec->expiration = expiration;
650   ec->found = GNUNET_NO;
651   GNUNET_HELLO_iterate_addresses (ec->h2, GNUNET_NO, &find_other_matching, ec);
652   if (ec->found == GNUNET_NO)
653   {
654     ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
655     return GNUNET_SYSERR;
656   }
657   return GNUNET_OK;
658 }
659
660
661 /**
662  * Test if two HELLO messages contain the same addresses.
663  * If they only differ in expiration time, the lowest
664  * expiration time larger than 'now' where they differ
665  * is returned.
666  *
667  * @param h1 first HELLO message
668  * @param h2 the second HELLO message
669  * @param now time to use for deciding which addresses have
670  *            expired and should not be considered at all
671  * @return absolute time forever if the two HELLOs are
672  *         totally identical; smallest timestamp >= now if
673  *         they only differ in timestamps;
674  *         zero if the some addresses with expirations >= now
675  *         do not match at all
676  */
677 struct GNUNET_TIME_Absolute
678 GNUNET_HELLO_equals (const struct GNUNET_HELLO_Message *h1,
679                      const struct GNUNET_HELLO_Message *h2,
680                      struct GNUNET_TIME_Absolute now)
681 {
682   struct EqualsContext ec;
683
684   if (h1->header.type != h2->header.type)
685         return GNUNET_TIME_UNIT_ZERO_ABS;
686
687   if (0 !=
688       memcmp (&h1->publicKey, &h2->publicKey,
689               sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)))
690     return GNUNET_TIME_UNIT_ZERO_ABS;
691   ec.expiration_limit = now;
692   ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
693   ec.h2 = h2;
694   GNUNET_HELLO_iterate_addresses (h1, GNUNET_NO, &find_matching, &ec);
695   if (ec.result.abs_value == GNUNET_TIME_UNIT_ZERO.rel_value)
696     return ec.result;
697   ec.h2 = h1;
698   GNUNET_HELLO_iterate_addresses (h2, GNUNET_NO, &find_matching, &ec);
699   return ec.result;
700 }
701
702
703 static int
704 find_min_expire (void *cls, const struct GNUNET_HELLO_Address *address,
705                  struct GNUNET_TIME_Absolute expiration)
706 {
707   struct GNUNET_TIME_Absolute *min = cls;
708
709   *min = GNUNET_TIME_absolute_min (*min, expiration);
710   return GNUNET_OK;
711 }
712
713
714 /**
715  * When does the last address in the given HELLO expire?
716  *
717  * @param msg HELLO to inspect
718  * @return time the last address expires, 0 if there are no addresses in the HELLO
719  */
720 struct GNUNET_TIME_Absolute
721 GNUNET_HELLO_get_last_expiration (const struct GNUNET_HELLO_Message *msg)
722 {
723   struct GNUNET_TIME_Absolute ret;
724
725   ret.abs_value = 0;
726   GNUNET_HELLO_iterate_addresses (msg, GNUNET_NO, &find_min_expire, &ret);
727   return ret;
728 }
729
730 /**
731  * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
732  * The specific structure of "IDENTIFIER" depends on the module and
733  * maybe differenciated into additional subcategories if applicable.
734  * This module only deals with hello identifiers (MODULE = "hello").
735  * <p>
736  * 
737  * The concrete URI format is:
738  * 
739  * "gnunet://hello/PEER[!YYYYMMDDHHMMSS!<TYPE>!<ADDRESS>]...".
740  * These URIs can be used to add a peer record to peerinfo service.
741  * PEER is the string representation of peer's public key.
742  * YYYYMMDDHHMMSS is the expiration date.
743  * TYPE is a transport type.
744  * ADDRESS is the address, its format depends upon the transport type.
745  * The concrete transport types and corresponding address formats are:
746  * 
747  * <ul><li>
748  * 
749  * <TCP|UDP>!IPADDRESS
750  * IPVDDRESS is either IPV4 .-delimited address in form of XXX.XXX.XXX.XXX:PPPPP
751  * or IPV6 :-delimited address, but with '(' and ')' instead of '[' and ']' (RFC2396 advises against using square brackets in URIs):
752  * (XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX):PPPPP
753  * PPPPP is the port number. May be 0.
754  * 
755  * </li><li>
756  * 
757  * [add SMTP, HTTP and other addresses here]
758  * 
759  * </li></ul>
760  * 
761  * The encoding for hexadecimal values is defined in the crypto_hash.c
762  * module in the gnunetutil library and discussed there.
763  * 
764  * Examples:
765  * 
766  * gnunet://hello/0430205UC7D56PTQK8NV05776671CNN44FK4TL6D0GQ35OMF8MEN4RNMKA5UF6AL3DQO8B1SC5AQF50SQ2MABIRU4HC8H2HAJKJ59JL1JVRJAK308F9GASRFLMGUBB5TQ5AKR94AS5T3MDG8B9O1EMPRKB0HVCG7T6QPP4CDJ913LAEHVJ2DI1TOBB15Q1JIT5ARBOD12U4SIGRFDV3Q7T66G4TBVSJJ90UQF1BG29TGJJKLGEIMSPHHKO544D6EALQ4F2K0416311JC22GVAD48R616I7VK03K7MP7N0RS2MBV1TE9JV8CK1LSQMR7KCDRTLDA6917UGA67DHTGHERIACCGQ54TGSR48RMSGS9BA5HLMOKASFC1I6V4TT09TUGCU8GNDHQF0JF3H7LPV59UL5I38QID040G000!20120302010059!TCP!192.168.0.1:2086!TCP!64.23.8.174:0
767  * gnunet://hello/0430205UC7D56PTQK8NV05776671CNN44FK4TL6D0GQ35OMF8MEN4RNMKA5UF6AL3DQO8B1SC5AQF50SQ2MABIRU4HC8H2HAJKJ59JL1JVRJAK308F9GASRFLMGUBB5TQ5AKR94AS5T3MDG8B9O1EMPRKB0HVCG7T6QPP4CDJ913LAEHVJ2DI1TOBB15Q1JIT5ARBOD12U4SIGRFDV3Q7T66G4TBVSJJ90UQF1BG29TGJJKLGEIMSPHHKO544D6EALQ4F2K0416311JC22GVAD48R616I7VK03K7MP7N0RS2MBV1TE9JV8CK1LSQMR7KCDRTLDA6917UGA67DHTGHERIACCGQ54TGSR48RMSGS9BA5HLMOKASFC1I6V4TT09TUGCU8GNDHQF0JF3H7LPV59UL5I38QID040G000!20120302010059!TCP!(2001:db8:85a3:8d3:1319:8a2e:370:7348):2086
768  * 
769  * <p>
770  */
771
772
773 /* ************************* Compose HELLO URI ************************** */
774
775
776 /**
777  * Replace all characters in the input 'in' according
778  * to the mapping.  The mapping says to map each character
779  * in 'oldchars' to the corresponding character (by offset)
780  * in 'newchars'.
781  *
782  * @param in input string to remap
783  * @param oldchars characters to replace
784  * @param newchars replacement characters, must have same length as 'oldchars'
785  * @return copy of string with replacement applied.
786  */
787 static char *
788 map_characters (const char *in,
789                 const char *oldchars,
790                 const char *newchars)
791 {
792   char *ret;
793   const char *off;
794   size_t i;
795
796   GNUNET_assert (strlen (oldchars) == strlen (newchars));
797   ret = GNUNET_strdup (in);
798   i = 0;
799   while (ret[i] != '\0')
800   {
801     off = strchr (oldchars, ret[i]);
802     if (NULL != off)
803       ret[i] = newchars[off - oldchars];
804     i++;
805   }
806   return ret;
807 }
808
809
810 /**
811  * Function that is called on each address of this peer.
812  * Expands the corresponding URI string.
813  *
814  * @param cls the 'GNUNET_HELLO_GetUriContext'
815  * @param address address to add
816  * @param expiration expiration time for the address
817  * @return GNUNET_OK (continue iteration).
818  */
819 static int
820 add_address_to_uri (void *cls, const struct GNUNET_HELLO_Address *address,
821                     struct GNUNET_TIME_Absolute expiration)
822 {
823   struct GNUNET_HELLO_ComposeUriContext *ctx = cls;
824   struct GNUNET_TRANSPORT_PluginFunctions *papi;
825   const char *addr;
826   char *uri_addr;
827   char *ret;
828   char tbuf[16] = "";
829   struct tm *t;
830   time_t seconds;
831
832   papi = ctx->plugins_find (address->transport_name);
833   if (papi == NULL)
834   {
835     /* Not an error - we might just not have the right plugin. */
836     return GNUNET_OK;
837   }
838   if (NULL == papi->address_to_string)
839   {
840     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841                 "URI conversion not implemented for plugin `%s'\n",
842                 address->transport_name);
843     return GNUNET_OK;
844   }
845   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
846   if ( (addr == NULL) || (strlen(addr) == 0) )
847     return GNUNET_OK;
848    /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
849       characters in URIs */
850   uri_addr = map_characters (addr, "[]", "()");
851   seconds = expiration.abs_value / 1000;
852   t = gmtime (&seconds);
853
854   GNUNET_asprintf (&ret,
855                    "%s!%s!%s!%s",
856                    ctx->uri,
857                    strftime (tbuf, sizeof (tbuf), "%Y%m%d%H%M%S", t) ? tbuf : "0",
858                    address->transport_name,
859                    uri_addr);
860   GNUNET_free (uri_addr);
861   GNUNET_free (ctx->uri);
862   ctx->uri = ret;
863   return GNUNET_OK;
864 }
865
866
867 /**
868  * Compose a hello URI string from a hello message.
869  *
870  * @param hello Hello message
871  * @param plugins_find Function to find transport plugins by name
872  * @return Hello URI string
873  */
874 char *
875 GNUNET_HELLO_compose_uri (const struct GNUNET_HELLO_Message *hello,
876                           GNUNET_HELLO_TransportPluginsFind plugins_find)
877 {
878   struct GNUNET_HELLO_ComposeUriContext ctx;
879   ctx.plugins_find = plugins_find;
880
881   char *pkey = GNUNET_CRYPTO_ecc_public_key_to_string (&(hello->publicKey));
882   GNUNET_asprintf (&(ctx.uri),
883                    "%s%s",
884                    GNUNET_HELLO_URI_PREFIX,
885                    pkey);
886   GNUNET_free (pkey);
887
888   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &add_address_to_uri, &ctx);
889   return ctx.uri;
890 }
891
892
893 /* ************************* Parse HELLO URI ********************* */
894
895
896 /**
897  * We're building a HELLO.  Parse the next address from the
898  * parsing context and append it.
899  *
900  * @param cls the 'struct GNUNET_HELLO_AddressParsingContext'
901  * @param max number of bytes available for HELLO construction
902  * @param buffer where to copy the next address (in binary format)
903  * @return number of bytes added to buffer
904  */
905 static size_t
906 add_address_to_hello (void *cls, size_t max, void *buffer)
907 {
908   struct GNUNET_HELLO_ParseUriContext *ctx = cls;
909   const char *tname;
910   const char *address;
911   char *uri_address;
912   char *plugin_address;
913   const char *end;
914   char *plugin_name;
915   struct tm expiration_time;
916   time_t expiration_seconds;
917   struct GNUNET_TIME_Absolute expire;
918   struct GNUNET_TRANSPORT_PluginFunctions *papi;
919   void *addr;
920   size_t addr_len;
921   struct GNUNET_HELLO_Address haddr;
922   size_t ret;
923
924   if (NULL == ctx->pos)
925     return 0;
926   if ('!' != ctx->pos[0])
927   {
928     ctx->ret = GNUNET_SYSERR;
929     GNUNET_break (0);
930     return 0;
931   }
932   ctx->pos++;
933
934   if ('0' == ctx->pos[0] && '!' == ctx->pos[1])
935   {
936     expire = GNUNET_TIME_UNIT_FOREVER_ABS;
937     tname = ctx->pos + 1;
938   }
939   else
940   {
941     memset (&expiration_time, 0, sizeof (expiration_time));
942     tname = strptime (ctx->pos,
943                       "%Y%m%d%H%M%S",
944                       &expiration_time);
945     if (NULL == tname)
946     {
947       ctx->ret = GNUNET_SYSERR;
948       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
949                   _("Failed to parse HELLO message: missing expiration time\n"));
950       GNUNET_break (0);
951       return 0;
952     }
953
954     expiration_seconds = mktime (&expiration_time);
955     if (expiration_seconds == (time_t) -1)
956     {
957       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
958                   _("Failed to parse HELLO message: invalid expiration time\n"));
959       ctx->ret = GNUNET_SYSERR;
960       GNUNET_break (0);
961       return 0;
962     }
963     expire.abs_value = expiration_seconds * 1000;
964   }
965   if ('!' != tname[0])
966   {
967     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
968                 _("Failed to parse HELLO message: malformed\n"));
969     ctx->ret = GNUNET_SYSERR;
970     GNUNET_break (0);
971     return 0;
972   }
973   tname++;
974   address = strchr (tname, (int) '!');
975   if (NULL == address)
976   {
977     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
978                 _("Failed to parse HELLO message: missing transport plugin\n"));
979     ctx->ret = GNUNET_SYSERR;
980     GNUNET_break (0);
981     return 0;
982   }
983   address++;
984   end = strchr (address, (int) '!');
985   ctx->pos = end;
986   plugin_name = GNUNET_strndup (tname, address - (tname+1));
987   papi = ctx->plugins_find (plugin_name);
988   if (NULL == papi)
989   {
990     /* Not an error - we might just not have the right plugin.
991      * Skip this part, advance to the next one and recurse.
992      * But only if this is not the end of string.
993      */
994     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
995                 _("Plugin `%s' not found\n"),
996                 plugin_name);
997     GNUNET_free (plugin_name);
998     GNUNET_break (0);
999     return 0;
1000   }
1001   if (NULL == papi->string_to_address)
1002   {
1003     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1004                 _("Plugin `%s' does not support URIs yet\n"),
1005                 plugin_name);
1006     GNUNET_free (plugin_name);
1007     GNUNET_break (0);
1008     return 0;
1009   }
1010   uri_address = GNUNET_strndup (address, end - address);
1011   /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
1012      characters in URIs; need to convert back to '[]' for the plugin */
1013    plugin_address = map_characters (uri_address, "()", "[]");
1014   GNUNET_free (uri_address);
1015   if (GNUNET_OK !=
1016       papi->string_to_address (papi->cls,
1017                                plugin_address,
1018                                strlen (plugin_address) + 1,
1019                                &addr,
1020                                &addr_len))
1021   {
1022     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1023                 _("Failed to parse `%s' as an address for plugin `%s'\n"),
1024                 plugin_address,
1025                 plugin_name);
1026     GNUNET_free (plugin_name);
1027     GNUNET_free (plugin_address);
1028     return 0;
1029   }
1030   GNUNET_free (plugin_address);
1031   /* address.peer is unset - not used by add_address() */
1032   haddr.address_length = addr_len;
1033   haddr.address = addr;
1034   haddr.transport_name = plugin_name;
1035   ret = GNUNET_HELLO_add_address (&haddr, expire, buffer, max);
1036   GNUNET_free (addr);
1037   GNUNET_free (plugin_name);
1038   return ret;
1039 }
1040
1041
1042 /**
1043  * Parse a hello URI string to a hello message.
1044  *
1045  * @param uri URI string to parse
1046  * @param pubkey Pointer to struct where public key is parsed
1047  * @param hello Pointer to struct where hello message is parsed
1048  * @param plugins_find Function to find transport plugins by name
1049  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
1050  */
1051 int
1052 GNUNET_HELLO_parse_uri (const char *uri,
1053                         struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pubkey,
1054                         struct GNUNET_HELLO_Message **hello,
1055                         GNUNET_HELLO_TransportPluginsFind plugins_find)
1056 {
1057   const char *pks;
1058   const char *exc;
1059   int friend_only;
1060   struct GNUNET_HELLO_ParseUriContext ctx;
1061
1062   if (0 == strncmp (uri,
1063                     GNUNET_HELLO_URI_PREFIX,
1064                     strlen (GNUNET_HELLO_URI_PREFIX)))
1065   {
1066                 pks = &uri[strlen (GNUNET_HELLO_URI_PREFIX)];
1067                 friend_only = GNUNET_NO;
1068   }
1069   else if (0 == strncmp (uri,
1070             GNUNET_FRIEND_HELLO_URI_PREFIX,
1071             strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)))
1072   {
1073         pks = &uri[strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)];
1074         friend_only = GNUNET_YES;
1075   }
1076   else
1077         return GNUNET_SYSERR;
1078   exc = strstr (pks, "!");
1079
1080   if (GNUNET_OK !=
1081       GNUNET_STRINGS_string_to_data (pks,
1082                                      (NULL == exc) ? strlen (pks) : (exc - pks),
1083                                      (unsigned char *) pubkey,
1084                                      sizeof (*pubkey)))
1085     return GNUNET_SYSERR;
1086
1087   ctx.pos = exc;
1088   ctx.ret = GNUNET_OK;
1089   ctx.plugins_find = plugins_find;
1090   *hello = GNUNET_HELLO_create (pubkey, &add_address_to_hello, &ctx, friend_only);
1091
1092   return ctx.ret;
1093 }
1094
1095
1096 /* end of hello.c */