namestore api change: include block expiration time in record create
[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
31 GNUNET_NETWORK_STRUCT_BEGIN
32
33 /**
34  * A HELLO message is used to exchange information about
35  * transports with other peers.  This struct is always
36  * followed by the actual network addresses which have
37  * the format:
38  *
39  * 1) transport-name (0-terminated)
40  * 2) address-length (uint16_t, network byte order; possibly
41  *    unaligned!)
42  * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
43  *    unaligned!)
44  * 4) address (address-length bytes; possibly unaligned!)
45  */
46 struct GNUNET_HELLO_Message
47 {
48   /**
49    * Type will be GNUNET_MESSAGE_TYPE_HELLO.
50    */
51   struct GNUNET_MessageHeader header;
52
53   /**
54    * Always zero (for alignment).
55    */
56   uint32_t reserved GNUNET_PACKED;
57
58   /**
59    * The public key of the peer.
60    */
61   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey;
62
63 };
64 GNUNET_NETWORK_STRUCT_END
65
66 /**
67  * Copy the given address information into
68  * the given buffer using the format of HELLOs.
69  *
70  * @param address the address
71  * @param expiration expiration for the address
72  * @param target where to copy the address
73  * @param max maximum number of bytes to copy to target
74  * @return number of bytes copied, 0 if
75  *         the target buffer was not big enough.
76  */
77 size_t
78 GNUNET_HELLO_add_address (const struct GNUNET_HELLO_Address *address,
79                           struct GNUNET_TIME_Absolute expiration, char *target,
80                           size_t max)
81 {
82   uint16_t alen;
83   size_t slen;
84   struct GNUNET_TIME_AbsoluteNBO exp;
85
86   slen = strlen (address->transport_name) + 1;
87   if (slen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) +
88       address->address_length > max)
89     return 0;
90   exp = GNUNET_TIME_absolute_hton (expiration);
91   alen = htons ((uint16_t) address->address_length);
92   memcpy (target, address->transport_name, slen);
93   memcpy (&target[slen], &alen, sizeof (uint16_t));
94   slen += sizeof (uint16_t);
95   memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO));
96   slen += sizeof (struct GNUNET_TIME_AbsoluteNBO);
97   memcpy (&target[slen], address->address, address->address_length);
98   slen += address->address_length;
99   return slen;
100 }
101
102
103 /**
104  * Get the size of an address entry in a HELLO message.
105  *
106  * @param buf pointer to the start of the address entry
107  * @param max maximum size of the entry (end of buf)
108  * @param ralen set to the address length
109  * @return size of the entry, or 0 if max is not large enough
110  */
111 static size_t
112 get_hello_address_size (const char *buf, size_t max, uint16_t * ralen)
113 {
114   const char *pos;
115   uint16_t alen;
116   size_t left;
117   size_t slen;
118
119   left = max;
120   pos = buf;
121   slen = 1;
122   while ((left > 0) && ('\0' != *pos))
123   {
124     left--;
125     pos++;
126     slen++;
127   }
128   if (left == 0)
129   {
130     /* 0-termination not found */
131     GNUNET_break_op (0);
132     return 0;
133   }
134   pos++;
135   if (left < sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO))
136   {
137     /* not enough space for addrlen */
138     GNUNET_break_op (0);
139     return 0;
140   }
141   memcpy (&alen, pos, sizeof (uint16_t));
142   alen = ntohs (alen);
143   *ralen = alen;
144   slen += alen + sizeof (uint16_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO);
145   if (max < slen)
146   {
147     /* not enough space for addr */
148     GNUNET_break_op (0);
149     return 0;
150   }
151   return slen;
152 }
153
154
155 /**
156  * Construct a HELLO message given the public key,
157  * expiration time and an iterator that spews the
158  * transport addresses.
159  *
160  * @return the hello message
161  */
162 struct GNUNET_HELLO_Message *
163 GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
164                      *publicKey,
165                      GNUNET_HELLO_GenerateAddressListCallback addrgen,
166                      void *addrgen_cls)
167 {
168   char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - 256 -
169               sizeof (struct GNUNET_HELLO_Message)];
170   size_t max;
171   size_t used;
172   size_t ret;
173   struct GNUNET_HELLO_Message *hello;
174
175   max = sizeof (buffer);
176   used = 0;
177   if (addrgen != NULL)
178   {
179     while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used])))
180     {
181       max -= ret;
182       used += ret;
183     }
184   }
185   hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used);
186   hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
187   hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used);
188   memcpy (&hello->publicKey, publicKey,
189           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
190   memcpy (&hello[1], buffer, used);
191   return hello;
192 }
193
194
195 /**
196  * Iterate over all of the addresses in the HELLO.
197  *
198  * @param msg HELLO to iterate over
199  * @param return_modified if a modified copy should be returned,
200  *         otherwise NULL will be returned
201  * @param it iterator to call on each address
202  * @param it_cls closure for it
203  */
204 struct GNUNET_HELLO_Message *
205 GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
206                                 int return_modified,
207                                 GNUNET_HELLO_AddressIterator it, void *it_cls)
208 {
209   struct GNUNET_HELLO_Address address;
210   uint16_t msize;
211   struct GNUNET_HELLO_Message *ret;
212   const char *inptr;
213   size_t insize;
214   size_t esize;
215   size_t wpos;
216   char *woff;
217   uint16_t alen;
218   struct GNUNET_TIME_AbsoluteNBO expire;
219   int iret;
220
221   msize = GNUNET_HELLO_size (msg);
222   if ((msize < sizeof (struct GNUNET_HELLO_Message)) ||
223       (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
224     return NULL;
225   ret = NULL;
226   if (return_modified)
227   {
228     ret = GNUNET_malloc (msize);
229     memcpy (ret, msg, msize);
230   }
231   inptr = (const char *) &msg[1];
232   insize = msize - sizeof (struct GNUNET_HELLO_Message);
233   wpos = 0;
234   woff = (ret != NULL) ? (char *) &ret[1] : NULL;
235   GNUNET_CRYPTO_hash (&msg->publicKey,
236                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
237                       &address.peer.hashPubKey);
238   while (insize > 0)
239   {
240     esize = get_hello_address_size (inptr, insize, &alen);
241     if (esize == 0)
242     {
243       GNUNET_break (0);
244       GNUNET_free_non_null (ret);
245       return NULL;
246     }
247     memcpy (&expire,
248             &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)],
249             sizeof (struct GNUNET_TIME_AbsoluteNBO));
250     address.address = &inptr[esize - alen];
251     address.address_length = alen;
252     address.transport_name = inptr;
253     iret = it (it_cls, &address, GNUNET_TIME_absolute_ntoh (expire));
254     if (iret == GNUNET_SYSERR)
255     {
256       if (ret != NULL)
257         ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
258       return ret;
259     }
260     if ((iret == GNUNET_OK) && (ret != NULL))
261     {
262       memcpy (woff, inptr, esize);
263       woff += esize;
264       wpos += esize;
265     }
266     insize -= esize;
267     inptr += esize;
268   }
269   if (ret != NULL)
270     ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos);
271   return ret;
272 }
273
274
275 struct ExpireContext
276 {
277   const struct GNUNET_HELLO_Address *address;
278   int found;
279   struct GNUNET_TIME_Absolute expiration;
280 };
281
282
283 static int
284 get_match_exp (void *cls, const struct GNUNET_HELLO_Address *address,
285                struct GNUNET_TIME_Absolute expiration)
286 {
287   struct ExpireContext *ec = cls;
288
289   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
290   {
291     ec->found = GNUNET_YES;
292     ec->expiration = expiration;
293     return GNUNET_SYSERR;       /* done here */
294   }
295   return GNUNET_OK;
296 }
297
298
299 struct MergeContext
300 {
301   const struct GNUNET_HELLO_Message *h1;
302   const struct GNUNET_HELLO_Message *h2;
303   const struct GNUNET_HELLO_Message *other;
304   char *buf;
305   size_t max;
306   size_t ret;
307   int take_equal;
308
309 };
310
311
312 static int
313 copy_latest (void *cls, const struct GNUNET_HELLO_Address *address,
314              struct GNUNET_TIME_Absolute expiration)
315 {
316   struct MergeContext *mc = cls;
317   struct ExpireContext ec;
318
319   ec.address = address;
320   ec.found = GNUNET_NO;
321   GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec);
322   if ((ec.found == GNUNET_NO) ||
323       (ec.expiration.abs_value < expiration.abs_value) ||
324       ((ec.expiration.abs_value == expiration.abs_value) &&
325        (mc->take_equal == GNUNET_YES)))
326   {
327     mc->ret +=
328         GNUNET_HELLO_add_address (address, expiration, &mc->buf[mc->ret],
329                                   mc->max - mc->ret);
330   }
331   return GNUNET_OK;
332 }
333
334
335 static size_t
336 merge_addr (void *cls, size_t max, void *buf)
337 {
338   struct MergeContext *mc = cls;
339
340   if (mc->h1 == NULL)
341     return 0;
342   mc->ret = 0;
343   mc->max = max;
344   mc->buf = buf;
345   mc->take_equal = GNUNET_NO;
346   mc->other = mc->h2;
347   GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, &copy_latest, mc);
348   mc->take_equal = GNUNET_YES;
349   mc->other = mc->h1;
350   GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, &copy_latest, mc);
351   mc->h1 = NULL;
352   return mc->ret;
353 }
354
355
356 /**
357  * Construct a HELLO message by merging the
358  * addresses in two existing HELLOs (which
359  * must be for the same peer).
360  *
361  * @param h1 first HELLO message
362  * @param h2 the second HELLO message
363  * @return the combined hello message
364  */
365 struct GNUNET_HELLO_Message *
366 GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
367                     const struct GNUNET_HELLO_Message *h2)
368 {
369   struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
370
371   return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc);
372 }
373
374
375 struct DeltaContext
376 {
377   struct GNUNET_TIME_Absolute expiration_limit;
378
379   GNUNET_HELLO_AddressIterator it;
380
381   void *it_cls;
382
383   const struct GNUNET_HELLO_Message *old_hello;
384 };
385
386
387 static int
388 delta_match (void *cls, const struct GNUNET_HELLO_Address *address,
389              struct GNUNET_TIME_Absolute expiration)
390 {
391   struct DeltaContext *dc = cls;
392   int ret;
393   struct ExpireContext ec;
394
395   ec.address = address;
396   ec.found = GNUNET_NO;
397   GNUNET_HELLO_iterate_addresses (dc->old_hello, GNUNET_NO, &get_match_exp,
398                                   &ec);
399   if ((ec.found == GNUNET_YES) &&
400       ((ec.expiration.abs_value > expiration.abs_value) ||
401        (ec.expiration.abs_value >= dc->expiration_limit.abs_value)))
402     return GNUNET_YES;          /* skip */
403   ret = dc->it (dc->it_cls, address, expiration);
404   return ret;
405 }
406
407
408 /**
409  * Iterate over addresses in "new_hello" that
410  * are NOT already present in "old_hello".
411  *
412  * @param new_hello a HELLO message
413  * @param old_hello a HELLO message
414  * @param expiration_limit ignore addresses in old_hello
415  *        that expired before the given time stamp
416  * @param it iterator to call on each address
417  * @param it_cls closure for it
418  */
419 void
420 GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message
421                                     *new_hello,
422                                     const struct GNUNET_HELLO_Message
423                                     *old_hello,
424                                     struct GNUNET_TIME_Absolute
425                                     expiration_limit,
426                                     GNUNET_HELLO_AddressIterator it,
427                                     void *it_cls)
428 {
429   struct DeltaContext dc;
430
431   dc.expiration_limit = expiration_limit;
432   dc.it = it;
433   dc.it_cls = it_cls;
434   dc.old_hello = old_hello;
435   GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc);
436 }
437
438
439 /**
440  * Return the size of the given HELLO message.
441  * @param hello to inspect
442  * @return the size, 0 if HELLO is invalid
443  */
444 uint16_t
445 GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
446 {
447   uint16_t ret = ntohs (hello->header.size);
448
449   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
450       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
451     return 0;
452   return ret;
453 }
454
455
456 /**
457  * Get the public key from a HELLO message.
458  *
459  * @param hello the hello message
460  * @param publicKey where to copy the public key information, can be NULL
461  * @return GNUNET_SYSERR if the HELLO was malformed
462  */
463 int
464 GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello,
465                       struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
466 {
467   uint16_t ret = ntohs (hello->header.size);
468
469   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
470       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
471     return GNUNET_SYSERR;
472   *publicKey = hello->publicKey;
473   return GNUNET_OK;
474 }
475
476
477 /**
478  * Get the peer identity from a HELLO message.
479  *
480  * @param hello the hello message
481  * @param peer where to store the peer's identity
482  * @return GNUNET_SYSERR if the HELLO was malformed
483  */
484 int
485 GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
486                      struct GNUNET_PeerIdentity *peer)
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 GNUNET_SYSERR;
493   GNUNET_CRYPTO_hash (&hello->publicKey,
494                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
495                       &peer->hashPubKey);
496   return GNUNET_OK;
497 }
498
499
500 /**
501  * Get the header from a HELLO message, used so other code
502  * can correctly send HELLO messages.
503  *
504  * @param hello the hello message
505  *
506  * @return header or NULL if the HELLO was malformed
507  */
508 struct GNUNET_MessageHeader *
509 GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
510 {
511   uint16_t ret = ntohs (hello->header.size);
512
513   if ((ret < sizeof (struct GNUNET_HELLO_Message)) ||
514       (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
515     return NULL;
516
517   return &hello->header;
518 }
519
520
521 struct EqualsContext
522 {
523   struct GNUNET_TIME_Absolute expiration_limit;
524
525   struct GNUNET_TIME_Absolute result;
526
527   const struct GNUNET_HELLO_Message *h2;
528
529   const struct GNUNET_HELLO_Address *address;
530
531   struct GNUNET_TIME_Absolute expiration;
532
533   int found;
534
535 };
536
537
538 static int
539 find_other_matching (void *cls, const struct GNUNET_HELLO_Address *address,
540                      struct GNUNET_TIME_Absolute expiration)
541 {
542   struct EqualsContext *ec = cls;
543
544   if (expiration.abs_value < ec->expiration_limit.abs_value)
545     return GNUNET_YES;
546   if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
547   {
548     ec->found = GNUNET_YES;
549     if (expiration.abs_value < ec->expiration.abs_value)
550       ec->result = GNUNET_TIME_absolute_min (expiration, ec->result);
551     return GNUNET_SYSERR;
552   }
553   return GNUNET_YES;
554 }
555
556
557 static int
558 find_matching (void *cls, const struct GNUNET_HELLO_Address *address,
559                struct GNUNET_TIME_Absolute expiration)
560 {
561   struct EqualsContext *ec = cls;
562
563   if (expiration.abs_value < ec->expiration_limit.abs_value)
564     return GNUNET_YES;
565   ec->address = address;
566   ec->expiration = expiration;
567   ec->found = GNUNET_NO;
568   GNUNET_HELLO_iterate_addresses (ec->h2, GNUNET_NO, &find_other_matching, ec);
569   if (ec->found == GNUNET_NO)
570   {
571     ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
572     return GNUNET_SYSERR;
573   }
574   return GNUNET_OK;
575 }
576
577
578 /**
579  * Test if two HELLO messages contain the same addresses.
580  * If they only differ in expiration time, the lowest
581  * expiration time larger than 'now' where they differ
582  * is returned.
583  *
584  * @param h1 first HELLO message
585  * @param h2 the second HELLO message
586  * @param now time to use for deciding which addresses have
587  *            expired and should not be considered at all
588  * @return absolute time forever if the two HELLOs are
589  *         totally identical; smallest timestamp >= now if
590  *         they only differ in timestamps;
591  *         zero if the some addresses with expirations >= now
592  *         do not match at all
593  */
594 struct GNUNET_TIME_Absolute
595 GNUNET_HELLO_equals (const struct GNUNET_HELLO_Message *h1,
596                      const struct GNUNET_HELLO_Message *h2,
597                      struct GNUNET_TIME_Absolute now)
598 {
599   struct EqualsContext ec;
600
601   if (0 !=
602       memcmp (&h1->publicKey, &h2->publicKey,
603               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
604     return GNUNET_TIME_UNIT_ZERO_ABS;
605   ec.expiration_limit = now;
606   ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
607   ec.h2 = h2;
608   GNUNET_HELLO_iterate_addresses (h1, GNUNET_NO, &find_matching, &ec);
609   if (ec.result.abs_value == GNUNET_TIME_UNIT_ZERO.rel_value)
610     return ec.result;
611   ec.h2 = h1;
612   GNUNET_HELLO_iterate_addresses (h2, GNUNET_NO, &find_matching, &ec);
613   return ec.result;
614 }
615
616
617 static int
618 find_min_expire (void *cls, const struct GNUNET_HELLO_Address *address,
619                  struct GNUNET_TIME_Absolute expiration)
620 {
621   struct GNUNET_TIME_Absolute *min = cls;
622
623   *min = GNUNET_TIME_absolute_min (*min, expiration);
624   return GNUNET_OK;
625 }
626
627
628 /**
629  * When does the last address in the given HELLO expire?
630  *
631  * @param msg HELLO to inspect
632  * @return time the last address expires, 0 if there are no addresses in the HELLO
633  */
634 struct GNUNET_TIME_Absolute
635 GNUNET_HELLO_get_last_expiration (const struct GNUNET_HELLO_Message *msg)
636 {
637   struct GNUNET_TIME_Absolute ret;
638
639   ret.abs_value = 0;
640   GNUNET_HELLO_iterate_addresses (msg, GNUNET_NO, &find_min_expire, &ret);
641   return ret;
642 }
643
644 /**
645  * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
646  * The specific structure of "IDENTIFIER" depends on the module and
647  * maybe differenciated into additional subcategories if applicable.
648  * This module only deals with hello identifiers (MODULE = "hello").
649  * <p>
650  * 
651  * The concrete URI format is:
652  * 
653  * "gnunet://hello/PEER[!YYYYMMDDHHNNSS!<TYPE>!<ADDRESS>]...".
654  * These URIs can be used to add a peer record to peerinfo service.
655  * PEER is the string representation of peer's public key.
656  * YYYYMMDDHHNNSS is the expiration date.
657  * TYPE is a transport type.
658  * ADDRESS is the address, its format depends upon the transport type.
659  * The concrete transport types and corresponding address formats are:
660  * 
661  * <ul><li>
662  * 
663  * <TCP|UDP>!IPADDRESS
664  * IPVDDRESS is either IPV4 .-delimited address in form of XXX.XXX.XXX.XXX:PPPPP
665  * or IPV6 :-delimited address, but with '(' and ')' instead of '[' and ']' (RFC2396 advises against using square brackets in URIs):
666  * (XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX):PPPPP
667  * PPPPP is the port number. May be 0.
668  * 
669  * </li><li>
670  * 
671  * [add SMTP, HTTP and other addresses here]
672  * 
673  * </li></ul>
674  * 
675  * The encoding for hexadecimal values is defined in the crypto_hash.c
676  * module in the gnunetutil library and discussed there.
677  * 
678  * Examples:
679  * 
680  * gnunet://hello/0430205UC7D56PTQK8NV05776671CNN44FK4TL6D0GQ35OMF8MEN4RNMKA5UF6AL3DQO8B1SC5AQF50SQ2MABIRU4HC8H2HAJKJ59JL1JVRJAK308F9GASRFLMGUBB5TQ5AKR94AS5T3MDG8B9O1EMPRKB0HVCG7T6QPP4CDJ913LAEHVJ2DI1TOBB15Q1JIT5ARBOD12U4SIGRFDV3Q7T66G4TBVSJJ90UQF1BG29TGJJKLGEIMSPHHKO544D6EALQ4F2K0416311JC22GVAD48R616I7VK03K7MP7N0RS2MBV1TE9JV8CK1LSQMR7KCDRTLDA6917UGA67DHTGHERIACCGQ54TGSR48RMSGS9BA5HLMOKASFC1I6V4TT09TUGCU8GNDHQF0JF3H7LPV59UL5I38QID040G000!20120302010059!TCP!192.168.0.1:2086!TCP!64.23.8.174:0
681  * gnunet://hello/0430205UC7D56PTQK8NV05776671CNN44FK4TL6D0GQ35OMF8MEN4RNMKA5UF6AL3DQO8B1SC5AQF50SQ2MABIRU4HC8H2HAJKJ59JL1JVRJAK308F9GASRFLMGUBB5TQ5AKR94AS5T3MDG8B9O1EMPRKB0HVCG7T6QPP4CDJ913LAEHVJ2DI1TOBB15Q1JIT5ARBOD12U4SIGRFDV3Q7T66G4TBVSJJ90UQF1BG29TGJJKLGEIMSPHHKO544D6EALQ4F2K0416311JC22GVAD48R616I7VK03K7MP7N0RS2MBV1TE9JV8CK1LSQMR7KCDRTLDA6917UGA67DHTGHERIACCGQ54TGSR48RMSGS9BA5HLMOKASFC1I6V4TT09TUGCU8GNDHQF0JF3H7LPV59UL5I38QID040G000!20120302010059!TCP!(2001:db8:85a3:8d3:1319:8a2e:370:7348):2086
682  * 
683  * <p>
684  */
685
686 /* end of hello.c */