aboutsummaryrefslogtreecommitdiff
path: root/src/lib/hello/hello.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/hello/hello.c')
-rw-r--r--src/lib/hello/hello.c1249
1 files changed, 0 insertions, 1249 deletions
diff --git a/src/lib/hello/hello.c b/src/lib/hello/hello.c
deleted file mode 100644
index 45d27c5d4..000000000
--- a/src/lib/hello/hello.c
+++ /dev/null
@@ -1,1249 +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
514struct GNUNET_HELLO_Message *
515GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1,
516 const struct GNUNET_HELLO_Message *h2)
517{
518 struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 };
519 int friend_only;
520
521 if (h1->friend_only != h2->friend_only)
522 friend_only = GNUNET_YES; /* One of the HELLOs is friend only */
523 else
524 friend_only = ntohl (h1->friend_only); /* Both HELLO's have the same type */
525
526 return GNUNET_HELLO_create (&h1->publicKey,
527 &merge_addr,
528 &mc,
529 friend_only);
530}
531
532
533/**
534 * Context used in #GNUNET_HELLO_iterate_new_addresses() to
535 * figure out which addresses are in fact 'new'.
536 */
537struct DeltaContext
538{
539 /**
540 * We should ignore addresses that expire before this time.
541 */
542 struct GNUNET_TIME_Absolute expiration_limit;
543
544 /**
545 * Function to call on addresses that are indeed new.
546 */
547 GNUNET_HELLO_AddressIterator it;
548
549 /**
550 * Closure for @e it.
551 */
552 void *it_cls;
553
554 /**
555 * HELLO with known addresses, addresses in this HELLO
556 * we must always ignore.
557 */
558 const struct GNUNET_HELLO_Message *old_hello;
559};
560
561
562/**
563 * Check if the given address is 'new', and if so, call
564 * the iterator. Compares the existing address against
565 * addresses in the context's `old_hello` and calls the
566 * iterator on those that are new (and not expired).
567 *
568 * @param cls the `struct DeltaContext`
569 * @param address an address to check whether it is new
570 * @param expiration expiration time for @a address
571 * @return #GNUNET_YES if the address is ignored, otherwise
572 * whatever the iterator returned.
573 */
574static int
575delta_match (void *cls,
576 const struct GNUNET_HELLO_Address *address,
577 struct GNUNET_TIME_Absolute expiration)
578{
579 struct DeltaContext *dc = cls;
580 int ret;
581 struct ExpireContext ec;
582
583 ec.address = address;
584 ec.found = GNUNET_NO;
585 GNUNET_HELLO_iterate_addresses (dc->old_hello,
586 GNUNET_NO,
587 &get_match_exp,
588 &ec);
589 if ((GNUNET_YES == ec.found) &&
590 ((ec.expiration.abs_value_us > expiration.abs_value_us) ||
591 (ec.expiration.abs_value_us >= dc->expiration_limit.abs_value_us)))
592 return GNUNET_YES; /* skip: found and boring */
593 ret = dc->it (dc->it_cls,
594 address,
595 expiration);
596 return ret;
597}
598
599
600void
601GNUNET_HELLO_iterate_new_addresses (const struct
602 GNUNET_HELLO_Message *new_hello,
603 const struct
604 GNUNET_HELLO_Message *old_hello,
605 struct GNUNET_TIME_Absolute
606 expiration_limit,
607 GNUNET_HELLO_AddressIterator it,
608 void *it_cls)
609{
610 struct DeltaContext dc;
611
612 dc.expiration_limit = expiration_limit;
613 dc.it = it;
614 dc.it_cls = it_cls;
615 dc.old_hello = old_hello;
616 GNUNET_assert (NULL ==
617 GNUNET_HELLO_iterate_addresses (new_hello,
618 GNUNET_NO,
619 &delta_match,
620 &dc));
621}
622
623
624/**
625 * Return the size of the given HELLO message.
626 * @param hello to inspect
627 * @return the size, 0 if HELLO is invalid
628 */
629uint16_t
630GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello)
631{
632 uint16_t ret = ntohs (hello->header.size);
633
634 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
635 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
636 return 0;
637 return ret;
638}
639
640
641/**
642 * Get the peer identity from a HELLO message.
643 *
644 * @param hello the hello message
645 * @param peer where to store the peer's identity
646 * @return #GNUNET_SYSERR if the HELLO was malformed
647 */
648int
649GNUNET_HELLO_get_id (const struct GNUNET_HELLO_Message *hello,
650 struct GNUNET_PeerIdentity *peer)
651{
652 uint16_t ret = ntohs (hello->header.size);
653
654 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
655 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
656 return GNUNET_SYSERR;
657 peer->public_key = hello->publicKey;
658 return GNUNET_OK;
659}
660
661
662/**
663 * Get the header from a HELLO message, used so other code
664 * can correctly send HELLO messages.
665 *
666 * @param hello the hello message
667 *
668 * @return header or NULL if the HELLO was malformed
669 */
670struct GNUNET_MessageHeader *
671GNUNET_HELLO_get_header (struct GNUNET_HELLO_Message *hello)
672{
673 uint16_t ret = ntohs (hello->header.size);
674
675 if ((ret < sizeof(struct GNUNET_HELLO_Message)) ||
676 (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO))
677 return NULL;
678
679 return &hello->header;
680}
681
682
683/**
684 * Context used for comparing HELLOs in #GNUNET_HELLO_equals().
685 */
686struct EqualsContext
687{
688 /**
689 * Addresses that expired before this date are ignored for
690 * the comparison.
691 */
692 struct GNUNET_TIME_Absolute expiration_limit;
693
694 /**
695 * Earliest expiration time for which we found a match
696 * with a difference in expiration times.
697 * At this time, the two HELLOs may start to diverge.
698 */
699 struct GNUNET_TIME_Absolute result;
700
701 /**
702 * HELLO message to compare against. (First set to the second
703 * HELLO, then set to the first HELLO.)
704 */
705 const struct GNUNET_HELLO_Message *ref;
706
707 /**
708 * Address we are currently looking for.
709 */
710 const struct GNUNET_HELLO_Address *address;
711
712 /**
713 * Expiration time of @e address.
714 */
715 struct GNUNET_TIME_Absolute expiration;
716
717 /**
718 * Did we find the address we were looking for?
719 */
720 int found;
721};
722
723
724/**
725 * Check if the given address matches the address we are currently
726 * looking for. If so, sets `found` to #GNUNET_YES and, if the
727 * expiration times for the two addresses differ, updates `result` to
728 * the minimum of our @a expiration and the existing value
729 *
730 * @param cls the `struct EqualsContext`
731 * @param address address from the reference HELLO
732 * @param expiration expiration time for @a address
733 * @return #GNUNET_YES if the address is expired or does not match
734 * #GNUNET_SYSERR if the address does match.
735 */
736static int
737find_other_matching (void *cls,
738 const struct GNUNET_HELLO_Address *address,
739 struct GNUNET_TIME_Absolute expiration)
740{
741 struct EqualsContext *ec = cls;
742
743 if (expiration.abs_value_us < ec->expiration_limit.abs_value_us)
744 return GNUNET_YES;
745 if (0 == GNUNET_HELLO_address_cmp (address, ec->address))
746 {
747 ec->found = GNUNET_YES;
748 if (expiration.abs_value_us < ec->expiration.abs_value_us)
749 ec->result = GNUNET_TIME_absolute_min (expiration,
750 ec->result);
751 return GNUNET_SYSERR;
752 }
753 return GNUNET_YES;
754}
755
756
757/**
758 * Helper function for #GNUNET_HELLO_equals(). Checks
759 * if the given @a address exists also in the other HELLO;
760 * if not, the result time is set to zero and the iteration
761 * is aborted.
762 *
763 * @param cls the `struct EqualsContext`
764 * @param address address to locate
765 * @param expiration expiration time of the current address
766 * @return #GNUNET_OK if the address exists or is expired,
767 * #GNUNET_SYSERR if it was not found
768 */
769static int
770find_matching (void *cls,
771 const struct GNUNET_HELLO_Address *address,
772 struct GNUNET_TIME_Absolute expiration)
773{
774 struct EqualsContext *ec = cls;
775
776 if (expiration.abs_value_us < ec->expiration_limit.abs_value_us)
777 return GNUNET_OK; /* expired, we don't care */
778 ec->address = address;
779 ec->expiration = expiration;
780 ec->found = GNUNET_NO;
781 GNUNET_HELLO_iterate_addresses (ec->ref,
782 GNUNET_NO,
783 &find_other_matching,
784 ec);
785 if (GNUNET_NO == ec->found)
786 {
787 /* not found, we differ *now* */
788 ec->result = GNUNET_TIME_UNIT_ZERO_ABS;
789 return GNUNET_SYSERR;
790 }
791 return GNUNET_OK;
792}
793
794
795/**
796 * Test if two HELLO messages contain the same addresses.
797 * If they only differ in expiration time, the lowest
798 * expiration time larger than @a now where they differ
799 * is returned.
800 *
801 * @param h1 first HELLO message
802 * @param h2 the second HELLO message
803 * @param now time to use for deciding which addresses have
804 * expired and should not be considered at all
805 * @return absolute time forever if the two HELLOs are
806 * totally identical; smallest timestamp >= @a now if
807 * they only differ in timestamps;
808 * zero if the some addresses with expirations >= @a now
809 * do not match at all
810 */
811struct GNUNET_TIME_Absolute
812GNUNET_HELLO_equals (const struct GNUNET_HELLO_Message *h1,
813 const struct GNUNET_HELLO_Message *h2,
814 struct GNUNET_TIME_Absolute now)
815{
816 struct EqualsContext ec;
817
818 if (h1->header.type != h2->header.type)
819 return GNUNET_TIME_UNIT_ZERO_ABS;
820 if (0 !=
821 GNUNET_memcmp (&h1->publicKey,
822 &h2->publicKey))
823 return GNUNET_TIME_UNIT_ZERO_ABS;
824 ec.expiration_limit = now;
825 ec.result = GNUNET_TIME_UNIT_FOREVER_ABS;
826 ec.ref = h2;
827 GNUNET_HELLO_iterate_addresses (h1,
828 GNUNET_NO,
829 &find_matching,
830 &ec);
831 if (ec.result.abs_value_us == GNUNET_TIME_UNIT_ZERO.rel_value_us)
832 return ec.result;
833 ec.ref = h1;
834 GNUNET_HELLO_iterate_addresses (h2,
835 GNUNET_NO,
836 &find_matching,
837 &ec);
838 return ec.result;
839}
840
841
842/**
843 * Iterator to find the time when the last address will expire.
844 * Updates the maximum value stored in @a cls.
845 *
846 * @param cls where to store the max, a `struct GNUNET_TIME_Absolute`
847 * @param address an address (ignored)
848 * @param expiration expiration time for @a address
849 * @return #GNUNET_OK (always)
850 */
851static int
852find_max_expire (void *cls,
853 const struct GNUNET_HELLO_Address *address,
854 struct GNUNET_TIME_Absolute expiration)
855{
856 struct GNUNET_TIME_Absolute *max = cls;
857
858 *max = GNUNET_TIME_absolute_max (*max, expiration);
859 return GNUNET_OK;
860}
861
862
863/**
864 * When does the last address in the given HELLO expire?
865 *
866 * @param msg HELLO to inspect
867 * @return time the last address expires, 0 if there are no addresses in the HELLO
868 */
869struct GNUNET_TIME_Absolute
870GNUNET_HELLO_get_last_expiration (const struct GNUNET_HELLO_Message *msg)
871{
872 struct GNUNET_TIME_Absolute ret;
873
874 ret = GNUNET_TIME_UNIT_ZERO_ABS;
875 GNUNET_HELLO_iterate_addresses (msg,
876 GNUNET_NO,
877 &find_max_expire,
878 &ret);
879 return ret;
880}
881
882
883/**
884 * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
885 * The specific structure of "IDENTIFIER" depends on the module and
886 * maybe differentiated into additional subcategories if applicable.
887 * This module only deals with hello identifiers (MODULE = "hello").
888 * <p>
889 *
890 * The concrete URI format is:
891 *
892 * "gnunet://hello/PEER[+YYYYMMDDHHMMSS+<TYPE>+<ADDRESS>]...".
893 * These URIs can be used to add a peer record to peerinfo service.
894 * PEER is the string representation of peer's public key.
895 * YYYYMMDDHHMMSS is the expiration date.
896 * TYPE is a transport type.
897 * ADDRESS is the address, its format depends upon the transport type.
898 * The concrete transport types and corresponding address formats are:
899 *
900 * <ul><li>
901 *
902 * <TCP|UDP>!IPADDRESS
903 * IPVDDRESS is either IPV4 .-delimited address in form of XXX.XXX.XXX.XXX:PPPPP
904 * or IPV6 :-delimited address with '[' and ']' (according to RFC2732):
905 * [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:PPPPP
906 * PPPPP is the port number. May be 0.
907 *
908 * </li><li>
909 *
910 * [add SMTP, HTTP and other addresses here]
911 *
912 * </li></ul>
913 *
914 * The encoding for hexadecimal values is defined in the crypto_hash.c
915 * module in the gnunetutil library and discussed there.
916 *
917 * Examples:
918 *
919 * gnunet://hello/V8XXK9GAN5ZJFRFQP8MQX3D83BZTSBQVHKWWD0JPE63Z821906EG+20120302010059+TCP+192.168.0.1:2086+TCP+64.23.8.174:0
920 * gnunet://hello/V8XXK9GAN5ZJFRFQP8MQX3D83BZTSBQVHKWWD0JPE63Z821906EG+20120302010059+TCP+[2001:db8:85a3:8d3:1319:8a2e:370:7348]:2086
921 *
922 * <p>
923 */
924
925
926/**
927 * Function that is called on each address of this peer.
928 * Expands the corresponding URI string.
929 *
930 * @param cls the `struct GNUNET_HELLO_ComposeUriContext`
931 * @param address address to add
932 * @param expiration expiration time for the address
933 * @return #GNUNET_OK (continue iteration).
934 */
935static int
936add_address_to_uri (void *cls,
937 const struct GNUNET_HELLO_Address *address,
938 struct GNUNET_TIME_Absolute expiration)
939{
940 struct GNUNET_HELLO_ComposeUriContext *ctx = cls;
941 struct GNUNET_TRANSPORT_PluginFunctions *papi;
942 const char *addr;
943 char *ret;
944 char *addr_dup;
945 char *pos;
946 char tbuf[16] = "";
947 char *client_str = "_client";
948 struct tm *t;
949 time_t seconds;
950
951 papi = ctx->plugins_find (address->transport_name);
952 if (NULL == papi)
953 {
954 /* Not an error - we might just not have the right plugin. */
955 return GNUNET_OK;
956 }
957 if (NULL == papi->address_to_string)
958 {
959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
960 "URI conversion not implemented for plugin `%s'\n",
961 address->transport_name);
962 return GNUNET_OK;
963 }
964 addr = papi->address_to_string (papi->cls,
965 address->address,
966 address->address_length);
967 if ((NULL == addr) ||
968 (0 == strlen (addr)))
969 return GNUNET_OK;
970
971 addr_dup = GNUNET_strdup (addr);
972 if (NULL != (pos = strstr (addr_dup, "_server")))
973 GNUNET_memcpy (pos,
974 client_str,
975 strlen (client_str)); /* Replace all server addresses with client addresses */
976
977 seconds = expiration.abs_value_us / 1000LL / 1000LL;
978 t = gmtime (&seconds);
979
980 GNUNET_asprintf (&ret,
981 "%s%c%s%c%s%c%s",
982 ctx->uri,
983 GNUNET_HELLO_URI_SEP,
984 strftime (tbuf,
985 sizeof(tbuf),
986 "%Y%m%d%H%M%S",
987 t) ? tbuf : "0",
988 GNUNET_HELLO_URI_SEP,
989 address->transport_name,
990 GNUNET_HELLO_URI_SEP,
991 addr_dup);
992 GNUNET_free (addr_dup);
993 GNUNET_free (ctx->uri);
994 ctx->uri = ret;
995 return GNUNET_OK;
996}
997
998
999/**
1000 * Compose a hello URI string from a hello message.
1001 *
1002 * @param hello Hello message
1003 * @param plugins_find Function to find transport plugins by name
1004 * @return Hello URI string
1005 */
1006char *
1007GNUNET_HELLO_compose_uri (const struct GNUNET_HELLO_Message *hello,
1008 GNUNET_HELLO_TransportPluginsFind plugins_find)
1009{
1010 struct GNUNET_HELLO_ComposeUriContext ctx;
1011 char *pkey;
1012
1013 ctx.plugins_find = plugins_find;
1014 pkey = GNUNET_CRYPTO_eddsa_public_key_to_string (&hello->publicKey);
1015 GNUNET_asprintf (&ctx.uri,
1016 "%s%s",
1017 (GNUNET_YES == GNUNET_HELLO_is_friend_only (hello))
1018 ? GNUNET_FRIEND_HELLO_URI_PREFIX
1019 : GNUNET_HELLO_URI_PREFIX,
1020 pkey);
1021 GNUNET_free (pkey);
1022 GNUNET_HELLO_iterate_addresses (hello,
1023 GNUNET_NO,
1024 &add_address_to_uri,
1025 &ctx);
1026 return ctx.uri;
1027}
1028
1029
1030/* ************************* Parse HELLO URI ********************* */
1031
1032
1033/**
1034 * We're building a HELLO. Parse the next address from the
1035 * parsing context and append it.
1036 *
1037 * @param cls the `struct GNUNET_HELLO_ParseUriContext`
1038 * @param max number of bytes available for HELLO construction
1039 * @param buffer where to copy the next address (in binary format)
1040 * @return number of bytes added to buffer, #GNUNET_SYSERR on error
1041 */
1042static ssize_t
1043add_address_to_hello (void *cls,
1044 size_t max,
1045 void *buffer)
1046{
1047 struct GNUNET_HELLO_ParseUriContext *ctx = cls;
1048 const char *tname;
1049 const char *address;
1050 char *uri_address;
1051 const char *end;
1052 char *plugin_name;
1053 struct tm expiration_time;
1054 time_t expiration_seconds;
1055 struct GNUNET_TIME_Absolute expire;
1056 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1057 void *addr;
1058 size_t addr_len;
1059 struct GNUNET_HELLO_Address haddr;
1060 ssize_t ret;
1061
1062 if (NULL == ctx->pos)
1063 return GNUNET_SYSERR;
1064 if (GNUNET_HELLO_URI_SEP != ctx->pos[0])
1065 {
1066 ctx->ret = GNUNET_SYSERR;
1067 GNUNET_break (0);
1068 return GNUNET_SYSERR;
1069 }
1070 ctx->pos++;
1071
1072 if (('0' == ctx->pos[0]) &&
1073 (GNUNET_HELLO_URI_SEP == ctx->pos[1]))
1074 {
1075 expire = GNUNET_TIME_UNIT_FOREVER_ABS;
1076 tname = ctx->pos + 1;
1077 }
1078 else
1079 {
1080 memset (&expiration_time, 0, sizeof(expiration_time));
1081 tname = strptime (ctx->pos,
1082 "%Y%m%d%H%M%S",
1083 &expiration_time);
1084 if (NULL == tname)
1085 {
1086 ctx->ret = GNUNET_SYSERR;
1087 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1088 _ (
1089 "Failed to parse HELLO message: missing expiration time\n"));
1090 GNUNET_break (0);
1091 return GNUNET_SYSERR;
1092 }
1093
1094 expiration_seconds = mktime (&expiration_time);
1095 if (expiration_seconds == (time_t) -1)
1096 {
1097 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1098 _ (
1099 "Failed to parse HELLO message: invalid expiration time\n"));
1100 ctx->ret = GNUNET_SYSERR;
1101 GNUNET_break (0);
1102 return GNUNET_SYSERR;
1103 }
1104 expire.abs_value_us = expiration_seconds * 1000LL * 1000LL;
1105 }
1106 if (GNUNET_HELLO_URI_SEP != tname[0])
1107 {
1108 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1109 _ ("Failed to parse HELLO message: malformed\n"));
1110 ctx->ret = GNUNET_SYSERR;
1111 GNUNET_break (0);
1112 return GNUNET_SYSERR;
1113 }
1114 tname++;
1115 address = strchr (tname,
1116 (int) GNUNET_HELLO_URI_SEP);
1117 if (NULL == address)
1118 {
1119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1120 _ (
1121 "Failed to parse HELLO message: missing transport plugin\n"));
1122 ctx->ret = GNUNET_SYSERR;
1123 GNUNET_break (0);
1124 return GNUNET_SYSERR;
1125 }
1126 address++;
1127 end = strchr (address, (int) GNUNET_HELLO_URI_SEP);
1128 ctx->pos = end;
1129 ctx->counter_total++;
1130 plugin_name = GNUNET_strndup (tname, address - (tname + 1));
1131 papi = ctx->plugins_find (plugin_name);
1132 if (NULL == papi)
1133 {
1134 /* Not an error - we might just not have the right plugin.
1135 * Skip this part, advance to the next one and recurse.
1136 * But only if this is not the end of string.
1137 */
1138 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1139 _ ("Plugin `%s' not found, skipping address\n"),
1140 plugin_name);
1141 GNUNET_free (plugin_name);
1142 return 0;
1143 }
1144 if (NULL == papi->string_to_address)
1145 {
1146 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1147 _ ("Plugin `%s' does not support URIs yet\n"),
1148 plugin_name);
1149 GNUNET_free (plugin_name);
1150 GNUNET_break (0);
1151 return 0;
1152 }
1153 uri_address = GNUNET_strndup (address, end - address);
1154 if (GNUNET_OK !=
1155 papi->string_to_address (papi->cls,
1156 uri_address,
1157 strlen (uri_address) + 1,
1158 &addr,
1159 &addr_len))
1160 {
1161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1162 _ ("Failed to parse `%s' as an address for plugin `%s'\n"),
1163 uri_address,
1164 plugin_name);
1165 GNUNET_free (plugin_name);
1166 GNUNET_free (uri_address);
1167 return 0;
1168 }
1169 GNUNET_free (uri_address);
1170 /* address.peer is unset - not used by add_address() */
1171 haddr.address_length = addr_len;
1172 haddr.address = addr;
1173 haddr.transport_name = plugin_name;
1174 ret = GNUNET_HELLO_add_address (&haddr,
1175 expire,
1176 buffer,
1177 max);
1178 ctx->counter_added++;
1179 GNUNET_free (addr);
1180 GNUNET_free (plugin_name);
1181 return ret;
1182}
1183
1184
1185/**
1186 * Parse a hello URI string to a hello message.
1187 *
1188 * @param uri URI string to parse
1189 * @param pubkey Pointer to struct where public key is parsed
1190 * @param hello Pointer to struct where hello message is parsed
1191 * @param plugins_find Function to find transport plugins by name
1192 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the URI was invalid, #GNUNET_NO on other errors
1193 */
1194int
1195GNUNET_HELLO_parse_uri (const char *uri,
1196 struct GNUNET_CRYPTO_EddsaPublicKey *pubkey,
1197 struct GNUNET_HELLO_Message **hello,
1198 GNUNET_HELLO_TransportPluginsFind plugins_find)
1199{
1200 const char *pks;
1201 const char *exc;
1202 int friend_only;
1203 struct GNUNET_HELLO_ParseUriContext ctx;
1204
1205 if (0 == strncmp (uri,
1206 GNUNET_HELLO_URI_PREFIX,
1207 strlen (GNUNET_HELLO_URI_PREFIX)))
1208 {
1209 pks = &uri[strlen (GNUNET_HELLO_URI_PREFIX)];
1210 friend_only = GNUNET_NO;
1211 }
1212 else if (0 == strncmp (uri,
1213 GNUNET_FRIEND_HELLO_URI_PREFIX,
1214 strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)))
1215 {
1216 pks = &uri[strlen (GNUNET_FRIEND_HELLO_URI_PREFIX)];
1217 friend_only = GNUNET_YES;
1218 }
1219 else
1220 return GNUNET_SYSERR;
1221 exc = strchr (pks, GNUNET_HELLO_URI_SEP);
1222
1223 if (GNUNET_OK !=
1224 GNUNET_STRINGS_string_to_data (pks,
1225 (NULL == exc) ? strlen (pks) : (exc - pks),
1226 (unsigned char *) pubkey,
1227 sizeof(*pubkey)))
1228 return GNUNET_SYSERR;
1229
1230 ctx.pos = exc;
1231 ctx.ret = GNUNET_OK;
1232 ctx.counter_total = 0;
1233 ctx.counter_added = 0;
1234 ctx.plugins_find = plugins_find;
1235 *hello = GNUNET_HELLO_create (pubkey,
1236 &add_address_to_hello,
1237 &ctx,
1238 friend_only);
1239
1240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1241 "HELLO URI contained %u addresses, added %u addresses\n",
1242 ctx.counter_total,
1243 ctx.counter_added);
1244
1245 return ctx.ret;
1246}
1247
1248
1249/* end of hello.c */