- document string termination
[oweals/gnunet.git] / src / gnsrecord / plugin_gnsrecord_dns.c
1 /*
2      This file is part of GNUnet
3      (C) 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet_gnsrecord_plugin.h"
31
32
33 /**
34  * Convert the 'value' of a record to a string.
35  *
36  * @param cls closure, unused
37  * @param type type of the record
38  * @param data value in binary encoding
39  * @param data_size number of bytes in @a data
40  * @return NULL on error, otherwise human-readable representation of the value
41  */
42 static char *
43 dns_value_to_string (void *cls,
44                      uint32_t type,
45                      const void *data,
46                      size_t data_size)
47 {
48   const char *cdata;
49   char* result;
50   char tmp[INET6_ADDRSTRLEN];
51
52   switch (type)
53   {
54   case GNUNET_DNSPARSER_TYPE_A:
55     if (data_size != sizeof (struct in_addr))
56       return NULL;
57     if (NULL == inet_ntop (AF_INET, data, tmp, sizeof (tmp)))
58       return NULL;
59     return GNUNET_strdup (tmp);
60   case GNUNET_DNSPARSER_TYPE_NS:
61     {
62       char *ns;
63       size_t off;
64
65       off = 0;
66       ns = GNUNET_DNSPARSER_parse_name (data,
67                                         data_size,
68                                         &off);
69       if ( (NULL == ns) ||
70            (off != data_size) )
71       {
72         GNUNET_break_op (0);
73         GNUNET_free_non_null (ns);
74         return NULL;
75       }
76       return ns;
77     }
78   case GNUNET_DNSPARSER_TYPE_CNAME:
79     {
80       char *cname;
81       size_t off;
82
83       off = 0;
84       cname = GNUNET_DNSPARSER_parse_name (data,
85                                            data_size,
86                                            &off);
87       if ( (NULL == cname) ||
88            (off != data_size) )
89       {
90         GNUNET_break_op (0);
91         GNUNET_free_non_null (cname);
92         return NULL;
93       }
94       return cname;
95     }
96   case GNUNET_DNSPARSER_TYPE_SOA:
97     {
98       struct GNUNET_DNSPARSER_SoaRecord *soa;
99       size_t off;
100
101       off = 0;
102       soa = GNUNET_DNSPARSER_parse_soa (data,
103                                         data_size,
104                                         &off);
105       if ( (NULL == soa) ||
106            (off != data_size) )
107       {
108         GNUNET_break_op (0);
109         if (NULL != soa)
110           GNUNET_DNSPARSER_free_soa (soa);
111         return NULL;
112       }
113       GNUNET_asprintf (&result,
114                        "rname=%s mname=%s %lu,%lu,%lu,%lu,%lu",
115                        soa->rname,
116                        soa->mname,
117                        soa->serial,
118                        soa->refresh,
119                        soa->retry,
120                        soa->expire,
121                        soa->minimum_ttl);
122       GNUNET_DNSPARSER_free_soa (soa);
123       return result;
124     }
125   case GNUNET_DNSPARSER_TYPE_PTR:
126     {
127       char *ptr;
128       size_t off;
129
130       off = 0;
131       ptr = GNUNET_DNSPARSER_parse_name (data,
132                                            data_size,
133                                            &off);
134       if ( (NULL == ptr) ||
135            (off != data_size) )
136       {
137         GNUNET_break_op (0);
138         GNUNET_free_non_null (ptr);
139         return NULL;
140       }
141       return ptr;
142     }
143   case GNUNET_DNSPARSER_TYPE_CERT:
144     {
145       struct GNUNET_DNSPARSER_CertRecord *cert;
146       size_t off;
147       char *base64;
148       int len;
149
150       off = 0;
151       cert = GNUNET_DNSPARSER_parse_cert (data,
152                                           data_size,
153                                           &off);
154       if ( (NULL == cert) ||
155            (off != data_size) )
156       {
157         GNUNET_break_op (0);
158         GNUNET_DNSPARSER_free_cert (cert);
159         return NULL;
160       }
161       len = GNUNET_STRINGS_base64_encode (cert->certificate_data,
162                                           cert->certificate_size,
163                                           &base64);
164       GNUNET_asprintf (&result,
165                        "%u %u %u %.*s",
166                        cert->cert_type,
167                        cert->cert_tag,
168                        cert->algorithm,
169                        len,
170                        base64);
171       GNUNET_free (base64);
172       GNUNET_DNSPARSER_free_cert (cert);
173       return result;
174     }
175   case GNUNET_DNSPARSER_TYPE_MX:
176     {
177       struct GNUNET_DNSPARSER_MxRecord *mx;
178       size_t off;
179
180       off = 0;
181       mx = GNUNET_DNSPARSER_parse_mx (data,
182                                       data_size,
183                                       &off);
184       if ( (NULL == mx) ||
185            (off != data_size) )
186       {
187         GNUNET_break_op (0);
188         GNUNET_DNSPARSER_free_mx (mx);
189         return NULL;
190       }
191       GNUNET_asprintf (&result,
192                        "%hu,%s",
193                        mx->preference,
194                        mx->mxhost);
195       GNUNET_DNSPARSER_free_mx (mx);
196       return result;
197     }
198   case GNUNET_DNSPARSER_TYPE_TXT:
199     return GNUNET_strndup (data, data_size);
200   case GNUNET_DNSPARSER_TYPE_AAAA:
201     if (data_size != sizeof (struct in6_addr))
202       return NULL;
203     if (NULL == inet_ntop (AF_INET6, data, tmp, sizeof (tmp)))
204       return NULL;
205     return GNUNET_strdup (tmp);
206   case GNUNET_DNSPARSER_TYPE_SRV:
207     {
208       struct GNUNET_DNSPARSER_SrvRecord *srv;
209       size_t off;
210
211       off = 0;
212       srv = GNUNET_DNSPARSER_parse_srv ("+", /* FIXME: is this OK? */
213                                         data,
214                                         data_size,
215                                         &off);
216       if ( (NULL == srv) ||
217            (off != data_size) )
218       {
219         GNUNET_break_op (0);
220         if (NULL != srv)
221           GNUNET_DNSPARSER_free_srv (srv);
222         return NULL;
223       }
224       GNUNET_asprintf (&result,
225                        "%d %d %d _%s._%s.%s",
226                        srv->priority,
227                        srv->weight,
228                        srv->port,
229                        srv->service,
230                        srv->proto,
231                        srv->domain_name);
232       GNUNET_DNSPARSER_free_srv (srv);
233       return result;
234     }
235   case GNUNET_DNSPARSER_TYPE_TLSA:
236     {
237       const struct GNUNET_TUN_DnsTlsaRecord *tlsa;
238       char* tlsa_str;
239
240       cdata = data;
241       if ( (data_size <= sizeof (struct GNUNET_TUN_DnsTlsaRecord)) ||
242            ('\0' != cdata[data_size - 1]) )
243         return NULL; /* malformed */
244       tlsa = data;
245       if (0 == GNUNET_asprintf (&tlsa_str,
246                                 "%c %c %c %s",
247                                 tlsa->usage,
248                                 tlsa->selector,
249                                 tlsa->matching_type,
250                                 (const char *) &tlsa[1]))
251       {
252         GNUNET_free (tlsa_str);
253         return NULL;
254       }
255       return tlsa_str;
256     }
257   default:
258     return NULL;
259   }
260 }
261
262
263 /**
264  * Convert human-readable version of a 'value' of a record to the binary
265  * representation.
266  *
267  * @param cls closure, unused
268  * @param type type of the record
269  * @param s human-readable string
270  * @param data set to value in binary encoding (will be allocated)
271  * @param data_size set to number of bytes in @a data
272  * @return #GNUNET_OK on success
273  */
274 static int
275 dns_string_to_value (void *cls,
276                      uint32_t type,
277                      const char *s,
278                      void **data,
279                      size_t *data_size)
280 {
281   struct in_addr value_a;
282   struct in6_addr value_aaaa;
283   struct GNUNET_TUN_DnsTlsaRecord *tlsa;
284
285   if (NULL == s)
286     return GNUNET_SYSERR;
287   switch (type)
288   {
289   case GNUNET_DNSPARSER_TYPE_A:
290     if (1 != inet_pton (AF_INET, s, &value_a))
291     {
292       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
293                   _("Unable to parse IPv4 address `%s'\n"),
294                   s);
295       return GNUNET_SYSERR;
296     }
297     *data = GNUNET_new (struct in_addr);
298     memcpy (*data, &value_a, sizeof (value_a));
299     *data_size = sizeof (value_a);
300     return GNUNET_OK;
301   case GNUNET_DNSPARSER_TYPE_NS:
302     {
303       char nsbuf[256];
304       size_t off;
305
306       off = 0;
307       if (GNUNET_OK !=
308           GNUNET_DNSPARSER_builder_add_name (nsbuf,
309                                              sizeof (nsbuf),
310                                              &off,
311                                              s))
312       {
313         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
314              _("Failed to serialize NS record with value `%s'\n"),
315              s);
316         return GNUNET_SYSERR;
317       }
318       *data_size = off;
319       *data = GNUNET_malloc (off);
320       memcpy (*data, nsbuf, off);
321       return GNUNET_OK;
322     }
323   case GNUNET_DNSPARSER_TYPE_CNAME:
324     {
325       char cnamebuf[256];
326       size_t off;
327
328       off = 0;
329       if (GNUNET_OK !=
330           GNUNET_DNSPARSER_builder_add_name (cnamebuf,
331                                              sizeof (cnamebuf),
332                                              &off,
333                                              s))
334       {
335         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336              _("Failed to serialize CNAME record with value `%s'\n"),
337              s);
338         return GNUNET_SYSERR;
339       }
340       *data_size = off;
341       *data = GNUNET_malloc (off);
342       memcpy (*data, cnamebuf, off);
343       return GNUNET_OK;
344     }
345   case GNUNET_DNSPARSER_TYPE_CERT:
346     {
347       char *sdup;
348       const char *typep;
349       const char *keyp;
350       const char *algp;
351       const char *certp;
352       unsigned int type;
353       unsigned int key;
354       unsigned int alg;
355       size_t cert_size;
356       char *cert_data;
357       struct GNUNET_DNSPARSER_CertRecord cert;
358
359       sdup = GNUNET_strdup (s);
360       typep = strtok (sdup, " ");
361       /* TODO: add typep mnemonic conversion according to RFC 4398 */
362       if ( (NULL == typep) ||
363            (1 != sscanf (typep,
364                          "%u",
365                          &type)) ||
366            (type > UINT16_MAX) )
367       {
368         GNUNET_free (sdup);
369         return GNUNET_SYSERR;
370       }
371       keyp = strtok (NULL, " ");
372       if ( (NULL == keyp) ||
373            (1 != sscanf (keyp,
374                          "%u",
375                          &key)) ||
376            (key > UINT16_MAX) )
377       {
378         GNUNET_free (sdup);
379         return GNUNET_SYSERR;
380       }
381       algp = strtok (NULL, " ");
382       /* TODO: add algp mnemonic conversion according to RFC 4398/RFC 4034 */
383       if ( (NULL == algp) ||
384            (1 != sscanf (algp,
385                          "%u",
386                          &alg)) ||
387            (alg > UINT8_MAX) )
388       {
389         GNUNET_free (sdup);
390         return GNUNET_SYSERR;
391       }
392       certp = strtok (NULL, " ");
393       if ( (NULL == certp) ||
394            (0 == strlen (certp) ) )
395       {
396         GNUNET_free (sdup);
397         return GNUNET_SYSERR;
398       }
399       cert_size = GNUNET_STRINGS_base64_decode (certp,
400                                                 strlen (certp),
401                                                 &cert_data);
402       GNUNET_free (sdup);
403       cert.cert_type = type;
404       cert.cert_tag = key;
405       cert.algorithm = alg;
406       cert.certificate_size = cert_size;
407       cert.certificate_data = cert_data;
408       {
409         char certbuf[cert_size + sizeof (struct GNUNET_TUN_DnsCertRecord)];
410         size_t off;
411
412         off = 0;
413         if (GNUNET_OK !=
414             GNUNET_DNSPARSER_builder_add_cert (certbuf,
415                                                sizeof (certbuf),
416                                                &off,
417                                                &cert))
418         {
419           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
420                       _("Failed to serialize CERT record with %u bytes\n"),
421                       (unsigned int) cert_size);
422           GNUNET_free (cert_data);
423           return GNUNET_SYSERR;
424         }
425         GNUNET_free (cert_data);
426         *data_size = off;
427         *data = GNUNET_malloc (off);
428         memcpy (*data, certbuf, off);
429       }
430       GNUNET_free (cert_data);
431       return GNUNET_OK;
432     }
433   case GNUNET_DNSPARSER_TYPE_SOA:
434     {
435       struct GNUNET_DNSPARSER_SoaRecord soa;
436       char soabuf[540];
437       char soa_rname[253 + 1];
438       char soa_mname[253 + 1];
439       unsigned int soa_serial;
440       unsigned int soa_refresh;
441       unsigned int soa_retry;
442       unsigned int soa_expire;
443       unsigned int soa_min;
444       size_t off;
445
446       if (7 != SSCANF (s,
447                        "rname=%253s mname=%253s %u,%u,%u,%u,%u",
448                        soa_rname, soa_mname,
449                        &soa_serial, &soa_refresh, &soa_retry, &soa_expire, &soa_min))
450       {
451         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
452              _("Unable to parse SOA record `%s'\n"),
453              s);
454         return GNUNET_SYSERR;
455       }
456       soa.mname = soa_mname;
457       soa.rname = soa_rname;
458       soa.serial = (uint32_t) soa_serial;
459       soa.refresh =(uint32_t)  soa_refresh;
460       soa.retry = (uint32_t) soa_retry;
461       soa.expire = (uint32_t) soa_expire;
462       soa.minimum_ttl = (uint32_t) soa_min;
463       off = 0;
464       if (GNUNET_OK !=
465           GNUNET_DNSPARSER_builder_add_soa (soabuf,
466                                             sizeof (soabuf),
467                                             &off,
468                                             &soa))
469       {
470         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
471              _("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
472              soa_mname,
473              soa_rname);
474         return GNUNET_SYSERR;
475       }
476       *data_size = off;
477       *data = GNUNET_malloc (off);
478       memcpy (*data, soabuf, off);
479       return GNUNET_OK;
480     }
481   case GNUNET_DNSPARSER_TYPE_PTR:
482     {
483       char ptrbuf[256];
484       size_t off;
485
486       off = 0;
487       if (GNUNET_OK !=
488           GNUNET_DNSPARSER_builder_add_name (ptrbuf,
489                                              sizeof (ptrbuf),
490                                              &off,
491                                              s))
492       {
493         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
494              _("Failed to serialize PTR record with value `%s'\n"),
495              s);
496         return GNUNET_SYSERR;
497       }
498       *data_size = off;
499       *data = GNUNET_malloc (off);
500       memcpy (*data, ptrbuf, off);
501       return GNUNET_OK;
502     }
503   case GNUNET_DNSPARSER_TYPE_MX:
504     {
505       struct GNUNET_DNSPARSER_MxRecord mx;
506       char mxbuf[258];
507       char mxhost[253 + 1];
508       uint16_t mx_pref;
509       size_t off;
510
511       if (2 != SSCANF(s, "%hu,%253s", &mx_pref, mxhost))
512       {
513         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514              _("Unable to parse MX record `%s'\n"),
515              s);
516       return GNUNET_SYSERR;
517       }
518       mx.preference = mx_pref;
519       mx.mxhost = mxhost;
520       off = 0;
521
522       if (GNUNET_OK !=
523           GNUNET_DNSPARSER_builder_add_mx (mxbuf,
524                                            sizeof (mxbuf),
525                                            &off,
526                                            &mx))
527       {
528         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
529              _("Failed to serialize MX record with hostname `%s'\n"),
530              mxhost);
531         return GNUNET_SYSERR;
532       }
533       *data_size = off;
534       *data = GNUNET_malloc (off);
535       memcpy (*data, mxbuf, off);
536       return GNUNET_OK;
537     }
538   case GNUNET_DNSPARSER_TYPE_SRV:
539     GNUNET_break (0); // FIXME: not implemented!
540     return GNUNET_SYSERR;
541   case GNUNET_DNSPARSER_TYPE_TXT:
542     *data = GNUNET_strdup (s);
543     *data_size = strlen (s);
544     return GNUNET_OK;
545   case GNUNET_DNSPARSER_TYPE_AAAA:
546     if (1 != inet_pton (AF_INET6, s, &value_aaaa))
547     {
548       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549            _("Unable to parse IPv6 address `%s'\n"),
550            s);
551       return GNUNET_SYSERR;
552     }
553     *data = GNUNET_new (struct in6_addr);
554     *data_size = sizeof (struct in6_addr);
555     memcpy (*data, &value_aaaa, sizeof (value_aaaa));
556     return GNUNET_OK;
557   case GNUNET_DNSPARSER_TYPE_TLSA:
558     *data_size = sizeof (struct GNUNET_TUN_DnsTlsaRecord) + strlen (s) - 6;
559     *data = tlsa = GNUNET_malloc (*data_size);
560     if (4 != SSCANF (s, "%c %c %c %s",
561                      &tlsa->usage,
562                      &tlsa->selector,
563                      &tlsa->matching_type,
564                      (char*)&tlsa[1]))
565     {
566       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
567                   _("Unable to parse TLSA record string `%s'\n"),
568                   s);
569       *data_size = 0;
570       GNUNET_free (tlsa);
571       return GNUNET_SYSERR;
572     }
573     return GNUNET_OK;
574   default:
575     return GNUNET_SYSERR;
576   }
577 }
578
579
580 /**
581  * Mapping of record type numbers to human-readable
582  * record type names.
583  */
584 static struct {
585   const char *name;
586   uint32_t number;
587 } name_map[] = {
588   { "A", GNUNET_DNSPARSER_TYPE_A },
589   { "NS", GNUNET_DNSPARSER_TYPE_NS },
590   { "CNAME", GNUNET_DNSPARSER_TYPE_CNAME },
591   { "SOA", GNUNET_DNSPARSER_TYPE_SOA },
592   { "PTR", GNUNET_DNSPARSER_TYPE_PTR },
593   { "MX", GNUNET_DNSPARSER_TYPE_MX },
594   { "TXT", GNUNET_DNSPARSER_TYPE_TXT },
595   { "AAAA", GNUNET_DNSPARSER_TYPE_AAAA },
596   { "TLSA", GNUNET_DNSPARSER_TYPE_TLSA },
597   { NULL, UINT32_MAX }
598 };
599
600
601 /**
602  * Convert a type name (i.e. "AAAA") to the corresponding number.
603  *
604  * @param cls closure, unused
605  * @param dns_typename name to convert
606  * @return corresponding number, UINT32_MAX on error
607  */
608 static uint32_t
609 dns_typename_to_number (void *cls,
610                         const char *dns_typename)
611 {
612   unsigned int i;
613
614   i=0;
615   while ( (name_map[i].name != NULL) &&
616           (0 != strcasecmp (dns_typename, name_map[i].name)) )
617     i++;
618   return name_map[i].number;
619 }
620
621
622 /**
623  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
624  *
625  * @param cls closure, unused
626  * @param type number of a type to convert
627  * @return corresponding typestring, NULL on error
628  */
629 static const char *
630 dns_number_to_typename (void *cls,
631                         uint32_t type)
632 {
633   unsigned int i;
634
635   i=0;
636   while ( (name_map[i].name != NULL) &&
637           (type != name_map[i].number) )
638     i++;
639   return name_map[i].name;
640 }
641
642
643 /**
644  * Entry point for the plugin.
645  *
646  * @param cls NULL
647  * @return the exported block API
648  */
649 void *
650 libgnunet_plugin_gnsrecord_dns_init (void *cls)
651 {
652   struct GNUNET_GNSRECORD_PluginFunctions *api;
653
654   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
655   api->value_to_string = &dns_value_to_string;
656   api->string_to_value = &dns_string_to_value;
657   api->typename_to_number = &dns_typename_to_number;
658   api->number_to_typename = &dns_number_to_typename;
659   return api;
660 }
661
662
663 /**
664  * Exit point from the plugin.
665  *
666  * @param cls the return value from #libgnunet_plugin_block_test_init
667  * @return NULL
668  */
669 void *
670 libgnunet_plugin_gnsrecord_dns_done (void *cls)
671 {
672   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
673
674   GNUNET_free (api);
675   return NULL;
676 }
677
678 /* end of plugin_gnsrecord_dns.c */