f2e5630
From 3e00d82827f80461f9fe6da37acd84235c08e5a5 Mon Sep 17 00:00:00 2001
f2e5630
From: Gustavo Luiz Duarte <gustavold@linux.vnet.ibm.com>
f2e5630
Date: Fri, 28 Sep 2012 19:42:07 -0400
f2e5630
Subject: [PATCH] Issue separate DNS queries for ipv4 and ipv6
f2e5630
f2e5630
Adding multiple questions on a single DNS query is not supportted by
f2e5630
most DNS servers. This patch issues two separate DNS queries
f2e5630
sequentially for ipv4 and then for ipv6.
f2e5630
f2e5630
There are 4 possible config options:
f2e5630
 DNS_OPTION_IPV4: issue only one ipv4 query
f2e5630
 DNS_OPTION_IPV6: issue only one ipv6 query
f2e5630
 DNS_OPTION_PREFER_IPV4: issue the ipv4 query first and fallback to ipv6
f2e5630
 DNS_OPTION_PREFER_IPV6: issue the ipv6 query first and fallback to ipv4
f2e5630
However, there is no code yet to set such config option. The default is
f2e5630
DNS_OPTION_PREFER_IPV4.
f2e5630
f2e5630
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=860829
f2e5630
---
f2e5630
 grub-core/net/dns.c | 99 ++++++++++++++++++++++++++++++++++++-----------------
f2e5630
 include/grub/net.h  |  9 +++++
f2e5630
 2 files changed, 76 insertions(+), 32 deletions(-)
f2e5630
f2e5630
diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
f2e5630
index 3381ea7..725725c 100644
f2e5630
--- a/grub-core/net/dns.c
f2e5630
+++ b/grub-core/net/dns.c
f2e5630
@@ -34,6 +34,14 @@ struct dns_cache_element
f2e5630
 #define DNS_CACHE_SIZE 1021
f2e5630
 #define DNS_HASH_BASE 423
f2e5630
 
f2e5630
+typedef enum grub_dns_qtype_id
f2e5630
+  {
f2e5630
+    GRUB_DNS_QTYPE_A = 1,
f2e5630
+    GRUB_DNS_QTYPE_AAAA = 28
f2e5630
+  } grub_dns_qtype_id_t;
f2e5630
+
f2e5630
+static grub_dns_option_t dns_type_option = DNS_OPTION_PREFER_IPV4;
f2e5630
+
f2e5630
 static struct dns_cache_element dns_cache[DNS_CACHE_SIZE];
f2e5630
 static struct grub_net_network_level_address *dns_servers;
f2e5630
 static grub_size_t dns_nservers, dns_servers_alloc;
f2e5630
@@ -410,13 +418,13 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
f2e5630
   return GRUB_ERR_NONE;
f2e5630
 }
f2e5630
 
f2e5630
-grub_err_t
f2e5630
-grub_net_dns_lookup (const char *name,
f2e5630
+static grub_err_t
f2e5630
+grub_net_dns_lookup_qtype (const char *name,
f2e5630
 		     const struct grub_net_network_level_address *servers,
f2e5630
 		     grub_size_t n_servers,
f2e5630
 		     grub_size_t *naddresses,
f2e5630
 		     struct grub_net_network_level_address **addresses,
f2e5630
-		     int cache)
f2e5630
+		     int cache, grub_dns_qtype_id_t qtype)
f2e5630
 {
f2e5630
   grub_size_t send_servers = 0;
f2e5630
   grub_size_t i, j;
f2e5630
@@ -471,8 +479,7 @@ grub_net_dns_lookup (const char *name,
f2e5630
 			   + GRUB_NET_MAX_LINK_HEADER_SIZE
f2e5630
 			   + GRUB_NET_UDP_HEADER_SIZE
f2e5630
 			   + sizeof (struct dns_header)
f2e5630
-			   + grub_strlen (name) + 2 + 4
f2e5630
-			   + 2 + 4);
f2e5630
+			   + grub_strlen (name) + 2 + 4);
f2e5630
   if (!nb)
f2e5630
     {
f2e5630
       grub_free (data.name);
f2e5630
@@ -482,7 +489,7 @@ grub_net_dns_lookup (const char *name,
f2e5630
 			+ GRUB_NET_MAX_LINK_HEADER_SIZE
f2e5630
 			+ GRUB_NET_UDP_HEADER_SIZE);
f2e5630
   grub_netbuff_put (nb, sizeof (struct dns_header)
f2e5630
-		    + grub_strlen (name) + 2 + 4 + 2 + 4);
f2e5630
+		    + grub_strlen (name) + 2 + 4);
f2e5630
   head = (struct dns_header *) nb->data;
f2e5630
   optr = (grub_uint8_t *) (head + 1);
f2e5630
   for (iptr = name; *iptr; )
f2e5630
@@ -509,18 +516,7 @@ grub_net_dns_lookup (const char *name,
f2e5630
 
f2e5630
   /* Type: A.  */
f2e5630
   *optr++ = 0;
f2e5630
-  *optr++ = 1;
f2e5630
-
f2e5630
-  /* Class.  */
f2e5630
-  *optr++ = 0;
f2e5630
-  *optr++ = 1;
f2e5630
-
f2e5630
-  /* Compressed name.  */
f2e5630
-  *optr++ = 0xc0;
f2e5630
-  *optr++ = 0x0c;
f2e5630
-  /* Type: AAAA.  */
f2e5630
-  *optr++ = 0;
f2e5630
-  *optr++ = 28;
f2e5630
+  *optr++ = qtype;
f2e5630
 
f2e5630
   /* Class.  */
f2e5630
   *optr++ = 0;
f2e5630
@@ -529,7 +525,7 @@ grub_net_dns_lookup (const char *name,
f2e5630
   head->id = data.id;
f2e5630
   head->flags = FLAGS_RD;
f2e5630
   head->ra_z_r_code = 0;
f2e5630
-  head->qdcount = grub_cpu_to_be16_compile_time (2);
f2e5630
+  head->qdcount = grub_cpu_to_be16_compile_time (1);
f2e5630
   head->ancount = grub_cpu_to_be16_compile_time (0);
f2e5630
   head->nscount = grub_cpu_to_be16_compile_time (0);
f2e5630
   head->arcount = grub_cpu_to_be16_compile_time (0);
f2e5630
@@ -587,16 +583,47 @@ grub_net_dns_lookup (const char *name,
f2e5630
   if (*data.naddresses)
f2e5630
     return GRUB_ERR_NONE;
f2e5630
   if (data.dns_err)
f2e5630
-    return grub_error (GRUB_ERR_NET_NO_DOMAIN,
f2e5630
-		       N_("no DNS record found"));
f2e5630
-    
f2e5630
+    {
f2e5630
+      grub_dprintf ("dns", "%s. QTYPE: %u QNAME: %s\n",
f2e5630
+                    N_("no DNS record found"), qtype, name);
f2e5630
+      return GRUB_ERR_NET_NO_DOMAIN;
f2e5630
+    }
f2e5630
   if (err)
f2e5630
     {
f2e5630
       grub_errno = err;
f2e5630
       return err;
f2e5630
     }
f2e5630
-  return grub_error (GRUB_ERR_TIMEOUT,
f2e5630
-		     N_("no DNS reply received"));
f2e5630
+  grub_dprintf ("dns", "%s. QTYPE: %u QNAME: %s\n",
f2e5630
+                N_("no DNS reply received"), qtype, name);
f2e5630
+  return GRUB_ERR_TIMEOUT;
f2e5630
+}
f2e5630
+
f2e5630
+grub_err_t
f2e5630
+grub_net_dns_lookup (const char *name,
f2e5630
+		     const struct grub_net_network_level_address *servers,
f2e5630
+		     grub_size_t n_servers,
f2e5630
+		     grub_size_t *naddresses,
f2e5630
+		     struct grub_net_network_level_address **addresses,
f2e5630
+		     int cache)
f2e5630
+{
f2e5630
+  if (dns_type_option == DNS_OPTION_IPV6 || dns_type_option == DNS_OPTION_PREFER_IPV6)
f2e5630
+      grub_net_dns_lookup_qtype (name, servers, n_servers, naddresses,
f2e5630
+                                 addresses, cache, GRUB_DNS_QTYPE_AAAA);
f2e5630
+  else
f2e5630
+      grub_net_dns_lookup_qtype (name, servers, n_servers, naddresses,
f2e5630
+                                 addresses, cache, GRUB_DNS_QTYPE_A);
f2e5630
+  if (!*naddresses)
f2e5630
+    {
f2e5630
+      if (dns_type_option == DNS_OPTION_PREFER_IPV4)
f2e5630
+          grub_net_dns_lookup_qtype (name, servers, n_servers, naddresses,
f2e5630
+                                     addresses, cache, GRUB_DNS_QTYPE_AAAA);
f2e5630
+      else if (dns_type_option == DNS_OPTION_PREFER_IPV6)
f2e5630
+          grub_net_dns_lookup_qtype (name, servers, n_servers, naddresses,
f2e5630
+                                     addresses, cache, GRUB_DNS_QTYPE_A);
f2e5630
+    }
f2e5630
+  if (!*naddresses)
f2e5630
+      return GRUB_ERR_NET_NO_DOMAIN;
f2e5630
+  return GRUB_ERR_NONE;
f2e5630
 }
f2e5630
 
f2e5630
 static grub_err_t
f2e5630
@@ -604,22 +631,28 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)),
f2e5630
 		   int argc, char **args)
f2e5630
 {
f2e5630
   grub_err_t err;
f2e5630
-  grub_size_t naddresses, i;
f2e5630
+  struct grub_net_network_level_address cmd_server;
f2e5630
+  struct grub_net_network_level_address *servers;
f2e5630
+  grub_size_t nservers, i, naddresses = 0;
f2e5630
   struct grub_net_network_level_address *addresses = 0;
f2e5630
   if (argc != 2 && argc != 1)
f2e5630
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
f2e5630
   if (argc == 2)
f2e5630
     {
f2e5630
-      struct grub_net_network_level_address server;
f2e5630
-      err = grub_net_resolve_address (args[1], &server);
f2e5630
+      err = grub_net_resolve_address (args[1], &cmd_server);
f2e5630
       if (err)
f2e5630
 	return err;
f2e5630
-      err = grub_net_dns_lookup (args[0], &server, 1, &naddresses,
f2e5630
-				 &addresses, 0);
f2e5630
+      servers = &cmd_server;
f2e5630
+      nservers = 1;
f2e5630
     }
f2e5630
   else
f2e5630
-    err = grub_net_dns_lookup (args[0], dns_servers, dns_nservers, &naddresses,
f2e5630
-			       &addresses, 0);
f2e5630
+    {
f2e5630
+      servers = dns_servers;
f2e5630
+      nservers = dns_nservers;
f2e5630
+    }
f2e5630
+
f2e5630
+  grub_net_dns_lookup (args[0], servers, nservers, &naddresses,
f2e5630
+                       &addresses, 0);
f2e5630
 
f2e5630
   for (i = 0; i < naddresses; i++)
f2e5630
     {
f2e5630
@@ -628,7 +661,9 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)),
f2e5630
       grub_printf ("%s\n", buf);
f2e5630
     }
f2e5630
   grub_free (addresses);
f2e5630
-  return GRUB_ERR_NONE;
f2e5630
+  if (naddresses)
f2e5630
+    return GRUB_ERR_NONE;
f2e5630
+  return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found"));
f2e5630
 }
f2e5630
 
f2e5630
 static grub_err_t
f2e5630
diff --git a/include/grub/net.h b/include/grub/net.h
f2e5630
index 3877451..a7e5b2c 100644
f2e5630
--- a/include/grub/net.h
f2e5630
+++ b/include/grub/net.h
f2e5630
@@ -505,6 +505,15 @@ grub_err_t
f2e5630
 grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf,
f2e5630
 			     const grub_net_network_level_address_t *proto_addr,
f2e5630
 			     grub_net_link_level_address_t *hw_addr);
f2e5630
+
f2e5630
+typedef enum
f2e5630
+  {
f2e5630
+    DNS_OPTION_IPV4,
f2e5630
+    DNS_OPTION_IPV6,
f2e5630
+    DNS_OPTION_PREFER_IPV4,
f2e5630
+    DNS_OPTION_PREFER_IPV6
f2e5630
+  } grub_dns_option_t;
f2e5630
+
f2e5630
 grub_err_t
f2e5630
 grub_net_dns_lookup (const char *name,
f2e5630
 		     const struct grub_net_network_level_address *servers,
f2e5630
-- 
f2e5630
1.7.11.4
f2e5630