Index: trunk/src/VBox/NetworkServices/NAT/Makefile.kmk
===================================================================
--- trunk/src/VBox/NetworkServices/NAT/Makefile.kmk	(revision 49210)
+++ trunk/src/VBox/NetworkServices/NAT/Makefile.kmk	(revision 49249)
@@ -58,4 +58,5 @@
     pxtcp.c \
     pxudp.c \
+    pxdns.c \
     fwtcp.c \
     fwudp.c \
Index: trunk/src/VBox/NetworkServices/NAT/VBoxNetLwipNAT.cpp
===================================================================
--- trunk/src/VBox/NetworkServices/NAT/VBoxNetLwipNAT.cpp	(revision 49210)
+++ trunk/src/VBox/NetworkServices/NAT/VBoxNetLwipNAT.cpp	(revision 49249)
@@ -193,4 +193,6 @@
     STDMETHOD(HandleEvent)(VBoxEventType_T aEventType, IEvent *pEvent);
 
+    const char **getHostNameservers();
+
     RTSEMEVENT hSemSVC;
     /* Only for debug needs, by default NAT service should load rules from SVC
@@ -375,4 +377,19 @@
             strHostAddr.setNull();
             strGuestAddr.setNull();
+            break;
+        }
+
+        case VBoxEventType_OnHostNameResolutionConfigurationChange:
+        {
+            const char **ppcszNameServers = getHostNameservers();
+            err_t error;
+
+            error = tcpip_callback_with_block(pxdns_set_nameservers,
+                                              ppcszNameServers,
+                                              /* :block */ 0);
+            if (error != ERR_OK && ppcszNameServers != NULL)
+            {
+                RTMemFree(ppcszNameServers);
+            }
             break;
         }
@@ -765,4 +782,5 @@
     m_src6.sin6_len = sizeof(m_src6);
 #endif
+    m_ProxyOptions.nameservers = NULL;
 
     m_LwipNetIf.name[0] = 'N';
@@ -1043,4 +1061,6 @@
     }
 
+    m_ProxyOptions.nameservers = getHostNameservers();
+
     /* end of COM initialization */
 
@@ -1067,4 +1087,54 @@
     LogFlowFuncLeaveRC(rc);
     return rc;
+}
+
+
+const char **VBoxNetLwipNAT::getHostNameservers()
+{
+    HRESULT hrc;
+
+    if (m_host.isNull())
+    {
+        return NULL;
+    }
+
+    com::SafeArray<BSTR> aNameServers;
+    hrc = m_host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(aNameServers));
+    if (FAILED(hrc))
+    {
+        return NULL;
+    }
+
+    const size_t cNameServers = aNameServers.size();
+    if (cNameServers == 0)
+    {
+        return NULL;
+    }
+
+    const char **ppcszNameServers =
+        (const char **)RTMemAllocZ(sizeof(char *) * (cNameServers + 1));
+    if (ppcszNameServers == NULL)
+    {
+        return NULL;
+    }
+
+    size_t idxLast = 0;
+    for (size_t i = 0; i < cNameServers; ++i)
+    {
+        com::Utf8Str strNameServer(aNameServers[i]);
+        ppcszNameServers[idxLast] = RTStrDup(strNameServer.c_str());
+        if (ppcszNameServers[idxLast] != NULL)
+        {
+            ++idxLast;
+        }
+    }
+
+    if (idxLast == 0)
+    {
+        RTMemFree(ppcszNameServers);
+        return NULL;
+    }
+
+    return ppcszNameServers;
 }
 
Index: trunk/src/VBox/NetworkServices/NAT/proxy.c
===================================================================
--- trunk/src/VBox/NetworkServices/NAT/proxy.c	(revision 49210)
+++ trunk/src/VBox/NetworkServices/NAT/proxy.c	(revision 49249)
@@ -35,5 +35,5 @@
 static SOCKET proxy_create_socket(int, int);
 
-volatile const struct proxy_options *g_proxy_options;
+volatile struct proxy_options *g_proxy_options;
 static sys_thread_t pollmgr_tid;
 
@@ -46,5 +46,5 @@
  */
 void
-proxy_init(struct netif *proxy_netif, const struct proxy_options *opts)
+proxy_init(struct netif *proxy_netif, struct proxy_options *opts)
 {
     int status;
@@ -83,4 +83,6 @@
 
     portfwd_init();
+
+    pxdns_init(proxy_netif);
 
     pollmgr_tid = sys_thread_new("pollmgr_thread",
Index: trunk/src/VBox/NetworkServices/NAT/proxy.h
===================================================================
--- trunk/src/VBox/NetworkServices/NAT/proxy.h	(revision 49210)
+++ trunk/src/VBox/NetworkServices/NAT/proxy.h	(revision 49249)
@@ -37,10 +37,11 @@
     const struct sockaddr_in6 *src6;
     const struct ip4_lomap_desc *lomap_desc;
+    const char **nameservers;
 };
 
-extern volatile const struct proxy_options *g_proxy_options;
+extern volatile struct proxy_options *g_proxy_options;
 extern struct netif *g_proxy_netif;
 
-void proxy_init(struct netif *, const struct proxy_options *);
+void proxy_init(struct netif *, struct proxy_options *);
 SOCKET proxy_connected_socket(int, int, ipX_addr_t *, u16_t);
 SOCKET proxy_bound_socket(int, int, struct sockaddr *);
@@ -69,4 +70,8 @@
 void pxudp_init(void);
 
+/* pxdns.c */
+err_t pxdns_init(struct netif *);
+void pxdns_set_nameservers(void *);
+
 
 #if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
Index: trunk/src/VBox/NetworkServices/NAT/pxdns.c
===================================================================
--- trunk/src/VBox/NetworkServices/NAT/pxdns.c	(revision 49210)
+++ trunk/src/VBox/NetworkServices/NAT/pxdns.c	(revision 49249)
@@ -34,4 +34,6 @@
  * DEALINGS IN THE SOFTWARE.
  */
+#include "winutils.h"
+
 #include "proxy.h"
 #include "proxy_pollmgr.h"
@@ -41,9 +43,21 @@
 #include "lwip/udp.h"
 
+#ifndef RT_OS_WINDOWS
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netdb.h>
+#else
+#include "winpoll.h"
+#endif
 
 #include <string.h>
+
+
+union sockaddr_inet {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
+};
 
 
@@ -55,16 +69,18 @@
  */
 struct pxdns {
-    struct pollmgr_handler pmhdl;
-
-    struct udp_pcb *pcb4;       /* lwIP doesn't support listening for */
-    struct udp_pcb *pcb6;       /*  both IPv4 and IPv6 on single pcb. */
-
-    SOCKET sock;
+    SOCKET sock4;
+    SOCKET sock6;
+
+    struct pollmgr_handler pmhdl4;
+    struct pollmgr_handler pmhdl6;
+
+    struct udp_pcb *pcb4;
+    struct udp_pcb *pcb6;
+
+    size_t generation;
+    size_t nresolvers;
+    union sockaddr_inet *resolvers;
 
     u16_t id;
-
-    /* XXX: TODO: support multiple, support IPv6 */
-    struct sockaddr_in resolver_sin;
-    socklen_t resolver_sinlen;
 
     sys_mutex_t lock;
@@ -92,8 +108,12 @@
 
     /**
-     * XXX: TODO: to test rexmit code, rexmit to the same resolver
-     * multiple times; to be replaced with trying the next resolver.
-     */
-    size_t rexmit_count;
+     * pxdns::generation used for this request
+     */
+    size_t generation;
+
+    /**
+     * Current index into pxdns::resolvers
+     */
+    size_t residx;
 
     /**
@@ -142,4 +162,7 @@
 };
 
+
+static void pxdns_create_resolver_sockaddrs(struct pxdns *pxdns,
+                                            const char **nameservers);
 
 static void pxdns_recv4(void *arg, struct udp_pcb *pcb, struct pbuf *p,
@@ -176,7 +199,11 @@
     LWIP_UNUSED_ARG(proxy_netif);
 
-    pxdns->pmhdl.callback = pxdns_pmgr_pump;
-    pxdns->pmhdl.data = (void *)pxdns;
-    pxdns->pmhdl.slot = -1;
+    pxdns->pmhdl4.callback = pxdns_pmgr_pump;
+    pxdns->pmhdl4.data = (void *)pxdns;
+    pxdns->pmhdl4.slot = -1;
+
+    pxdns->pmhdl6.callback = pxdns_pmgr_pump;
+    pxdns->pmhdl6.data = (void *)pxdns;
+    pxdns->pmhdl6.slot = -1;
 
     pxdns->pcb4 = udp_new();
@@ -197,5 +224,5 @@
     }
 
-    error = udp_bind_ip6(pxdns->pcb4, IP6_ADDR_ANY, 53);
+    error = udp_bind_ip6(pxdns->pcb6, IP6_ADDR_ANY, 53);
     if (error != ERR_OK) {
         goto err_cleanup_pcb;
@@ -205,17 +232,19 @@
     udp_recv_ip6(pxdns->pcb6, pxdns_recv6, pxdns);
 
-    pxdns->sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (pxdns->sock == INVALID_SOCKET) {
+    pxdns->sock4 = socket(AF_INET, SOCK_DGRAM, 0);
+    if (pxdns->sock4 == INVALID_SOCKET) {
         goto err_cleanup_pcb;
     }
 
-    /* XXX: TODO: support multiple, support IPv6 */
-    pxdns->resolver_sin.sin_family = AF_INET;
-    pxdns->resolver_sin.sin_addr.s_addr = PP_HTONL(0x7f000001); /* XXX */
-    pxdns->resolver_sin.sin_port = PP_HTONS(53);
-#if HAVE_SA_LEN
-    pxdns->resolver_sin.sin_len =
-#endif
-        pxdns->resolver_sinlen = sizeof(pxdns->resolver_sin);
+    pxdns->sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (pxdns->sock6 == INVALID_SOCKET) {
+        /* it's ok if the host doesn't support IPv6 */
+        /* XXX: TODO: log */
+    }
+
+    pxdns->generation = 0;
+    pxdns->nresolvers = 0;
+    pxdns->resolvers = NULL;
+    pxdns_create_resolver_sockaddrs(pxdns, g_proxy_options->nameservers);
 
     sys_mutex_new(&pxdns->lock);
@@ -223,6 +252,9 @@
     pxdns->timeout_slot = 0;
 
-    /* XXX: assumes pollmgr thread is not running yet */
-    pollmgr_add(&pxdns->pmhdl, pxdns->sock, POLLIN);
+    /* NB: assumes pollmgr thread is not running yet */
+    pollmgr_add(&pxdns->pmhdl4, pxdns->sock4, POLLIN);
+    if (pxdns->sock6 != INVALID_SOCKET) {
+        pollmgr_add(&pxdns->pmhdl6, pxdns->sock6, POLLIN);
+    }
 
     sys_timeout(1 * 1000, pxdns_timer, pxdns);
@@ -241,4 +273,123 @@
 
     return error;
+}
+
+
+/**
+ * lwIP thread callback to set the new list of nameservers.
+ */
+void
+pxdns_set_nameservers(void *arg)
+{
+    const char **nameservers = (const char **)arg;
+
+    if (g_proxy_options->nameservers != NULL) {
+        RTMemFree(g_proxy_options->nameservers);
+    }
+    g_proxy_options->nameservers = nameservers;
+
+    pxdns_create_resolver_sockaddrs(&g_pxdns, nameservers);
+}
+
+
+/**
+ * Use this list of nameservers to resolve guest requests.
+ *
+ * Runs on lwIP thread, so no new queries or retramsmits compete with
+ * it for the use of the existing list of resolvers (to be replaced).
+ */
+static void
+pxdns_create_resolver_sockaddrs(struct pxdns *pxdns, const char **nameservers)
+{
+    /*
+     * XXX: TODO: Windows supports getaddrinfo(), including execution
+     * on older version of Windows where runtime tricks hide the gory
+     * compatibility details.
+     *
+     * http://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
+     */
+#ifndef RT_OS_WINDOWS
+    struct addrinfo hints;
+#endif
+    union sockaddr_inet *resolvers;
+    size_t nnames, nresolvers;
+    const char **p;
+    int status;
+
+    resolvers = NULL;
+    nresolvers = 0;
+
+    if (nameservers == NULL) {
+        goto update_resolvers;
+    }
+
+    nnames = 0;
+    for (p = nameservers; *p != NULL; ++p) {
+        ++nnames;
+    }
+
+    if (nnames == 0) {
+        goto update_resolvers;
+    }
+
+    resolvers = (union sockaddr_inet *)calloc(sizeof(resolvers[0]), nnames);
+    if (resolvers == NULL) {
+        nresolvers = 0;
+        goto update_resolvers;
+    }
+
+#ifndef RT_OS_WINDOWS
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+    for (p = nameservers; *p != NULL; ++p) {
+        const char *name = *p;
+        struct addrinfo *ai;
+        status = getaddrinfo(name, /* "domain" */ "53", &hints, &ai);
+        if (status != 0) {
+            /* XXX: log failed resolution */
+            continue;
+        }
+
+        if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
+            /* XXX: log unsupported address family */
+            freeaddrinfo(ai);
+            continue;
+        }
+
+        if (ai->ai_addrlen > sizeof(resolvers[nresolvers])) {
+            /* XXX: log */
+            freeaddrinfo(ai);
+            continue;
+        }
+
+        if (ai->ai_family == AF_INET6 && pxdns->sock6 == INVALID_SOCKET) {
+            /* no IPv6 support on the host, can't use this resolver */
+            freeaddrinfo(ai);
+            continue;
+        }
+
+        memcpy(&resolvers[nresolvers], ai->ai_addr, ai->ai_addrlen);
+        freeaddrinfo(ai);
+        ++nresolvers;
+    }
+#endif  /* RT_OS_WINDOWS */
+
+    if (nresolvers == 0) {
+        if (resolvers != NULL) {
+            free(resolvers);
+        }
+        resolvers = NULL;
+    }
+
+  update_resolvers:
+    ++pxdns->generation;
+    if (pxdns->resolvers != NULL) {
+        free(pxdns->resolvers);
+    }
+    pxdns->resolvers = resolvers;
+    pxdns->nresolvers = nresolvers;
 }
 
@@ -447,6 +598,13 @@
     int sent;
 
+    if (pxdns->nresolvers == 0) {
+        /* nothing we can do */
+        pbuf_free(p);
+        return;
+    }
+
     req = calloc(1, sizeof(struct request) - 1 + p->tot_len);
     if (req == NULL) {
+        pbuf_free(p);
         return;
     }
@@ -466,7 +624,9 @@
     memcpy(req->data, &req->id, sizeof(u16_t));
 
-    /* XXX */
-    req->rexmit_count = 1;
-
+    /* resolver to forward to */
+    req->generation = pxdns->generation;
+    req->residx = 0;
+
+    /* prepare for relaying the reply back to guest */
     req->msg_reply.type = TCPIP_MSG_CALLBACK_STATIC;
     req->msg_reply.sem = NULL;
@@ -480,5 +640,5 @@
 
     sent = pxdns_forward_outbound(pxdns, req);
-    while (!sent) {
+    if (!sent) {
         sent = pxdns_rexmit(pxdns, req);
     }
@@ -490,14 +650,42 @@
 
 
+/**
+ * Forward request to the req::residx resolver in the pxdns::resolvers
+ * array of upstream resolvers.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
 static int
 pxdns_forward_outbound(struct pxdns *pxdns, struct request *req)
 {
+    union sockaddr_inet *resolver;
     ssize_t nsent;
 
-    DPRINTF2(("%s: req %p\n", __func__, (void *)req));
-
-    nsent = sendto(pxdns->sock, req->data, req->size, 0,
-                   (struct sockaddr *)&pxdns->resolver_sin,
-                   pxdns->resolver_sinlen);
+    DPRINTF2(("%s: req %p: sending to resolver #%lu\n",
+              __func__, (void *)req, (unsigned long)req->residx));
+
+    LWIP_ASSERT1(req->generation == pxdns->generation);
+    LWIP_ASSERT1(req->residx < pxdns->nresolvers);
+    resolver = &pxdns->resolvers[req->residx];
+
+    if (resolver->sa.sa_family == AF_INET) {
+        nsent = sendto(pxdns->sock4, req->data, req->size, 0,
+                       &resolver->sa, sizeof(resolver->sin));
+        
+    }
+    else if (resolver->sa.sa_family == AF_INET6) {
+        if (pxdns->sock6 != INVALID_SOCKET) {
+            nsent = sendto(pxdns->sock6, req->data, req->size, 0,
+                           &resolver->sa, sizeof(resolver->sin6));
+        }
+        else {
+            /* shouldn't happen, we should have weeded out IPv6 resolvers */
+            return 0;
+        }
+    }
+    else {
+        /* shouldn't happen, we should have weeded out unsupported families */
+        return 0;
+    }
 
     if ((size_t)nsent == req->size) {
@@ -506,9 +694,9 @@
 
     if (nsent < 0) {
-        perror("dnsproxy");
-    }
-    else if ((size_t)nsent != req->size) {
-        DPRINTF(("%s: sent only %lu of %lu\n",
-                 __func__, (unsigned long)nsent, (unsigned long)req->size));
+        DPRINTF2(("%s: send: errno %d\n", __func__, errno));
+    }
+    else {
+        DPRINTF2(("%s: sent only %lu of %lu\n",
+                  __func__, (unsigned long)nsent, (unsigned long)req->size));
     }
     return 0; /* not sent, caller will retry as necessary */
@@ -516,17 +704,31 @@
 
 
+/**
+ * Forward request to the next resolver in the pxdns::resolvers array
+ * of upstream resolvers if there are any left.
+ */
 static int
 pxdns_rexmit(struct pxdns *pxdns, struct request *req)
 {
-    DPRINTF2(("%s: req %p: rexmit count %lu\n",
-              __func__, (void *)req, (unsigned long)req->rexmit_count));
-
-    /* XXX: TODO: use the next resolver instead */
-    if (req->rexmit_count == 0) {
+    int sent;
+
+    if (/* __predict_false */ req->generation != pxdns->generation) {
+        DPRINTF2(("%s: req %p: generation %lu != pxdns generation %lu\n",
+                  __func__, (void *)req,
+                  (unsigned long)req->generation,
+                  (unsigned long)pxdns->generation));
         return 0;
     }
-    --req->rexmit_count;
-
-    return pxdns_forward_outbound(pxdns, req);
+
+    LWIP_ASSERT1(req->residx < pxdns->nresolvers);
+    do {
+        if (++req->residx == pxdns->nresolvers) {
+            return 0;
+        }
+
+        sent = pxdns_forward_outbound(pxdns, req);
+    } while (!sent);
+
+    return 1;
 }
 
@@ -542,7 +744,6 @@
 
     pxdns = (struct pxdns *)handler->data;
-    LWIP_ASSERT1(handler == &pxdns->pmhdl);
-    LWIP_ASSERT1(fd = pxdns->sock);
-    LWIP_UNUSED_ARG(fd);
+    LWIP_ASSERT1(handler == &pxdns->pmhdl4 || handler == &pxdns->pmhdl6);
+    LWIP_ASSERT1(fd == (handler == &pxdns->pmhdl4 ? pxdns->sock4 : pxdns->sock6));
 
     if (revents & ~(POLLIN|POLLERR)) {
@@ -556,13 +757,13 @@
         int status;
 
-        status = getsockopt(pxdns->sock, SOL_SOCKET,
+        status = getsockopt(fd, SOL_SOCKET,
                             SO_ERROR, &sockerr, &optlen);
         if (status < 0) {
             DPRINTF(("%s: sock %d: SO_ERROR failed with errno %d\n",
-                     __func__, pxdns->sock, errno));
+                     __func__, fd, errno));
         }
         else {
             DPRINTF(("%s: sock %d: errno %d\n",
-                     __func__, pxdns->sock, sockerr));
+                     __func__, fd, sockerr));
         }
     }
@@ -573,5 +774,5 @@
 
 
-    nread = recv(pxdns->sock, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
+    nread = recv(fd, pollmgr_udpbuf, sizeof(pollmgr_udpbuf), 0);
     if (nread < 0) {
         perror(__func__);
@@ -585,4 +786,6 @@
         return POLLIN;
     }
+
+    /* XXX: shall we proxy back RCODE=Refused responses? */
 
     memcpy(&id, pollmgr_udpbuf, sizeof(id));
@@ -594,6 +797,6 @@
     }
 
-    DPRINTF2(("%s: reply for req=%p: client id %d -> id %d\n",
-              __func__, (void *)req, req->client_id, req->id));
+    DPRINTF2(("%s: reply for req=%p: id %d -> client id %d\n",
+              __func__, (void *)req, req->id, req->client_id));
 
     req->reply = pbuf_alloc(PBUF_RAW, nread, PBUF_RAM);
@@ -629,5 +832,5 @@
                        ipX_2_ip(&req->client_addr), req->client_port);
     if (error != ERR_OK) {
-        DPRINTF(("%s: udp_sendto err %d\n",
+        DPRINTF(("%s: udp_sendto err %s\n",
                  __func__, proxy_lwip_strerr(error)));
     }
