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