aboutsummaryrefslogtreecommitdiff
path: root/src/lib/hello
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/hello')
-rw-r--r--src/lib/hello/.gitignore5
-rw-r--r--src/lib/hello/Makefile.am33
-rw-r--r--src/lib/hello/gnunet-hello.c426
-rw-r--r--src/lib/hello/hello-ng.c198
-rw-r--r--src/lib/hello/hello-uri.c1045
-rw-r--r--src/lib/hello/meson.build27
-rw-r--r--src/lib/hello/test_hello-uri.c215
7 files changed, 1949 insertions, 0 deletions
diff --git a/src/lib/hello/.gitignore b/src/lib/hello/.gitignore
new file mode 100644
index 000000000..d175d148e
--- /dev/null
+++ b/src/lib/hello/.gitignore
@@ -0,0 +1,5 @@
1gnunet-hello
2test_friend_hello
3test_hello
4test_hello-uri
5test_hello-ng
diff --git a/src/lib/hello/Makefile.am b/src/lib/hello/Makefile.am
new file mode 100644
index 000000000..c7b3e4a05
--- /dev/null
+++ b/src/lib/hello/Makefile.am
@@ -0,0 +1,33 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9lib_LTLIBRARIES = libgnunethello.la
10
11libgnunethello_la_SOURCES = \
12 hello-uri.c
13libgnunethello_la_LIBADD = \
14 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) \
15 $(LTLIBINTL)
16libgnunethello_la_LDFLAGS = \
17 $(GN_LIB_LDFLAGS) \
18 -version-info 1:0:1
19
20check_PROGRAMS = \
21 test_hello-uri
22
23if ENABLE_TEST_RUN
24AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
25TESTS = $(check_PROGRAMS)
26endif
27
28test_hello_uri_SOURCES = \
29 test_hello-uri.c
30test_hello_uri_LDADD = \
31 libgnunethello.la \
32 $(top_builddir)/src/lib/util/libgnunetutil.la \
33 -lgcrypt
diff --git a/src/lib/hello/gnunet-hello.c b/src/lib/hello/gnunet-hello.c
new file mode 100644
index 000000000..aaa4b5005
--- /dev/null
+++ b/src/lib/hello/gnunet-hello.c
@@ -0,0 +1,426 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 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 * @file hello/gnunet-hello.c
22 * @brief change HELLO files to never expire
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_protocols.h"
27#include "gnunet_hello_uri_lib.h"
28#include "gnunet_transport_plugin.h"
29
30/**
31 * Closure for #add_to_buf().
32 */
33struct AddContext
34{
35 /**
36 * Where to add.
37 */
38 char *buf;
39
40 /**
41 * Maximum number of bytes left
42 */
43 size_t max;
44
45 /**
46 * Number of bytes added so far.
47 */
48 size_t ret;
49
50 struct GNUNET_HELLO_Builder *builder;
51};
52
53/**
54 * Entry in doubly-linked list of all of our plugins.
55 */
56struct TransportPlugin
57{
58 /**
59 * This is a doubly-linked list.
60 */
61 struct TransportPlugin *next;
62
63 /**
64 * This is a doubly-linked list.
65 */
66 struct TransportPlugin *prev;
67
68 /**
69 * API of the transport as returned by the plugin's
70 * initialization function.
71 */
72 struct GNUNET_TRANSPORT_PluginFunctions *api;
73
74 /**
75 * Short name for the plugin (e.g. "tcp").
76 */
77 char *short_name;
78
79 /**
80 * Name of the library (e.g. "gnunet_plugin_transport_tcp").
81 */
82 char *lib_name;
83
84 /**
85 * Environment this transport service is using
86 * for this plugin.
87 */
88 struct GNUNET_TRANSPORT_PluginEnvironment env;
89};
90
91static int address_count;
92
93/**
94 * Our private key.
95 */
96static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
97
98/**
99 * Local peer own ID.
100 */
101struct GNUNET_PeerIdentity my_full_id;
102
103/**
104 * The file with hello in old style which we like to replace with the new one.
105 */
106static char *hello_file;
107
108/**
109 * Head of DLL of all loaded plugins.
110 */
111static struct TransportPlugin *plugins_head;
112
113/**
114 * Head of DLL of all loaded plugins.
115 */
116static struct TransportPlugin *plugins_tail;
117
118static void
119plugins_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
120{
121 struct TransportPlugin *plug;
122 struct TransportPlugin *next;
123 char *libname;
124 char *plugs;
125 char *pos;
126
127 if (NULL != plugins_head)
128 return; /* already loaded */
129 if (GNUNET_OK !=
130 GNUNET_CONFIGURATION_get_value_string (cfg, "TRANSPORT", "PLUGINS",
131 &plugs))
132 return;
133 fprintf (stdout,"Starting transport plugins `%s'\n",
134 plugs);
135 for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
136 {
137 fprintf (stdout,"Loading `%s' transport plugin\n",
138 pos);
139 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", pos);
140 plug = GNUNET_new (struct TransportPlugin);
141 plug->short_name = GNUNET_strdup (pos);
142 plug->lib_name = libname;
143 plug->env.cfg = cfg;
144 plug->env.cls = plug->short_name;
145 GNUNET_CONTAINER_DLL_insert (plugins_head, plugins_tail, plug);
146 }
147 GNUNET_free (plugs);
148 next = plugins_head;
149 while (next != NULL)
150 {
151 plug = next;
152 next = plug->next;
153 plug->api = GNUNET_PLUGIN_load (plug->lib_name, &plug->env);
154 if (plug->api == NULL)
155 {
156 fprintf (stdout,"Failed to load transport plugin for `%s'\n",
157 plug->lib_name);
158 GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
159 GNUNET_free (plug->short_name);
160 GNUNET_free (plug->lib_name);
161 GNUNET_free (plug);
162 }
163 }
164}
165
166
167static int
168add_to_builder (void *cls,
169 const struct GNUNET_HELLO_Address *address,
170 struct GNUNET_TIME_Absolute expiration)
171{
172 struct GNUNET_HELLO_Builder *builder= cls;
173 struct TransportPlugin *pos = plugins_head;
174 const char *addr;
175 char *uri;
176
177 while (NULL != pos)
178 {
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "short_name: %s transport_name: %s\n",
181 pos->short_name,
182 address->transport_name);
183 if (0 == strcmp (address->transport_name, pos->short_name))
184 {
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 "short_name: %s transport_name: %s are the same\n",
187 pos->short_name,
188 address->transport_name);
189 addr = strchr (strchr (pos->api->address_to_string (pos, address, address->address_length), '.')+1, '.') + 1;
190 }
191 pos = pos->next;
192 }
193
194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195 "Hello address string: %s\n",
196 addr);
197 GNUNET_asprintf (&uri, "%s://%s", address->transport_name, addr);
198 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199 "Hello address uri string: %s\n",
200 uri);
201 GNUNET_HELLO_builder_add_address (builder,
202 uri);
203}
204
205
206/**
207 * Add the given address with infinite expiration to the buffer.
208 *
209 * @param cls closure
210 * @param address address to add
211 * @param expiration old expiration
212 * @return #GNUNET_OK keep iterating
213 */
214static int
215add_to_buf (void *cls,
216 const struct GNUNET_HELLO_Address *address,
217 struct GNUNET_TIME_Absolute expiration)
218{
219 struct AddContext *ac = cls;
220 size_t ret;
221
222 ret = GNUNET_HELLO_add_address (address,
223 GNUNET_TIME_UNIT_FOREVER_ABS,
224 ac->buf,
225 ac->max);
226
227 ac->buf += ret;
228 ac->max -= ret;
229 ac->ret += ret;
230 address_count++;
231 return GNUNET_OK;
232}
233
234
235/**
236 * Add addresses from the address list to the HELLO.
237 *
238 * @param cls the HELLO with the addresses to add
239 * @param max maximum space available
240 * @param buf where to add the addresses
241 * @return number of bytes added, 0 to terminate
242 */
243static ssize_t
244add_from_hello (void *cls, size_t max, void *buf)
245{
246 struct GNUNET_HELLO_Message **orig = cls;
247 struct AddContext ac;
248
249 if (NULL == *orig)
250 return GNUNET_SYSERR; /* already done */
251 ac.buf = buf;
252 ac.max = max;
253 ac.ret = 0;
254 GNUNET_assert (
255 NULL ==
256 GNUNET_HELLO_iterate_addresses (*orig, GNUNET_NO, &add_to_buf, &ac));
257 *orig = NULL;
258 return ac.ret;
259}
260
261
262/**
263 * Main function that will be run without the scheduler.
264 *
265 * @param cls closure
266 * @param args remaining command-line arguments
267 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
268 * @param c configuration
269 */
270static void
271run (void *cls,
272 char *const *args,
273 const char *cfgfile,
274 const struct GNUNET_CONFIGURATION_Handle *c)
275{
276 struct GNUNET_DISK_FileHandle *fh;
277 struct GNUNET_HELLO_Message *orig;
278 struct GNUNET_HELLO_Message *result;
279 struct GNUNET_PeerIdentity pid;
280 uint64_t fsize;
281 ssize_t size_written;
282 struct GNUNET_HELLO_Builder *builder;
283 char *url;
284 const struct GNUNET_MessageHeader *msg;
285 struct GNUNET_MQ_Envelope *env;
286
287 plugins_load (c);
288 address_count = 0;
289
290 my_private_key =
291 GNUNET_CRYPTO_eddsa_key_create_from_configuration (c);
292 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
293 &my_full_id.public_key);
294 fprintf (stdout,"We are peer %s\n", GNUNET_i2s (&my_full_id));
295
296 GNUNET_log_setup ("gnunet-hello", "DEBUG", NULL);
297
298 if (GNUNET_OK !=
299 GNUNET_DISK_file_size (hello_file, &fsize, GNUNET_YES, GNUNET_YES))
300 {
301 fprintf (stderr,
302 _ ("Error accessing file `%s': %s\n"),
303 hello_file,
304 strerror (errno));
305 return;
306 }
307 if (fsize > 65536)
308 {
309 fprintf (stderr, _ ("File `%s' is too big to be a HELLO\n"), hello_file);
310 return;
311 }
312 if (fsize < sizeof(struct GNUNET_MessageHeader))
313 {
314 fprintf (stderr, _ ("File `%s' is too small to be a HELLO\n"), hello_file);
315 return;
316 }
317 fh = GNUNET_DISK_file_open (hello_file,
318 GNUNET_DISK_OPEN_READ,
319 GNUNET_DISK_PERM_USER_READ);
320 if (NULL == fh)
321 {
322 fprintf (stderr,
323 _ ("Error opening file `%s': %s\n"),
324 hello_file,
325 strerror (errno));
326 return;
327 }
328 {
329 char buf[fsize] GNUNET_ALIGN;
330
331 GNUNET_assert (fsize == GNUNET_DISK_file_read (fh, buf, fsize));
332 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
333 orig = (struct GNUNET_HELLO_Message *) buf;
334 if ((fsize < GNUNET_HELLO_size (orig)) ||
335 (GNUNET_OK != GNUNET_HELLO_get_id (orig, &pid)))
336 {
337 fprintf (stderr,
338 _ ("Did not find well-formed HELLO in file `%s'\n"),
339 hello_file);
340 return;
341 }
342 {
343 char *pids;
344
345 pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&my_full_id.public_key);
346 fprintf (stdout, "Processing HELLO for peer `%s'\n", pids);
347 GNUNET_free (pids);
348 }
349 /* result = GNUNET_HELLO_create (&pid.public_key, */
350 /* &add_from_hello, */
351 /* &orig, */
352 /* GNUNET_HELLO_is_friend_only (orig)); */
353
354 builder = GNUNET_HELLO_builder_new (&my_full_id);
355 GNUNET_assert (
356 NULL ==
357 GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) orig, GNUNET_NO, &add_to_builder, builder));
358 url = GNUNET_HELLO_builder_to_url (builder, my_private_key);
359 fprintf (stdout,"url: %s\n", url);
360 env = GNUNET_HELLO_builder_to_env (builder,
361 my_private_key,
362 GNUNET_TIME_UNIT_FOREVER_REL);
363 msg = GNUNET_MQ_env_get_msg (env);
364 //GNUNET_assert (NULL != result);
365 GNUNET_assert (NULL != msg);
366 fh =
367 GNUNET_DISK_file_open (hello_file,
368 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE,
369 GNUNET_DISK_PERM_USER_READ
370 | GNUNET_DISK_PERM_USER_WRITE);
371 if (NULL == fh)
372 {
373 fprintf (stderr,
374 _ ("Error opening file `%s': %s\n"),
375 hello_file,
376 strerror (errno));
377 GNUNET_free (result);
378 return;
379 }
380 //fsize = GNUNET_HELLO_size (result);
381 size_written = GNUNET_DISK_file_write (fh, msg, ntohs (msg->size));
382 if (ntohs (msg->size) != size_written)
383 {
384 fprintf (stderr,
385 _ ("Error writing HELLO to file `%s': %s expected size %u size written %u\n"),
386 hello_file,
387 strerror (errno));
388 (void) GNUNET_DISK_file_close (fh);
389 return;
390 }
391 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
392 }
393 fprintf (stderr,
394 _ ("Modified %u addresses, wrote %u bytes\n"),
395 address_count,
396 (unsigned int) ntohs (msg->size));
397 GNUNET_HELLO_builder_free (builder);
398}
399
400
401int
402main (int argc, char *argv[])
403{
404 struct GNUNET_GETOPT_CommandLineOption options[] =
405 { GNUNET_GETOPT_option_string ('h',
406 "hello-file",
407 "HELLO_FILE",
408 gettext_noop ("Hello file to read"),
409 &hello_file),
410 GNUNET_GETOPT_OPTION_END };
411 int ret;
412
413 ret = (GNUNET_OK ==
414 GNUNET_PROGRAM_run2 (argc,
415 argv,
416 "gnunet-peerinfo",
417 gettext_noop ("Print information about peers."),
418 options,
419 &run,
420 NULL,
421 GNUNET_YES));
422 return ret;
423}
424
425
426/* end of gnunet-hello.c */
diff --git a/src/lib/hello/hello-ng.c b/src/lib/hello/hello-ng.c
new file mode 100644
index 000000000..9c255b361
--- /dev/null
+++ b/src/lib/hello/hello-ng.c
@@ -0,0 +1,198 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2018 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-ng.c
23 * @brief helper library for handling HELLOs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_signatures.h"
28#include "gnunet_hello_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_util_lib.h"
31
32GNUNET_NETWORK_STRUCT_BEGIN
33/**
34 * Binary block we sign when we sign an address.
35 */
36struct SignedAddress
37{
38 /**
39 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS
40 */
41 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
42
43 /**
44 * When was the address generated.
45 */
46 struct GNUNET_TIME_AbsoluteNBO mono_time;
47
48 /**
49 * Hash of the address.
50 */
51 struct GNUNET_HashCode addr_hash GNUNET_PACKED;
52};
53GNUNET_NETWORK_STRUCT_END
54
55/**
56 * Build address record by signing raw information with private key.
57 *
58 * @param address text address at @a communicator to sign
59 * @param nt network type of @a address
60 * @param mono_time monotonic time at which @a address was valid
61 * @param private_key signing key to use
62 * @param[out] result where to write address record (allocated)
63 * @param[out] result_size set to size of @a result
64 */
65void
66GNUNET_HELLO_sign_address (
67 const char *address,
68 enum GNUNET_NetworkType nt,
69 struct GNUNET_TIME_Absolute mono_time,
70 const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key,
71 void **result,
72 size_t *result_size)
73{
74 struct SignedAddress sa;
75 struct GNUNET_CRYPTO_EddsaSignature sig;
76 char *sig_str;
77
78 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
79 sa.purpose.size = htonl (sizeof(sa));
80 sa.mono_time = GNUNET_TIME_absolute_hton (mono_time);
81 GNUNET_CRYPTO_hash (address, strlen (address), &sa.addr_hash);
82 GNUNET_CRYPTO_eddsa_sign (private_key, &sa, &sig);
83 sig_str = NULL;
84 (void) GNUNET_STRINGS_base64_encode (&sig, sizeof(sig), &sig_str);
85 *result_size =
86 1 + GNUNET_asprintf ((char **) result,
87 "%s;%llu;%u;%s",
88 sig_str,
89 (unsigned long long) mono_time.abs_value_us,
90 (unsigned int) nt,
91 address);
92 GNUNET_free (sig_str);
93}
94
95
96/**
97 * Check signature and extract address record.
98 *
99 * @param raw raw signed address
100 * @param raw_size size of @a raw
101 * @param pid public key to use for signature verification
102 * @param nt[out] set to network type
103 * @param mono_time[out] when was the address generated
104 * @return NULL on error, otherwise the address
105 */
106char *
107GNUNET_HELLO_extract_address (const void *raw,
108 size_t raw_size,
109 const struct GNUNET_PeerIdentity *pid,
110 enum GNUNET_NetworkType *nt,
111 struct GNUNET_TIME_Absolute *mono_time)
112{
113 const struct GNUNET_CRYPTO_EddsaPublicKey *public_key = &pid->public_key;
114 const char *raws = raw;
115 unsigned long long raw_us = 0;
116 unsigned int raw_nt = 0;
117 const char *sc;
118 const char *sc2;
119 const char *sc3;
120 const char *raw_addr;
121 char *data = NULL;
122 struct GNUNET_TIME_Absolute raw_mono_time;
123 struct SignedAddress sa;
124 struct GNUNET_CRYPTO_EddsaSignature *sig;
125
126 if ('\0' != raws[raw_size-1])
127 {
128 GNUNET_break_op (0);
129 return NULL;
130 }
131 if (NULL == (sc = strchr (raws, ';')))
132 {
133 GNUNET_break_op (0);
134 return NULL;
135 }
136 if (NULL == (sc2 = strchr (sc + 1, ';')))
137 {
138 GNUNET_break_op (0);
139 return NULL;
140 }
141 if (NULL == (sc3 = strchr (sc2 + 1, ';')))
142 {
143 GNUNET_break_op (0);
144 return NULL;
145 }
146 if (2 != sscanf (sc + 1, "%llu;%u;%*s", &raw_us, &raw_nt))
147 {
148 GNUNET_break_op (0);
149 return NULL;
150 }
151 raw_addr = sc3 + 1;
152 raw_mono_time.abs_value_us = raw_us;
153 if (sizeof(struct GNUNET_CRYPTO_EddsaSignature) !=
154 GNUNET_STRINGS_base64_decode (raws, sc - raws, (void **) &data))
155 {
156 GNUNET_break_op (0);
157 GNUNET_free (data);
158 return NULL;
159 }
160 sig = (struct GNUNET_CRYPTO_EddsaSignature*) data;
161 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
162 sa.purpose.size = htonl (sizeof(sa));
163 sa.mono_time = GNUNET_TIME_absolute_hton (raw_mono_time);
164 GNUNET_CRYPTO_hash (raw_addr, strlen (raw_addr), &sa.addr_hash);
165 if (GNUNET_YES !=
166 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS,
167 &sa,
168 sig,
169 public_key))
170 {
171 GNUNET_free (data);
172 GNUNET_break_op (0);
173 return NULL;
174 }
175 GNUNET_free (data);
176 *mono_time = raw_mono_time;
177 *nt = raw_nt;
178 return GNUNET_strdup (raw_addr);
179}
180
181
182/**
183 * Given an address as a string, extract the prefix that identifies
184 * the communicator offering transmissions to that address.
185 *
186 * @param address a peer's address
187 * @return NULL if the address is mal-formed, otherwise the prefix
188 */
189char *
190GNUNET_HELLO_address_to_prefix (const char *address)
191{
192 const char *dash;
193
194 dash = strchr (address, '-');
195 if (NULL == dash)
196 return NULL;
197 return GNUNET_strndup (address, dash - address);
198}
diff --git a/src/lib/hello/hello-uri.c b/src/lib/hello/hello-uri.c
new file mode 100644
index 000000000..49acaf7a9
--- /dev/null
+++ b/src/lib/hello/hello-uri.c
@@ -0,0 +1,1045 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 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-uri.c
23 * @brief helper library for handling URI-based HELLOs
24 * @author Christian Grothoff
25 *
26 * Note:
27 * - Current API does not support deserializing HELLO of
28 * another peer and then serializing it into another
29 * format (we always require the private key).
30 * Not sure if we need this, but if we do, we need
31 * to extend the builder and the API.
32 * - Current API does not allow overriding the default
33 * HELLO expiration time. We may want to add a function
34 * that does this to create bootstrap HELLOs shipped with
35 * the TGZ.
36 */
37#include "platform.h"
38#include "gnunet_signatures.h"
39#include "gnunet_hello_uri_lib.h"
40#include "gnunet_protocols.h"
41#include "gnunet_util_lib.h"
42
43
44GNUNET_NETWORK_STRUCT_BEGIN
45
46/**
47 * Binary block we sign when we sign an address.
48 */
49struct SignedAddress
50{
51 /**
52 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS
53 */
54 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
55
56 /**
57 * When was the address generated.
58 */
59 struct GNUNET_TIME_AbsoluteNBO mono_time;
60
61 /**
62 * Hash of the address.
63 */
64 struct GNUNET_HashCode addr_hash GNUNET_PACKED;
65};
66
67/**
68 * Message signed as part of a HELLO block/URL.
69 */
70struct HelloSignaturePurpose
71{
72 /**
73 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_HELLO
74 */
75 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
76
77 /**
78 * When does the signature expire?
79 */
80 struct GNUNET_TIME_AbsoluteNBO expiration_time;
81
82 /**
83 * Hash over all addresses.
84 */
85 struct GNUNET_HashCode h_addrs;
86
87};
88
89/**
90 * Message used when gossiping HELLOs between peers.
91 */
92struct HelloUriMessage
93{
94 /**
95 * Type must be #GNUNET_MESSAGE_TYPE_HELLO_URI
96 */
97 struct GNUNET_MessageHeader header;
98
99 /**
100 * Reserved. 0.
101 */
102 uint16_t reserved GNUNET_PACKED;
103
104 /**
105 * Number of URLs encoded after the end of the struct, in NBO.
106 */
107 uint16_t url_counter GNUNET_PACKED;
108
109 /* followed by a 'block' */
110};
111
112
113/**
114 * Start of a 'block'.
115 */
116struct BlockHeader
117{
118 /**
119 * Public key of the peer.
120 */
121 struct GNUNET_PeerIdentity pid;
122
123 /**
124 * Signature over the block, of purpose #GNUNET_SIGNATURE_PURPOSE_HELLO.
125 */
126 struct GNUNET_CRYPTO_EddsaSignature sig;
127
128 /**
129 * When does the HELLO expire?
130 */
131 struct GNUNET_TIME_AbsoluteNBO expiration_time;
132
133};
134
135
136/**
137 * Message used when a DHT provides its HELLO to direct
138 * neighbours.
139 */
140struct DhtHelloMessage
141{
142 /**
143 * Type must be #GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO
144 */
145 struct GNUNET_MessageHeader header;
146
147 /**
148 * Reserved. 0.
149 */
150 uint16_t reserved GNUNET_PACKED;
151
152 /**
153 * Number of URLs encoded after the end of the struct, in NBO.
154 */
155 uint16_t url_counter GNUNET_PACKED;
156
157 /**
158 * Signature over the block, of purpose #GNUNET_SIGNATURE_PURPOSE_HELLO.
159 */
160 struct GNUNET_CRYPTO_EddsaSignature sig;
161
162 /**
163 * When does the HELLO expire?
164 */
165 struct GNUNET_TIME_AbsoluteNBO expiration_time;
166
167 /* followed by the serialized addresses of the 'block' */
168};
169
170
171GNUNET_NETWORK_STRUCT_END
172
173
174/**
175 * Address of a peer.
176 */
177struct Address
178{
179 /**
180 * Kept in a DLL.
181 */
182 struct Address *next;
183
184 /**
185 * Kept in a DLL.
186 */
187 struct Address *prev;
188
189 /**
190 * Actual URI, allocated at the end of this struct.
191 */
192 const char *uri;
193
194 /**
195 * Length of @a uri including 0-terminator.
196 */
197 size_t uri_len;
198};
199
200
201/**
202 * Context for building (or parsing) HELLO URIs.
203 */
204struct GNUNET_HELLO_Builder
205{
206 /**
207 * Public key of the peer.
208 */
209 struct GNUNET_PeerIdentity pid;
210
211 /**
212 * Head of the addresses DLL.
213 */
214 struct Address *a_head;
215
216 /**
217 * Tail of the addresses DLL.
218 */
219 struct Address *a_tail;
220
221 /**
222 * Length of the @a a_head DLL.
223 */
224 unsigned int a_length;
225
226};
227
228/**
229 * Struct to wrap data to do the merge of to hello uris.
230 */
231struct AddressUriMergeResult
232{
233 /**
234 * The builder of the hello uri we merge with.
235 */
236 struct GNUNET_HELLO_Builder *builder;
237
238 /**
239 * The actual address to check, if it is allready in the hello uri we merge with.
240 */
241 const char *address_uri;
242
243 /**
244 * Did we found the actual address to check.
245 */
246 unsigned int found;
247
248 /**
249 * Did we found at least one address to merge.
250 */
251 unsigned int merged;
252};
253
254
255/**
256 * Compute @a hash over addresses in @a builder.
257 *
258 * @param builder the builder to hash addresses of
259 * @param[out] hash where to write the hash
260 */
261static void
262hash_addresses (const struct GNUNET_HELLO_Builder *builder,
263 struct GNUNET_HashCode *hash)
264{
265 struct GNUNET_HashContext *hc;
266
267 hc = GNUNET_CRYPTO_hash_context_start ();
268 for (struct Address *a = builder->a_head;
269 NULL != a;
270 a = a->next)
271 {
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273 "Hashing over %.*s\n",
274 (int) a->uri_len,
275 a->uri);
276 GNUNET_CRYPTO_hash_context_read (hc,
277 a->uri,
278 a->uri_len);
279 }
280 GNUNET_CRYPTO_hash_context_finish (hc,
281 hash);
282
283}
284
285
286/**
287 * Create HELLO signature.
288 *
289 * @param builder the builder to use
290 * @param et expiration time to sign
291 * @param priv key to sign with
292 * @param[out] sig where to write the signature
293 */
294static void
295sign_hello (const struct GNUNET_HELLO_Builder *builder,
296 struct GNUNET_TIME_Timestamp et,
297 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
298 struct GNUNET_CRYPTO_EddsaSignature *sig)
299{
300 struct HelloSignaturePurpose hsp = {
301 .purpose.size = htonl (sizeof (hsp)),
302 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_HELLO),
303 .expiration_time = GNUNET_TIME_absolute_hton (et.abs_time)
304 };
305
306 hash_addresses (builder,
307 &hsp.h_addrs);
308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 "Address hash is %s\n",
310 GNUNET_h2s_full (&hsp.h_addrs));
311 GNUNET_CRYPTO_eddsa_sign (priv,
312 &hsp,
313 sig);
314}
315
316
317/**
318 * Verify HELLO signature.
319 *
320 * @param builder the builder to use
321 * @param et expiration time to verify
322 * @param sig signature to verify
323 * @return #GNUNET_OK if everything is ok, #GNUNET_NO if the
324 * HELLO expired, #GNUNET_SYSERR if the signature is wrong
325 */
326static enum GNUNET_GenericReturnValue
327verify_hello (const struct GNUNET_HELLO_Builder *builder,
328 struct GNUNET_TIME_Absolute et,
329 const struct GNUNET_CRYPTO_EddsaSignature *sig)
330{
331 struct HelloSignaturePurpose hsp = {
332 .purpose.size = htonl (sizeof (hsp)),
333 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_HELLO),
334 .expiration_time = GNUNET_TIME_absolute_hton (et)
335 };
336
337 hash_addresses (builder,
338 &hsp.h_addrs);
339 if (GNUNET_OK !=
340 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_HELLO,
341 &hsp,
342 sig,
343 &builder->pid.public_key))
344 {
345 GNUNET_break_op (0);
346 return GNUNET_SYSERR;
347 }
348 if (GNUNET_TIME_absolute_is_past (et))
349 return GNUNET_NO;
350 return GNUNET_OK;
351}
352
353
354struct GNUNET_HELLO_Builder *
355GNUNET_HELLO_builder_new (const struct GNUNET_PeerIdentity *pid)
356{
357 struct GNUNET_HELLO_Builder *builder;
358
359 builder = GNUNET_new (struct GNUNET_HELLO_Builder);
360 builder->pid = *pid;
361 return builder;
362}
363
364
365const struct GNUNET_PeerIdentity *
366GNUNET_HELLO_builder_get_id (const struct GNUNET_HELLO_Builder *builder)
367{
368 return &builder->pid;
369}
370
371
372void
373GNUNET_HELLO_builder_free (struct GNUNET_HELLO_Builder *builder)
374{
375 struct Address *a;
376
377 while (NULL != (a = builder->a_head))
378 {
379 GNUNET_CONTAINER_DLL_remove (builder->a_head,
380 builder->a_tail,
381 a);
382 builder->a_length--;
383 GNUNET_free (a);
384 }
385 GNUNET_assert (0 == builder->a_length);
386 GNUNET_free (builder);
387}
388
389
390struct GNUNET_HELLO_Builder *
391GNUNET_HELLO_builder_from_msg (const struct GNUNET_MessageHeader *msg)
392{
393 const struct HelloUriMessage *h;
394 uint16_t size = ntohs (msg->size);
395
396 if (GNUNET_MESSAGE_TYPE_HELLO_URI != ntohs (msg->type))
397 {
398 GNUNET_break (0);
399 return NULL;
400 }
401 if (sizeof (struct HelloUriMessage) > size)
402 {
403 GNUNET_break_op (0);
404 return NULL;
405 }
406 h = (const struct HelloUriMessage *) msg;
407 size -= sizeof (*h);
408 return GNUNET_HELLO_builder_from_block (&h[1],
409 size);
410}
411
412
413struct GNUNET_HELLO_Builder *
414GNUNET_HELLO_builder_from_block (const void *block,
415 size_t block_size)
416{
417 const struct BlockHeader *bh = block;
418 struct GNUNET_HELLO_Builder *b;
419
420 if (block_size < sizeof (*bh))
421 {
422 GNUNET_break_op (0);
423 return NULL;
424 }
425 b = GNUNET_HELLO_builder_new (&bh->pid);
426 block += sizeof (*bh);
427 block_size -= sizeof (*bh);
428 while (block_size > 0)
429 {
430 const void *end = memchr (block,
431 '\0',
432 block_size);
433
434 if (NULL == end)
435 {
436 GNUNET_break_op (0);
437 GNUNET_HELLO_builder_free (b);
438 return NULL;
439 }
440 if (GNUNET_OK !=
441 GNUNET_HELLO_builder_add_address (b,
442 block))
443 {
444 GNUNET_break_op (0);
445 GNUNET_HELLO_builder_free (b);
446 return NULL;
447 }
448 end++;
449 block_size -= (end - block);
450 block = end;
451 }
452 {
453 enum GNUNET_GenericReturnValue ret;
454
455 ret = verify_hello (b,
456 GNUNET_TIME_absolute_ntoh (bh->expiration_time),
457 &bh->sig);
458 GNUNET_break (GNUNET_SYSERR != ret);
459 if (GNUNET_OK != ret)
460 {
461 GNUNET_HELLO_builder_free (b);
462 return NULL;
463 }
464 }
465 return b;
466}
467
468
469struct GNUNET_TIME_Absolute
470GNUNET_HELLO_builder_get_expiration_time (const struct
471 GNUNET_MessageHeader *msg)
472{
473 if (GNUNET_MESSAGE_TYPE_HELLO_URI == ntohs (msg->type))
474 {
475 const struct HelloUriMessage *h = (struct HelloUriMessage *) msg;
476 const struct BlockHeader *bh = (const struct BlockHeader *) &h[1];
477
478 return GNUNET_TIME_absolute_ntoh (bh->expiration_time);
479 }
480 else if (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO == ntohs (msg->type))
481 {
482 const struct DhtHelloMessage *dht_hello = (struct DhtHelloMessage *) msg;
483
484 return GNUNET_TIME_absolute_ntoh (dht_hello->expiration_time);
485 }
486 else
487 GNUNET_break (0);
488 return GNUNET_TIME_UNIT_ZERO_ABS;
489}
490
491
492struct GNUNET_HELLO_Builder *
493GNUNET_HELLO_builder_from_url (const char *url)
494{
495 const char *q;
496 const char *s1;
497 const char *s2;
498 struct GNUNET_PeerIdentity pid;
499 struct GNUNET_CRYPTO_EddsaSignature sig;
500 struct GNUNET_TIME_Absolute et;
501 size_t len;
502 struct GNUNET_HELLO_Builder *b;
503
504 if (0 != strncasecmp (url,
505 "gnunet://hello/",
506 strlen ("gnunet://hello/")))
507 return NULL;
508 url += strlen ("gnunet://hello/");
509 s1 = strchr (url, '/');
510 if (NULL == s1)
511 {
512 GNUNET_break_op (0);
513 return NULL;
514 }
515 s2 = strchr (s1 + 1, '/');
516 if (NULL == s1)
517 {
518 GNUNET_break_op (0);
519 return NULL;
520 }
521 q = strchr (url, '?');
522 if (NULL == q)
523 q = url + strlen (url);
524 if (GNUNET_OK !=
525 GNUNET_STRINGS_string_to_data (url,
526 s1 - url,
527 &pid,
528 sizeof(pid)))
529 {
530 GNUNET_break_op (0);
531 return NULL;
532 }
533 if (GNUNET_OK !=
534 GNUNET_STRINGS_string_to_data (s1 + 1,
535 s2 - (s1 + 1),
536 &sig,
537 sizeof(sig)))
538 {
539 GNUNET_break_op (0);
540 return NULL;
541 }
542 {
543 unsigned long long sec;
544 char dummy = '?';
545
546 if ( (0 == sscanf (s2 + 1,
547 "%llu%c",
548 &sec,
549 &dummy)) ||
550 ('?' != dummy) )
551 {
552 GNUNET_break_op (0);
553 return NULL;
554 }
555 et = GNUNET_TIME_absolute_from_s (sec);
556 }
557
558 b = GNUNET_HELLO_builder_new (&pid);
559 len = strlen (q);
560 while (len > 0)
561 {
562 const char *eq;
563 const char *amp;
564 char *addr = NULL;
565 char *uri;
566
567 /* skip ?/& separator */
568 len--;
569 q++;
570 eq = strchr (q, '=');
571 if ( (eq == q) ||
572 (NULL == eq) )
573 {
574 GNUNET_break_op (0);
575 GNUNET_HELLO_builder_free (b);
576 return NULL;
577 }
578 amp = strchr (eq, '&');
579 if (NULL == amp)
580 amp = &q[len];
581 GNUNET_STRINGS_urldecode (eq + 1,
582 amp - (eq + 1),
583 &addr);
584 if ( (NULL == addr) ||
585 (0 == strlen (addr)) )
586 {
587 GNUNET_free (addr);
588 GNUNET_break_op (0);
589 GNUNET_HELLO_builder_free (b);
590 return NULL;
591 }
592 GNUNET_asprintf (&uri,
593 "%.*s://%s",
594 (int) (eq - q),
595 q,
596 addr);
597 GNUNET_free (addr);
598 if (GNUNET_OK !=
599 GNUNET_HELLO_builder_add_address (b,
600 uri))
601 {
602 GNUNET_break_op (0);
603 GNUNET_free (uri);
604 GNUNET_HELLO_builder_free (b);
605 return NULL;
606 }
607 GNUNET_free (uri);
608 /* move to next URL */
609 len -= (amp - q);
610 q = amp;
611 }
612
613 {
614 enum GNUNET_GenericReturnValue ret;
615
616 ret = verify_hello (b,
617 et,
618 &sig);
619 GNUNET_break (GNUNET_SYSERR != ret);
620 if (GNUNET_OK != ret)
621 {
622 GNUNET_HELLO_builder_free (b);
623 return NULL;
624 }
625 }
626 return b;
627}
628
629
630struct GNUNET_MQ_Envelope *
631GNUNET_HELLO_builder_to_env (const struct GNUNET_HELLO_Builder *builder,
632 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
633 struct GNUNET_TIME_Relative expiration_time)
634{
635 struct GNUNET_MQ_Envelope *env;
636 struct HelloUriMessage *msg;
637 size_t blen;
638
639 if (builder->a_length > UINT16_MAX)
640 {
641 GNUNET_break (0);
642 return NULL;
643 }
644 blen = 0;
645 GNUNET_assert (GNUNET_NO ==
646 GNUNET_HELLO_builder_to_block (builder,
647 priv,
648 NULL,
649 &blen,
650 expiration_time));
651 env = GNUNET_MQ_msg_extra (msg,
652 blen,
653 GNUNET_MESSAGE_TYPE_HELLO_URI);
654 msg->url_counter = htons ((uint16_t) builder->a_length);
655 GNUNET_assert (GNUNET_OK ==
656 GNUNET_HELLO_builder_to_block (builder,
657 priv,
658 &msg[1],
659 &blen,
660 expiration_time));
661 return env;
662}
663
664
665struct GNUNET_MessageHeader *
666GNUNET_HELLO_builder_to_dht_hello_msg (
667 const struct GNUNET_HELLO_Builder *builder,
668 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
669 struct GNUNET_TIME_Relative expiration_time)
670{
671 struct DhtHelloMessage *msg;
672 size_t blen;
673
674 if (builder->a_length > UINT16_MAX)
675 {
676 GNUNET_break (0);
677 return NULL;
678 }
679 blen = 0;
680 GNUNET_assert (GNUNET_NO ==
681 GNUNET_HELLO_builder_to_block (builder,
682 priv,
683 NULL,
684 &blen,
685 expiration_time));
686 GNUNET_assert (blen < UINT16_MAX);
687 GNUNET_assert (blen >= sizeof (struct BlockHeader));
688 {
689 char buf[blen] GNUNET_ALIGN;
690 const struct BlockHeader *block = (const struct BlockHeader *) buf;
691
692 GNUNET_assert (GNUNET_OK ==
693 GNUNET_HELLO_builder_to_block (builder,
694 priv,
695 buf,
696 &blen,
697 expiration_time));
698 msg = GNUNET_malloc (sizeof (*msg)
699 + blen
700 - sizeof (*block));
701 msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO);
702 msg->header.size = htons (sizeof (*msg)
703 + blen
704 - sizeof (*block));
705 memcpy (&msg[1],
706 &block[1],
707 blen - sizeof (*block));
708 msg->sig = block->sig;
709 msg->expiration_time = block->expiration_time;
710 }
711 msg->url_counter = htons ((uint16_t) builder->a_length);
712 return &msg->header;
713}
714
715
716char *
717GNUNET_HELLO_builder_to_url (const struct GNUNET_HELLO_Builder *builder,
718 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
719{
720 struct GNUNET_CRYPTO_EddsaSignature sig;
721 struct GNUNET_TIME_Timestamp et;
722 char *result;
723 char *pids;
724 char *sigs;
725 const char *sep = "?";
726
727 et = GNUNET_TIME_relative_to_timestamp (GNUNET_HELLO_ADDRESS_EXPIRATION);
728 sign_hello (builder,
729 et,
730 priv,
731 &sig);
732 pids = GNUNET_STRINGS_data_to_string_alloc (&builder->pid,
733 sizeof (builder->pid));
734 sigs = GNUNET_STRINGS_data_to_string_alloc (&sig,
735 sizeof (sig));
736 GNUNET_asprintf (&result,
737 "gnunet://hello/%s/%s/%llu",
738 pids,
739 sigs,
740 (unsigned long long) GNUNET_TIME_timestamp_to_s (et));
741 GNUNET_free (sigs);
742 GNUNET_free (pids);
743 for (struct Address *a = builder->a_head;
744 NULL != a;
745 a = a->next)
746 {
747 char *ue;
748 char *tmp;
749 int pfx_len;
750 const char *eou;
751
752 eou = strstr (a->uri,
753 "://");
754 if (NULL == eou)
755 {
756 GNUNET_break (0);
757 GNUNET_free (result);
758 return NULL;
759 }
760 pfx_len = eou - a->uri;
761 eou += 3;
762 GNUNET_STRINGS_urlencode (a->uri_len - 4 - pfx_len,
763 eou,
764 &ue);
765 GNUNET_asprintf (&tmp,
766 "%s%s%.*s=%s",
767 result,
768 sep,
769 pfx_len,
770 a->uri,
771 ue);
772 GNUNET_free (ue);
773 GNUNET_free (result);
774 result = tmp;
775 sep = "&";
776 }
777 return result;
778}
779
780
781enum GNUNET_GenericReturnValue
782GNUNET_HELLO_builder_to_block (const struct GNUNET_HELLO_Builder *builder,
783 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
784 void *block,
785 size_t *block_size,
786 struct GNUNET_TIME_Relative expiration_time)
787{
788 struct BlockHeader bh;
789 size_t needed = sizeof (bh);
790 char *pos;
791 struct GNUNET_TIME_Timestamp et;
792
793 for (struct Address *a = builder->a_head;
794 NULL != a;
795 a = a->next)
796 {
797 GNUNET_assert (needed + a->uri_len > needed);
798 needed += a->uri_len;
799 }
800 if ( (NULL == block) ||
801 (needed < *block_size) )
802 {
803 *block_size = needed;
804 return GNUNET_NO;
805 }
806 bh.pid = builder->pid;
807 if (GNUNET_TIME_UNIT_ZERO.rel_value_us == expiration_time.rel_value_us)
808 et = GNUNET_TIME_relative_to_timestamp (GNUNET_HELLO_ADDRESS_EXPIRATION);
809 else
810 et = GNUNET_TIME_relative_to_timestamp (expiration_time);
811 bh.expiration_time = GNUNET_TIME_absolute_hton (et.abs_time);
812 sign_hello (builder,
813 et,
814 priv,
815 &bh.sig);
816 memcpy (block,
817 &bh,
818 sizeof (bh));
819 pos = block + sizeof (bh);
820 for (struct Address *a = builder->a_head;
821 NULL != a;
822 a = a->next)
823 {
824 memcpy (pos,
825 a->uri,
826 a->uri_len);
827 pos += a->uri_len;
828 }
829 *block_size = needed;
830 return GNUNET_OK;
831}
832
833
834enum GNUNET_GenericReturnValue
835GNUNET_HELLO_builder_add_address (struct GNUNET_HELLO_Builder *builder,
836 const char *address)
837{
838 size_t alen = strlen (address) + 1;
839 struct Address *a;
840 const char *e;
841
842 if (NULL == (e = strstr (address,
843 "://")))
844 {
845 GNUNET_break_op (0);
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "Invalid address `%s'\n",
848 address);
849 return GNUNET_SYSERR;
850 }
851 if (e == address)
852 {
853 GNUNET_break_op (0);
854 return GNUNET_SYSERR;
855 }
856 for (const char *p = address; p != e; p++)
857 if ( (! isalpha ((unsigned char) *p)) &&
858 ('+' != *p) )
859 {
860 GNUNET_break_op (0);
861 return GNUNET_SYSERR;
862 }
863 /* check for duplicates */
864 for (a = builder->a_head;
865 NULL != a;
866 a = a->next)
867 if (0 == strcmp (address,
868 a->uri))
869 return GNUNET_NO;
870 a = GNUNET_malloc (sizeof (struct Address) + alen);
871 a->uri_len = alen;
872 memcpy (&a[1],
873 address,
874 alen);
875 a->uri = (const char *) &a[1];
876 GNUNET_CONTAINER_DLL_insert_tail (builder->a_head,
877 builder->a_tail,
878 a);
879 builder->a_length++;
880 return GNUNET_OK;
881}
882
883
884enum GNUNET_GenericReturnValue
885GNUNET_HELLO_builder_del_address (struct GNUNET_HELLO_Builder *builder,
886 const char *address)
887{
888 struct Address *a;
889
890 /* check for duplicates */
891 for (a = builder->a_head;
892 NULL != a;
893 a = a->next)
894 if (0 == strcmp (address,
895 a->uri))
896 break;
897 if (NULL == a)
898 return GNUNET_NO;
899 GNUNET_CONTAINER_DLL_remove (builder->a_head,
900 builder->a_tail,
901 a);
902 builder->a_length--;
903 GNUNET_free (a);
904 return GNUNET_OK;
905}
906
907
908const struct GNUNET_PeerIdentity*
909GNUNET_HELLO_builder_iterate (const struct GNUNET_HELLO_Builder *builder,
910 GNUNET_HELLO_UriCallback uc,
911 void *uc_cls)
912{
913 struct Address *nxt;
914
915 if (NULL == uc)
916 return &builder->pid;
917 for (struct Address *a = builder->a_head;
918 NULL != a;
919 a = nxt)
920 {
921 nxt = a->next;
922 uc (uc_cls,
923 &builder->pid,
924 a->uri);
925 }
926 return &builder->pid;
927}
928
929
930enum GNUNET_GenericReturnValue
931GNUNET_HELLO_dht_msg_to_block (const struct GNUNET_MessageHeader *hello,
932 const struct GNUNET_PeerIdentity *pid,
933 void **block,
934 size_t *block_size,
935 struct GNUNET_TIME_Absolute *block_expiration)
936{
937 const struct DhtHelloMessage *msg
938 = (const struct DhtHelloMessage *) hello;
939 uint16_t len = ntohs (hello->size);
940 struct BlockHeader *bh;
941 struct GNUNET_HELLO_Builder *b;
942 enum GNUNET_GenericReturnValue ret;
943
944 if (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO != ntohs (hello->type))
945 {
946 GNUNET_break (0);
947 return GNUNET_SYSERR;
948 }
949 if (len < sizeof (*msg))
950 {
951 GNUNET_break_op (0);
952 return GNUNET_SYSERR;
953 }
954 len -= sizeof (*msg);
955 *block_size = len + sizeof (*bh);
956 *block = GNUNET_malloc (*block_size);
957 bh = *block;
958 bh->pid = *pid;
959 bh->sig = msg->sig;
960 bh->expiration_time = msg->expiration_time;
961 *block_expiration = GNUNET_TIME_absolute_ntoh (msg->expiration_time);
962 memcpy (&bh[1],
963 &msg[1],
964 len);
965 b = GNUNET_HELLO_builder_from_block (*block,
966 *block_size);
967 if (NULL == b)
968 {
969 GNUNET_break_op (0);
970 GNUNET_free (*block);
971 *block_size = 0;
972 return GNUNET_SYSERR;
973 }
974 ret = verify_hello (b,
975 *block_expiration,
976 &msg->sig);
977 GNUNET_HELLO_builder_free (b);
978 if (GNUNET_SYSERR == ret)
979 {
980 GNUNET_free (*block);
981 *block_size = 0;
982 return GNUNET_SYSERR;
983 }
984 return ret;
985}
986
987
988/**
989 * Given an address as a string, extract the prefix that identifies
990 * the communicator offering transmissions to that address.
991 *
992 * @param address a peer's address
993 * @return NULL if the address is mal-formed, otherwise the prefix
994 */
995char *
996GNUNET_HELLO_address_to_prefix (const char *address)
997{
998 const char *dash;
999
1000 dash = strchr (address, '-');
1001 if (NULL == dash)
1002 return NULL;
1003 return GNUNET_strndup (address, dash - address);
1004}
1005
1006
1007/**
1008 * Build address record by signing raw information with private key.
1009 *
1010 * @param address text address at @a communicator to sign
1011 * @param nt network type of @a address
1012 * @param mono_time monotonic time at which @a address was valid
1013 * @param private_key signing key to use
1014 * @param[out] result where to write address record (allocated)
1015 * @param[out] result_size set to size of @a result
1016 */
1017void
1018GNUNET_HELLO_sign_address (
1019 const char *address,
1020 enum GNUNET_NetworkType nt,
1021 struct GNUNET_TIME_Absolute mono_time,
1022 const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key,
1023 void **result,
1024 size_t *result_size)
1025{
1026 struct SignedAddress sa;
1027 struct GNUNET_CRYPTO_EddsaSignature sig;
1028 char *sig_str;
1029
1030 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
1031 sa.purpose.size = htonl (sizeof(sa));
1032 sa.mono_time = GNUNET_TIME_absolute_hton (mono_time);
1033 GNUNET_CRYPTO_hash (address, strlen (address), &sa.addr_hash);
1034 GNUNET_CRYPTO_eddsa_sign (private_key, &sa, &sig);
1035 sig_str = NULL;
1036 (void) GNUNET_STRINGS_base64_encode (&sig, sizeof(sig), &sig_str);
1037 *result_size =
1038 1 + GNUNET_asprintf ((char **) result,
1039 "%s;%llu;%u;%s",
1040 sig_str,
1041 (unsigned long long) mono_time.abs_value_us,
1042 (unsigned int) nt,
1043 address);
1044 GNUNET_free (sig_str);
1045}
diff --git a/src/lib/hello/meson.build b/src/lib/hello/meson.build
new file mode 100644
index 000000000..caf70e4a7
--- /dev/null
+++ b/src/lib/hello/meson.build
@@ -0,0 +1,27 @@
1libgnunethello_src = ['hello-uri.c']
2
3libgnunethello = library('gnunethello',
4 libgnunethello_src,
5 soversion: '0',
6 version: '0.1.0',
7 dependencies: libgnunetutil_dep,
8 include_directories: [incdir, configuration_inc],
9 install: true,
10 install_dir: get_option('libdir'))
11libgnunethello_dep = declare_dependency(link_with : libgnunethello)
12pkg.generate(libgnunethello, url: 'https://www.gnunet.org',
13 description : 'Helper library for handling GNUnet HELLO messages')
14
15
16test_hello_uri = executable ('test_hello_uri',
17 ['test_hello-uri.c'],
18 dependencies: [libgnunethello_dep,
19 libgnunetutil_dep,
20 gcrypt_dep],
21 include_directories: [incdir, configuration_inc],
22 build_by_default: false,
23 install: false)
24
25test('test_hello_uri', test_hello_uri,
26 workdir: meson.current_build_dir(),
27 suite: ['hello'])
diff --git a/src/lib/hello/test_hello-uri.c b/src/lib/hello/test_hello-uri.c
new file mode 100644
index 000000000..9f3179270
--- /dev/null
+++ b/src/lib/hello/test_hello-uri.c
@@ -0,0 +1,215 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2022 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 * @file hello/test_hello-uri.c
22 * @brief test for helper library for handling URI-based HELLOs
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_signatures.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_hello_uri_lib.h"
29
30
31/**
32 * Check for expected URIs.
33 *
34 * @param cls a `unsigned int*`, bitmask set to found URIs
35 * @param uri URI to check for
36 */
37static void
38check_uris (void *cls,
39 const struct GNUNET_PeerIdentity *pid,
40 const char *uri)
41{
42 unsigned int *found = cls;
43
44 if (0 == strcmp (uri,
45 "test://address"))
46 *found |= 1;
47 else if (0 == strcmp (uri,
48 "test://more"))
49 *found |= 2;
50 else
51 *found = (unsigned int) -1;
52}
53
54
55int
56main (int argc,
57 char *argv[])
58{
59 struct GNUNET_PeerIdentity pid;
60 struct GNUNET_HELLO_Builder *b;
61 struct GNUNET_CRYPTO_EddsaPrivateKey priv;
62
63 GNUNET_log_setup ("test-hell-uri",
64 "WARNING",
65 NULL);
66 GNUNET_CRYPTO_eddsa_key_create (&priv);
67 GNUNET_CRYPTO_eddsa_key_get_public (&priv,
68 &pid.public_key);
69 b = GNUNET_HELLO_builder_new (&pid);
70 GNUNET_assert (GNUNET_SYSERR ==
71 GNUNET_HELLO_builder_add_address (b,
72 "invalid"));
73 GNUNET_assert (GNUNET_SYSERR ==
74 GNUNET_HELLO_builder_add_address (b,
75 "i%v://bla"));
76 GNUNET_assert (GNUNET_SYSERR ==
77 GNUNET_HELLO_builder_add_address (b,
78 "://empty"));
79 GNUNET_assert (GNUNET_OK ==
80 GNUNET_HELLO_builder_add_address (b,
81 "test://address"));
82 GNUNET_assert (GNUNET_NO ==
83 GNUNET_HELLO_builder_add_address (b,
84 "test://address"));
85 GNUNET_assert (GNUNET_OK ==
86 GNUNET_HELLO_builder_add_address (b,
87 "test://more"));
88 {
89 void *block;
90 size_t block_size = 0;
91 struct GNUNET_HELLO_Builder *b2;
92 const struct GNUNET_PeerIdentity *p2;
93 unsigned int found;
94
95 GNUNET_assert (GNUNET_NO ==
96 GNUNET_HELLO_builder_to_block (b,
97 &priv,
98 NULL,
99 &block_size,
100 GNUNET_TIME_UNIT_FOREVER_REL));
101 GNUNET_assert (GNUNET_NO ==
102 GNUNET_HELLO_builder_to_block (b,
103 &priv,
104 NULL,
105 &block_size,
106 GNUNET_TIME_UNIT_FOREVER_REL));
107 GNUNET_assert (0 != block_size);
108 block = GNUNET_malloc (block_size);
109 GNUNET_assert (GNUNET_OK ==
110 GNUNET_HELLO_builder_to_block (b,
111 &priv,
112 block,
113 &block_size,
114 GNUNET_TIME_UNIT_FOREVER_REL));
115 b2 = GNUNET_HELLO_builder_from_block (block,
116 block_size);
117 GNUNET_free (block);
118 GNUNET_assert (NULL != b2);
119 found = 0;
120 p2 = GNUNET_HELLO_builder_iterate (b2,
121 &check_uris,
122 &found);
123 GNUNET_assert (3 == found);
124 GNUNET_assert (0 ==
125 GNUNET_memcmp (p2,
126 &pid));
127 GNUNET_HELLO_builder_free (b2);
128 }
129
130 {
131 char *url;
132 struct GNUNET_HELLO_Builder *b2;
133 const struct GNUNET_PeerIdentity *p2;
134 unsigned int found;
135
136 url = GNUNET_HELLO_builder_to_url (b,
137 &priv);
138 b2 = GNUNET_HELLO_builder_from_url (url);
139 GNUNET_free (url);
140 GNUNET_assert (NULL != b2);
141 found = 0;
142 p2 = GNUNET_HELLO_builder_iterate (b2,
143 &check_uris,
144 &found);
145 GNUNET_assert (3 == found);
146 GNUNET_assert (0 ==
147 GNUNET_memcmp (p2,
148 &pid));
149 GNUNET_HELLO_builder_free (b2);
150 }
151
152 {
153 struct GNUNET_MQ_Envelope *env;
154 struct GNUNET_HELLO_Builder *b2;
155 const struct GNUNET_PeerIdentity *p2;
156 unsigned int found;
157
158 env = GNUNET_HELLO_builder_to_env (b,
159 &priv,
160 GNUNET_TIME_UNIT_FOREVER_REL);
161 b2 = GNUNET_HELLO_builder_from_msg (GNUNET_MQ_env_get_msg (env));
162 GNUNET_free (env);
163 GNUNET_assert (NULL != b2);
164 found = 0;
165 p2 = GNUNET_HELLO_builder_iterate (b2,
166 &check_uris,
167 &found);
168 GNUNET_assert (3 == found);
169 GNUNET_assert (0 ==
170 GNUNET_memcmp (p2,
171 &pid));
172 GNUNET_HELLO_builder_free (b2);
173 }
174
175 GNUNET_HELLO_builder_free (b);
176
177 GNUNET_CRYPTO_mpi_print_unsigned (priv.d,
178 sizeof (priv.d),
179 GCRYMPI_CONST_ONE);
180 priv.d[0] &= 248;
181 priv.d[31] &= 127;
182 priv.d[31] |= 64;
183 {
184 char *buf;
185
186 buf = GNUNET_STRINGS_data_to_string_alloc (&priv,
187 sizeof (priv));
188 fprintf (stderr,
189 "PK: %s\n",
190 buf);
191 GNUNET_free (buf);
192 }
193 GNUNET_CRYPTO_eddsa_key_get_public (&priv,
194 &pid.public_key);
195 b = GNUNET_HELLO_builder_new (&pid);
196 GNUNET_assert (GNUNET_OK ==
197 GNUNET_HELLO_builder_add_address (b,
198 "a://first"));
199 GNUNET_assert (GNUNET_OK ==
200 GNUNET_HELLO_builder_add_address (b,
201 "b://second"));
202 {
203 char *url;
204
205 url = GNUNET_HELLO_builder_to_url (b,
206 &priv);
207 fprintf (stderr,
208 "TV: %s\n",
209 url);
210 GNUNET_free (url);
211 }
212 GNUNET_HELLO_builder_free (b);
213
214 return 0;
215}