428c4139c6c71af21cfdd5d4fa360a085048122c
[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  * @param plugins_find Function to find transport plugins by name
773  * @return GNUNET_OK (continue iteration).
774  */
775 static int
776 add_address_to_uri (void *cls, const struct GNUNET_HELLO_Address *address,
777                     struct GNUNET_TIME_Absolute expiration)
778 {
779   struct GNUNET_HELLO_ComposeUriContext *ctx = cls;
780   struct GNUNET_TRANSPORT_PluginFunctions *papi;
781   const char *addr;
782   char *uri_addr;
783   char *ret;
784   char tbuf[16] = "";
785   struct tm *t;
786   time_t seconds;
787
788   papi = ctx->plugins_find (address->transport_name);
789   if (papi == NULL)
790   {
791     /* Not an error - we might just not have the right plugin. */
792     return GNUNET_OK;
793   }
794   if (NULL == papi->address_to_string)
795   {
796     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
797                 "URI conversion not implemented for plugin `%s'\n",
798                 address->transport_name);
799     return GNUNET_OK;
800   }
801   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
802   if ( (addr == NULL) || (strlen(addr) == 0) )
803     return GNUNET_OK;
804    /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
805       characters in URIs */
806   uri_addr = map_characters (addr, "[]", "()");
807   seconds = expiration.abs_value / 1000;
808   t = gmtime (&seconds);
809
810   GNUNET_asprintf (&ret,
811                    "%s!%s!%s!%s",
812                    ctx->uri,
813                    strftime (tbuf, sizeof (tbuf), "%Y%m%d%H%M%S", t) ? tbuf : "0",
814                    address->transport_name,
815                    uri_addr);
816   GNUNET_free (uri_addr);
817   GNUNET_free (ctx->uri);
818   ctx->uri = ret;
819   return GNUNET_OK;
820 }
821
822
823 /**
824  * Compose a hello URI string from a hello message.
825  *
826  * @param hello Hello message
827  * @param plugins_find Function to find transport plugins by name
828  * @return Hello URI string
829  */
830 char *
831 GNUNET_HELLO_compose_uri (const struct GNUNET_HELLO_Message *hello,
832                           GNUNET_HELLO_TransportPluginsFind plugins_find)
833 {
834   struct GNUNET_HELLO_ComposeUriContext ctx;
835   ctx.plugins_find = plugins_find;
836
837   char *pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&(hello->publicKey));
838   GNUNET_asprintf (&(ctx.uri),
839                    "%s%s",
840                    GNUNET_HELLO_URI_PREFIX,
841                    pkey);
842   GNUNET_free (pkey);
843
844   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &add_address_to_uri, &ctx);
845   return ctx.uri;
846 }
847
848
849 /* ************************* Parse HELLO URI ********************* */
850
851
852 /**
853  * We're building a HELLO.  Parse the next address from the
854  * parsing context and append it.
855  *
856  * @param cls the 'struct GNUNET_HELLO_AddressParsingContext'
857  * @param max number of bytes available for HELLO construction
858  * @param buffer where to copy the next address (in binary format)
859  * @return number of bytes added to buffer
860  */
861 static size_t
862 add_address_to_hello (void *cls, size_t max, void *buffer)
863 {
864   struct GNUNET_HELLO_ParseUriContext *ctx = cls;
865   const char *tname;
866   const char *address;
867   char *uri_address;
868   char *plugin_address;
869   const char *end;
870   char *plugin_name;
871   struct tm expiration_time;
872   time_t expiration_seconds;
873   struct GNUNET_TIME_Absolute expire;
874   struct GNUNET_TRANSPORT_PluginFunctions *papi;
875   void *addr;
876   size_t addr_len;
877   struct GNUNET_HELLO_Address haddr;
878   size_t ret;
879
880   if (NULL == ctx->pos)
881     return 0;
882   if ('!' != ctx->pos[0])
883   {
884     ctx->ret = GNUNET_SYSERR;
885     GNUNET_break (0);
886     return 0;
887   }
888   ctx->pos++;
889
890   if ('0' == ctx->pos[0] && '!' == ctx->pos[1])
891   {
892     expire = GNUNET_TIME_UNIT_FOREVER_ABS;
893     tname = ctx->pos + 1;
894   }
895   else
896   {
897     memset (&expiration_time, 0, sizeof (expiration_time));
898     tname = strptime (ctx->pos,
899                       "%Y%m%d%H%M%S",
900                       &expiration_time);
901     if (NULL == tname)
902     {
903       ctx->ret = GNUNET_SYSERR;
904       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
905                   _("Failed to parse HELLO message: missing expiration time\n"));
906       GNUNET_break (0);
907       return 0;
908     }
909
910     expiration_seconds = mktime (&expiration_time);
911     if (expiration_seconds == (time_t) -1)
912     {
913       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
914                   _("Failed to parse HELLO message: invalid expiration time\n"));
915       ctx->ret = GNUNET_SYSERR;
916       GNUNET_break (0);
917       return 0;
918     }
919     expire.abs_value = expiration_seconds * 1000;
920   }
921   if ('!' != tname[0])
922   {
923     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
924                 _("Failed to parse HELLO message: malformed\n"));
925     ctx->ret = GNUNET_SYSERR;
926     GNUNET_break (0);
927     return 0;
928   }
929   tname++;
930   address = strchr (tname, (int) '!');
931   if (NULL == address)
932   {
933     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
934                 _("Failed to parse HELLO message: missing transport plugin\n"));
935     ctx->ret = GNUNET_SYSERR;
936     GNUNET_break (0);
937     return 0;
938   }
939   address++;
940   end = strchr (address, (int) '!');
941   ctx->pos = end;
942   plugin_name = GNUNET_strndup (tname, address - (tname+1));
943   papi = ctx->plugins_find (plugin_name);
944   if (NULL == papi)
945   {
946     /* Not an error - we might just not have the right plugin.
947      * Skip this part, advance to the next one and recurse.
948      * But only if this is not the end of string.
949      */
950     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951                 _("Plugin `%s' not found\n"),
952                 plugin_name);
953     GNUNET_free (plugin_name);
954     GNUNET_break (0);
955     return 0;
956   }
957   if (NULL == papi->string_to_address)
958   {
959     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
960                 _("Plugin `%s' does not support URIs yet\n"),
961                 plugin_name);
962     GNUNET_free (plugin_name);
963     GNUNET_break (0);
964     return 0;
965   }
966   uri_address = GNUNET_strndup (address, end - address);
967   /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
968      characters in URIs; need to convert back to '[]' for the plugin */
969    plugin_address = map_characters (uri_address, "()", "[]");
970   GNUNET_free (uri_address);
971   if (GNUNET_OK !=
972       papi->string_to_address (papi->cls,
973                                plugin_address,
974                                strlen (plugin_address) + 1,
975                                &addr,
976                                &addr_len))
977   {
978     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
979                 _("Failed to parse `%s' as an address for plugin `%s'\n"),
980                 plugin_address,
981                 plugin_name);
982     GNUNET_free (plugin_name);
983     GNUNET_free (plugin_address);
984     return 0;
985   }
986   GNUNET_free (plugin_address);
987   /* address.peer is unset - not used by add_address() */
988   haddr.address_length = addr_len;
989   haddr.address = addr;
990   haddr.transport_name = plugin_name;
991   ret = GNUNET_HELLO_add_address (&haddr, expire, buffer, max);
992   GNUNET_free (addr);
993   GNUNET_free (plugin_name);
994   return ret;
995 }
996
997
998 /**
999  * Parse a hello URI string to a hello message.
1000  *
1001  * @param uri URI string to parse
1002  * @param pubkey Pointer to struct where public key is parsed
1003  * @param hello Pointer to struct where hello message is parsed
1004  * @param plugins_find Function to find transport plugins by name
1005  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
1006  */
1007 int
1008 GNUNET_HELLO_parse_uri (const char *uri,
1009                         struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pubkey,
1010                         struct GNUNET_HELLO_Message **hello,
1011                         GNUNET_HELLO_TransportPluginsFind plugins_find)
1012 {
1013   const char *pks;
1014   const char *exc;
1015   struct GNUNET_HELLO_ParseUriContext ctx;
1016
1017   if (0 != strncmp (uri,
1018                     GNUNET_HELLO_URI_PREFIX,
1019                     strlen (GNUNET_HELLO_URI_PREFIX)))
1020     return GNUNET_SYSERR;
1021   pks = &uri[strlen (GNUNET_HELLO_URI_PREFIX)];
1022   exc = strstr (pks, "!");
1023
1024   if (GNUNET_OK !=
1025       GNUNET_STRINGS_string_to_data (pks,
1026                                      (NULL == exc) ? strlen (pks) : (exc - pks),
1027                                      (unsigned char *) pubkey,
1028                                      sizeof (*pubkey)))
1029     return GNUNET_SYSERR;
1030
1031   ctx.pos = exc;
1032   ctx.ret = GNUNET_OK;
1033   ctx.plugins_find = plugins_find;
1034   *hello = GNUNET_HELLO_create (pubkey, &add_address_to_hello, &ctx);
1035
1036   return ctx.ret;
1037 }
1038
1039
1040 /* end of hello.c */