[41977] | 1 | /* $Id: slirp_dns.c 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * NAT - dns initialization.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2012-2023 Oracle and/or its affiliates.
|
---|
[41977] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[41977] | 26 | */
|
---|
| 27 |
|
---|
| 28 | #include "slirp.h"
|
---|
| 29 | #ifdef RT_OS_OS2
|
---|
| 30 | # include <paths.h>
|
---|
| 31 | #endif
|
---|
| 32 |
|
---|
[76474] | 33 | #include <iprt/errcore.h>
|
---|
[41977] | 34 | #include <VBox/vmm/pdmdrv.h>
|
---|
| 35 | #include <iprt/assert.h>
|
---|
| 36 | #include <iprt/file.h>
|
---|
| 37 |
|
---|
| 38 | #ifdef RT_OS_WINDOWS
|
---|
[76409] | 39 | # include <iprt/utf16.h>
|
---|
[41980] | 40 | # include <Winnls.h>
|
---|
| 41 | # define _WINSOCK2API_
|
---|
[62692] | 42 | # include <iprt/win/iphlpapi.h>
|
---|
[41980] | 43 |
|
---|
[63012] | 44 | static int get_dns_addr_domain(PNATState pData)
|
---|
[41977] | 45 | {
|
---|
[63217] | 46 | /*ULONG flags = GAA_FLAG_INCLUDE_PREFIX;*/ /*GAA_FLAG_INCLUDE_ALL_INTERFACES;*/ /* all interfaces registered in NDIS */
|
---|
[41977] | 47 | PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
|
---|
| 48 | PIP_ADAPTER_ADDRESSES pAddr = NULL;
|
---|
| 49 | PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr = NULL;
|
---|
| 50 | ULONG size;
|
---|
| 51 | char *pszSuffix;
|
---|
| 52 | struct dns_domain_entry *pDomain = NULL;
|
---|
| 53 | ULONG ret = ERROR_SUCCESS;
|
---|
| 54 |
|
---|
[49436] | 55 | /** @todo add SKIPing flags to get only required information */
|
---|
[41977] | 56 |
|
---|
| 57 | /* determine size of buffer */
|
---|
| 58 | size = 0;
|
---|
[62696] | 59 | ret = pData->pfnGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
|
---|
[41977] | 60 | if (ret != ERROR_BUFFER_OVERFLOW)
|
---|
| 61 | {
|
---|
| 62 | Log(("NAT: error %lu occurred on capacity detection operation\n", ret));
|
---|
| 63 | return -1;
|
---|
| 64 | }
|
---|
| 65 | if (size == 0)
|
---|
| 66 | {
|
---|
| 67 | Log(("NAT: Win socket API returns non capacity\n"));
|
---|
| 68 | return -1;
|
---|
| 69 | }
|
---|
| 70 |
|
---|
| 71 | pAdapterAddr = RTMemAllocZ(size);
|
---|
| 72 | if (!pAdapterAddr)
|
---|
| 73 | {
|
---|
| 74 | Log(("NAT: No memory available\n"));
|
---|
| 75 | return -1;
|
---|
| 76 | }
|
---|
[62696] | 77 | ret = pData->pfnGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
|
---|
[41977] | 78 | if (ret != ERROR_SUCCESS)
|
---|
| 79 | {
|
---|
| 80 | Log(("NAT: error %lu occurred on fetching adapters info\n", ret));
|
---|
| 81 | RTMemFree(pAdapterAddr);
|
---|
| 82 | return -1;
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | for (pAddr = pAdapterAddr; pAddr != NULL; pAddr = pAddr->Next)
|
---|
| 86 | {
|
---|
| 87 | int found;
|
---|
| 88 | if (pAddr->OperStatus != IfOperStatusUp)
|
---|
| 89 | continue;
|
---|
| 90 |
|
---|
| 91 | for (pDnsAddr = pAddr->FirstDnsServerAddress; pDnsAddr != NULL; pDnsAddr = pDnsAddr->Next)
|
---|
| 92 | {
|
---|
| 93 | struct sockaddr *SockAddr = pDnsAddr->Address.lpSockaddr;
|
---|
| 94 | struct in_addr InAddr;
|
---|
| 95 | struct dns_entry *pDns;
|
---|
| 96 |
|
---|
| 97 | if (SockAddr->sa_family != AF_INET)
|
---|
| 98 | continue;
|
---|
| 99 |
|
---|
| 100 | InAddr = ((struct sockaddr_in *)SockAddr)->sin_addr;
|
---|
| 101 |
|
---|
| 102 | /* add dns server to list */
|
---|
| 103 | pDns = RTMemAllocZ(sizeof(struct dns_entry));
|
---|
| 104 | if (!pDns)
|
---|
| 105 | {
|
---|
| 106 | Log(("NAT: Can't allocate buffer for DNS entry\n"));
|
---|
| 107 | RTMemFree(pAdapterAddr);
|
---|
| 108 | return VERR_NO_MEMORY;
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | Log(("NAT: adding %RTnaipv4 to DNS server list\n", InAddr));
|
---|
| 112 | if ((InAddr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET)) == RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
|
---|
| 113 | pDns->de_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
|
---|
| 114 | else
|
---|
| 115 | pDns->de_addr.s_addr = InAddr.s_addr;
|
---|
| 116 |
|
---|
| 117 | TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
|
---|
| 118 |
|
---|
| 119 | if (pAddr->DnsSuffix == NULL)
|
---|
| 120 | continue;
|
---|
| 121 |
|
---|
| 122 | /* uniq */
|
---|
| 123 | RTUtf16ToUtf8(pAddr->DnsSuffix, &pszSuffix);
|
---|
| 124 | if (!pszSuffix || strlen(pszSuffix) == 0)
|
---|
| 125 | {
|
---|
| 126 | RTStrFree(pszSuffix);
|
---|
| 127 | continue;
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | found = 0;
|
---|
| 131 | LIST_FOREACH(pDomain, &pData->pDomainList, dd_list)
|
---|
| 132 | {
|
---|
| 133 | if ( pDomain->dd_pszDomain != NULL
|
---|
| 134 | && strcmp(pDomain->dd_pszDomain, pszSuffix) == 0)
|
---|
| 135 | {
|
---|
| 136 | found = 1;
|
---|
| 137 | RTStrFree(pszSuffix);
|
---|
| 138 | break;
|
---|
| 139 | }
|
---|
| 140 | }
|
---|
| 141 | if (!found)
|
---|
| 142 | {
|
---|
| 143 | pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
|
---|
| 144 | if (!pDomain)
|
---|
| 145 | {
|
---|
| 146 | Log(("NAT: not enough memory\n"));
|
---|
| 147 | RTStrFree(pszSuffix);
|
---|
| 148 | RTMemFree(pAdapterAddr);
|
---|
| 149 | return VERR_NO_MEMORY;
|
---|
| 150 | }
|
---|
| 151 | pDomain->dd_pszDomain = pszSuffix;
|
---|
| 152 | Log(("NAT: adding domain name %s to search list\n", pDomain->dd_pszDomain));
|
---|
| 153 | LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
|
---|
| 154 | }
|
---|
| 155 | }
|
---|
| 156 | }
|
---|
| 157 | RTMemFree(pAdapterAddr);
|
---|
| 158 | return 0;
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | #else /* !RT_OS_WINDOWS */
|
---|
| 162 |
|
---|
[50129] | 163 | #include "resolv_conf_parser.h"
|
---|
[41977] | 164 |
|
---|
[63012] | 165 | static int get_dns_addr_domain(PNATState pData)
|
---|
[43673] | 166 | {
|
---|
[50129] | 167 | struct rcp_state st;
|
---|
[43673] | 168 | int rc;
|
---|
[50129] | 169 | unsigned i;
|
---|
[43673] | 170 |
|
---|
[50129] | 171 | /* XXX: perhaps IPv6 shouldn't be ignored if we're using DNS proxy */
|
---|
| 172 | st.rcps_flags = RCPSF_IGNORE_IPV6;
|
---|
| 173 | rc = rcp_parse(&st, RESOLV_CONF_FILE);
|
---|
| 174 |
|
---|
[54011] | 175 | if (rc < 0)
|
---|
| 176 | return -1;
|
---|
[41977] | 177 |
|
---|
[50129] | 178 | /* for historical reasons: Slirp returns -1 if no nameservers were found */
|
---|
| 179 | if (st.rcps_num_nameserver == 0)
|
---|
| 180 | return -1;
|
---|
[41977] | 181 |
|
---|
[53624] | 182 |
|
---|
| 183 | /* XXX: We're composing the list, but we already knows
|
---|
| 184 | * its size so we can allocate array instead (Linux guests
|
---|
| 185 | * dont like >3 servers in the list anyway)
|
---|
[50129] | 186 | * or use pre-allocated array in NATState.
|
---|
| 187 | */
|
---|
| 188 | for (i = 0; i != st.rcps_num_nameserver; ++i)
|
---|
[41977] | 189 | {
|
---|
[50129] | 190 | struct dns_entry *pDns;
|
---|
| 191 | RTNETADDRU *address = &st.rcps_nameserver[i].uAddr;
|
---|
[71203] | 192 |
|
---|
| 193 | if (address->IPv4.u == INADDR_ANY)
|
---|
| 194 | {
|
---|
| 195 | /*
|
---|
| 196 | * This doesn't seem to be very well documented except for
|
---|
| 197 | * RTFS of res_init.c, but INADDR_ANY is a valid value for
|
---|
| 198 | * for "nameserver".
|
---|
| 199 | */
|
---|
| 200 | address->IPv4.u = RT_H2N_U32_C(INADDR_LOOPBACK);
|
---|
| 201 | }
|
---|
| 202 |
|
---|
[53624] | 203 | if ( (address->IPv4.u & RT_H2N_U32_C(IN_CLASSA_NET))
|
---|
| 204 | == RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
|
---|
[41977] | 205 | {
|
---|
[53624] | 206 | /**
|
---|
[50129] | 207 | * XXX: Note shouldn't patch the address in case of using DNS proxy,
|
---|
[53624] | 208 | * because DNS proxy we do revert it back actually.
|
---|
[50129] | 209 | */
|
---|
[92108] | 210 | if ( address->IPv4.u == RT_N2H_U32_C(INADDR_LOOPBACK)
|
---|
| 211 | && pData->fLocalhostReachable)
|
---|
[50129] | 212 | address->IPv4.u = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
|
---|
| 213 | else if (pData->fUseDnsProxy == 0) {
|
---|
[92108] | 214 | /*
|
---|
| 215 | * Either the resolver lives somewhere else on the 127/8 network or the loopback interface
|
---|
| 216 | * is blocked for access from the guest, either way switch to the DNS proxy.
|
---|
| 217 | */
|
---|
| 218 | if (pData->fLocalhostReachable)
|
---|
| 219 | LogRel(("NAT: DNS server %RTnaipv4 registration detected, switching to the DNS proxy\n", address->IPv4));
|
---|
| 220 | else
|
---|
| 221 | LogRel(("NAT: Switching to DNS proxying due to access to the loopback interface being blocked\n"));
|
---|
[50129] | 222 | pData->fUseDnsProxy = 1;
|
---|
| 223 | }
|
---|
[41977] | 224 | }
|
---|
[50129] | 225 |
|
---|
| 226 | pDns = RTMemAllocZ(sizeof(struct dns_entry));
|
---|
| 227 | if (pDns == NULL)
|
---|
[41977] | 228 | {
|
---|
[50129] | 229 | slirpReleaseDnsSettings(pData);
|
---|
| 230 | return VERR_NO_MEMORY;
|
---|
| 231 | }
|
---|
[41977] | 232 |
|
---|
[50129] | 233 | pDns->de_addr.s_addr = address->IPv4.u;
|
---|
| 234 | TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
|
---|
| 235 | }
|
---|
[41977] | 236 |
|
---|
[50129] | 237 | if (st.rcps_domain != 0)
|
---|
| 238 | {
|
---|
| 239 | struct dns_domain_entry *pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
|
---|
| 240 | if (pDomain == NULL)
|
---|
[41977] | 241 | {
|
---|
[50129] | 242 | slirpReleaseDnsSettings(pData);
|
---|
| 243 | return -1;
|
---|
[41977] | 244 | }
|
---|
[50129] | 245 |
|
---|
| 246 | pDomain->dd_pszDomain = RTStrDup(st.rcps_domain);
|
---|
[55178] | 247 | LogRel(("NAT: Adding domain name %s\n", pDomain->dd_pszDomain));
|
---|
[50129] | 248 | LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
|
---|
[41977] | 249 | }
|
---|
[50129] | 250 |
|
---|
[41977] | 251 | return 0;
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 | #endif /* !RT_OS_WINDOWS */
|
---|
| 255 |
|
---|
| 256 | int slirpInitializeDnsSettings(PNATState pData)
|
---|
| 257 | {
|
---|
| 258 | int rc = VINF_SUCCESS;
|
---|
| 259 | AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
|
---|
| 260 | LogFlowFuncEnter();
|
---|
[48526] | 261 | if (!pData->fUseHostResolverPermanent)
|
---|
[41977] | 262 | {
|
---|
| 263 | TAILQ_INIT(&pData->pDnsList);
|
---|
| 264 | LIST_INIT(&pData->pDomainList);
|
---|
[49436] | 265 |
|
---|
| 266 | /*
|
---|
[41977] | 267 | * Some distributions haven't got /etc/resolv.conf
|
---|
| 268 | * so we should other way to configure DNS settings.
|
---|
| 269 | */
|
---|
[63012] | 270 | if (get_dns_addr_domain(pData) < 0)
|
---|
[48526] | 271 | pData->fUseHostResolver = true;
|
---|
[41977] | 272 | else
|
---|
[48526] | 273 | {
|
---|
| 274 | pData->fUseHostResolver = false;
|
---|
[41977] | 275 | dnsproxy_init(pData);
|
---|
[48526] | 276 | }
|
---|
[42137] | 277 |
|
---|
| 278 | if (!pData->fUseHostResolver)
|
---|
| 279 | {
|
---|
| 280 | struct dns_entry *pDNSEntry = NULL;
|
---|
| 281 | int cDNSListEntry = 0;
|
---|
| 282 | TAILQ_FOREACH_REVERSE(pDNSEntry, &pData->pDnsList, dns_list_head, de_list)
|
---|
| 283 | {
|
---|
| 284 | LogRel(("NAT: DNS#%i: %RTnaipv4\n", cDNSListEntry, pDNSEntry->de_addr.s_addr));
|
---|
| 285 | cDNSListEntry++;
|
---|
| 286 | }
|
---|
| 287 | }
|
---|
[41977] | 288 | }
|
---|
| 289 |
|
---|
| 290 | LogFlowFuncLeaveRC(rc);
|
---|
| 291 | return rc;
|
---|
| 292 | }
|
---|
| 293 |
|
---|
| 294 | int slirpReleaseDnsSettings(PNATState pData)
|
---|
| 295 | {
|
---|
| 296 | struct dns_entry *pDns = NULL;
|
---|
| 297 | struct dns_domain_entry *pDomain = NULL;
|
---|
| 298 | int rc = VINF_SUCCESS;
|
---|
[41979] | 299 | AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
|
---|
[41977] | 300 | LogFlowFuncEnter();
|
---|
| 301 |
|
---|
| 302 | while (!TAILQ_EMPTY(&pData->pDnsList))
|
---|
| 303 | {
|
---|
| 304 | pDns = TAILQ_FIRST(&pData->pDnsList);
|
---|
| 305 | TAILQ_REMOVE(&pData->pDnsList, pDns, de_list);
|
---|
| 306 | RTMemFree(pDns);
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 | while (!LIST_EMPTY(&pData->pDomainList))
|
---|
| 310 | {
|
---|
| 311 | pDomain = LIST_FIRST(&pData->pDomainList);
|
---|
| 312 | LIST_REMOVE(pDomain, dd_list);
|
---|
| 313 | if (pDomain->dd_pszDomain != NULL)
|
---|
| 314 | RTStrFree(pDomain->dd_pszDomain);
|
---|
| 315 | RTMemFree(pDomain);
|
---|
| 316 | }
|
---|
[53448] | 317 |
|
---|
| 318 | /* tell any pending dnsproxy requests their copy is expired */
|
---|
| 319 | ++pData->dnsgen;
|
---|
| 320 |
|
---|
[41977] | 321 | LogFlowFuncLeaveRC(rc);
|
---|
| 322 | return rc;
|
---|
| 323 | }
|
---|