- FIX: GNUNET_SET_STATUS_HALF_DONE is never called only GNUNET_SET_STATUS_DONE
[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_MX:
144     {
145       struct GNUNET_DNSPARSER_MxRecord *mx;
146       size_t off;
147
148       off = 0;
149       mx = GNUNET_DNSPARSER_parse_mx (data,
150                                       data_size,
151                                       &off);
152       if ( (NULL == mx) ||
153            (off != data_size) )
154       {
155         GNUNET_break_op (0);
156         GNUNET_free_non_null (mx);
157         return NULL;
158       }
159       GNUNET_asprintf (&result,
160                        "%hu,%s",
161                        mx->preference,
162                        mx->mxhost);
163       GNUNET_DNSPARSER_free_mx (mx);
164       return result;
165     }
166   case GNUNET_DNSPARSER_TYPE_TXT:
167     return GNUNET_strndup (data, data_size);
168   case GNUNET_DNSPARSER_TYPE_AAAA:
169     if (data_size != sizeof (struct in6_addr))
170       return NULL;
171     if (NULL == inet_ntop (AF_INET6, data, tmp, sizeof (tmp)))
172       return NULL;
173     return GNUNET_strdup (tmp);
174   case GNUNET_DNSPARSER_TYPE_SRV:
175     {
176       struct GNUNET_DNSPARSER_SrvRecord *srv;
177       size_t off;
178
179       off = 0;
180       srv = GNUNET_DNSPARSER_parse_srv ("+", /* FIXME: is this OK? */
181                                         data,
182                                         data_size,
183                                         &off);
184       if ( (NULL == srv) ||
185            (off != data_size) )
186       {
187         GNUNET_break_op (0);
188         if (NULL != srv)
189           GNUNET_DNSPARSER_free_srv (srv);
190         return NULL;
191       }
192       GNUNET_asprintf (&result,
193                        "%d %d %d _%s._%s.%s",
194                        srv->priority,
195                        srv->weight,
196                        srv->port,
197                        srv->service,
198                        srv->proto,
199                        srv->domain_name);
200       GNUNET_DNSPARSER_free_srv (srv);
201       return result;
202     }
203   case GNUNET_DNSPARSER_TYPE_TLSA:
204     {
205       const struct GNUNET_TUN_DnsTlsaRecord *tlsa;
206       char* tlsa_str;
207
208       cdata = data;
209       if ( (data_size <= sizeof (struct GNUNET_TUN_DnsTlsaRecord)) ||
210            ('\0' != cdata[data_size - 1]) )
211         return NULL; /* malformed */
212       tlsa = data;
213       if (0 == GNUNET_asprintf (&tlsa_str,
214                                 "%c %c %c %s",
215                                 tlsa->usage,
216                                 tlsa->selector,
217                                 tlsa->matching_type,
218                                 (const char *) &tlsa[1]))
219       {
220         GNUNET_free (tlsa_str);
221         return NULL;
222       }
223       return tlsa_str;
224     }
225   default:
226     return NULL;
227   }
228 }
229
230
231 /**
232  * Convert human-readable version of a 'value' of a record to the binary
233  * representation.
234  *
235  * @param cls closure, unused
236  * @param type type of the record
237  * @param s human-readable string
238  * @param data set to value in binary encoding (will be allocated)
239  * @param data_size set to number of bytes in @a data
240  * @return #GNUNET_OK on success
241  */
242 static int
243 dns_string_to_value (void *cls,
244                      uint32_t type,
245                      const char *s,
246                      void **data,
247                      size_t *data_size)
248 {
249   struct in_addr value_a;
250   struct in6_addr value_aaaa;
251   struct GNUNET_TUN_DnsTlsaRecord *tlsa;
252
253   if (NULL == s)
254     return GNUNET_SYSERR;
255   switch (type)
256   {
257   case GNUNET_DNSPARSER_TYPE_A:
258     if (1 != inet_pton (AF_INET, s, &value_a))
259     {
260       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
261                   _("Unable to parse IPv4 address `%s'\n"),
262                   s);
263       return GNUNET_SYSERR;
264     }
265     *data = GNUNET_malloc (sizeof (struct in_addr));
266     memcpy (*data, &value_a, sizeof (value_a));
267     *data_size = sizeof (value_a);
268     return GNUNET_OK;
269   case GNUNET_DNSPARSER_TYPE_NS:
270     {
271       char nsbuf[256];
272       size_t off;
273
274       off = 0;
275       if (GNUNET_OK !=
276           GNUNET_DNSPARSER_builder_add_name (nsbuf,
277                                              sizeof (nsbuf),
278                                              &off,
279                                              s))
280       {
281         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282              _("Failed to serialize NS record with value `%s'\n"),
283              s);
284         return GNUNET_SYSERR;
285       }
286       *data_size = off;
287       *data = GNUNET_malloc (off);
288       memcpy (*data, nsbuf, off);
289       return GNUNET_OK;
290     }
291   case GNUNET_DNSPARSER_TYPE_CNAME:
292     {
293       char cnamebuf[256];
294       size_t off;
295
296       off = 0;
297       if (GNUNET_OK !=
298           GNUNET_DNSPARSER_builder_add_name (cnamebuf,
299                                              sizeof (cnamebuf),
300                                              &off,
301                                              s))
302       {
303         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
304              _("Failed to serialize CNAME record with value `%s'\n"),
305              s);
306         return GNUNET_SYSERR;
307       }
308       *data_size = off;
309       *data = GNUNET_malloc (off);
310       memcpy (*data, cnamebuf, off);
311       return GNUNET_OK;
312     }
313   case GNUNET_DNSPARSER_TYPE_SOA:
314     {
315       struct GNUNET_DNSPARSER_SoaRecord soa;
316       char soabuf[540];
317       char soa_rname[253 + 1];
318       char soa_mname[253 + 1];
319       unsigned int soa_serial;
320       unsigned int soa_refresh;
321       unsigned int soa_retry;
322       unsigned int soa_expire;
323       unsigned int soa_min;
324       size_t off;
325
326       if (7 != SSCANF (s,
327                        "rname=%253s mname=%253s %u,%u,%u,%u,%u",
328                        soa_rname, soa_mname,
329                        &soa_serial, &soa_refresh, &soa_retry, &soa_expire, &soa_min))
330       {
331         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
332              _("Unable to parse SOA record `%s'\n"),
333              s);
334         return GNUNET_SYSERR;
335       }
336       soa.mname = soa_mname;
337       soa.rname = soa_rname;
338       soa.serial = (uint32_t) soa_serial;
339       soa.refresh =(uint32_t)  soa_refresh;
340       soa.retry = (uint32_t) soa_retry;
341       soa.expire = (uint32_t) soa_expire;
342       soa.minimum_ttl = (uint32_t) soa_min;
343       off = 0;
344       if (GNUNET_OK !=
345           GNUNET_DNSPARSER_builder_add_soa (soabuf,
346                                             sizeof (soabuf),
347                                             &off,
348                                             &soa))
349       {
350         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351              _("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
352              soa_mname,
353              soa_rname);
354         return GNUNET_SYSERR;
355       }
356       *data_size = off;
357       *data = GNUNET_malloc (off);
358       memcpy (*data, soabuf, off);
359       return GNUNET_OK;
360     }
361   case GNUNET_DNSPARSER_TYPE_PTR:
362     {
363       char ptrbuf[256];
364       size_t off;
365
366       off = 0;
367       if (GNUNET_OK !=
368           GNUNET_DNSPARSER_builder_add_name (ptrbuf,
369                                              sizeof (ptrbuf),
370                                              &off,
371                                              s))
372       {
373         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
374              _("Failed to serialize PTR record with value `%s'\n"),
375              s);
376         return GNUNET_SYSERR;
377       }
378       *data_size = off;
379       *data = GNUNET_malloc (off);
380       memcpy (*data, ptrbuf, off);
381       return GNUNET_OK;
382     }
383   case GNUNET_DNSPARSER_TYPE_MX:
384     {
385       struct GNUNET_DNSPARSER_MxRecord mx;
386       char mxbuf[258];
387       char mxhost[253 + 1];
388       uint16_t mx_pref;
389       size_t off;
390
391       if (2 != SSCANF(s, "%hu,%253s", &mx_pref, mxhost))
392       {
393         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
394              _("Unable to parse MX record `%s'\n"),
395              s);
396       return GNUNET_SYSERR;
397       }
398       mx.preference = mx_pref;
399       mx.mxhost = mxhost;
400       off = 0;
401
402       if (GNUNET_OK !=
403           GNUNET_DNSPARSER_builder_add_mx (mxbuf,
404                                            sizeof (mxbuf),
405                                            &off,
406                                            &mx))
407       {
408         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
409              _("Failed to serialize MX record with hostname `%s'\n"),
410              mxhost);
411         return GNUNET_SYSERR;
412       }
413       *data_size = off;
414       *data = GNUNET_malloc (off);
415       memcpy (*data, mxbuf, off);
416       return GNUNET_OK;
417     }
418   case GNUNET_DNSPARSER_TYPE_SRV:
419     GNUNET_break (0); // FIXME: not implemented!
420     return GNUNET_SYSERR;
421   case GNUNET_DNSPARSER_TYPE_TXT:
422     *data = GNUNET_strdup (s);
423     *data_size = strlen (s);
424     return GNUNET_OK;
425   case GNUNET_DNSPARSER_TYPE_AAAA:
426     if (1 != inet_pton (AF_INET6, s, &value_aaaa))
427     {
428       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429            _("Unable to parse IPv6 address `%s'\n"),
430            s);
431       return GNUNET_SYSERR;
432     }
433     *data = GNUNET_malloc (sizeof (struct in6_addr));
434     *data_size = sizeof (struct in6_addr);
435     memcpy (*data, &value_aaaa, sizeof (value_aaaa));
436     return GNUNET_OK;
437   case GNUNET_DNSPARSER_TYPE_TLSA:
438     *data_size = sizeof (struct GNUNET_TUN_DnsTlsaRecord) + strlen (s) - 6;
439     *data = tlsa = GNUNET_malloc (*data_size);
440     if (4 != SSCANF (s, "%c %c %c %s",
441                      &tlsa->usage,
442                      &tlsa->selector,
443                      &tlsa->matching_type,
444                      (char*)&tlsa[1]))
445     {
446       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
447                   _("Unable to parse TLSA record string `%s'\n"),
448                   s);
449       *data_size = 0;
450       GNUNET_free (tlsa);
451       return GNUNET_SYSERR;
452     }
453     return GNUNET_OK;
454   default:
455     return GNUNET_SYSERR;
456   }
457 }
458
459
460 /**
461  * Mapping of record type numbers to human-readable
462  * record type names.
463  */
464 static struct {
465   const char *name;
466   uint32_t number;
467 } name_map[] = {
468   { "A", GNUNET_DNSPARSER_TYPE_A },
469   { "NS", GNUNET_DNSPARSER_TYPE_NS },
470   { "CNAME", GNUNET_DNSPARSER_TYPE_CNAME },
471   { "SOA", GNUNET_DNSPARSER_TYPE_SOA },
472   { "PTR", GNUNET_DNSPARSER_TYPE_PTR },
473   { "MX", GNUNET_DNSPARSER_TYPE_MX },
474   { "TXT", GNUNET_DNSPARSER_TYPE_TXT },
475   { "AAAA", GNUNET_DNSPARSER_TYPE_AAAA },
476   { "TLSA", GNUNET_DNSPARSER_TYPE_TLSA },
477   { NULL, UINT32_MAX }
478 };
479
480
481 /**
482  * Convert a type name (i.e. "AAAA") to the corresponding number.
483  *
484  * @param cls closure, unused
485  * @param dns_typename name to convert
486  * @return corresponding number, UINT32_MAX on error
487  */
488 static uint32_t
489 dns_typename_to_number (void *cls,
490                         const char *dns_typename)
491 {
492   unsigned int i;
493
494   i=0;
495   while ( (name_map[i].name != NULL) &&
496           (0 != strcasecmp (dns_typename, name_map[i].name)) )
497     i++;
498   return name_map[i].number;
499 }
500
501
502 /**
503  * Convert a type number (i.e. 1) to the corresponding type string (i.e. "A")
504  *
505  * @param cls closure, unused
506  * @param type number of a type to convert
507  * @return corresponding typestring, NULL on error
508  */
509 static const char *
510 dns_number_to_typename (void *cls,
511                         uint32_t type)
512 {
513   unsigned int i;
514
515   i=0;
516   while ( (name_map[i].name != NULL) &&
517           (type != name_map[i].number) )
518     i++;
519   return name_map[i].name;
520 }
521
522
523 /**
524  * Entry point for the plugin.
525  *
526  * @param cls NULL
527  * @return the exported block API
528  */
529 void *
530 libgnunet_plugin_gnsrecord_dns_init (void *cls)
531 {
532   struct GNUNET_GNSRECORD_PluginFunctions *api;
533
534   api = GNUNET_new (struct GNUNET_GNSRECORD_PluginFunctions);
535   api->value_to_string = &dns_value_to_string;
536   api->string_to_value = &dns_string_to_value;
537   api->typename_to_number = &dns_typename_to_number;
538   api->number_to_typename = &dns_number_to_typename;
539   return api;
540 }
541
542
543 /**
544  * Exit point from the plugin.
545  *
546  * @param cls the return value from #libgnunet_plugin_block_test_init
547  * @return NULL
548  */
549 void *
550 libgnunet_plugin_gnsrecord_dns_done (void *cls)
551 {
552   struct GNUNET_GNSRECORD_PluginFunctions *api = cls;
553
554   GNUNET_free (api);
555   return NULL;
556 }
557
558 /* end of plugin_gnsrecord_dns.c */