add logic to handle SRV/DANE names (#3003 and 2526) in GNS resolver
[oweals/gnunet.git] / src / gns / gnunet-service-gns_resolver.c
index ede81b49d504d3d2fd388459ccaf338be59b6946..67f1902cd05c15290b1813b60aaefdbea5d192f8 100644 (file)
  * @brief GNU Name System resolver logic
  * @author Martin Schanzenbach
  * @author Christian Grothoff
- *
- * TODO:
- * - GNS: handle special SRV names --- no delegation, direct lookup;
- *        can likely be done in 'resolver_lookup_get_next_label'. (#3003)
- * - revocation checks (use REVOCATION service!), (#3004)
- * - DNAME support (#3005)
  */
 #include "platform.h"
 #include "gnunet_util_lib.h"
@@ -41,6 +35,7 @@
 #include "gnunet_resolver_service.h"
 #include "gnunet_revocation_service.h"
 #include "gnunet_dnsparser_lib.h"
+#include "gnunet_tun_lib.h"
 #include "gnunet_gns_service.h"
 #include "gns.h"
 #include "gnunet-service-gns_resolver.h"
@@ -364,7 +359,7 @@ struct GNS_ResolverHandle
   /**
    * Use only cache
    */
-  int only_cached;
+  enum GNUNET_GNS_LocalOptions options;
 
   /**
    * Desired type for the resolution.
@@ -600,7 +595,17 @@ memrchr (const void *s,
 
 /**
  * Get the next, rightmost label from the name that we are trying to resolve,
- * and update the resolution position accordingly.
+ * and update the resolution position accordingly.  Labels usually consist
+ * of up to 63 characters without a period ("."); however, we use a special
+ * convention to support SRV and TLSA records where the domain name
+ * includes an encoding for a service and protocol in the name.  The
+ * syntax (see RFC 2782) here is "_Service._Proto.Name" and in this
+ * special case we include the "_Service._Proto" in the rightmost label.
+ * Thus, for "_443._tcp.foo.bar" we first return the label "bar" and then
+ * the label "_443._tcp.foo".  The special case is detected by the
+ * presence of labels beginning with an underscore.  Whenever a label
+ * begins with an underscore, it is combined with the label to its right
+ * (and the "." is preserved).
  *
  * @param rh handle to the resolution operation to get the next label from
  * @return NULL if there are no more labels
@@ -614,7 +619,9 @@ resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh)
 
   if (0 == rh->name_resolution_pos)
     return NULL;
-  dot = memrchr (rh->name, (int) '.', rh->name_resolution_pos);
+  dot = memrchr (rh->name,
+                 (int) '.',
+                 rh->name_resolution_pos);
   if (NULL == dot)
   {
     /* done, this was the last one */
@@ -629,6 +636,16 @@ resolver_lookup_get_next_label (struct GNS_ResolverHandle *rh)
     rp = dot + 1;
     rh->name_resolution_pos = dot - rh->name;
   }
+  /* merge labels starting with underscore with label on the right (SRV/DANE case) */
+  while ( (NULL != (dot = memrchr (rh->name,
+                                   (int) '.',
+                                   rh->name_resolution_pos))) &&
+          ('_' == dot[1]) )
+  {
+    len += rh->name_resolution_pos - (dot - rh->name) - 1;
+    rp = dot + 1;
+    rh->name_resolution_pos = dot - rh->name;
+  }
   return GNUNET_strndup (rp, len);
 }
 
@@ -1396,7 +1413,7 @@ handle_gns_resolution_result (void *cls,
        case GNUNET_GNSRECORD_TYPE_VPN:
          {
            af = (GNUNET_DNSPARSER_TYPE_A == rh->record_type) ? AF_INET : AF_INET6;
-           if (sizeof (struct GNUNET_TUN_GnsVpnRecord) <
+           if (sizeof (struct GNUNET_TUN_GnsVpnRecord) >
                rd[i].data_size)
            {
              GNUNET_break_op (0);
@@ -1413,19 +1430,26 @@ handle_gns_resolution_result (void *cls,
              GNS_resolver_lookup_cancel (rh);
              return;
            }
-           GNUNET_CRYPTO_hash (vname,
-                               strlen (vname), // FIXME: +1?
-                               &vhash);
+           GNUNET_TUN_service_name_to_hash (vname,
+                                             &vhash);
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Attempting VPN allocation for %s-%s (AF: %d, proto %d)\n",
+                        GNUNET_i2s (&vpn->peer),
+                        vname,
+                        (int) af,
+                        (int) ntohs (vpn->proto));
            vpn_ctx = GNUNET_new (struct VpnContext);
            rh->vpn_ctx = vpn_ctx;
            vpn_ctx->rh = rh;
            vpn_ctx->rd_data_size = GNUNET_GNSRECORD_records_get_size (rd_count,
                                                                       rd);
            vpn_ctx->rd_data = GNUNET_malloc (vpn_ctx->rd_data_size);
-           (void) GNUNET_GNSRECORD_records_serialize (rd_count,
-                                                      rd,
-                                                      vpn_ctx->rd_data_size,
-                                                      vpn_ctx->rd_data);
+            vpn_ctx->rd_count = rd_count;
+           GNUNET_assert (vpn_ctx->rd_data_size ==
+                           GNUNET_GNSRECORD_records_serialize (rd_count,
+                                                               rd,
+                                                               vpn_ctx->rd_data_size,
+                                                               vpn_ctx->rd_data));
            vpn_ctx->vpn_request = GNUNET_VPN_redirect_to_peer (vpn_handle,
                                                                af,
                                                                ntohs (vpn->proto),
@@ -1433,7 +1457,7 @@ handle_gns_resolution_result (void *cls,
                                                                &vhash,
                                                                GNUNET_TIME_relative_to_absolute (VPN_TIMEOUT),
                                                                &vpn_allocation_cb,
-                                                               rh);
+                                                               vpn_ctx);
            return;
          }
        case GNUNET_GNSRECORD_TYPE_GNS2DNS:
@@ -1576,8 +1600,6 @@ handle_gns_resolution_result (void *cls,
          struct GNUNET_DNSPARSER_SrvRecord *srv;
 
          off = 0;
-         /* FIXME: passing rh->name here is is not necessarily what we want
-            (SRV support not finished) */
          srv = GNUNET_DNSPARSER_parse_srv (rh->name,
                                            rd[i].data,
                                            rd[i].data_size,
@@ -1771,7 +1793,7 @@ handle_gns_resolution_result (void *cls,
         g2dc->rh->proc = &handle_gns2dns_result;
         g2dc->rh->proc_cls = rh;
         g2dc->rh->record_type = GNUNET_GNSRECORD_TYPE_ANY;
-        g2dc->rh->only_cached = GNUNET_NO;
+        g2dc->rh->options = GNUNET_GNS_LO_DEFAULT;
         g2dc->rh->loop_limiter = rh->loop_limiter + 1;
         rh->g2dc = g2dc;
         start_resolver_lookup (g2dc->rh);
@@ -1929,14 +1951,23 @@ handle_dht_response (void *cls,
                               co);
 }
 
+
+/**
+ * Initiate a DHT query for a set of GNS records.
+ *
+ * @param rh resolution handle
+ * @param query key to use in the DHT lookup
+ */
 static void
-start_dht_request (struct GNS_ResolverHandle *rh, struct GNUNET_HashCode query)
+start_dht_request (struct GNS_ResolverHandle *rh,
+                   const struct GNUNET_HashCode *query)
 {
   struct GNS_ResolverHandle *rx;
+
   GNUNET_assert (NULL == rh->get_handle);
   rh->get_handle = GNUNET_DHT_get_start (dht_handle,
                                          GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
-                                         &query,
+                                         query,
                                          DHT_GNS_REPLICATION_LEVEL,
                                          GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
                                          NULL, 0,
@@ -1955,6 +1986,27 @@ start_dht_request (struct GNS_ResolverHandle *rh, struct GNUNET_HashCode query)
 }
 
 
+/**
+ * Process a records that were decrypted from a block that we got from
+ * the namecache.  Simply calls #handle_gns_resolution_result().
+ *
+ * @param cls closure with the `struct GNS_ResolverHandle`
+ * @param rd_count number of entries in @a rd array
+ * @param rd array of records with data to store
+ */
+static void
+handle_gns_namecache_resolution_result (void *cls,
+                                        unsigned int rd_count,
+                                        const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct GNS_ResolverHandle *rh = cls;
+
+  handle_gns_resolution_result (rh,
+                                rd_count,
+                                rd);
+}
+
+
 /**
  * Process a record that was stored in the namecache.
  *
@@ -1973,20 +2025,23 @@ handle_namecache_block_response (void *cls,
 
   GNUNET_assert (NULL != rh->namecache_qe);
   rh->namecache_qe = NULL;
-  if ( (GNUNET_NO == rh->only_cached) &&
+  if ( ( (GNUNET_GNS_LO_DEFAULT == rh->options) ||
+        ( (GNUNET_GNS_LO_LOCAL_MASTER == rh->options) &&
+          (ac != rh->ac_head) ) ) &&
        ( (NULL == block) ||
         (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (block->expiration_time)).rel_value_us) ) )
   {
     /* namecache knows nothing; try DHT lookup */
+    GNUNET_GNSRECORD_query_from_public_key (auth,
+                                            label,
+                                            &query);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n",
                 ac->label,
                 GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority),
                 GNUNET_h2s (&query));
-    GNUNET_GNSRECORD_query_from_public_key (auth,
-                                            label,
-                                            &query);
-    start_dht_request(rh, query);
+    start_dht_request (rh, &query);
+    return;
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2009,12 +2064,20 @@ handle_namecache_block_response (void *cls,
       GNUNET_GNSRECORD_block_decrypt (block,
                                      auth,
                                      label,
-                                     &handle_gns_resolution_result,
+                                     &handle_gns_namecache_resolution_result,
                                      rh))
   {
     GNUNET_break_op (0); /* block was ill-formed */
-    rh->proc (rh->proc_cls, 0, NULL);
-    GNS_resolver_lookup_cancel (rh);
+    /* try DHT instead */
+    GNUNET_GNSRECORD_query_from_public_key (auth,
+                                            label,
+                                            &query);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Starting DHT lookup for `%s' in zone `%s' under key `%s'\n",
+                ac->label,
+                GNUNET_GNSRECORD_z2s (&ac->authority_info.gns_authority),
+                GNUNET_h2s (&query));
+    start_dht_request (rh, &query);
     return;
   }
 }
@@ -2040,14 +2103,17 @@ recursive_gns_resolution_namecache (struct GNS_ResolverHandle *rh)
                                          &query);
   if (GNUNET_YES == use_cache)
   {
-    rh->namecache_qe = GNUNET_NAMECACHE_lookup_block (namecache_handle,
-                                                   &query,
-                                                   &handle_namecache_block_response,
-                                                   rh);
+    rh->namecache_qe
+      = GNUNET_NAMECACHE_lookup_block (namecache_handle,
+                                       &query,
+                                       &handle_namecache_block_response,
+                                       rh);
     GNUNET_assert (NULL != rh->namecache_qe);
   }
   else
-    start_dht_request (rh, query);
+  {
+    start_dht_request (rh, &query);
+  }
 }
 
 
@@ -2253,7 +2319,7 @@ start_resolver_lookup (struct GNS_ResolverHandle *rh)
  * @param record_type the record type to look up
  * @param name the name to look up
  * @param shorten_key a private key for use with PSEU import (can be NULL)
- * @param only_cached #GNUNET_NO to only check locally not DHT for performance
+ * @param options local options to control local lookup
  * @param proc the processor to call on result
  * @param proc_cls the closure to pass to @a proc
  * @return handle to cancel operation
@@ -2263,7 +2329,7 @@ GNS_resolver_lookup (const struct GNUNET_CRYPTO_EcdsaPublicKey *zone,
                     uint32_t record_type,
                     const char *name,
                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key,
-                    int only_cached,
+                    enum GNUNET_GNS_LocalOptions options,
                     GNS_ResultProcessor proc, void *proc_cls)
 {
   struct GNS_ResolverHandle *rh;
@@ -2280,7 +2346,7 @@ GNS_resolver_lookup (const struct GNUNET_CRYPTO_EcdsaPublicKey *zone,
   rh->authority_zone = *zone;
   rh->proc = proc;
   rh->proc_cls = proc_cls;
-  rh->only_cached = only_cached;
+  rh->options = options;
   rh->record_type = record_type;
   rh->name = GNUNET_strdup (name);
   rh->name_resolution_pos = strlen (name);