diff options
| author | Odd Stranne <odd@mullvad.net> | 2019-10-18 09:50:11 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2019-11-18 09:53:00 +0100 |
| commit | a69536219475fb4d683b86cf35e4eff5056bf4b7 (patch) | |
| tree | ec79e69d20811b86bd2f27057b4732a99e8dc134 | |
| parent | a069a27fd8914e52e08c39733f999f90fbde83a8 (diff) | |
| download | mullvadvpn-a69536219475fb4d683b86cf35e4eff5056bf4b7.tar.xz mullvadvpn-a69536219475fb4d683b86cf35e4eff5056bf4b7.zip | |
Only set DNS when configuration is not already up-to-date
| -rw-r--r-- | windows/windns/src/windns/windns.cpp | 167 |
1 files changed, 166 insertions, 1 deletions
diff --git a/windows/windns/src/windns/windns.cpp b/windows/windns/src/windns/windns.cpp index 268751eb8b..eae828973c 100644 --- a/windows/windns/src/windns/windns.cpp +++ b/windows/windns/src/windns/windns.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include <libcommon/string.h> +#include <libcommon/network/adapters.h> #include "windns.h" #include "confineoperation.h" #include "netsh.h" @@ -7,7 +8,21 @@ #include <memory> #include <vector> #include <string> -#include <iphlpapi.h> +#include <sstream> +#include <ws2tcpip.h> +#include <ws2ipdef.h> +#include <winsock2.h> // magic order :-( +#include <iphlpapi.h> // if we don't do this then most of iphlpapi is not actually defined + +bool operator==(const IN_ADDR &lhs, const IN_ADDR &rhs) +{ + return 0 == memcmp(&lhs, &rhs, sizeof(IN_ADDR)); +} + +bool operator==(const IN6_ADDR &lhs, const IN6_ADDR &rhs) +{ + return 0 == memcmp(&lhs, &rhs, sizeof(IN6_ADDR)); +} namespace { @@ -62,6 +77,101 @@ uint32_t ConvertInterfaceAliasToIndex(const std::wstring &interfaceAlias) return static_cast<uint32_t>(index); } +struct AdapterDnsAddresses +{ + std::vector<IN_ADDR> ipv4; + std::vector<IN6_ADDR> ipv6; +}; + +// +// Use name when finding the adapter to be more resilient over time. +// The adapter structure that is returned has two fields for interface index. +// If IPv4 is enabled, 'IfIndex' will be set. Otherwise set to 0. +// If IPv6 is enabled, 'Ipv6IfIndex' will be set. Otherwise set to 0. +// If both IPv4 and IPv6 is enabled, then both fields will be set, and have the same value. +// +AdapterDnsAddresses GetAdapterDnsAddresses(const std::wstring &adapterAlias) +{ + common::network::Adapters adapters(AF_UNSPEC, GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST); + + const IP_ADAPTER_ADDRESSES *adapter; + + while (nullptr != (adapter = adapters.next())) + { + if (0 != _wcsicmp(adapter->FriendlyName, adapterAlias.c_str())) + { + continue; + } + + AdapterDnsAddresses out; + + for (auto server = adapter->FirstDnsServerAddress; nullptr != server; server = server->Next) + { + if (AF_INET == server->Address.lpSockaddr->sa_family) + { + out.ipv4.push_back(((const SOCKADDR_IN*)server->Address.lpSockaddr)->sin_addr); + } + else if (AF_INET6 == server->Address.lpSockaddr->sa_family) + { + out.ipv6.push_back(((const SOCKADDR_IN6_LH*)server->Address.lpSockaddr)->sin6_addr); + } + } + + return out; + } + + throw std::runtime_error(std::string("Could not find interface: ") + .append(common::string::ToAnsi(adapterAlias)).c_str()); +} + +AdapterDnsAddresses ConvertAddresses( + const wchar_t **ipv4Servers, + uint32_t numIpv4Servers, + const wchar_t **ipv6Servers, + uint32_t numIpv6Servers +) +{ + AdapterDnsAddresses out; + + if (nullptr != ipv4Servers && 0 != numIpv4Servers) + { + for (uint32_t i = 0; i < numIpv4Servers; ++i) + { + IN_ADDR converted; + + if (1 != InetPtonW(AF_INET, ipv4Servers[i], &converted)) + { + throw std::runtime_error("Failed to convert IPv4 address"); + } + + out.ipv4.push_back(converted); + } + } + + if (nullptr != ipv6Servers && 0 != numIpv6Servers) + { + for (uint32_t i = 0; i < numIpv6Servers; ++i) + { + IN6_ADDR converted; + + if (1 != InetPtonW(AF_INET6, ipv6Servers[i], &converted)) + { + throw std::runtime_error("Failed to convert IPv6 address"); + } + + out.ipv6.push_back(converted); + } + } + + return out; +} + +bool Equal(const AdapterDnsAddresses &lhs, const AdapterDnsAddresses &rhs) +{ + return lhs.ipv4 == rhs.ipv4 + && lhs.ipv6 == rhs.ipv6; +} + } // anonymous namespace WINDNS_LINKAGE @@ -118,16 +228,71 @@ WinDns_Set( { return ConfineOperation("Apply DNS settings", ForwardError, [&]() { + // + // Check the settings on the adapter. + // If it already has the exact same settings we need, we're done. + // + + try + { + const auto activeSettings = GetAdapterDnsAddresses(interfaceAlias); + const auto wantedSetting = ConvertAddresses(ipv4Servers, numIpv4Servers, ipv6Servers, numIpv6Servers); + + if (Equal(activeSettings, wantedSetting)) + { + std::stringstream ss; + + ss << "DNS settings on adapter with alias \"" << common::string::ToAnsi(interfaceAlias) + << "\" are up-to-date"; + + g_LogSink->info(ss.str().c_str(), nullptr, 0); + + return; + } + } + catch (const std::exception &ex) + { + std::stringstream ss; + + ss << "Failed to evaluate DNS settings on adapter with alias \"" + << common::string::ToAnsi(interfaceAlias) << "\": " << ex.what(); + + g_LogSink->info(ss.str().c_str(), nullptr, 0); + } + catch (...) + { + std::stringstream ss; + + ss << "Failed to evaluate DNS settings on adapter with alias \"" + << common::string::ToAnsi(interfaceAlias) << "\": Unspecified failure"; + + g_LogSink->info(ss.str().c_str(), nullptr, 0); + } + + // + // Onwards + // + const auto interfaceIndex = ConvertInterfaceAliasToIndex(interfaceAlias); if (nullptr != ipv4Servers && 0 != numIpv4Servers) { g_NetSh->setIpv4StaticDns(interfaceIndex, MakeStringArray(ipv4Servers, numIpv4Servers)); } + else + { + // This is required to clear any current settings. + g_NetSh->setIpv4DhcpDns(interfaceIndex); + } if (nullptr != ipv6Servers && 0 != numIpv6Servers) { g_NetSh->setIpv6StaticDns(interfaceIndex, MakeStringArray(ipv6Servers, numIpv6Servers)); } + else + { + // This is required to clear any current settings. + g_NetSh->setIpv6DhcpDns(interfaceIndex); + } }); } |
