2 This file is part of GNUnet
3 Copyright (C) 2013, 2014 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file gnsrecord/plugin_gnsrecord_dns.c
23 * @brief gnsrecord plugin to provide the API for basic DNS records
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dnsparser_lib.h"
29 #include "gnunet_gnsrecord_plugin.h"
33 * Convert the 'value' of a record to a string.
35 * @param cls closure, unused
36 * @param type type of the record
37 * @param data value in binary encoding
38 * @param data_size number of bytes in @a data
39 * @return NULL on error, otherwise human-readable representation of the value
42 dns_value_to_string (void *cls,
48 char tmp[INET6_ADDRSTRLEN];
52 case GNUNET_DNSPARSER_TYPE_A:
53 if (data_size != sizeof (struct in_addr))
55 if (NULL == inet_ntop (AF_INET, data, tmp, sizeof (tmp)))
57 return GNUNET_strdup (tmp);
58 case GNUNET_DNSPARSER_TYPE_NS: {
63 ns = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
64 if ((NULL == ns) || (off != data_size))
67 GNUNET_free_non_null (ns);
72 case GNUNET_DNSPARSER_TYPE_CNAME: {
77 cname = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
78 if ((NULL == cname) || (off != data_size))
81 GNUNET_free_non_null (cname);
86 case GNUNET_DNSPARSER_TYPE_SOA: {
87 struct GNUNET_DNSPARSER_SoaRecord *soa;
91 soa = GNUNET_DNSPARSER_parse_soa (data, data_size, &off);
92 if ((NULL == soa) || (off != data_size))
96 GNUNET_DNSPARSER_free_soa (soa);
99 GNUNET_asprintf (&result,
100 "rname=%s mname=%s %lu,%lu,%lu,%lu,%lu",
108 GNUNET_DNSPARSER_free_soa (soa);
111 case GNUNET_DNSPARSER_TYPE_PTR: {
116 ptr = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
117 if ((NULL == ptr) || (off != data_size))
120 GNUNET_free_non_null (ptr);
125 case GNUNET_DNSPARSER_TYPE_CERT: {
126 struct GNUNET_DNSPARSER_CertRecord *cert;
132 cert = GNUNET_DNSPARSER_parse_cert (data, data_size, &off);
133 if ((NULL == cert) || (off != data_size))
136 GNUNET_DNSPARSER_free_cert (cert);
139 len = GNUNET_STRINGS_base64_encode (cert->certificate_data,
140 cert->certificate_size,
142 GNUNET_asprintf (&result,
149 GNUNET_free (base64);
150 GNUNET_DNSPARSER_free_cert (cert);
153 case GNUNET_DNSPARSER_TYPE_MX: {
154 struct GNUNET_DNSPARSER_MxRecord *mx;
158 mx = GNUNET_DNSPARSER_parse_mx (data, data_size, &off);
159 if ((NULL == mx) || (off != data_size))
162 GNUNET_DNSPARSER_free_mx (mx);
165 GNUNET_asprintf (&result,
167 (unsigned int) mx->preference,
169 GNUNET_DNSPARSER_free_mx (mx);
172 case GNUNET_DNSPARSER_TYPE_TXT:
173 return GNUNET_strndup (data, data_size);
174 case GNUNET_DNSPARSER_TYPE_AAAA:
175 if (data_size != sizeof (struct in6_addr))
177 if (NULL == inet_ntop (AF_INET6, data, tmp, sizeof (tmp)))
179 return GNUNET_strdup (tmp);
180 case GNUNET_DNSPARSER_TYPE_SRV: {
181 struct GNUNET_DNSPARSER_SrvRecord *srv;
185 srv = GNUNET_DNSPARSER_parse_srv (data, data_size, &off);
186 if ((NULL == srv) || (off != data_size))
190 GNUNET_DNSPARSER_free_srv (srv);
193 GNUNET_asprintf (&result,
199 GNUNET_DNSPARSER_free_srv (srv);
202 case GNUNET_DNSPARSER_TYPE_TLSA: {
203 const struct GNUNET_TUN_DnsTlsaRecord *tlsa;
207 if (data_size < sizeof (struct GNUNET_TUN_DnsTlsaRecord))
208 return NULL; /* malformed */
211 GNUNET_DNSPARSER_bin_to_hex (&tlsa[1],
213 sizeof (struct GNUNET_TUN_DnsTlsaRecord));
214 if (0 == GNUNET_asprintf (&tlsa_str,
216 (unsigned int) tlsa->usage,
217 (unsigned int) tlsa->selector,
218 (unsigned int) tlsa->matching_type,
222 GNUNET_free (tlsa_str);
228 case GNUNET_DNSPARSER_TYPE_CAA: { //RFC6844
229 const struct GNUNET_DNSPARSER_CaaRecord *caa;
230 char tag[15]; // between 1 and 15 bytes
231 char value[data_size];
233 if (data_size < sizeof (struct GNUNET_DNSPARSER_CaaRecord))
234 return NULL; /* malformed */
236 if ((1 > caa->tag_len) || (15 < caa->tag_len))
237 return NULL; /* malformed */
238 memset (tag, 0, sizeof (tag));
239 memset (value, 0, data_size);
240 memcpy (tag, &caa[1], caa->tag_len);
242 (char *) &caa[1] + caa->tag_len,
243 data_size - caa->tag_len - 2);
244 if (0 == GNUNET_asprintf (&caa_str,
246 (unsigned int) caa->flags,
250 GNUNET_free (caa_str);
262 * Convert RFC 4394 Mnemonics to the corresponding integer values.
264 * @param mnemonic string to look up
265 * @return the value, 0 if not found
268 rfc4398_mnemonic_to_value (const char *mnemonic)
272 const char *mnemonic;
274 } table[] = {{"PKIX", 1},
287 for (i = 0; NULL != table[i].mnemonic; i++)
288 if (0 == strcasecmp (mnemonic, table[i].mnemonic))
295 * Convert RFC 4034 algorithm types to the corresponding integer values.
297 * @param mnemonic string to look up
298 * @return the value, 0 if not found
301 rfc4034_mnemonic_to_value (const char *mnemonic)
305 const char *mnemonic;
307 } table[] = {{"RSAMD5", 1},
318 for (i = 0; NULL != table[i].mnemonic; i++)
319 if (0 == strcasecmp (mnemonic, table[i].mnemonic))
326 * Convert human-readable version of a 'value' of a record to the binary
329 * @param cls closure, unused
330 * @param type type of the record
331 * @param s human-readable string
332 * @param data set to value in binary encoding (will be allocated)
333 * @param data_size set to number of bytes in @a data
334 * @return #GNUNET_OK on success
337 dns_string_to_value (void *cls,
343 struct in_addr value_a;
344 struct in6_addr value_aaaa;
345 struct GNUNET_TUN_DnsTlsaRecord *tlsa;
348 return GNUNET_SYSERR;
351 case GNUNET_DNSPARSER_TYPE_A:
352 if (1 != inet_pton (AF_INET, s, &value_a))
354 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
355 _ ("Unable to parse IPv4 address `%s'\n"),
357 return GNUNET_SYSERR;
359 *data = GNUNET_new (struct in_addr);
360 GNUNET_memcpy (*data, &value_a, sizeof (value_a));
361 *data_size = sizeof (value_a);
363 case GNUNET_DNSPARSER_TYPE_NS: {
369 GNUNET_DNSPARSER_builder_add_name (nsbuf, sizeof (nsbuf), &off, s))
371 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
372 _ ("Failed to serialize NS record with value `%s'\n"),
374 return GNUNET_SYSERR;
377 *data = GNUNET_malloc (off);
378 GNUNET_memcpy (*data, nsbuf, off);
381 case GNUNET_DNSPARSER_TYPE_CNAME: {
386 if (GNUNET_OK != GNUNET_DNSPARSER_builder_add_name (cnamebuf,
391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392 _ ("Failed to serialize CNAME record with value `%s'\n"),
394 return GNUNET_SYSERR;
397 *data = GNUNET_malloc (off);
398 GNUNET_memcpy (*data, cnamebuf, off);
401 case GNUNET_DNSPARSER_TYPE_CERT: {
412 struct GNUNET_DNSPARSER_CertRecord cert;
414 sdup = GNUNET_strdup (s);
415 typep = strtok (sdup, " ");
416 if ((NULL == typep) ||
417 ((0 == (type = rfc4398_mnemonic_to_value (typep))) &&
418 ((1 != SSCANF (typep, "%u", &type)) || (type > UINT16_MAX))))
421 return GNUNET_SYSERR;
423 keyp = strtok (NULL, " ");
424 if ((NULL == keyp) || (1 != SSCANF (keyp, "%u", &key)) ||
428 return GNUNET_SYSERR;
431 algp = strtok (NULL, " ");
432 if ((NULL == algp) ||
433 ((0 == (type = rfc4034_mnemonic_to_value (typep))) &&
434 ((1 != sscanf (algp, "%u", &alg)) || (alg > UINT8_MAX))))
437 return GNUNET_SYSERR;
439 certp = strtok (NULL, " ");
440 if ((NULL == certp) || (0 == strlen (certp)))
443 return GNUNET_SYSERR;
445 cert_size = GNUNET_STRINGS_base64_decode (certp,
447 (void **) &cert_data);
449 cert.cert_type = type;
451 cert.algorithm = alg;
452 cert.certificate_size = cert_size;
453 cert.certificate_data = cert_data;
455 char certbuf[cert_size + sizeof (struct GNUNET_TUN_DnsCertRecord)];
459 if (GNUNET_OK != GNUNET_DNSPARSER_builder_add_cert (certbuf,
464 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
465 _ ("Failed to serialize CERT record with %u bytes\n"),
466 (unsigned int) cert_size);
467 GNUNET_free (cert_data);
468 return GNUNET_SYSERR;
471 *data = GNUNET_malloc (off);
472 GNUNET_memcpy (*data, certbuf, off);
474 GNUNET_free (cert_data);
477 case GNUNET_DNSPARSER_TYPE_SOA: {
478 struct GNUNET_DNSPARSER_SoaRecord soa;
480 char soa_rname[253 + 1];
481 char soa_mname[253 + 1];
482 unsigned int soa_serial;
483 unsigned int soa_refresh;
484 unsigned int soa_retry;
485 unsigned int soa_expire;
486 unsigned int soa_min;
490 "rname=%253s mname=%253s %u,%u,%u,%u,%u",
499 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500 _ ("Unable to parse SOA record `%s'\n"),
502 return GNUNET_SYSERR;
504 soa.mname = soa_mname;
505 soa.rname = soa_rname;
506 soa.serial = (uint32_t) soa_serial;
507 soa.refresh = (uint32_t) soa_refresh;
508 soa.retry = (uint32_t) soa_retry;
509 soa.expire = (uint32_t) soa_expire;
510 soa.minimum_ttl = (uint32_t) soa_min;
513 GNUNET_DNSPARSER_builder_add_soa (soabuf, sizeof (soabuf), &off, &soa))
516 GNUNET_ERROR_TYPE_ERROR,
517 _ ("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
520 return GNUNET_SYSERR;
523 *data = GNUNET_malloc (off);
524 GNUNET_memcpy (*data, soabuf, off);
527 case GNUNET_DNSPARSER_TYPE_PTR: {
533 GNUNET_DNSPARSER_builder_add_name (ptrbuf, sizeof (ptrbuf), &off, s))
535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536 _ ("Failed to serialize PTR record with value `%s'\n"),
538 return GNUNET_SYSERR;
541 *data = GNUNET_malloc (off);
542 GNUNET_memcpy (*data, ptrbuf, off);
545 case GNUNET_DNSPARSER_TYPE_MX: {
546 struct GNUNET_DNSPARSER_MxRecord mx;
548 char mxhost[253 + 1];
549 unsigned int mx_pref;
552 if (2 != SSCANF (s, "%u,%253s", &mx_pref, mxhost))
554 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555 _ ("Unable to parse MX record `%s'\n"),
557 return GNUNET_SYSERR;
559 mx.preference = (uint16_t) mx_pref;
564 GNUNET_DNSPARSER_builder_add_mx (mxbuf, sizeof (mxbuf), &off, &mx))
566 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
567 _ ("Failed to serialize MX record with hostname `%s'\n"),
569 return GNUNET_SYSERR;
572 *data = GNUNET_malloc (off);
573 GNUNET_memcpy (*data, mxbuf, off);
576 case GNUNET_DNSPARSER_TYPE_SRV: {
577 struct GNUNET_DNSPARSER_SrvRecord srv;
579 char srvtarget[253 + 1];
580 unsigned int priority;
585 if (4 != SSCANF (s, "%u %u %u %253s", &priority, &weight, &port, srvtarget))
587 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588 _ ("Unable to parse SRV record `%s'\n"),
590 return GNUNET_SYSERR;
592 srv.priority = (uint16_t) priority;
593 srv.weight = (uint16_t) weight;
594 srv.port = (uint16_t) port;
595 srv.target = srvtarget;
598 GNUNET_DNSPARSER_builder_add_srv (srvbuf, sizeof (srvbuf), &off, &srv))
600 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
601 _ ("Failed to serialize SRV record with target `%s'\n"),
603 return GNUNET_SYSERR;
606 *data = GNUNET_malloc (off);
607 GNUNET_memcpy (*data, srvbuf, off);
610 case GNUNET_DNSPARSER_TYPE_TXT:
611 *data = GNUNET_strdup (s);
612 *data_size = strlen (s);
614 case GNUNET_DNSPARSER_TYPE_AAAA:
615 if (1 != inet_pton (AF_INET6, s, &value_aaaa))
617 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
618 _ ("Unable to parse IPv6 address `%s'\n"),
620 return GNUNET_SYSERR;
622 *data = GNUNET_new (struct in6_addr);
623 *data_size = sizeof (struct in6_addr);
624 GNUNET_memcpy (*data, &value_aaaa, sizeof (value_aaaa));
626 case GNUNET_DNSPARSER_TYPE_TLSA: {
628 unsigned int selector;
629 unsigned int matching_type;
630 size_t slen = strlen (s) + 1;
633 if (4 != SSCANF (s, "%u %u %u %s", &usage, &selector, &matching_type, hex))
635 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
636 _ ("Unable to parse TLSA record string `%s'\n"),
639 return GNUNET_SYSERR;
642 *data_size = sizeof (struct GNUNET_TUN_DnsTlsaRecord) + strlen (hex) / 2;
643 *data = tlsa = GNUNET_malloc (*data_size);
644 tlsa->usage = (uint8_t) usage;
645 tlsa->selector = (uint8_t) selector;
646 tlsa->matching_type = (uint8_t) matching_type;
647 if (strlen (hex) / 2 != GNUNET_DNSPARSER_hex_to_bin (hex, &tlsa[1]))
649 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650 _ ("Unable to parse TLSA record string `%s'\n"),
655 return GNUNET_SYSERR;
659 case GNUNET_DNSPARSER_TYPE_CAA: { //RFC6844
660 struct GNUNET_DNSPARSER_CaaRecord *caa;
662 char tag[15]; //Max tag length 15
663 char value[strlen (s) + 1]; //Should be more than enough
665 if (3 != SSCANF (s, "%u %s %[^\n]", &flags, tag, value))
667 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668 _ ("Unable to parse CAA record string `%s'\n"),
671 return GNUNET_SYSERR;
673 *data_size = sizeof (struct GNUNET_DNSPARSER_CaaRecord) + strlen (tag) +
675 *data = caa = GNUNET_malloc (*data_size);
677 memcpy (&caa[1], tag, strlen (tag));
678 caa->tag_len = strlen (tag);
679 memcpy ((char *) &caa[1] + caa->tag_len, value, strlen (value));
683 return GNUNET_SYSERR;
689 * Mapping of record type numbers to human-readable
696 } name_map[] = {{"A", GNUNET_DNSPARSER_TYPE_A},
697 {"NS", GNUNET_DNSPARSER_TYPE_NS},
698 {"CNAME", GNUNET_DNSPARSER_TYPE_CNAME},
699 {"SOA", GNUNET_DNSPARSER_TYPE_SOA},
700 {"PTR", GNUNET_DNSPARSER_TYPE_PTR},
701 {"MX", GNUNET_DNSPARSER_TYPE_MX},
702 {"TXT", GNUNET_DNSPARSER_TYPE_TXT},
703 {"AAAA", GNUNET_DNSPARSER_TYPE_AAAA},
704 {"SRV", GNUNET_DNSPARSER_TYPE_SRV},
705 {"TLSA", GNUNET_DNSPARSER_TYPE_TLSA},
706 {"CERT", GNUNET_DNSPARSER_TYPE_CERT},
707 {"CAA", GNUNET_DNSPARSER_TYPE_CAA},
712 * Convert a type name (i.e. "AAAA") to the corresponding number.
714 * @param cls closure, unused
715 * @param dns_typename name to convert
716 * @return corresponding number, UINT32_MAX on error
719 dns_typename_to_number (void *cls, const char *dns_typename)
724 while ((NULL != name_map[i].name) &&
725 (0 != strcasecmp (dns_typename, name_map[i].name)))
727 return name_map[i].number;
732 * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
734 * @param cls closure, unused
735 * @param type number of a type to convert
736 * @return corresponding typestring, NULL on error
739 dns_number_to_typename (void *cls, uint32_t type)
744 while ((NULL != name_map[i].name) && (type != name_map[i].number))
746 return name_map[i].name;
751 * Entry point for the plugin.
754 * @return the exported block API
757 libgnunet_plugin_gnsrecord_dns_init (void *cls)
759 struct GNUNET_GNSRECORD_PluginFunctions *api;
761 api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
762 api->value_to_string = &dns_value_to_string;
763 api->string_to_value = &dns_string_to_value;
764 api->typename_to_number = &dns_typename_to_number;
765 api->number_to_typename = &dns_number_to_typename;
771 * Exit point from the plugin.
773 * @param cls the return value from #libgnunet_plugin_block_test_init
777 libgnunet_plugin_gnsrecord_dns_done (void *cls)
779 struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
785 /* end of plugin_gnsrecord_dns.c */