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