- test and log for bug #0003423
[oweals/gnunet.git] / src / gnsrecord / plugin_gnsrecord_dns.c
index e5475118dcc9142e65adcd99d1c3ed39956eab11..0761e687fff23ea9e0217c1bd0e7201fa7cfa052 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet
-     (C) 2013 Christian Grothoff (and other contributing authors)
+     (C) 2013, 2014 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -23,7 +23,6 @@
  * @brief gnsrecord plugin to provide the API for basic DNS records
  * @author Christian Grothoff
  */
-
 #include "platform.h"
 #include "gnunet_util_lib.h"
 #include "gnunet_dnsparser_lib.h"
@@ -45,7 +44,6 @@ dns_value_to_string (void *cls,
                      const void *data,
                      size_t data_size)
 {
-  const char *cdata;
   char* result;
   char tmp[INET6_ADDRSTRLEN];
 
@@ -70,6 +68,7 @@ dns_value_to_string (void *cls,
           (off != data_size) )
       {
        GNUNET_break_op (0);
+        GNUNET_free_non_null (ns);
        return NULL;
       }
       return ns;
@@ -105,6 +104,8 @@ dns_value_to_string (void *cls,
           (off != data_size) )
       {
        GNUNET_break_op (0);
+        if (NULL != soa)
+          GNUNET_DNSPARSER_free_soa (soa);
        return NULL;
       }
       GNUNET_asprintf (&result,
@@ -137,6 +138,38 @@ dns_value_to_string (void *cls,
       }
       return ptr;
     }
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    {
+      struct GNUNET_DNSPARSER_CertRecord *cert;
+      size_t off;
+      char *base64;
+      int len;
+
+      off = 0;
+      cert = GNUNET_DNSPARSER_parse_cert (data,
+                                          data_size,
+                                          &off);
+      if ( (NULL == cert) ||
+          (off != data_size) )
+      {
+       GNUNET_break_op (0);
+        GNUNET_DNSPARSER_free_cert (cert);
+       return NULL;
+      }
+      len = GNUNET_STRINGS_base64_encode (cert->certificate_data,
+                                          cert->certificate_size,
+                                          &base64);
+      GNUNET_asprintf (&result,
+                      "%u %u %u %.*s",
+                       cert->cert_type,
+                       cert->cert_tag,
+                       cert->algorithm,
+                       len,
+                       base64);
+      GNUNET_free (base64);
+      GNUNET_DNSPARSER_free_cert (cert);
+      return result;
+    }
   case GNUNET_DNSPARSER_TYPE_MX:
     {
       struct GNUNET_DNSPARSER_MxRecord *mx;
@@ -150,12 +183,12 @@ dns_value_to_string (void *cls,
           (off != data_size) )
       {
        GNUNET_break_op (0);
-       GNUNET_free_non_null (mx);
+        GNUNET_DNSPARSER_free_mx (mx);
        return NULL;
       }
       GNUNET_asprintf (&result,
-                      "%hu,%s",
-                      mx->preference,
+                      "%u,%s",
+                      (unsigned int) mx->preference,
                       mx->mxhost);
       GNUNET_DNSPARSER_free_mx (mx);
       return result;
@@ -174,47 +207,49 @@ dns_value_to_string (void *cls,
       size_t off;
 
       off = 0;
-      srv = GNUNET_DNSPARSER_parse_srv ("+", /* FIXME: is this OK? */
-                                       data,
+      srv = GNUNET_DNSPARSER_parse_srv (data,
                                        data_size,
                                        &off);
       if ( (NULL == srv) ||
           (off != data_size) )
       {
        GNUNET_break_op (0);
+        if (NULL != srv)
+          GNUNET_DNSPARSER_free_srv (srv);
        return NULL;
       }
       GNUNET_asprintf (&result,
-                      "%d %d %d _%s._%s.%s",
+                      "%d %d %d %s",
                       srv->priority,
                       srv->weight,
                       srv->port,
-                      srv->service,
-                      srv->proto,
-                      srv->domain_name);
+                      srv->target);
       GNUNET_DNSPARSER_free_srv (srv);
       return result;
     }
   case GNUNET_DNSPARSER_TYPE_TLSA:
     {
       const struct GNUNET_TUN_DnsTlsaRecord *tlsa;
-      char* tlsa_str;
+      char *tlsa_str;
+      char *hex;
 
-      cdata = data;
-      if ( (data_size <= sizeof (struct GNUNET_TUN_DnsTlsaRecord)) ||
-          ('\0' != cdata[data_size - 1]) )
+      if (data_size < sizeof (struct GNUNET_TUN_DnsTlsaRecord))
        return NULL; /* malformed */
       tlsa = data;
+      hex = GNUNET_DNSPARSER_bin_to_hex (&tlsa[1],
+                                         data_size - sizeof (struct GNUNET_TUN_DnsTlsaRecord));
       if (0 == GNUNET_asprintf (&tlsa_str,
-                               "%c %c %c %s",
-                               tlsa->usage,
-                               tlsa->selector,
-                               tlsa->matching_type,
-                               (const char *) &tlsa[1]))
+                               "%u %u %u %s",
+                               (unsigned int) tlsa->usage,
+                               (unsigned int) tlsa->selector,
+                               (unsigned int) tlsa->matching_type,
+                               hex))
       {
+        GNUNET_free (hex);
        GNUNET_free (tlsa_str);
        return NULL;
       }
+      GNUNET_free (hex);
       return tlsa_str;
     }
   default:
@@ -257,7 +292,7 @@ dns_string_to_value (void *cls,
                   s);
       return GNUNET_SYSERR;
     }
-    *data = GNUNET_malloc (sizeof (struct in_addr));
+    *data = GNUNET_new (struct in_addr);
     memcpy (*data, &value_a, sizeof (value_a));
     *data_size = sizeof (value_a);
     return GNUNET_OK;
@@ -305,6 +340,94 @@ dns_string_to_value (void *cls,
       memcpy (*data, cnamebuf, off);
       return GNUNET_OK;
     }
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    {
+      char *sdup;
+      const char *typep;
+      const char *keyp;
+      const char *algp;
+      const char *certp;
+      unsigned int type;
+      unsigned int key;
+      unsigned int alg;
+      size_t cert_size;
+      char *cert_data;
+      struct GNUNET_DNSPARSER_CertRecord cert;
+
+      sdup = GNUNET_strdup (s);
+      typep = strtok (sdup, " ");
+      /* TODO: add typep mnemonic conversion according to RFC 4398 */
+      if ( (NULL == typep) ||
+           (1 != SSCANF (typep,
+                         "%u",
+                         &type)) ||
+           (type > UINT16_MAX) )
+      {
+        GNUNET_free (sdup);
+        return GNUNET_SYSERR;
+      }
+      keyp = strtok (NULL, " ");
+      if ( (NULL == keyp) ||
+           (1 != SSCANF (keyp,
+                         "%u",
+                         &key)) ||
+           (key > UINT16_MAX) )
+      {
+        GNUNET_free (sdup);
+        return GNUNET_SYSERR;
+      }
+      algp = strtok (NULL, " ");
+      /* TODO: add algp mnemonic conversion according to RFC 4398/RFC 4034 */
+      if ( (NULL == algp) ||
+           (1 != sscanf (algp,
+                         "%u",
+                         &alg)) ||
+           (alg > UINT8_MAX) )
+      {
+        GNUNET_free (sdup);
+        return GNUNET_SYSERR;
+      }
+      certp = strtok (NULL, " ");
+      if ( (NULL == certp) ||
+           (0 == strlen (certp) ) )
+      {
+        GNUNET_free (sdup);
+        return GNUNET_SYSERR;
+      }
+      cert_size = GNUNET_STRINGS_base64_decode (certp,
+                                                strlen (certp),
+                                                &cert_data);
+      GNUNET_free (sdup);
+      cert.cert_type = type;
+      cert.cert_tag = key;
+      cert.algorithm = alg;
+      cert.certificate_size = cert_size;
+      cert.certificate_data = cert_data;
+      {
+        char certbuf[cert_size + sizeof (struct GNUNET_TUN_DnsCertRecord)];
+        size_t off;
+
+        off = 0;
+        if (GNUNET_OK !=
+            GNUNET_DNSPARSER_builder_add_cert (certbuf,
+                                               sizeof (certbuf),
+                                               &off,
+                                               &cert))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("Failed to serialize CERT record with %u bytes\n"),
+                      (unsigned int) cert_size);
+          GNUNET_free (cert_data);
+          return GNUNET_SYSERR;
+        }
+        GNUNET_free (cert_data);
+        *data_size = off;
+        *data = GNUNET_malloc (off);
+        memcpy (*data, certbuf, off);
+      }
+      GNUNET_free (cert_data);
+      return GNUNET_OK;
+    }
   case GNUNET_DNSPARSER_TYPE_SOA:
     {
       struct GNUNET_DNSPARSER_SoaRecord soa;
@@ -320,8 +443,13 @@ dns_string_to_value (void *cls,
 
       if (7 != SSCANF (s,
                       "rname=%253s mname=%253s %u,%u,%u,%u,%u",
-                      soa_rname, soa_mname,
-                      &soa_serial, &soa_refresh, &soa_retry, &soa_expire, &soa_min))
+                      soa_rname,
+                       soa_mname,
+                      &soa_serial,
+                       &soa_refresh,
+                       &soa_retry,
+                       &soa_expire,
+                       &soa_min))
       {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
              _("Unable to parse SOA record `%s'\n"),
@@ -343,9 +471,9 @@ dns_string_to_value (void *cls,
                                            &soa))
       {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-             _("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
-             soa_mname,
-             soa_rname);
+                    _("Failed to serialize SOA record with mname `%s' and rname `%s'\n"),
+                    soa_mname,
+                    soa_rname);
        return GNUNET_SYSERR;
       }
       *data_size = off;
@@ -366,8 +494,8 @@ dns_string_to_value (void *cls,
                                             s))
       {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-             _("Failed to serialize PTR record with value `%s'\n"),
-             s);
+                    _("Failed to serialize PTR record with value `%s'\n"),
+                    s);
        return GNUNET_SYSERR;
       }
       *data_size = off;
@@ -380,17 +508,20 @@ dns_string_to_value (void *cls,
       struct GNUNET_DNSPARSER_MxRecord mx;
       char mxbuf[258];
       char mxhost[253 + 1];
-      uint16_t mx_pref;
+      unsigned int mx_pref;
       size_t off;
 
-      if (2 != SSCANF(s, "%hu,%253s", &mx_pref, mxhost))
+      if (2 != SSCANF(s,
+                      "%u,%253s",
+                      &mx_pref,
+                      mxhost))
       {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
              _("Unable to parse MX record `%s'\n"),
              s);
       return GNUNET_SYSERR;
       }
-      mx.preference = mx_pref;
+      mx.preference = (uint16_t) mx_pref;
       mx.mxhost = mxhost;
       off = 0;
 
@@ -411,8 +542,48 @@ dns_string_to_value (void *cls,
       return GNUNET_OK;
     }
   case GNUNET_DNSPARSER_TYPE_SRV:
-    GNUNET_break (0); // FIXME: not implemented!
-    return GNUNET_SYSERR;
+    {
+      struct GNUNET_DNSPARSER_SrvRecord srv;
+      char srvbuf[270];
+      char srvtarget[253 + 1];
+      unsigned int priority;
+      unsigned int weight;
+      unsigned int port;
+      size_t off;
+
+      if (4 != SSCANF(s,
+                      "%u %u %u %253s",
+                      &priority,
+                      &weight,
+                      &port,
+                      srvtarget))
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+             _("Unable to parse SRV record `%s'\n"),
+             s);
+        return GNUNET_SYSERR;
+      }
+      srv.priority = (uint16_t) priority;
+      srv.weight = (uint16_t) weight;
+      srv.port = (uint16_t) port;
+      srv.target = srvtarget;
+      off = 0;
+      if (GNUNET_OK !=
+         GNUNET_DNSPARSER_builder_add_srv (srvbuf,
+                                            sizeof (srvbuf),
+                                            &off,
+                                            &srv))
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Failed to serialize SRV record with target `%s'\n"),
+                    srvtarget);
+       return GNUNET_SYSERR;
+      }
+      *data_size = off;
+      *data = GNUNET_malloc (off);
+      memcpy (*data, srvbuf, off);
+      return GNUNET_OK;
+    }
   case GNUNET_DNSPARSER_TYPE_TXT:
     *data = GNUNET_strdup (s);
     *data_size = strlen (s);
@@ -425,27 +596,51 @@ dns_string_to_value (void *cls,
            s);
       return GNUNET_SYSERR;
     }
-    *data = GNUNET_malloc (sizeof (struct in6_addr));
+    *data = GNUNET_new (struct in6_addr);
     *data_size = sizeof (struct in6_addr);
     memcpy (*data, &value_aaaa, sizeof (value_aaaa));
     return GNUNET_OK;
   case GNUNET_DNSPARSER_TYPE_TLSA:
-    *data_size = sizeof (struct GNUNET_TUN_DnsTlsaRecord) + strlen (s) - 6;
-    *data = tlsa = GNUNET_malloc (*data_size);
-    if (4 != SSCANF (s, "%c %c %c %s",
-                    &tlsa->usage,
-                    &tlsa->selector,
-                    &tlsa->matching_type,
-                    (char*)&tlsa[1]))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  _("Unable to parse TLSA record string `%s'\n"),
-                  s);
-      *data_size = 0;
-      GNUNET_free (tlsa);
-      return GNUNET_SYSERR;
+      unsigned int usage;
+      unsigned int selector;
+      unsigned int matching_type;
+      size_t slen = strlen (s) + 1;
+      char hex[slen];
+
+      if (4 != SSCANF (s,
+                       "%u %u %u %s",
+                       &usage,
+                       &selector,
+                       &matching_type,
+                       hex))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Unable to parse TLSA record string `%s'\n"),
+                    s);
+        *data_size = 0;
+        return GNUNET_SYSERR;
+      }
+
+      *data_size = sizeof (struct GNUNET_TUN_DnsTlsaRecord) + strlen (hex) / 2;
+      *data = tlsa = GNUNET_malloc (*data_size);
+      tlsa->usage = (uint8_t) usage;
+      tlsa->selector = (uint8_t) selector;
+      tlsa->matching_type = (uint8_t) matching_type;
+      if (strlen (hex) / 2 !=
+          GNUNET_DNSPARSER_hex_to_bin (hex,
+                                       &tlsa[1]))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Unable to parse TLSA record string `%s'\n"),
+                    s);
+        GNUNET_free (*data);
+        *data = NULL;
+        *data_size = 0;
+        return GNUNET_SYSERR;
+      }
+      return GNUNET_OK;
     }
-    return GNUNET_OK;
   default:
     return GNUNET_SYSERR;
   }
@@ -468,6 +663,7 @@ static struct {
   { "MX", GNUNET_DNSPARSER_TYPE_MX },
   { "TXT", GNUNET_DNSPARSER_TYPE_TXT },
   { "AAAA", GNUNET_DNSPARSER_TYPE_AAAA },
+  { "SRV", GNUNET_DNSPARSER_TYPE_SRV },
   { "TLSA", GNUNET_DNSPARSER_TYPE_TLSA },
   { NULL, UINT32_MAX }
 };
@@ -487,7 +683,7 @@ dns_typename_to_number (void *cls,
   unsigned int i;
 
   i=0;
-  while ( (name_map[i].name != NULL) &&
+  while ( (NULL != name_map[i].name) &&
          (0 != strcasecmp (dns_typename, name_map[i].name)) )
     i++;
   return name_map[i].number;
@@ -508,7 +704,7 @@ dns_number_to_typename (void *cls,
   unsigned int i;
 
   i=0;
-  while ( (name_map[i].name != NULL) &&
+  while ( (NULL != name_map[i].name) &&
          (type != name_map[i].number) )
     i++;
   return name_map[i].name;