aboutsummaryrefslogtreecommitdiff
path: root/src/lib/gnsrecord/gnsrecord_misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/gnsrecord/gnsrecord_misc.c')
-rw-r--r--src/lib/gnsrecord/gnsrecord_misc.c567
1 files changed, 567 insertions, 0 deletions
diff --git a/src/lib/gnsrecord/gnsrecord_misc.c b/src/lib/gnsrecord/gnsrecord_misc.c
new file mode 100644
index 000000000..888295148
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_misc.c
@@ -0,0 +1,567 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 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 gnsrecord/gnsrecord_misc.c
23 * @brief MISC functions related to GNS records
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_gnsrecord_lib.h"
34
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
37
38char *
39GNUNET_GNSRECORD_string_normalize (const char *src)
40{
41 /*FIXME: We may want to follow RFC5890/RFC5891 */
42 return GNUNET_STRINGS_utf8_normalize (src);
43}
44
45
46enum GNUNET_GenericReturnValue
47GNUNET_GNSRECORD_label_check (const char*label, char **emsg)
48{
49 if (NULL == label)
50 {
51 *emsg = GNUNET_strdup (_ ("Label is NULL which is not allowed\n"));
52 return GNUNET_NO;
53 }
54 if (0 != strchr (label, '.'))
55 {
56 *emsg = GNUNET_strdup (_ ("Label contains `.' which is not allowed\n"));
57 return GNUNET_NO;
58 }
59 return GNUNET_OK;
60}
61
62
63const char *
64GNUNET_GNSRECORD_z2s (const struct GNUNET_CRYPTO_PublicKey *z)
65{
66 static char buf[sizeof(struct GNUNET_CRYPTO_PublicKey) * 8];
67 char *end;
68
69 end = GNUNET_STRINGS_data_to_string ((const unsigned char *) z,
70 sizeof(struct
71 GNUNET_CRYPTO_PublicKey),
72 buf, sizeof(buf));
73 if (NULL == end)
74 {
75 GNUNET_break (0);
76 return NULL;
77 }
78 *end = '\0';
79 return buf;
80}
81
82
83/**
84 * Compares if two records are equal (ignoring flags such
85 * as authority, private and pending, but not relative vs.
86 * absolute expiration time).
87 *
88 * @param a record
89 * @param b record
90 * @return #GNUNET_YES if the records are equal or #GNUNET_NO if they are not
91 */
92enum GNUNET_GenericReturnValue
93GNUNET_GNSRECORD_records_cmp (const struct GNUNET_GNSRECORD_Data *a,
94 const struct GNUNET_GNSRECORD_Data *b)
95{
96 LOG (GNUNET_ERROR_TYPE_DEBUG,
97 "Comparing records\n");
98 if (a->record_type != b->record_type)
99 {
100 LOG (GNUNET_ERROR_TYPE_DEBUG,
101 "Record type %u != %u\n", a->record_type, b->record_type);
102 return GNUNET_NO;
103 }
104 if ((a->expiration_time != b->expiration_time) &&
105 ((a->expiration_time != 0) && (b->expiration_time != 0)))
106 {
107 LOG (GNUNET_ERROR_TYPE_DEBUG,
108 "Expiration time %llu != %llu\n",
109 (unsigned long long) a->expiration_time,
110 (unsigned long long) b->expiration_time);
111 return GNUNET_NO;
112 }
113 if ((a->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS)
114 != (b->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS))
115 {
116 LOG (GNUNET_ERROR_TYPE_DEBUG,
117 "Flags %u (%u) != %u (%u)\n", a->flags,
118 a->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS, b->flags,
119 b->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS);
120 return GNUNET_NO;
121 }
122 if (a->data_size != b->data_size)
123 {
124 LOG (GNUNET_ERROR_TYPE_DEBUG,
125 "Data size %lu != %lu\n",
126 a->data_size,
127 b->data_size);
128 return GNUNET_NO;
129 }
130 if (0 != memcmp (a->data, b->data, a->data_size))
131 {
132 LOG (GNUNET_ERROR_TYPE_DEBUG,
133 "Data contents do not match\n");
134 return GNUNET_NO;
135 }
136 LOG (GNUNET_ERROR_TYPE_DEBUG,
137 "Records are equal\n");
138 return GNUNET_YES;
139}
140
141
142struct GNUNET_TIME_Absolute
143GNUNET_GNSRECORD_record_get_expiration_time (unsigned int rd_count,
144 const struct
145 GNUNET_GNSRECORD_Data *rd,
146 struct GNUNET_TIME_Absolute min)
147{
148 struct GNUNET_TIME_Absolute expire;
149 struct GNUNET_TIME_Absolute at;
150 struct GNUNET_TIME_Relative rt;
151 struct GNUNET_TIME_Absolute at_shadow;
152 struct GNUNET_TIME_Relative rt_shadow;
153
154 if (0 == rd_count)
155 return GNUNET_TIME_absolute_max (GNUNET_TIME_UNIT_ZERO_ABS, min);
156 expire = GNUNET_TIME_UNIT_FOREVER_ABS;
157 for (unsigned int c = 0; c < rd_count; c++)
158 {
159 if (0 != (rd[c].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
160 {
161 rt.rel_value_us = rd[c].expiration_time;
162 at = GNUNET_TIME_relative_to_absolute (rt);
163 }
164 else
165 {
166 at.abs_value_us = rd[c].expiration_time;
167 }
168
169 for (unsigned int c2 = 0; c2 < rd_count; c2++)
170 {
171 /* Check for shadow record */
172 if ((c == c2) ||
173 (rd[c].record_type != rd[c2].record_type) ||
174 (0 == (rd[c2].flags & GNUNET_GNSRECORD_RF_SHADOW)))
175 continue;
176 /* We have a shadow record */
177 if (0 != (rd[c2].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
178 {
179 rt_shadow.rel_value_us = rd[c2].expiration_time;
180 at_shadow = GNUNET_TIME_relative_to_absolute (rt_shadow);
181 }
182 else
183 {
184 at_shadow.abs_value_us = rd[c2].expiration_time;
185 }
186 at = GNUNET_TIME_absolute_max (at,
187 at_shadow);
188 }
189 expire = GNUNET_TIME_absolute_min (at,
190 expire);
191 }
192 expire = GNUNET_TIME_absolute_max (expire, min);
193 LOG (GNUNET_ERROR_TYPE_DEBUG,
194 "Determined expiration time for block with %u records to be %s\n",
195 rd_count,
196 GNUNET_STRINGS_absolute_time_to_string (expire));
197 return expire;
198}
199
200
201/**
202 * Test if a given record is expired.
203 *
204 * @return #GNUNET_YES if the record is expired,
205 * #GNUNET_NO if not
206 */
207int
208GNUNET_GNSRECORD_is_expired (const struct GNUNET_GNSRECORD_Data *rd)
209{
210 struct GNUNET_TIME_Absolute at;
211
212 if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
213 return GNUNET_NO;
214 at.abs_value_us = rd->expiration_time;
215 return (0 == GNUNET_TIME_absolute_get_remaining (at).rel_value_us) ?
216 GNUNET_YES : GNUNET_NO;
217}
218
219
220/**
221 * Convert public key to the respective absolute domain name in the
222 * ".zkey" pTLD.
223 * This is one of the very few calls in the entire API that is
224 * NOT reentrant!
225 *
226 * @param pkey a public key with a point on the eliptic curve
227 * @return string "X.zkey" where X is the public
228 * key in an encoding suitable for DNS labels.
229 */
230const char *
231GNUNET_GNSRECORD_pkey_to_zkey (const struct GNUNET_CRYPTO_PublicKey *pkey)
232{
233 static char ret[128];
234 char *pkeys;
235
236 pkeys = GNUNET_CRYPTO_public_key_to_string (pkey);
237 GNUNET_snprintf (ret,
238 sizeof(ret),
239 "%s",
240 pkeys);
241 GNUNET_free (pkeys);
242 return ret;
243}
244
245
246/**
247 * Convert an absolute domain name to the
248 * respective public key.
249 *
250 * @param zkey string encoding the coordinates of the public
251 * key in an encoding suitable for DNS labels.
252 * @param pkey set to a public key on the eliptic curve
253 * @return #GNUNET_SYSERR if @a zkey has the wrong syntax
254 */
255int
256GNUNET_GNSRECORD_zkey_to_pkey (const char *zkey,
257 struct GNUNET_CRYPTO_PublicKey *pkey)
258{
259 if (GNUNET_OK !=
260 GNUNET_CRYPTO_public_key_from_string (zkey,
261 pkey))
262 return GNUNET_SYSERR;
263 return GNUNET_OK;
264}
265
266
267enum GNUNET_GenericReturnValue
268GNUNET_GNSRECORD_identity_from_data (const char *data,
269 size_t data_size,
270 uint32_t type,
271 struct GNUNET_CRYPTO_PublicKey *key)
272{
273 if (GNUNET_NO == GNUNET_GNSRECORD_is_zonekey_type (type))
274 return GNUNET_SYSERR;
275 switch (type)
276 {
277 case GNUNET_GNSRECORD_TYPE_PKEY:
278 if (data_size > sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
279 return GNUNET_SYSERR;
280 memcpy (&key->ecdsa_key, data, data_size);
281 break;
282 case GNUNET_GNSRECORD_TYPE_EDKEY:
283 if (data_size > sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))
284 return GNUNET_SYSERR;
285 memcpy (&key->eddsa_key, data, data_size);
286 break;
287 default:
288 return GNUNET_NO;
289 }
290 key->type = htonl (type);
291
292 return GNUNET_YES;
293}
294
295
296enum GNUNET_GenericReturnValue
297GNUNET_GNSRECORD_data_from_identity (const struct
298 GNUNET_CRYPTO_PublicKey *key,
299 char **data,
300 size_t *data_size,
301 uint32_t *type)
302{
303 char *tmp;
304 *type = ntohl (key->type);
305 *data_size = GNUNET_CRYPTO_public_key_get_length (key) - sizeof (key->type);
306 if (0 == *data_size)
307 return GNUNET_SYSERR;
308 tmp = GNUNET_malloc (*data_size);
309 memcpy (tmp, ((char*) key) + sizeof (key->type), *data_size);
310 *data = tmp;
311 return GNUNET_OK;
312}
313
314
315enum GNUNET_GenericReturnValue
316GNUNET_GNSRECORD_is_zonekey_type (uint32_t type)
317{
318 switch (type)
319 {
320 case GNUNET_GNSRECORD_TYPE_PKEY:
321 case GNUNET_GNSRECORD_TYPE_EDKEY:
322 return GNUNET_YES;
323 default:
324 return GNUNET_NO;
325 }
326}
327
328
329size_t
330GNUNET_GNSRECORD_block_get_size (const struct GNUNET_GNSRECORD_Block *block)
331{
332 return ntohl (block->size);
333}
334
335
336struct GNUNET_TIME_Absolute
337GNUNET_GNSRECORD_block_get_expiration (const struct
338 GNUNET_GNSRECORD_Block *block)
339{
340
341 switch (ntohl (block->type))
342 {
343 case GNUNET_GNSRECORD_TYPE_PKEY:
344 return GNUNET_TIME_absolute_ntoh (block->ecdsa_block.expiration_time);
345 case GNUNET_GNSRECORD_TYPE_EDKEY:
346 return GNUNET_TIME_absolute_ntoh (block->eddsa_block.expiration_time);
347 default:
348 GNUNET_break (0); /* Hopefully we never get here, but we might */
349 }
350 return GNUNET_TIME_absolute_get_zero_ ();
351
352}
353
354
355enum GNUNET_GenericReturnValue
356GNUNET_GNSRECORD_query_from_block (const struct GNUNET_GNSRECORD_Block *block,
357 struct GNUNET_HashCode *query)
358{
359 switch (ntohl (block->type))
360 {
361 case GNUNET_GNSRECORD_TYPE_PKEY:
362 GNUNET_CRYPTO_hash (&(block->ecdsa_block.derived_key),
363 sizeof (block->ecdsa_block.derived_key),
364 query);
365 return GNUNET_OK;
366 case GNUNET_GNSRECORD_TYPE_EDKEY:
367 GNUNET_CRYPTO_hash (&block->eddsa_block.derived_key,
368 sizeof (block->eddsa_block.derived_key),
369 query);
370 return GNUNET_OK;
371 default:
372 return GNUNET_SYSERR;
373 }
374 return GNUNET_SYSERR;
375
376}
377
378
379enum GNUNET_GenericReturnValue
380GNUNET_GNSRECORD_record_to_identity_key (const struct GNUNET_GNSRECORD_Data *rd,
381 struct GNUNET_CRYPTO_PublicKey *key)
382{
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384 "Got record of type %u\n",
385 rd->record_type);
386 switch (rd->record_type)
387 {
388 case GNUNET_GNSRECORD_TYPE_PKEY:
389 key->type = htonl (rd->record_type);
390 memcpy (&key->ecdsa_key, rd->data, sizeof (key->ecdsa_key));
391 return GNUNET_OK;
392 case GNUNET_GNSRECORD_TYPE_EDKEY:
393 key->type = htonl (rd->record_type);
394 memcpy (&key->eddsa_key, rd->data, sizeof (key->eddsa_key));
395 return GNUNET_OK;
396 default:
397 return GNUNET_SYSERR;
398 }
399 return GNUNET_SYSERR;
400
401
402}
403
404
405enum GNUNET_GenericReturnValue
406GNUNET_GNSRECORD_normalize_record_set (const char *label,
407 const struct
408 GNUNET_GNSRECORD_Data *rd,
409 unsigned int rd_count,
410 struct GNUNET_GNSRECORD_Data *
411 rd_public,
412 unsigned int *rd_count_public,
413 struct GNUNET_TIME_Absolute *expiry,
414 enum GNUNET_GNSRECORD_Filter filter,
415 char **emsg)
416{
417 struct GNUNET_TIME_Absolute now;
418 struct GNUNET_TIME_Absolute minimum_expiration;
419 int have_zone_delegation = GNUNET_NO;
420 int have_gns2dns = GNUNET_NO;
421 int have_other = GNUNET_NO;
422 int have_redirect = GNUNET_NO;
423 int have_empty_label = (0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, label));
424 unsigned int rd_count_tmp;
425
426 minimum_expiration = GNUNET_TIME_UNIT_ZERO_ABS;
427 now = GNUNET_TIME_absolute_get ();
428 rd_count_tmp = 0;
429 for (unsigned int i = 0; i < rd_count; i++)
430 {
431 /* Ignore private records for public record set */
432 if ((0 != (filter & GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE)) &&
433 (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)))
434 {
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436 "Filtering private record filter=%u...\n", filter);
437 continue;
438 }
439 /* Skip expired records */
440 if ((0 == (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) &&
441 (rd[i].expiration_time < now.abs_value_us))
442 {
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Filtering expired record...\n");
445 continue; /* record already expired, skip it */
446 }
447 /* Ignore the tombstone unless filter permits explicitly.
448 * Remember expiration time. */
449 if (GNUNET_GNSRECORD_TYPE_TOMBSTONE == rd[i].record_type)
450 {
451 minimum_expiration.abs_value_us = rd[i].expiration_time;
452 if (0 != (filter & GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE))
453 {
454 rd_public[rd_count_tmp] = rd[i];
455 rd_count_tmp++;
456 }
457 continue;
458 }
459 /* No NICK records unless empty label */
460 if (have_empty_label &&
461 (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type))
462 continue;
463
464 /**
465 * Check for delegation and redirect consistency.
466 * Note that we check for consistency BEFORE we filter for
467 * private records ON PURPOSE.
468 * We also want consistent record sets in our local zone(s).
469 * The only exception is the tombstone (above) which we ignore
470 * for the consistency check(s).
471 * FIXME: What about shadow records? Should we ignore them?
472 */
473 if (GNUNET_YES == GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type))
474 {
475 /* No delegation records under empty label*/
476 if (have_empty_label)
477 {
478 *emsg = GNUNET_strdup (_ (
479 "Zone delegation record not allowed in apex."));
480 return GNUNET_SYSERR;
481 }
482 if ((GNUNET_YES == have_other) ||
483 (GNUNET_YES == have_redirect) ||
484 (GNUNET_YES == have_gns2dns))
485 {
486 *emsg = GNUNET_strdup (_ (
487 "Zone delegation record set contains mutually exclusive records."));
488 return GNUNET_SYSERR;
489 }
490 have_zone_delegation = GNUNET_YES;
491 }
492 else if (GNUNET_GNSRECORD_TYPE_REDIRECT == rd[i].record_type)
493 {
494 if (GNUNET_YES == have_redirect)
495 {
496 *emsg = GNUNET_strdup (_ (
497 "Multiple REDIRECT records."));
498 return GNUNET_SYSERR;
499
500 }
501 if ((GNUNET_YES == have_other) ||
502 (GNUNET_YES == have_zone_delegation) ||
503 (GNUNET_YES == have_gns2dns))
504 {
505 *emsg = GNUNET_strdup (_ (
506 "Redirection record set contains mutually exclusive records."));
507 return GNUNET_SYSERR;
508 }
509 /* No redirection records under empty label*/
510 if (have_empty_label)
511 {
512 *emsg = GNUNET_strdup (_ (
513 "Redirection records not allowed in apex."));
514 return GNUNET_SYSERR;
515 }
516 have_redirect = GNUNET_YES;
517 }
518 else if (GNUNET_GNSRECORD_TYPE_GNS2DNS == rd[i].record_type)
519 {
520 /* No gns2dns records under empty label*/
521 if (have_empty_label)
522 {
523 *emsg = GNUNET_strdup (_ (
524 "Redirection records not allowed in apex.."));
525 return GNUNET_SYSERR;
526 }
527 if ((GNUNET_YES == have_other) ||
528 (GNUNET_YES == have_redirect) ||
529 (GNUNET_YES == have_zone_delegation))
530 {
531 *emsg = GNUNET_strdup (_ (
532 "Redirection record set contains mutually exclusive records."));
533 return GNUNET_SYSERR;
534 }
535 have_gns2dns = GNUNET_YES;
536 }
537 else
538 {
539 /* Some other record.
540 * Not allowed for zone delegations or redirections */
541 if ((GNUNET_YES == have_zone_delegation) ||
542 (GNUNET_YES == have_redirect) ||
543 (GNUNET_YES == have_gns2dns))
544 {
545 *emsg = GNUNET_strdup (_ (
546 "Mutually exclusive records."));
547 return GNUNET_SYSERR;
548 }
549 have_other = GNUNET_YES;
550 }
551
552 rd_public[rd_count_tmp] = rd[i];
553 /* Make sure critical record types are marked as such */
554 if (GNUNET_YES == GNUNET_GNSRECORD_is_critical (rd[i].record_type))
555 rd_public[rd_count_tmp].flags |= GNUNET_GNSRECORD_RF_CRITICAL;
556 rd_count_tmp++;
557 }
558
559 *expiry = GNUNET_GNSRECORD_record_get_expiration_time (rd_count_tmp,
560 rd_public,
561 minimum_expiration);
562 *rd_count_public = rd_count_tmp;
563 return GNUNET_OK;
564}
565
566
567/* end of gnsrecord_misc.c */