/* $Id$ */
/** @file
* NAT - dns initialization.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "slirp.h"
#ifdef RT_OS_OS2
# include <paths.h>
#endif
#include <VBox/err.h>
#include <VBox/vmm/pdmdrv.h>
#include <iprt/assert.h>
#include <iprt/file.h>
#ifdef RT_OS_WINDOWS
# include <Winnls.h>
# define _WINSOCK2API_
# include <IPHlpApi.h>
static int get_dns_addr_domain(PNATState pData,
const char **ppszDomain)
{
ULONG flags = GAA_FLAG_INCLUDE_PREFIX; /*GAA_FLAG_INCLUDE_ALL_INTERFACES;*/ /* all interfaces registered in NDIS */
PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
PIP_ADAPTER_ADDRESSES pAddr = NULL;
PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr = NULL;
ULONG size;
int wlen = 0;
char *pszSuffix;
struct dns_domain_entry *pDomain = NULL;
ULONG ret = ERROR_SUCCESS;
/** @todo add SKIPing flags to get only required information */
/* determine size of buffer */
size = 0;
ret = pData->pfGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
if (ret != ERROR_BUFFER_OVERFLOW)
{
Log(("NAT: error %lu occurred on capacity detection operation\n", ret));
return -1;
}
if (size == 0)
{
Log(("NAT: Win socket API returns non capacity\n"));
return -1;
}
pAdapterAddr = RTMemAllocZ(size);
if (!pAdapterAddr)
{
Log(("NAT: No memory available\n"));
return -1;
}
ret = pData->pfGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
if (ret != ERROR_SUCCESS)
{
Log(("NAT: error %lu occurred on fetching adapters info\n", ret));
RTMemFree(pAdapterAddr);
return -1;
}
for (pAddr = pAdapterAddr; pAddr != NULL; pAddr = pAddr->Next)
{
int found;
if (pAddr->OperStatus != IfOperStatusUp)
continue;
for (pDnsAddr = pAddr->FirstDnsServerAddress; pDnsAddr != NULL; pDnsAddr = pDnsAddr->Next)
{
struct sockaddr *SockAddr = pDnsAddr->Address.lpSockaddr;
struct in_addr InAddr;
struct dns_entry *pDns;
if (SockAddr->sa_family != AF_INET)
continue;
InAddr = ((struct sockaddr_in *)SockAddr)->sin_addr;
/* add dns server to list */
pDns = RTMemAllocZ(sizeof(struct dns_entry));
if (!pDns)
{
Log(("NAT: Can't allocate buffer for DNS entry\n"));
RTMemFree(pAdapterAddr);
return VERR_NO_MEMORY;
}
Log(("NAT: adding %RTnaipv4 to DNS server list\n", InAddr));
if ((InAddr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET)) == RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
pDns->de_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
else
pDns->de_addr.s_addr = InAddr.s_addr;
TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
if (pAddr->DnsSuffix == NULL)
continue;
/* uniq */
RTUtf16ToUtf8(pAddr->DnsSuffix, &pszSuffix);
if (!pszSuffix || strlen(pszSuffix) == 0)
{
RTStrFree(pszSuffix);
continue;
}
found = 0;
LIST_FOREACH(pDomain, &pData->pDomainList, dd_list)
{
if ( pDomain->dd_pszDomain != NULL
&& strcmp(pDomain->dd_pszDomain, pszSuffix) == 0)
{
found = 1;
RTStrFree(pszSuffix);
break;
}
}
if (!found)
{
pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
if (!pDomain)
{
Log(("NAT: not enough memory\n"));
RTStrFree(pszSuffix);
RTMemFree(pAdapterAddr);
return VERR_NO_MEMORY;
}
pDomain->dd_pszDomain = pszSuffix;
Log(("NAT: adding domain name %s to search list\n", pDomain->dd_pszDomain));
LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
}
}
}
RTMemFree(pAdapterAddr);
return 0;
}
#else /* !RT_OS_WINDOWS */
#include "resolv_conf_parser.h"
static int get_dns_addr_domain(PNATState pData, const char **ppszDomain)
{
struct rcp_state st;
int rc;
unsigned i;
/* XXX: perhaps IPv6 shouldn't be ignored if we're using DNS proxy */
st.rcps_flags = RCPSF_IGNORE_IPV6;
rc = rcp_parse(&st, RESOLV_CONF_FILE);
if (rc < 0)
return -1;
/* for historical reasons: Slirp returns -1 if no nameservers were found */
if (st.rcps_num_nameserver == 0)
return -1;
/* XXX: We're composing the list, but we already knows
* its size so we can allocate array instead (Linux guests
* dont like >3 servers in the list anyway)
* or use pre-allocated array in NATState.
*/
for (i = 0; i != st.rcps_num_nameserver; ++i)
{
struct dns_entry *pDns;
RTNETADDRU *address = &st.rcps_nameserver[i].uAddr;
if ( (address->IPv4.u & RT_H2N_U32_C(IN_CLASSA_NET))
== RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
{
/**
* XXX: Note shouldn't patch the address in case of using DNS proxy,
* because DNS proxy we do revert it back actually.
*/
if (address->IPv4.u == RT_N2H_U32_C(INADDR_LOOPBACK))
address->IPv4.u = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
else if (pData->fUseDnsProxy == 0) {
/* We detects that using some address in 127/8 network */
LogRel(("NAT: DNS server %RTnaipv4 registration detected, switching to the DNS proxy\n", address->IPv4));
pData->fUseDnsProxy = 1;
}
}
pDns = RTMemAllocZ(sizeof(struct dns_entry));
if (pDns == NULL)
{
slirpReleaseDnsSettings(pData);
return VERR_NO_MEMORY;
}
pDns->de_addr.s_addr = address->IPv4.u;
TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
}
if (st.rcps_domain != 0)
{
struct dns_domain_entry *pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
if (pDomain == NULL)
{
slirpReleaseDnsSettings(pData);
return -1;
}
pDomain->dd_pszDomain = RTStrDup(st.rcps_domain);
LogRel(("NAT: Adding domain name %s\n", pDomain->dd_pszDomain));
LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
}
if (ppszDomain && st.rcps_domain != 0)
*ppszDomain = RTStrDup(st.rcps_domain);
return 0;
}
#endif /* !RT_OS_WINDOWS */
int slirpInitializeDnsSettings(PNATState pData)
{
int rc = VINF_SUCCESS;
AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
LogFlowFuncEnter();
if (!pData->fUseHostResolverPermanent)
{
TAILQ_INIT(&pData->pDnsList);
LIST_INIT(&pData->pDomainList);
/*
* Some distributions haven't got /etc/resolv.conf
* so we should other way to configure DNS settings.
*/
if (get_dns_addr_domain(pData, NULL) < 0)
{
/* Load the DNS handler if host resolver mode was not used before. */
if (!pData->fUseHostResolver)
dns_alias_load(pData);
pData->fUseHostResolver = true;
}
else
{
/* Unload to not intercept in the future. */
if (pData->fUseHostResolver)
dns_alias_unload(pData);
pData->fUseHostResolver = false;
dnsproxy_init(pData);
}
if (!pData->fUseHostResolver)
{
struct dns_entry *pDNSEntry = NULL;
int cDNSListEntry = 0;
TAILQ_FOREACH_REVERSE(pDNSEntry, &pData->pDnsList, dns_list_head, de_list)
{
LogRel(("NAT: DNS#%i: %RTnaipv4\n", cDNSListEntry, pDNSEntry->de_addr.s_addr));
cDNSListEntry++;
}
}
}
LogFlowFuncLeaveRC(rc);
return rc;
}
int slirpReleaseDnsSettings(PNATState pData)
{
struct dns_entry *pDns = NULL;
struct dns_domain_entry *pDomain = NULL;
int rc = VINF_SUCCESS;
AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
LogFlowFuncEnter();
while (!TAILQ_EMPTY(&pData->pDnsList))
{
pDns = TAILQ_FIRST(&pData->pDnsList);
TAILQ_REMOVE(&pData->pDnsList, pDns, de_list);
RTMemFree(pDns);
}
while (!LIST_EMPTY(&pData->pDomainList))
{
pDomain = LIST_FIRST(&pData->pDomainList);
LIST_REMOVE(pDomain, dd_list);
if (pDomain->dd_pszDomain != NULL)
RTStrFree(pDomain->dd_pszDomain);
RTMemFree(pDomain);
}
/* tell any pending dnsproxy requests their copy is expired */
++pData->dnsgen;
LogFlowFuncLeaveRC(rc);
return rc;
}