use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / gnsrecord / plugin_gnsrecord_dns.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2013, 2014 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/plugin_gnsrecord_dns.c
23  * @brief gnsrecord plugin to provide the API for basic DNS records
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dnsparser_lib.h"
29 #include "gnunet_gnsrecord_plugin.h"
30
31
32 /**
33  * Convert the 'value' of a record to a string.
34  *
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
40  */
41 static char *
42 dns_value_to_string (void *cls,
43                      uint32_t type,
44                      const void *data,
45                      size_t data_size)
46 {
47   char *result;
48   char tmp[INET6_ADDRSTRLEN];
49
50   switch (type)
51   {
52   case GNUNET_DNSPARSER_TYPE_A:
53     if (data_size != sizeof(struct in_addr))
54       return NULL;
55     if (NULL == inet_ntop (AF_INET, data, tmp, sizeof(tmp)))
56       return NULL;
57     return GNUNET_strdup (tmp);
58
59   case GNUNET_DNSPARSER_TYPE_NS: {
60       char *ns;
61       size_t off;
62
63       off = 0;
64       ns = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
65       if ((NULL == ns) || (off != data_size))
66       {
67         GNUNET_break_op (0);
68         GNUNET_free_non_null (ns);
69         return NULL;
70       }
71       return ns;
72     }
73
74   case GNUNET_DNSPARSER_TYPE_CNAME: {
75       char *cname;
76       size_t off;
77
78       off = 0;
79       cname = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
80       if ((NULL == cname) || (off != data_size))
81       {
82         GNUNET_break_op (0);
83         GNUNET_free_non_null (cname);
84         return NULL;
85       }
86       return cname;
87     }
88
89   case GNUNET_DNSPARSER_TYPE_SOA: {
90       struct GNUNET_DNSPARSER_SoaRecord *soa;
91       size_t off;
92
93       off = 0;
94       soa = GNUNET_DNSPARSER_parse_soa (data, data_size, &off);
95       if ((NULL == soa) || (off != data_size))
96       {
97         GNUNET_break_op (0);
98         if (NULL != soa)
99           GNUNET_DNSPARSER_free_soa (soa);
100         return NULL;
101       }
102       GNUNET_asprintf (&result,
103                        "rname=%s mname=%s %lu,%lu,%lu,%lu,%lu",
104                        soa->rname,
105                        soa->mname,
106                        soa->serial,
107                        soa->refresh,
108                        soa->retry,
109                        soa->expire,
110                        soa->minimum_ttl);
111       GNUNET_DNSPARSER_free_soa (soa);
112       return result;
113     }
114
115   case GNUNET_DNSPARSER_TYPE_PTR: {
116       char *ptr;
117       size_t off;
118
119       off = 0;
120       ptr = GNUNET_DNSPARSER_parse_name (data, data_size, &off);
121       if ((NULL == ptr) || (off != data_size))
122       {
123         GNUNET_break_op (0);
124         GNUNET_free_non_null (ptr);
125         return NULL;
126       }
127       return ptr;
128     }
129
130   case GNUNET_DNSPARSER_TYPE_CERT: {
131       struct GNUNET_DNSPARSER_CertRecord *cert;
132       size_t off;
133       char *base64;
134       int len;
135
136       off = 0;
137       cert = GNUNET_DNSPARSER_parse_cert (data, data_size, &off);
138       if ((NULL == cert) || (off != data_size))
139       {
140         GNUNET_break_op (0);
141         GNUNET_DNSPARSER_free_cert (cert);
142         return NULL;
143       }
144       len = GNUNET_STRINGS_base64_encode (cert->certificate_data,
145                                           cert->certificate_size,
146                                           &base64);
147       GNUNET_asprintf (&result,
148                        "%u %u %u %.*s",
149                        cert->cert_type,
150                        cert->cert_tag,
151                        cert->algorithm,
152                        len,
153                        base64);
154       GNUNET_free (base64);
155       GNUNET_DNSPARSER_free_cert (cert);
156       return result;
157     }
158
159   case GNUNET_DNSPARSER_TYPE_MX: {
160       struct GNUNET_DNSPARSER_MxRecord *mx;
161       size_t off;
162
163       off = 0;
164       mx = GNUNET_DNSPARSER_parse_mx (data, data_size, &off);
165       if ((NULL == mx) || (off != data_size))
166       {
167         GNUNET_break_op (0);
168         GNUNET_DNSPARSER_free_mx (mx);
169         return NULL;
170       }
171       GNUNET_asprintf (&result,
172                        "%u,%s",
173                        (unsigned int) mx->preference,
174                        mx->mxhost);
175       GNUNET_DNSPARSER_free_mx (mx);
176       return result;
177     }
178
179   case GNUNET_DNSPARSER_TYPE_TXT:
180     return GNUNET_strndup (data, data_size);
181
182   case GNUNET_DNSPARSER_TYPE_AAAA:
183     if (data_size != sizeof(struct in6_addr))
184       return NULL;
185     if (NULL == inet_ntop (AF_INET6, data, tmp, sizeof(tmp)))
186       return NULL;
187     return GNUNET_strdup (tmp);
188
189   case GNUNET_DNSPARSER_TYPE_SRV: {
190       struct GNUNET_DNSPARSER_SrvRecord *srv;
191       size_t off;
192
193       off = 0;
194       srv = GNUNET_DNSPARSER_parse_srv (data, data_size, &off);
195       if ((NULL == srv) || (off != data_size))
196       {
197         GNUNET_break_op (0);
198         if (NULL != srv)
199           GNUNET_DNSPARSER_free_srv (srv);
200         return NULL;
201       }
202       GNUNET_asprintf (&result,
203                        "%d %d %d %s",
204                        srv->priority,
205                        srv->weight,
206                        srv->port,
207                        srv->target);
208       GNUNET_DNSPARSER_free_srv (srv);
209       return result;
210     }
211
212   case GNUNET_DNSPARSER_TYPE_TLSA: {
213       const struct GNUNET_TUN_DnsTlsaRecord *tlsa;
214       char *tlsa_str;
215       char *hex;
216
217       if (data_size < sizeof(struct GNUNET_TUN_DnsTlsaRecord))
218         return NULL; /* malformed */
219       tlsa = data;
220       hex =
221         GNUNET_DNSPARSER_bin_to_hex (&tlsa[1],
222                                      data_size
223                                      - sizeof(struct GNUNET_TUN_DnsTlsaRecord));
224       if (0 == GNUNET_asprintf (&tlsa_str,
225                                 "%u %u %u %s",
226                                 (unsigned int) tlsa->usage,
227                                 (unsigned int) tlsa->selector,
228                                 (unsigned int) tlsa->matching_type,
229                                 hex))
230       {
231         GNUNET_free (hex);
232         GNUNET_free (tlsa_str);
233         return NULL;
234       }
235       GNUNET_free (hex);
236       return tlsa_str;
237     }
238
239   case GNUNET_DNSPARSER_TYPE_CAA: {   // RFC6844
240       const struct GNUNET_DNSPARSER_CaaRecord *caa;
241       char tag[15]; // between 1 and 15 bytes
242       char value[data_size];
243       char *caa_str;
244       if (data_size < sizeof(struct GNUNET_DNSPARSER_CaaRecord))
245         return NULL; /* malformed */
246       caa = data;
247       if ((1 > caa->tag_len) || (15 < caa->tag_len))
248         return NULL; /* malformed */
249       memset (tag, 0, sizeof(tag));
250       memset (value, 0, data_size);
251       memcpy (tag, &caa[1], caa->tag_len);
252       memcpy (value,
253               (char *) &caa[1] + caa->tag_len,
254               data_size - caa->tag_len - 2);
255       if (0 == GNUNET_asprintf (&caa_str,
256                                 "%u %s %s",
257                                 (unsigned int) caa->flags,
258                                 tag,
259                                 value))
260       {
261         GNUNET_free (caa_str);
262         return NULL;
263       }
264       return caa_str;
265     }
266
267   default:
268     return NULL;
269   }
270 }
271
272
273 /**
274  * Convert RFC 4394 Mnemonics to the corresponding integer values.
275  *
276  * @param mnemonic string to look up
277  * @return the value, 0 if not found
278  */
279 static unsigned int
280 rfc4398_mnemonic_to_value (const char *mnemonic)
281 {
282   static struct
283   {
284     const char *mnemonic;
285     unsigned int val;
286   } table[] = { { "PKIX", 1 },
287                 { "SPKI", 2 },
288                 { "PGP", 3 },
289                 { "IPKIX", 4 },
290                 { "ISPKI", 5 },
291                 { "IPGP", 6 },
292                 { "ACPKIX", 7 },
293                 { "IACPKIX", 8 },
294                 { "URI", 253 },
295                 { "OID", 254 },
296                 { NULL, 0 } };
297   unsigned int i;
298
299   for (i = 0; NULL != table[i].mnemonic; i++)
300     if (0 == strcasecmp (mnemonic, table[i].mnemonic))
301       return table[i].val;
302   return 0;
303 }
304
305
306 /**
307  * Convert RFC 4034 algorithm types to the corresponding integer values.
308  *
309  * @param mnemonic string to look up
310  * @return the value, 0 if not found
311  */
312 static unsigned int
313 rfc4034_mnemonic_to_value (const char *mnemonic)
314 {
315   static struct
316   {
317     const char *mnemonic;
318     unsigned int val;
319   } table[] = { { "RSAMD5", 1 },
320                 { "DH", 2 },
321                 { "DSA", 3 },
322                 { "ECC", 4 },
323                 { "RSASHA1", 5 },
324                 { "INDIRECT", 252 },
325                 { "PRIVATEDNS", 253 },
326                 { "PRIVATEOID", 254 },
327                 { NULL, 0 } };
328   unsigned int i;
329
330   for (i = 0; NULL != table[i].mnemonic; i++)
331     if (0 == strcasecmp (mnemonic, table[i].mnemonic))
332       return table[i].val;
333   return 0;
334 }
335
336
337 /**
338  * Convert human-readable version of a 'value' of a record to the binary
339  * representation.
340  *
341  * @param cls closure, unused
342  * @param type type of the record
343  * @param s human-readable string
344  * @param data set to value in binary encoding (will be allocated)
345  * @param data_size set to number of bytes in @a data
346  * @return #GNUNET_OK on success
347  */
348 static int
349 dns_string_to_value (void *cls,
350                      uint32_t type,
351                      const char *s,
352                      void **data,
353                      size_t *data_size)
354 {
355   struct in_addr value_a;
356   struct in6_addr value_aaaa;
357   struct GNUNET_TUN_DnsTlsaRecord *tlsa;
358
359   if (NULL == s)
360     return GNUNET_SYSERR;
361   switch (type)
362   {
363   case GNUNET_DNSPARSER_TYPE_A:
364     if (1 != inet_pton (AF_INET, s, &value_a))
365     {
366       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367                   _ ("Unable to parse IPv4 address `%s'\n"),
368                   s);
369       return GNUNET_SYSERR;
370     }
371     *data = GNUNET_new (struct in_addr);
372     GNUNET_memcpy (*data, &value_a, sizeof(value_a));
373     *data_size = sizeof(value_a);
374     return GNUNET_OK;
375
376   case GNUNET_DNSPARSER_TYPE_NS: {
377       char nsbuf[256];
378       size_t off;
379
380       off = 0;
381       if (GNUNET_OK !=
382           GNUNET_DNSPARSER_builder_add_name (nsbuf, sizeof(nsbuf), &off, s))
383       {
384         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385                     _ ("Failed to serialize NS record with value `%s'\n"),
386                     s);
387         return GNUNET_SYSERR;
388       }
389       *data_size = off;
390       *data = GNUNET_malloc (off);
391       GNUNET_memcpy (*data, nsbuf, off);
392       return GNUNET_OK;
393     }
394
395   case GNUNET_DNSPARSER_TYPE_CNAME: {
396       char cnamebuf[256];
397       size_t off;
398
399       off = 0;
400       if (GNUNET_OK != GNUNET_DNSPARSER_builder_add_name (cnamebuf,
401                                                           sizeof(cnamebuf),
402                                                           &off,
403                                                           s))
404       {
405         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
406                     _ ("Failed to serialize CNAME record with value `%s'\n"),
407                     s);
408         return GNUNET_SYSERR;
409       }
410       *data_size = off;
411       *data = GNUNET_malloc (off);
412       GNUNET_memcpy (*data, cnamebuf, off);
413       return GNUNET_OK;
414     }
415
416   case GNUNET_DNSPARSER_TYPE_CERT: {
417       char *sdup;
418       const char *typep;
419       const char *keyp;
420       const char *algp;
421       const char *certp;
422       unsigned int type;
423       unsigned int key;
424       unsigned int alg;
425       size_t cert_size;
426       char *cert_data;
427       struct GNUNET_DNSPARSER_CertRecord cert;
428
429       sdup = GNUNET_strdup (s);
430       typep = strtok (sdup, " ");
431       if ((NULL == typep) ||
432           ((0 == (type = rfc4398_mnemonic_to_value (typep))) &&
433            ((1 != sscanf (typep, "%u", &type)) || (type > UINT16_MAX))))
434       {
435         GNUNET_free (sdup);
436         return GNUNET_SYSERR;
437       }
438       keyp = strtok (NULL, " ");
439       if ((NULL == keyp) || (1 != sscanf (keyp, "%u", &key)) ||
440           (key > UINT16_MAX))
441       {
442         GNUNET_free (sdup);
443         return GNUNET_SYSERR;
444       }
445       alg = 0;
446       algp = strtok (NULL, " ");
447       if ((NULL == algp) ||
448           ((0 == (type = rfc4034_mnemonic_to_value (typep))) &&
449            ((1 != sscanf (algp, "%u", &alg)) || (alg > UINT8_MAX))))
450       {
451         GNUNET_free (sdup);
452         return GNUNET_SYSERR;
453       }
454       certp = strtok (NULL, " ");
455       if ((NULL == certp) || (0 == strlen (certp)))
456       {
457         GNUNET_free (sdup);
458         return GNUNET_SYSERR;
459       }
460       cert_size = GNUNET_STRINGS_base64_decode (certp,
461                                                 strlen (certp),
462                                                 (void **) &cert_data);
463       GNUNET_free (sdup);
464       cert.cert_type = type;
465       cert.cert_tag = key;
466       cert.algorithm = alg;
467       cert.certificate_size = cert_size;
468       cert.certificate_data = cert_data;
469       {
470         char certbuf[cert_size + sizeof(struct GNUNET_TUN_DnsCertRecord)];
471         size_t off;
472
473         off = 0;
474         if (GNUNET_OK != GNUNET_DNSPARSER_builder_add_cert (certbuf,
475                                                             sizeof(certbuf),
476                                                             &off,
477                                                             &cert))
478         {
479           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
480                       _ ("Failed to serialize CERT record with %u bytes\n"),
481                       (unsigned int) cert_size);
482           GNUNET_free (cert_data);
483           return GNUNET_SYSERR;
484         }
485         *data_size = off;
486         *data = GNUNET_malloc (off);
487         GNUNET_memcpy (*data, certbuf, off);
488       }
489       GNUNET_free (cert_data);
490       return GNUNET_OK;
491     }
492
493   case GNUNET_DNSPARSER_TYPE_SOA: {
494       struct GNUNET_DNSPARSER_SoaRecord soa;
495       char soabuf[540];
496       char soa_rname[253 + 1];
497       char soa_mname[253 + 1];
498       unsigned int soa_serial;
499       unsigned int soa_refresh;
500       unsigned int soa_retry;
501       unsigned int soa_expire;
502       unsigned int soa_min;
503       size_t off;
504
505       if (7 != sscanf (s,
506                        "rname=%253s mname=%253s %u,%u,%u,%u,%u",
507                        soa_rname,
508                        soa_mname,
509                        &soa_serial,
510                        &soa_refresh,
511                        &soa_retry,
512                        &soa_expire,
513                        &soa_min))
514       {
515         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
516                     _ ("Unable to parse SOA record `%s'\n"),
517                     s);
518         return GNUNET_SYSERR;
519       }
520       soa.mname = soa_mname;
521       soa.rname = soa_rname;
522       soa.serial = (uint32_t) soa_serial;
523       soa.refresh = (uint32_t) soa_refresh;
524       soa.retry = (uint32_t) soa_retry;
525       soa.expire = (uint32_t) soa_expire;
526       soa.minimum_ttl = (uint32_t) soa_min;
527       off = 0;
528       if (GNUNET_OK !=
529           GNUNET_DNSPARSER_builder_add_soa (soabuf, sizeof(soabuf), &off, &soa))
530       {
531         GNUNET_log (
532           GNUNET_ERROR_TYPE_ERROR,
533           _ ("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
534           soa_mname,
535           soa_rname);
536         return GNUNET_SYSERR;
537       }
538       *data_size = off;
539       *data = GNUNET_malloc (off);
540       GNUNET_memcpy (*data, soabuf, off);
541       return GNUNET_OK;
542     }
543
544   case GNUNET_DNSPARSER_TYPE_PTR: {
545       char ptrbuf[256];
546       size_t off;
547
548       off = 0;
549       if (GNUNET_OK !=
550           GNUNET_DNSPARSER_builder_add_name (ptrbuf, sizeof(ptrbuf), &off, s))
551       {
552         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
553                     _ ("Failed to serialize PTR record with value `%s'\n"),
554                     s);
555         return GNUNET_SYSERR;
556       }
557       *data_size = off;
558       *data = GNUNET_malloc (off);
559       GNUNET_memcpy (*data, ptrbuf, off);
560       return GNUNET_OK;
561     }
562
563   case GNUNET_DNSPARSER_TYPE_MX: {
564       struct GNUNET_DNSPARSER_MxRecord mx;
565       char mxbuf[258];
566       char mxhost[253 + 1];
567       unsigned int mx_pref;
568       size_t off;
569
570       if (2 != sscanf (s, "%u,%253s", &mx_pref, mxhost))
571       {
572         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573                     _ ("Unable to parse MX record `%s'\n"),
574                     s);
575         return GNUNET_SYSERR;
576       }
577       mx.preference = (uint16_t) mx_pref;
578       mx.mxhost = mxhost;
579       off = 0;
580
581       if (GNUNET_OK !=
582           GNUNET_DNSPARSER_builder_add_mx (mxbuf, sizeof(mxbuf), &off, &mx))
583       {
584         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
585                     _ ("Failed to serialize MX record with hostname `%s'\n"),
586                     mxhost);
587         return GNUNET_SYSERR;
588       }
589       *data_size = off;
590       *data = GNUNET_malloc (off);
591       GNUNET_memcpy (*data, mxbuf, off);
592       return GNUNET_OK;
593     }
594
595   case GNUNET_DNSPARSER_TYPE_SRV: {
596       struct GNUNET_DNSPARSER_SrvRecord srv;
597       char srvbuf[270];
598       char srvtarget[253 + 1];
599       unsigned int priority;
600       unsigned int weight;
601       unsigned int port;
602       size_t off;
603
604       if (4 != sscanf (s, "%u %u %u %253s", &priority, &weight, &port,
605                        srvtarget))
606       {
607         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
608                     _ ("Unable to parse SRV record `%s'\n"),
609                     s);
610         return GNUNET_SYSERR;
611       }
612       srv.priority = (uint16_t) priority;
613       srv.weight = (uint16_t) weight;
614       srv.port = (uint16_t) port;
615       srv.target = srvtarget;
616       off = 0;
617       if (GNUNET_OK !=
618           GNUNET_DNSPARSER_builder_add_srv (srvbuf, sizeof(srvbuf), &off, &srv))
619       {
620         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
621                     _ ("Failed to serialize SRV record with target `%s'\n"),
622                     srvtarget);
623         return GNUNET_SYSERR;
624       }
625       *data_size = off;
626       *data = GNUNET_malloc (off);
627       GNUNET_memcpy (*data, srvbuf, off);
628       return GNUNET_OK;
629     }
630
631   case GNUNET_DNSPARSER_TYPE_TXT:
632     *data = GNUNET_strdup (s);
633     *data_size = strlen (s);
634     return GNUNET_OK;
635
636   case GNUNET_DNSPARSER_TYPE_AAAA:
637     if (1 != inet_pton (AF_INET6, s, &value_aaaa))
638     {
639       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
640                   _ ("Unable to parse IPv6 address `%s'\n"),
641                   s);
642       return GNUNET_SYSERR;
643     }
644     *data = GNUNET_new (struct in6_addr);
645     *data_size = sizeof(struct in6_addr);
646     GNUNET_memcpy (*data, &value_aaaa, sizeof(value_aaaa));
647     return GNUNET_OK;
648
649   case GNUNET_DNSPARSER_TYPE_TLSA: {
650       unsigned int usage;
651       unsigned int selector;
652       unsigned int matching_type;
653       size_t slen = strlen (s) + 1;
654       char hex[slen];
655
656       if (4 != sscanf (s, "%u %u %u %s", &usage, &selector, &matching_type,
657                        hex))
658       {
659         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
660                     _ ("Unable to parse TLSA record string `%s'\n"),
661                     s);
662         *data_size = 0;
663         return GNUNET_SYSERR;
664       }
665
666       *data_size = sizeof(struct GNUNET_TUN_DnsTlsaRecord) + strlen (hex) / 2;
667       *data = tlsa = GNUNET_malloc (*data_size);
668       tlsa->usage = (uint8_t) usage;
669       tlsa->selector = (uint8_t) selector;
670       tlsa->matching_type = (uint8_t) matching_type;
671       if (strlen (hex) / 2 != GNUNET_DNSPARSER_hex_to_bin (hex, &tlsa[1]))
672       {
673         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674                     _ ("Unable to parse TLSA record string `%s'\n"),
675                     s);
676         GNUNET_free (*data);
677         *data = NULL;
678         *data_size = 0;
679         return GNUNET_SYSERR;
680       }
681       return GNUNET_OK;
682     }
683
684   case GNUNET_DNSPARSER_TYPE_CAA: {   // RFC6844
685       struct GNUNET_DNSPARSER_CaaRecord *caa;
686       unsigned int flags;
687       char tag[15]; // Max tag length 15
688       char value[strlen (s) + 1]; // Should be more than enough
689
690       if (3 != sscanf (s, "%u %s %[^\n]", &flags, tag, value))
691       {
692         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693                     _ ("Unable to parse CAA record string `%s'\n"),
694                     s);
695         *data_size = 0;
696         return GNUNET_SYSERR;
697       }
698       *data_size = sizeof(struct GNUNET_DNSPARSER_CaaRecord) + strlen (tag)
699                    + strlen (value);
700       *data = caa = GNUNET_malloc (*data_size);
701       caa->flags = flags;
702       memcpy (&caa[1], tag, strlen (tag));
703       caa->tag_len = strlen (tag);
704       memcpy ((char *) &caa[1] + caa->tag_len, value, strlen (value));
705       return GNUNET_OK;
706     }
707
708   default:
709     return GNUNET_SYSERR;
710   }
711 }
712
713
714 /**
715  * Mapping of record type numbers to human-readable
716  * record type names.
717  */
718 static struct
719 {
720   const char *name;
721   uint32_t number;
722 } name_map[] = { { "A", GNUNET_DNSPARSER_TYPE_A },
723                  { "NS", GNUNET_DNSPARSER_TYPE_NS },
724                  { "CNAME", GNUNET_DNSPARSER_TYPE_CNAME },
725                  { "SOA", GNUNET_DNSPARSER_TYPE_SOA },
726                  { "PTR", GNUNET_DNSPARSER_TYPE_PTR },
727                  { "MX", GNUNET_DNSPARSER_TYPE_MX },
728                  { "TXT", GNUNET_DNSPARSER_TYPE_TXT },
729                  { "AAAA", GNUNET_DNSPARSER_TYPE_AAAA },
730                  { "SRV", GNUNET_DNSPARSER_TYPE_SRV },
731                  { "TLSA", GNUNET_DNSPARSER_TYPE_TLSA },
732                  { "CERT", GNUNET_DNSPARSER_TYPE_CERT },
733                  { "CAA", GNUNET_DNSPARSER_TYPE_CAA },
734                  { NULL, UINT32_MAX } };
735
736
737 /**
738  * Convert a type name (i.e. "AAAA") to the corresponding number.
739  *
740  * @param cls closure, unused
741  * @param dns_typename name to convert
742  * @return corresponding number, UINT32_MAX on error
743  */
744 static uint32_t
745 dns_typename_to_number (void *cls, const char *dns_typename)
746 {
747   unsigned int i;
748
749   i = 0;
750   while ((NULL != name_map[i].name) &&
751          (0 != strcasecmp (dns_typename, name_map[i].name)))
752     i++;
753   return name_map[i].number;
754 }
755
756
757 /**
758  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
759  *
760  * @param cls closure, unused
761  * @param type number of a type to convert
762  * @return corresponding typestring, NULL on error
763  */
764 static const char *
765 dns_number_to_typename (void *cls, uint32_t type)
766 {
767   unsigned int i;
768
769   i = 0;
770   while ((NULL != name_map[i].name) && (type != name_map[i].number))
771     i++;
772   return name_map[i].name;
773 }
774
775
776 /**
777  * Entry point for the plugin.
778  *
779  * @param cls NULL
780  * @return the exported block API
781  */
782 void *
783 libgnunet_plugin_gnsrecord_dns_init (void *cls)
784 {
785   struct GNUNET_GNSRECORD_PluginFunctions *api;
786
787   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
788   api->value_to_string = &dns_value_to_string;
789   api->string_to_value = &dns_string_to_value;
790   api->typename_to_number = &dns_typename_to_number;
791   api->number_to_typename = &dns_number_to_typename;
792   return api;
793 }
794
795
796 /**
797  * Exit point from the plugin.
798  *
799  * @param cls the return value from #libgnunet_plugin_block_test_init
800  * @return NULL
801  */
802 void *
803 libgnunet_plugin_gnsrecord_dns_done (void *cls)
804 {
805   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
806
807   GNUNET_free (api);
808   return NULL;
809 }
810
811
812 /* end of plugin_gnsrecord_dns.c */