aboutsummaryrefslogtreecommitdiff
path: root/src/hello/hello.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hello/hello.c')
-rw-r--r--src/hello/hello.c1271
1 files changed, 0 insertions, 1271 deletions
diff --git a/src/hello/hello.c b/src/hello/hello.c
deleted file mode 100644
index 12b576ceb..000000000
--- a/src/hello/hello.c
+++ /dev/null
@@ -1,1271 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file hello/hello.c
23 * @brief helper library for handling HELLOs
24 * @author Christian Grothoff
25 * @author Matthias Wachs
26 */
27#include "platform.h"
28#include "gnunet_hello_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_transport_plugin.h"
32
33/**
34 * Context used for building our own URI.
35 */
36struct GNUNET_HELLO_ComposeUriContext
37{
38 /**
39 * Final URI.
40 */
41 char *uri;
42
43 /**
44 * Function for finding transport plugins by name.
45 */
46 GNUNET_HELLO_TransportPluginsFind plugins_find;
47};
48
49
50/**
51 * Context for #add_address_to_hello().
52 */
53struct GNUNET_HELLO_ParseUriContext
54{
55 /**
56 * Position in the URI with the next address to parse.
57 */
58 const char *pos;
59
60 /**
61 * Set to #GNUNET_SYSERR to indicate parse errors.
62 */
63 int ret;
64
65 /**
66 * Counter
67 */
68 unsigned int counter_total;
69
70 /**
71 * Counter skipped addresses
72 */
73 unsigned int counter_added;
74
75 /**
76 * Function for finding transport plugins by name.
77 */
78 GNUNET_HELLO_TransportPluginsFind plugins_find;
79};
80
81
82/**
83 * Return HELLO type
84 *
85 * @param h HELLO Message to test
86 * @return #GNUNET_YES for friend-only or #GNUNET_NO otherwise
87 */
88int
89GNUNET_HELLO_is_friend_only (const struct GNUNET_HELLO_Message *h)
90{
91 if (GNUNET_YES == ntohl (h->friend_only))
92 return GNUNET_YES;
93 return GNUNET_NO;
94}
95
96
97/**
98 * Copy the given address information into
99 * the given buffer using the format of HELLOs.
100 *
101 * @param address the address
102 * @param expiration expiration for the @a address
103 * @param target where to copy the @a address
104 * @param max maximum number of bytes to copy to target
105 * @return number of bytes copied, 0 if
106 * the target buffer was not big enough.
107 */
108size_t
109GNUNET_HELLO_add_address (const struct GNUNET_HELLO_Address *address,
110 struct GNUNET_TIME_Absolute expiration,
111 char *target,
112 size_t max)
113{
114 uint16_t alen;
115 size_t slen;
116 struct GNUNET_TIME_AbsoluteNBO exp;
117
118 slen = strlen (address->transport_name) + 1;
119 if (slen + sizeof(uint16_t) + sizeof(struct GNUNET_TIME_AbsoluteNBO)
120 + address->address_length > max)
121 return 0;
122 exp = GNUNET_TIME_absolute_hton (expiration);
123 alen = htons ((uint16_t) address->address_length);
124 GNUNET_memcpy (target, address->transport_name, slen);
125 GNUNET_memcpy (&target[slen], &alen, sizeof(uint16_t));
126 slen += sizeof(uint16_t);
127 GNUNET_memcpy (&target[slen], &exp, sizeof(struct GNUNET_TIME_AbsoluteNBO));
128 slen += sizeof(struct GNUNET_TIME_AbsoluteNBO);
129 GNUNET_memcpy (&target[slen], address->address, address->address_length);
130 slen += address->address_length;
131 return slen;
132}
133
134
135/**
136 * Get the size of an address entry in a HELLO message.
137 *
138 * @param buf pointer to the start of the address entry
139 * @param max maximum size of the entry (end of @a buf)
140 * @param ralen set to the address length
141 * @return size of the entry, or 0 if @a max is not large enough
142 */
143static size_t
144get_hello_address_size (const char *buf,
145 size_t max,
146 uint16_t *ralen)
147{
148 const char *pos;
149 uint16_t alen;
150 size_t left;
151 size_t slen;
152
153 left = max;
154 pos = buf;
155 slen = 1;
156 while ((left > 0) && ('\0' != *pos))
157 {
158 left--;
159 pos++;
160 slen++;
161 }
162 if (0 == left)
163 {
164 /* 0-termination not found */
165 GNUNET_break_op (0);
166 return 0;
167 }
168 pos++;
169 if (left < sizeof(uint16_t) + sizeof(struct GNUNET_TIME_AbsoluteNBO))
170 {
171 /* not enough space for addrlen */
172 GNUNET_break_op (0);
173 return 0;
174 }
175 GNUNET_memcpy (&alen, pos, sizeof(uint16_t));
176 alen = ntohs (alen);
177 *ralen = alen;
178 slen += alen + sizeof(uint16_t) + sizeof(struct GNUNET_TIME_AbsoluteNBO);
179 if (max < slen)
180 {
181 /* not enough space for addr */
182 GNUNET_break_op (0);
183 return 0;
184 }
185 return slen;
186}
187
188
189/**
190 * Construct a HELLO message given the public key,
191 * expiration time and an iterator that spews the
192 * transport addresses.
193 *
194 * If friend only is set to #GNUNET_YES we create a FRIEND_HELLO which
195 * will not be gossiped to other peers.
196 *
197 * @param public_key public key to include in the HELLO
198 * @param addrgen callback to invoke to get addresses
199 * @param addrgen_cls closure for @a addrgen
200 * @param friend_only should the returned HELLO be only visible to friends?
201 * @return the hello message
202 */
203struct GNUNET_HELLO_Message *
204GNUNET_HELLO_create (const struct GNUNET_CRYPTO_EddsaPublicKey *public_key,
205 GNUNET_HELLO_GenerateAddressListCallback addrgen,
206 void *addrgen_cls,
207 int friend_only)
208{
209 char buffer[GNUNET_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 GNUNET_assert (NULL != public_key);
217 GNUNET_assert ((GNUNET_YES == friend_only) ||
218 (GNUNET_NO == friend_only));
219 max = sizeof(buffer);
220 used = 0;
221 if (NULL != addrgen)
222 {
223 while (GNUNET_SYSERR != (ret = addrgen (addrgen_cls,
224 max,
225 &buffer[used])))
226 {
227 max -= ret;
228 used += ret;
229 }
230 }
231 hello = GNUNET_malloc (sizeof(struct GNUNET_HELLO_Message) + used);
232 hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO);
233 hello->header.size = htons (sizeof(struct GNUNET_HELLO_Message) + used);
234 hello->friend_only = htonl (friend_only);
235 hello->publicKey = *public_key;
236 GNUNET_memcpy (&hello[1],
237 buffer,
238 used);
239 return hello;
240}
241
242
243/**
244 * Iterate over all of the addresses in the HELLO.
245 *
246 * @param msg HELLO to iterate over
247 * @param return_modified if a modified copy should be returned,
248 * otherwise NULL will be returned
249 * @param it iterator to call on each address
250 * @param it_cls closure for @a it
251 * @return modified HELLO message
252 */
253struct GNUNET_HELLO_Message *
254GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg,
255 int return_modified,
256 GNUNET_HELLO_AddressIterator it,
257 void *it_cls)
258{
259 struct GNUNET_HELLO_Address address;
260 uint16_t msize;
261 struct GNUNET_HELLO_Message *ret;
262 const char *inptr;
263 size_t insize;
264 size_t esize;
265 size_t wpos;
266 char *woff;
267 uint16_t alen;
268 struct GNUNET_TIME_AbsoluteNBO expire;
269 int iret;
270
271 msize = GNUNET_HELLO_size (msg);
272 if ((msize < sizeof(struct GNUNET_HELLO_Message)) ||
273 (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
274 {
275 GNUNET_break_op (0);
276 return NULL;
277 }
278 ret = NULL;
279 if (return_modified)
280 {
281 ret = GNUNET_malloc (msize);
282 GNUNET_memcpy (ret,
283 msg,
284 msize);
285 }
286 inptr = (const char *) &msg[1];
287 insize = msize - sizeof(struct GNUNET_HELLO_Message);
288 wpos = 0;
289 woff = (NULL != ret) ? (char *) &ret[1] : NULL;
290 address.peer.public_key = msg->publicKey;
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "HELLO has %u bytes of address data\n",
293 (unsigned int) insize);
294
295 while (insize > 0)
296 {
297 esize = get_hello_address_size (inptr,
298 insize,
299 &alen);
300 if (0 == esize)
301 {
302 GNUNET_break (0);
303 GNUNET_free (ret);
304 return NULL;
305 }
306 /* need GNUNET_memcpy() due to possibility of misalignment */
307 GNUNET_memcpy (&expire,
308 &inptr[esize - alen - sizeof(struct
309 GNUNET_TIME_AbsoluteNBO)],
310 sizeof(struct GNUNET_TIME_AbsoluteNBO));
311 address.address = &inptr[esize - alen];
312 address.address_length = alen;
313 address.transport_name = inptr;
314 address.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE;
315 iret = it (it_cls,
316 &address,
317 GNUNET_TIME_absolute_ntoh (expire));
318 if (GNUNET_SYSERR == iret)
319 break;
320 if ((GNUNET_OK == iret) &&
321 (NULL != ret))
322 {
323 /* copy address over */
324 GNUNET_memcpy (woff,
325 inptr,
326 esize);
327 woff += esize;
328 wpos += esize;
329 }
330 insize -= esize;
331 inptr += esize;
332 }
333 if (NULL != ret)
334 ret->header.size = ntohs (sizeof(struct GNUNET_HELLO_Message) + wpos);
335 return ret;
336}
337
338
339/**
340 * Closure for #get_match_exp().
341 */
342struct ExpireContext
343{
344 /**
345 * Address we are looking for.
346 */
347 const struct GNUNET_HELLO_Address *address;
348
349 /**
350 * Set to #GNUNET_YES if we found the @e address.
351 */
352 int found;
353
354 /**
355 * Set to the expiration of the match if @e found is #GNUNET_YES.
356 */
357 struct GNUNET_TIME_Absolute expiration;
358};
359
360
361/**
362 * Store the expiration time of an address that matches the template.
363 *
364 * @param cls the `struct ExpireContext`
365 * @param address address to match against the template
366 * @param expiration expiration time of @a address, to store in @a cls
367 * @return #GNUNET_SYSERR if we found a matching address, #GNUNET_OK otherwise
368 */
369static int
370get_match_exp (void *cls,
371 const struct GNUNET_HELLO_Address *address,
372 struct GNUNET_TIME_Absolute expiration)
373{
374 struct ExpireContext *ec = cls;
375
376 if (0 != GNUNET_HELLO_address_cmp (address,
377 ec->address))
378 return GNUNET_OK;
379 ec->found = GNUNET_YES;
380 ec->expiration = expiration;
381 return GNUNET_SYSERR; /* done here */
382}
383
384
385/**
386 * Context for a #GNUNET_HELLO_Merge operation.
387 */
388struct MergeContext
389{
390 /**
391 * First HELLO we are merging.
392 */
393 const struct GNUNET_HELLO_Message *h1;
394
395 /**
396 * Second HELLO we are merging.
397 */
398 const struct GNUNET_HELLO_Message *h2;
399
400 /**
401 * Either @e h1 or @e h2, used when copying
402 * to compare against (so we only copy the
403 * most recent entry).
404 */
405 const struct GNUNET_HELLO_Message *other;
406
407 /**
408 * Buffer where we copy to.
409 */
410 char *buf;
411
412 /**
413 * Number of bytes allocated in @e buf
414 */
415 size_t max;
416
417 /**
418 * Current (write) offset in @e buf.
419 */
420 size_t ret;
421
422 /**
423 * Should we copy addresses with an identical value
424 * and expiration time in @e other, or do we only
425 * copy addresses with strictly later expiration times?
426 */
427 int take_equal;
428};
429
430
431/**
432 * Append the address @a address to the buffer from
433 * the merge context IF it is more recent than equivalent
434 * addresses in `other`.
435 *
436 * @param cls the `struct MergeContext`
437 * @param address the HELLO address we might copy
438 * @param expiration expiration time for @a address
439 * @return always #GNUNET_OK
440 */
441static int
442copy_latest (void *cls,
443 const struct GNUNET_HELLO_Address *address,
444 struct GNUNET_TIME_Absolute expiration)
445{
446 struct MergeContext *mc = cls;
447 struct ExpireContext ec;
448
449 ec.address = address;
450 ec.found = GNUNET_NO;
451 /* check if address exists in other */
452 GNUNET_HELLO_iterate_addresses (mc->other,
453 GNUNET_NO,
454 &get_match_exp,
455 &ec);
456 if ((GNUNET_NO == ec.found) ||
457 (ec.expiration.abs_value_us < expiration.abs_value_us) ||
458 ((ec.expiration.abs_value_us == expiration.abs_value_us) &&
459 (GNUNET_YES == mc->take_equal)))
460 {
461 /* copy address to buffer */
462 mc->ret +=
463 GNUNET_HELLO_add_address (address,
464 expiration,
465 &mc->buf[mc->ret],
466 mc->max - mc->ret);
467 }
468 return GNUNET_OK;
469}
470
471
472/**
473 * Function called to build the HELLO during
474 * #GNUNET_HELLO_merge() by merging addresses from
475 * two original HELLOs.
476 *
477 * @param cls the `struct MergeContext`
478 * @param max number of bytes we can write at most in @a buf
479 * @param buf where to copy the addresses
480 * @return #GNUNET_SYSERR to end iteration, otherwise number of bytes written to @a buf
481 */
482static ssize_t
483merge_addr (void *cls,
484 size_t max,
485 void *buf)
486{
487 struct MergeContext *mc = cls;
488
489 if (NULL == mc->h1)
490 return GNUNET_SYSERR; /* Stop iteration */
491 mc->ret = 0;
492 mc->max = max;
493 mc->buf = buf;
494 mc->take_equal = GNUNET_NO;
495 mc->other = mc->h2;
496 /* copy addresses from h1, if strictly larger expiration than h2 */
497 GNUNET_HELLO_iterate_addresses (mc->h1,
498 GNUNET_NO,
499 &copy_latest,
500 mc);
501 mc->take_equal = GNUNET_YES;
502 mc->other = mc->h1;
503 /* copy addresses from h2, if larger or equal expiration than h1 */
504 GNUNET_HELLO_iterate_addresses (mc->h2,
505 GNUNET_NO,
506 &copy_latest,
507 mc);
508 /* set marker to stop iteration */
509 mc->h1 = NULL;
510 return mc->ret;
511}
512
513
514/**
515 * Construct a HELLO message by merging the
516 * addresses in two existing HELLOs (which
517 * must be for the same peer).
518 *
519 * @param h1 first HELLO message
520 * @param h2 the second HELLO message
521 * @return the combined HELLO message
522 */
523struct GNUNET_HELLO_Message *
524GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
525 const struct GNUNET_HELLO_Message *h2)
526{
527 struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
528 int friend_only;
529
530 if (h1->friend_only != h2->friend_only)
531 friend_only = GNUNET_YES; /* One of the HELLOs is friend only */
532 else
533 friend_only = ntohl (h1->friend_only); /* Both HELLO's have the same type */
534
535 return GNUNET_HELLO_create (&h1->publicKey,
536 &merge_addr,
537 &mc,
538 friend_only);
539}
540
541
542/**
543 * Context used in #GNUNET_HELLO_iterate_new_addresses() to
544 * figure out which addresses are in fact 'new'.
545 */
546struct DeltaContext
547{
548 /**
549 * We should ignore addresses that expire before this time.
550 */
551 struct GNUNET_TIME_Absolute expiration_limit;
552
553 /**
554 * Function to call on addresses that are indeed new.
555 */
556 GNUNET_HELLO_AddressIterator it;
557
558 /**
559 * Closure for @e it.
560 */
561 void *it_cls;
562
563 /**
564 * HELLO with known addresses, addresses in this HELLO
565 * we must always ignore.
566 */
567 const struct GNUNET_HELLO_Message *old_hello;
568};
569
570
571/**
572 * Check if the given address is 'new', and if so, call
573 * the iterator. Compares the existing address against
574 * addresses in the context's `old_hello` and calls the
575 * iterator on those that are new (and not expired).
576 *
577 * @param cls the `struct DeltaContext`
578 * @param address an address to check whether it is new
579 * @param expiration expiration time for @a address
580 * @return #GNUNET_YES if the address is ignored, otherwise
581 * whatever the iterator returned.
582 */
583static int
584delta_match (void *cls,
585 const struct GNUNET_HELLO_Address *address,
586 struct GNUNET_TIME_Absolute expiration)
587{
588 struct DeltaContext *dc = cls;
589 int ret;
590 struct ExpireContext ec;
591
592 ec.address = address;
593 ec.found = GNUNET_NO;
594 GNUNET_HELLO_iterate_addresses (dc->old_hello,
595 GNUNET_NO,
596 &get_match_exp,
597 &ec);
598 if ((GNUNET_YES == ec.found) &&
599 ((ec.expiration.abs_value_us > expiration.abs_value_us) ||
600 (ec.expiration.abs_value_us >= dc->expiration_limit.abs_value_us)))
601 return GNUNET_YES; /* skip: found and boring */
602 ret = dc->it (dc->it_cls,
603 address,
604 expiration);
605 return ret;
606}
607
608
609/**
610 * Iterate over addresses in @a new_hello that are NOT already present
611 * in @a old_hello. Note that if the address is present in @a old_hello
612 * but the expiration time in @a new_hello is more recent, the iterator
613 * is also called.
614 *
615 * @param new_hello a HELLO message
616 * @param old_hello a HELLO message
617 * @param expiration_limit ignore addresses in @a old_hello
618 * that expired before the given time stamp
619 * @param it iterator to call on each address
620 * @param it_cls closure for @a it
621 */
622void
623GNUNET_HELLO_iterate_new_addresses (const struct
624 GNUNET_HELLO_Message *new_hello,
625 const struct
626 GNUNET_HELLO_Message *old_hello,
627 struct GNUNET_TIME_Absolute
628 expiration_limit,
629 GNUNET_HELLO_AddressIterator it,
630 void *it_cls)
631{
632 struct DeltaContext dc;
633
634 dc.expiration_limit = expiration_limit;
635 dc.it = it;
636 dc.it_cls = it_cls;
637 dc.old_hello = old_hello;
638 GNUNET_assert (NULL ==
639 GNUNET_HELLO_iterate_addresses (new_hello,
640 GNUNET_NO,
641 &delta_match,
642 &dc));
643}
644
645
646/**
647 * Return the size of the given HELLO message.
648 * @param hello to inspect
649 * @return the size, 0 if HELLO is invalid
650 */
651uint16_t
652GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
653{
654 uint16_t ret = ntohs (hello->header.size);
655
656 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
657 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
658 return 0;
659 return ret;
660}
661
662
663/**
664 * Get the peer identity from a HELLO message.
665 *
666 * @param hello the hello message
667 * @param peer where to store the peer's identity
668 * @return #GNUNET_SYSERR if the HELLO was malformed
669 */
670int
671GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
672 struct GNUNET_PeerIdentity *peer)
673{
674 uint16_t ret = ntohs (hello->header.size);
675
676 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
677 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
678 return GNUNET_SYSERR;
679 peer->public_key = hello->publicKey;
680 return GNUNET_OK;
681}
682
683
684/**
685 * Get the header from a HELLO message, used so other code
686 * can correctly send HELLO messages.
687 *
688 * @param hello the hello message
689 *
690 * @return header or NULL if the HELLO was malformed
691 */
692struct GNUNET_MessageHeader *
693GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
694{
695 uint16_t ret = ntohs (hello->header.size);
696
697 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
698 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
699 return NULL;
700
701 return &hello->header;
702}
703
704
705/**
706 * Context used for comparing HELLOs in #GNUNET_HELLO_equals().
707 */
708struct EqualsContext
709{
710 /**
711 * Addresses that expired before this date are ignored for
712 * the comparison.
713 */
714 struct GNUNET_TIME_Absolute expiration_limit;
715
716 /**
717 * Earliest expiration time for which we found a match
718 * with a difference in expiration times.
719 * At this time, the two HELLOs may start to diverge.
720 */
721 struct GNUNET_TIME_Absolute result;
722
723 /**
724 * HELLO message to compare against. (First set to the second
725 * HELLO, then set to the first HELLO.)
726 */
727 const struct GNUNET_HELLO_Message *ref;
728
729 /**
730 * Address we are currently looking for.
731 */
732 const struct GNUNET_HELLO_Address *address;
733
734 /**
735 * Expiration time of @e address.
736 */
737 struct GNUNET_TIME_Absolute expiration;
738
739 /**
740 * Did we find the address we were looking for?
741 */
742 int found;
743};
744
745
746/**
747 * Check if the given address matches the address we are currently
748 * looking for. If so, sets `found` to #GNUNET_YES and, if the
749 * expiration times for the two addresses differ, updates `result` to
750 * the minimum of our @a expiration and the existing value
751 *
752 * @param cls the `struct EqualsContext`
753 * @param address address from the reference HELLO
754 * @param expiration expiration time for @a address
755 * @return #GNUNET_YES if the address is expired or does not match
756 * #GNUNET_SYSERR if the address does match.
757 */
758static int
759find_other_matching (void *cls,
760 const struct GNUNET_HELLO_Address *address,
761 struct GNUNET_TIME_Absolute expiration)
762{
763 struct EqualsContext *ec = cls;
764
765 if (expiration.abs_value_us < ec->expiration_limit.abs_value_us)
766 return GNUNET_YES;
767 if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
768 {
769 ec->found = GNUNET_YES;
770 if (expiration.abs_value_us < ec->expiration.abs_value_us)
771 ec->result = GNUNET_TIME_absolute_min (expiration,
772 ec->result);
773 return GNUNET_SYSERR;
774 }
775 return GNUNET_YES;
776}
777
778
779/**
780 * Helper function for #GNUNET_HELLO_equals(). Checks
781 * if the given @a address exists also in the other HELLO;
782 * if not, the result time is set to zero and the iteration
783 * is aborted.
784 *
785 * @param cls the `struct EqualsContext`
786 * @param address address to locate
787 * @param expiration expiration time of the current address
788 * @return #GNUNET_OK if the address exists or is expired,
789 * #GNUNET_SYSERR if it was not found
790 */
791static int
792find_matching (void *cls,
793 const struct GNUNET_HELLO_Address *address,
794 struct GNUNET_TIME_Absolute expiration)
795{
796 struct EqualsContext *ec = cls;
797
798 if (expiration.abs_value_us < ec->expiration_limit.abs_value_us)
799 return GNUNET_OK; /* expired, we don't care */
800 ec->address = address;
801 ec->expiration = expiration;
802 ec->found = GNUNET_NO;
803 GNUNET_HELLO_iterate_addresses (ec->ref,
804 GNUNET_NO,
805 &find_other_matching,
806 ec);
807 if (GNUNET_NO == ec->found)
808 {
809 /* not found, we differ *now* */
810 ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
811 return GNUNET_SYSERR;
812 }
813 return GNUNET_OK;
814}
815
816
817/**
818 * Test if two HELLO messages contain the same addresses.
819 * If they only differ in expiration time, the lowest
820 * expiration time larger than @a now where they differ
821 * is returned.
822 *
823 * @param h1 first HELLO message
824 * @param h2 the second HELLO message
825 * @param now time to use for deciding which addresses have
826 * expired and should not be considered at all
827 * @return absolute time forever if the two HELLOs are
828 * totally identical; smallest timestamp >= @a now if
829 * they only differ in timestamps;
830 * zero if the some addresses with expirations >= @a now
831 * do not match at all
832 */
833struct GNUNET_TIME_Absolute
834GNUNET_HELLO_equals (const struct GNUNET_HELLO_Message *h1,
835 const struct GNUNET_HELLO_Message *h2,
836 struct GNUNET_TIME_Absolute now)
837{
838 struct EqualsContext ec;
839
840 if (h1->header.type != h2->header.type)
841 return GNUNET_TIME_UNIT_ZERO_ABS;
842 if (0 !=
843 GNUNET_memcmp (&h1->publicKey,
844 &h2->publicKey))
845 return GNUNET_TIME_UNIT_ZERO_ABS;
846 ec.expiration_limit = now;
847 ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
848 ec.ref = h2;
849 GNUNET_HELLO_iterate_addresses (h1,
850 GNUNET_NO,
851 &find_matching,
852 &ec);
853 if (ec.result.abs_value_us == GNUNET_TIME_UNIT_ZERO.rel_value_us)
854 return ec.result;
855 ec.ref = h1;
856 GNUNET_HELLO_iterate_addresses (h2,
857 GNUNET_NO,
858 &find_matching,
859 &ec);
860 return ec.result;
861}
862
863
864/**
865 * Iterator to find the time when the last address will expire.
866 * Updates the maximum value stored in @a cls.
867 *
868 * @param cls where to store the max, a `struct GNUNET_TIME_Absolute`
869 * @param address an address (ignored)
870 * @param expiration expiration time for @a address
871 * @return #GNUNET_OK (always)
872 */
873static int
874find_max_expire (void *cls,
875 const struct GNUNET_HELLO_Address *address,
876 struct GNUNET_TIME_Absolute expiration)
877{
878 struct GNUNET_TIME_Absolute *max = cls;
879
880 *max = GNUNET_TIME_absolute_max (*max, expiration);
881 return GNUNET_OK;
882}
883
884
885/**
886 * When does the last address in the given HELLO expire?
887 *
888 * @param msg HELLO to inspect
889 * @return time the last address expires, 0 if there are no addresses in the HELLO
890 */
891struct GNUNET_TIME_Absolute
892GNUNET_HELLO_get_last_expiration (const struct GNUNET_HELLO_Message *msg)
893{
894 struct GNUNET_TIME_Absolute ret;
895
896 ret = GNUNET_TIME_UNIT_ZERO_ABS;
897 GNUNET_HELLO_iterate_addresses (msg,
898 GNUNET_NO,
899 &find_max_expire,
900 &ret);
901 return ret;
902}
903
904
905/**
906 * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
907 * The specific structure of "IDENTIFIER" depends on the module and
908 * maybe differentiated into additional subcategories if applicable.
909 * This module only deals with hello identifiers (MODULE = "hello").
910 * <p>
911 *
912 * The concrete URI format is:
913 *
914 * "gnunet://hello/PEER[+YYYYMMDDHHMMSS+<TYPE>+<ADDRESS>]...".
915 * These URIs can be used to add a peer record to peerinfo service.
916 * PEER is the string representation of peer's public key.
917 * YYYYMMDDHHMMSS is the expiration date.
918 * TYPE is a transport type.
919 * ADDRESS is the address, its format depends upon the transport type.
920 * The concrete transport types and corresponding address formats are:
921 *
922 * <ul><li>
923 *
924 * <TCP|UDP>!IPADDRESS
925 * IPVDDRESS is either IPV4 .-delimited address in form of XXX.XXX.XXX.XXX:PPPPP
926 * or IPV6 :-delimited address with '[' and ']' (according to RFC2732):
927 * [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:PPPPP
928 * PPPPP is the port number. May be 0.
929 *
930 * </li><li>
931 *
932 * [add SMTP, HTTP and other addresses here]
933 *
934 * </li></ul>
935 *
936 * The encoding for hexadecimal values is defined in the crypto_hash.c
937 * module in the gnunetutil library and discussed there.
938 *
939 * Examples:
940 *
941 * gnunet://hello/V8XXK9GAN5ZJFRFQP8MQX3D83BZTSBQVHKWWD0JPE63Z821906EG+20120302010059+TCP+192.168.0.1:2086+TCP+64.23.8.174:0
942 * gnunet://hello/V8XXK9GAN5ZJFRFQP8MQX3D83BZTSBQVHKWWD0JPE63Z821906EG+20120302010059+TCP+[2001:db8:85a3:8d3:1319:8a2e:370:7348]:2086
943 *
944 * <p>
945 */
946
947
948/**
949 * Function that is called on each address of this peer.
950 * Expands the corresponding URI string.
951 *
952 * @param cls the `struct GNUNET_HELLO_ComposeUriContext`
953 * @param address address to add
954 * @param expiration expiration time for the address
955 * @return #GNUNET_OK (continue iteration).
956 */
957static int
958add_address_to_uri (void *cls,
959 const struct GNUNET_HELLO_Address *address,
960 struct GNUNET_TIME_Absolute expiration)
961{
962 struct GNUNET_HELLO_ComposeUriContext *ctx = cls;
963 struct GNUNET_TRANSPORT_PluginFunctions *papi;
964 const char *addr;
965 char *ret;
966 char *addr_dup;
967 char *pos;
968 char tbuf[16] = "";
969 char *client_str = "_client";
970 struct tm *t;
971 time_t seconds;
972
973 papi = ctx->plugins_find (address->transport_name);
974 if (NULL == papi)
975 {
976 /* Not an error - we might just not have the right plugin. */
977 return GNUNET_OK;
978 }
979 if (NULL == papi->address_to_string)
980 {
981 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
982 "URI conversion not implemented for plugin `%s'\n",
983 address->transport_name);
984 return GNUNET_OK;
985 }
986 addr = papi->address_to_string (papi->cls,
987 address->address,
988 address->address_length);
989 if ((NULL == addr) ||
990 (0 == strlen (addr)))
991 return GNUNET_OK;
992
993 addr_dup = GNUNET_strdup (addr);
994 if (NULL != (pos = strstr (addr_dup, "_server")))
995 GNUNET_memcpy (pos,
996 client_str,
997 strlen (client_str)); /* Replace all server addresses with client addresses */
998
999 seconds = expiration.abs_value_us / 1000LL / 1000LL;
1000 t = gmtime (&seconds);
1001
1002 GNUNET_asprintf (&ret,
1003 "%s%c%s%c%s%c%s",
1004 ctx->uri,
1005 GNUNET_HELLO_URI_SEP,
1006 strftime (tbuf,
1007 sizeof(tbuf),
1008 "%Y%m%d%H%M%S",
1009 t) ? tbuf : "0",
1010 GNUNET_HELLO_URI_SEP,
1011 address->transport_name,
1012 GNUNET_HELLO_URI_SEP,
1013 addr_dup);
1014 GNUNET_free (addr_dup);
1015 GNUNET_free (ctx->uri);
1016 ctx->uri = ret;
1017 return GNUNET_OK;
1018}
1019
1020
1021/**
1022 * Compose a hello URI string from a hello message.
1023 *
1024 * @param hello Hello message
1025 * @param plugins_find Function to find transport plugins by name
1026 * @return Hello URI string
1027 */
1028char *
1029GNUNET_HELLO_compose_uri (const struct GNUNET_HELLO_Message *hello,
1030 GNUNET_HELLO_TransportPluginsFind plugins_find)
1031{
1032 struct GNUNET_HELLO_ComposeUriContext ctx;
1033 char *pkey;
1034
1035 ctx.plugins_find = plugins_find;
1036 pkey = GNUNET_CRYPTO_eddsa_public_key_to_string (&hello->publicKey);
1037 GNUNET_asprintf (&ctx.uri,
1038 "%s%s",
1039 (GNUNET_YES == GNUNET_HELLO_is_friend_only (hello))
1040 ? GNUNET_FRIEND_HELLO_URI_PREFIX
1041 : GNUNET_HELLO_URI_PREFIX,
1042 pkey);
1043 GNUNET_free (pkey);
1044 GNUNET_HELLO_iterate_addresses (hello,
1045 GNUNET_NO,
1046 &add_address_to_uri,
1047 &ctx);
1048 return ctx.uri;
1049}
1050
1051
1052/* ************************* Parse HELLO URI ********************* */
1053
1054
1055/**
1056 * We're building a HELLO. Parse the next address from the
1057 * parsing context and append it.
1058 *
1059 * @param cls the `struct GNUNET_HELLO_ParseUriContext`
1060 * @param max number of bytes available for HELLO construction
1061 * @param buffer where to copy the next address (in binary format)
1062 * @return number of bytes added to buffer, #GNUNET_SYSERR on error
1063 */
1064static ssize_t
1065add_address_to_hello (void *cls,
1066 size_t max,
1067 void *buffer)
1068{
1069 struct GNUNET_HELLO_ParseUriContext *ctx = cls;
1070 const char *tname;
1071 const char *address;
1072 char *uri_address;
1073 const char *end;
1074 char *plugin_name;
1075 struct tm expiration_time;
1076 time_t expiration_seconds;
1077 struct GNUNET_TIME_Absolute expire;
1078 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1079 void *addr;
1080 size_t addr_len;
1081 struct GNUNET_HELLO_Address haddr;
1082 ssize_t ret;
1083
1084 if (NULL == ctx->pos)
1085 return GNUNET_SYSERR;
1086 if (GNUNET_HELLO_URI_SEP != ctx->pos[0])
1087 {
1088 ctx->ret = GNUNET_SYSERR;
1089 GNUNET_break (0);
1090 return GNUNET_SYSERR;
1091 }
1092 ctx->pos++;
1093
1094 if (('0' == ctx->pos[0]) &&
1095 (GNUNET_HELLO_URI_SEP == ctx->pos[1]))
1096 {
1097 expire = GNUNET_TIME_UNIT_FOREVER_ABS;
1098 tname = ctx->pos + 1;
1099 }
1100 else
1101 {
1102 memset (&expiration_time, 0, sizeof(expiration_time));
1103 tname = strptime (ctx->pos,
1104 "%Y%m%d%H%M%S",
1105 &expiration_time);
1106 if (NULL == tname)
1107 {
1108 ctx->ret = GNUNET_SYSERR;
1109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1110 _ (
1111 "Failed to parse HELLO message: missing expiration time\n"));
1112 GNUNET_break (0);
1113 return GNUNET_SYSERR;
1114 }
1115
1116 expiration_seconds = mktime (&expiration_time);
1117 if (expiration_seconds == (time_t) -1)
1118 {
1119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1120 _ (
1121 "Failed to parse HELLO message: invalid expiration time\n"));
1122 ctx->ret = GNUNET_SYSERR;
1123 GNUNET_break (0);
1124 return GNUNET_SYSERR;
1125 }
1126 expire.abs_value_us = expiration_seconds * 1000LL * 1000LL;
1127 }
1128 if (GNUNET_HELLO_URI_SEP != tname[0])
1129 {
1130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1131 _ ("Failed to parse HELLO message: malformed\n"));
1132 ctx->ret = GNUNET_SYSERR;
1133 GNUNET_break (0);
1134 return GNUNET_SYSERR;
1135 }
1136 tname++;
1137 address = strchr (tname,
1138 (int) GNUNET_HELLO_URI_SEP);
1139 if (NULL == address)
1140 {
1141 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1142 _ (
1143 "Failed to parse HELLO message: missing transport plugin\n"));
1144 ctx->ret = GNUNET_SYSERR;
1145 GNUNET_break (0);
1146 return GNUNET_SYSERR;
1147 }
1148 address++;
1149 end = strchr (address, (int) GNUNET_HELLO_URI_SEP);
1150 ctx->pos = end;
1151 ctx->counter_total++;
1152 plugin_name = GNUNET_strndup (tname, address - (tname + 1));
1153 papi = ctx->plugins_find (plugin_name);
1154 if (NULL == papi)
1155 {
1156 /* Not an error - we might just not have the right plugin.
1157 * Skip this part, advance to the next one and recurse.
1158 * But only if this is not the end of string.
1159 */
1160 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1161 _ ("Plugin `%s' not found, skipping address\n"),
1162 plugin_name);
1163 GNUNET_free (plugin_name);
1164 return 0;
1165 }
1166 if (NULL == papi->string_to_address)
1167 {
1168 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1169 _ ("Plugin `%s' does not support URIs yet\n"),
1170 plugin_name);
1171 GNUNET_free (plugin_name);
1172 GNUNET_break (0);
1173 return 0;
1174 }
1175 uri_address = GNUNET_strndup (address, end - address);
1176 if (GNUNET_OK !=
1177 papi->string_to_address (papi->cls,
1178 uri_address,
1179 strlen (uri_address) + 1,
1180 &addr,
1181 &addr_len))
1182 {
1183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1184 _ ("Failed to parse `%s' as an address for plugin `%s'\n"),
1185 uri_address,
1186 plugin_name);
1187 GNUNET_free (plugin_name);
1188 GNUNET_free (uri_address);
1189 return 0;
1190 }
1191 GNUNET_free (uri_address);
1192 /* address.peer is unset - not used by add_address() */
1193 haddr.address_length = addr_len;
1194 haddr.address = addr;
1195 haddr.transport_name = plugin_name;
1196 ret = GNUNET_HELLO_add_address (&haddr,
1197 expire,
1198 buffer,
1199 max);
1200 ctx->counter_added++;
1201 GNUNET_free (addr);
1202 GNUNET_free (plugin_name);
1203 return ret;
1204}
1205
1206
1207/**
1208 * Parse a hello URI string to a hello message.
1209 *
1210 * @param uri URI string to parse
1211 * @param pubkey Pointer to struct where public key is parsed
1212 * @param hello Pointer to struct where hello message is parsed
1213 * @param plugins_find Function to find transport plugins by name
1214 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the URI was invalid, #GNUNET_NO on other errors
1215 */
1216int
1217GNUNET_HELLO_parse_uri (const char *uri,
1218 struct GNUNET_CRYPTO_EddsaPublicKey *pubkey,
1219 struct GNUNET_HELLO_Message **hello,
1220 GNUNET_HELLO_TransportPluginsFind plugins_find)
1221{
1222 const char *pks;
1223 const char *exc;
1224 int friend_only;
1225 struct GNUNET_HELLO_ParseUriContext ctx;
1226
1227 if (0 == strncmp (uri,
1228 GNUNET_HELLO_URI_PREFIX,
1229 strlen (GNUNET_HELLO_URI_PREFIX)))
1230 {
1231 pks = &uri[strlen (GNUNET_HELLO_URI_PREFIX)];
1232 friend_only = GNUNET_NO;
1233 }
1234 else if (0 == strncmp (uri,
1235 GNUNET_FRIEND_HELLO_URI_PREFIX,
1236 strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)))
1237 {
1238 pks = &uri[strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)];
1239 friend_only = GNUNET_YES;
1240 }
1241 else
1242 return GNUNET_SYSERR;
1243 exc = strchr (pks, GNUNET_HELLO_URI_SEP);
1244
1245 if (GNUNET_OK !=
1246 GNUNET_STRINGS_string_to_data (pks,
1247 (NULL == exc) ? strlen (pks) : (exc - pks),
1248 (unsigned char *) pubkey,
1249 sizeof(*pubkey)))
1250 return GNUNET_SYSERR;
1251
1252 ctx.pos = exc;
1253 ctx.ret = GNUNET_OK;
1254 ctx.counter_total = 0;
1255 ctx.counter_added = 0;
1256 ctx.plugins_find = plugins_find;
1257 *hello = GNUNET_HELLO_create (pubkey,
1258 &add_address_to_hello,
1259 &ctx,
1260 friend_only);
1261
1262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1263 "HELLO URI contained %u addresses, added %u addresses\n",
1264 ctx.counter_total,
1265 ctx.counter_added);
1266
1267 return ctx.ret;
1268}
1269
1270
1271/* end of hello.c */