slirp: Read host DNS config on demand
authorEd Swierk <eswierk@aristanetworks.com>
Fri, 21 Aug 2009 02:00:31 +0000 (19:00 -0700)
committerAnthony Liguori <aliguori@us.ibm.com>
Fri, 28 Aug 2009 01:46:58 +0000 (20:46 -0500)
Currently the qemu user-mode networking stack reads the host DNS
configuration (/etc/resolv.conf or the Windows equivalent) only once
when qemu starts.  This causes name lookups in the guest to fail if the
host is moved to a different network from which the original DNS servers
are unreachable, a common occurrence when the host is a laptop.

This patch changes the slirp code to read the host DNS configuration on
demand, caching the results for at most 1 second to avoid unnecessary
overhead if name lookups occur in rapid succession.  On non-Windows
hosts, /etc/resolv.conf is re-read only if the file has been replaced or
if its size or mtime has changed.

Signed-off-by: Ed Swierk <eswierk@aristanetworks.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
slirp/ip_icmp.c
slirp/libslirp.h
slirp/main.h
slirp/slirp.c
slirp/socket.c
slirp/tcp_subr.c

index 95a4b39a4e400140bdad553ba5c81ba97d854d5e..751a8e249ae0ecd73f0684e73c691381c0c2adfd 100644 (file)
@@ -127,7 +127,8 @@ icmp_input(struct mbuf *m, int hlen)
           slirp->vnetwork_addr.s_addr) {
        /* It's an alias */
        if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
-         addr.sin_addr = dns_addr;
+         if (get_dns_addr(&addr.sin_addr) < 0)
+           addr.sin_addr = loopback_addr;
        } else {
          addr.sin_addr = loopback_addr;
        }
index 93087ed74ae0e9c0a3d21e5f7c6009f547e65a2a..67c70e32e3c3f71fa533f02a774147339c7d69b1 100644 (file)
@@ -8,6 +8,8 @@
 struct Slirp;
 typedef struct Slirp Slirp;
 
+int get_dns_addr(struct in_addr *pdns_addr);
+
 Slirp *slirp_init(int restricted, struct in_addr vnetwork,
                   struct in_addr vnetmask, struct in_addr vhost,
                   const char *vhostname, const char *tftp_path,
index 9f22fe1b0ad3c60eebbdf5778ccf08491b15e2e1..8d09df9d4c4cc83e320e0e7394db963d76590927 100644 (file)
@@ -31,7 +31,6 @@ extern char *exec_shell;
 extern u_int curtime;
 extern fd_set *global_readfds, *global_writefds, *global_xfds;
 extern struct in_addr loopback_addr;
-extern struct in_addr dns_addr;
 extern char *username;
 extern char *socket_path;
 extern int towrite_max;
index da04ad19c83256d4a9e5f2c186faa048598cc3a5..b5335a54ac1437b7d7ccef433c770b5321f1b1de 100644 (file)
@@ -27,8 +27,6 @@
 #include "slirp.h"
 #include "hw/hw.h"
 
-/* host dns address */
-struct in_addr dns_addr;
 /* host loopback address */
 struct in_addr loopback_addr;
 
@@ -49,9 +47,12 @@ static int do_slowtimo;
 static TAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
     TAILQ_HEAD_INITIALIZER(slirp_instances);
 
+struct in_addr dns_addr = { 0 };
+u_int dns_addr_time = 0;
+
 #ifdef _WIN32
 
-static int get_dns_addr(struct in_addr *pdns_addr)
+int get_dns_addr(struct in_addr *pdns_addr)
 {
     FIXED_INFO *FixedInfo=NULL;
     ULONG    BufLen;
@@ -59,6 +60,11 @@ static int get_dns_addr(struct in_addr *pdns_addr)
     IP_ADDR_STRING *pIPAddr;
     struct in_addr tmp_addr;
 
+    if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
+        *pdns_addr = dns_addr;
+        return 0;
+    }
+
     FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
     BufLen = sizeof(FIXED_INFO);
 
@@ -82,6 +88,8 @@ static int get_dns_addr(struct in_addr *pdns_addr)
     pIPAddr = &(FixedInfo->DnsServerList);
     inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
     *pdns_addr = tmp_addr;
+    dns_addr = tmp_addr;
+    dns_addr_time = curtime;
     if (FixedInfo) {
         GlobalFree(FixedInfo);
         FixedInfo = NULL;
@@ -96,7 +104,9 @@ static void winsock_cleanup(void)
 
 #else
 
-static int get_dns_addr(struct in_addr *pdns_addr)
+struct stat dns_addr_stat;
+
+int get_dns_addr(struct in_addr *pdns_addr)
 {
     char buff[512];
     char buff2[257];
@@ -104,6 +114,24 @@ static int get_dns_addr(struct in_addr *pdns_addr)
     int found = 0;
     struct in_addr tmp_addr;
 
+    if (dns_addr.s_addr != 0) {
+        struct stat old_stat;
+        if ((curtime - dns_addr_time) < 1000) {
+            *pdns_addr = dns_addr;
+            return 0;
+        }
+        old_stat = dns_addr_stat;
+        if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
+            return -1;
+        if ((dns_addr_stat.st_dev == old_stat.st_dev)
+            && (dns_addr_stat.st_ino == old_stat.st_ino)
+            && (dns_addr_stat.st_size == old_stat.st_size)
+            && (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
+            *pdns_addr = dns_addr;
+            return 0;
+        }
+    }
+
     f = fopen("/etc/resolv.conf", "r");
     if (!f)
         return -1;
@@ -116,8 +144,11 @@ static int get_dns_addr(struct in_addr *pdns_addr)
             if (!inet_aton(buff2, &tmp_addr))
                 continue;
             /* If it's the first one, set it to dns_addr */
-            if (!found)
+            if (!found) {
                 *pdns_addr = tmp_addr;
+                dns_addr = tmp_addr;
+                dns_addr_time = curtime;
+            }
 #ifdef DEBUG
             else
                 lprint(", ");
@@ -160,11 +191,6 @@ static void slirp_init_once(void)
 #endif
 
     loopback_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-    /* FIXME: This address may change during runtime */
-    if (get_dns_addr(&dns_addr) < 0) {
-        dns_addr = loopback_addr;
-    }
 }
 
 static void slirp_state_save(QEMUFile *f, void *opaque);
index d8fbe89a749209e01fd5592fb0ad06e92a8e3ab2..207109c7eca223745db81b4037ecfd2b22708c66 100644 (file)
@@ -552,7 +552,8 @@ sosendto(struct socket *so, struct mbuf *m)
            slirp->vnetwork_addr.s_addr) {
          /* It's an alias */
          if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
-           addr.sin_addr = dns_addr;
+           if (get_dns_addr(&addr.sin_addr) < 0)
+             addr.sin_addr = loopback_addr;
          } else {
            addr.sin_addr = loopback_addr;
          }
index 51b38344ef357b34e4eb84e0129dc98723ddfcd6..04173450d7763576eada2edc7a17a9abf9a65659 100644 (file)
@@ -340,7 +340,8 @@ int tcp_fconnect(struct socket *so)
         slirp->vnetwork_addr.s_addr) {
       /* It's an alias */
       if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
-       addr.sin_addr = dns_addr;
+       if (get_dns_addr(&addr.sin_addr) < 0)
+         addr.sin_addr = loopback_addr;
       } else {
        addr.sin_addr = loopback_addr;
       }