[PATCH] 7006496: Use modern Windows API to retrieve OS DNS servers
Aleks Efimov
aleksej.efimov at oracle.com
Wed Dec 11 16:18:02 UTC 2019
Hi Anuraag,
The webrev with the latest patch can be viewed here:
http://cr.openjdk.java.net/~aefimov/anuraaga/7006496/02
Thanks for replacing FirstDnsSuffix usages with DnsSuffix. I've double
checked it with native application and can confirm that on my test host
(Microsoft Windows Server 2016 Standard) the DnsSuffix contains correct
information while the FirstDnsSuffix list is empty:
DNS Suffix (From DnsSuffix field): test.domain.com
Number of IP Adapter DNS suffix entries: 0
DNS Suffix (From DnsSuffix field):
Number of IP Adapter DNS suffix entries: 0
DNS Suffix (From DnsSuffix field):
Number of IP Adapter DNS suffix entries: 0
DNS Suffix (From DnsSuffix field):
Number of IP Adapter DNS suffix entries: 0
DNS Suffix (From DnsSuffix field): test.domain.com
Number of IP Adapter DNS suffix entries: 0
About site-local addresses: Maybe we can move the site-local address
check to native code? i.e. analyze dnsServer->Address.lpSockaddr and
filter out site-local addresses.
Testing:
Run your latest patch though our CI system - no issues detected.
-Aleksei
On 10/12/2019 06:59, Anuraag Agrawal wrote:
> Hi Aleksei,
>
> Thanks for running the test. I had checked the global search list, but
> realize I didn't check connection-specific suffixes which were causing
> the issue you see. While the documentation states that FirstDnsSuffix
> is populated on Vista+, amazingly it doesn't seem to be
>
> https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh
>
>
> Chromium has run into the same issue and just doesn't use the field
>
> https://cs.chromium.org/chromium/src/net/dns/dns_config_service_win.cc?q=firstdnssuffix&sq=package:chromium&dr=C&l=532
>
> So I have gone ahead and switched to using the normal DnsSuffix field.
> DnsSuffix and looking up the search list in the registry should
> generally give a full list.
>
> During this testing, other issues I found are that the search list
> returns a comma-separated list. So to keep the string list parsing
> simple, I changed the delimiter from space to comma. Also, on my
> machine I would always have DNS servers like fec0:0:0:ffff:1 on some
> adapters. At first, I assumed it is ok to return these, since they are
> configured by the OS and should be valid, but I learnt that these are
> deprecated IPv6 site-local addresses and should not be used.
>
> https://en.wikipedia.org/wiki/Unique_local_address
>
> I used the same check for site-local as Inet6Address, which just sees
> if the first bytes are fec0.
>
> Thanks again - inline patch follows.
>
> diff --git
> a/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
> b/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
> index 2250b3158e..b9a06461ab 100644
> ---
> a/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
> +++
> b/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
> @@ -25,9 +25,9 @@
>
> package sun.net.dns;
>
> +import java.util.ArrayList;
> import java.util.List;
> -import java.util.LinkedList;
> -import java.util.StringTokenizer;
> +import java.util.concurrent.TimeUnit;
>
> /*
> * An implementation of sun.net.ResolverConfiguration for Windows.
> @@ -50,30 +50,65 @@ public class ResolverConfigurationImpl
>
> // Cache timeout (120 seconds) - should be converted into property
> // or configured as preference in the future.
> - private static final int TIMEOUT = 120000;
> + private static final long TIMEOUT_NANOS =
> TimeUnit.SECONDS.toNanos(120);
>
> // DNS suffix list and name servers populated by native method
> private static String os_searchlist;
> private static String os_nameservers;
>
> // Cached lists
> - private static LinkedList<String> searchlist;
> - private static LinkedList<String> nameservers;
> -
> - // Parse string that consists of token delimited by space or commas
> - // and return LinkedHashMap
> - private LinkedList<String> stringToList(String str) {
> - LinkedList<String> ll = new LinkedList<>();
> -
> - // comma and space are valid delimiters
> - StringTokenizer st = new StringTokenizer(str, ", ");
> - while (st.hasMoreTokens()) {
> - String s = st.nextToken();
> - if (!ll.contains(s)) {
> - ll.add(s);
> + private static ArrayList<String> searchlist;
> + private static ArrayList<String> nameservers;
> +
> + // Parse string that consists of token delimited by comma
> + // and return ArrayList. Refer to ResolverConfigurationImpl.c and
> + // strappend to see how the string is created.
> + private ArrayList<String> stringToList(String str) {
> + // String is delimited by comma.
> + String[] tokens = str.split(",");
> + ArrayList<String> l = new ArrayList<>(tokens.length);
> + for (String s : tokens) {
> + if (!l.contains(s)) {
> + l.add(s);
> }
> }
> - return ll;
> + l.trimToSize();
> + return l;
> + }
> +
> + // Parse string that consists of token delimited by comma
> + // and return ArrayList. Refer to ResolverConfigurationImpl.c and
> + // strappend to see how the string is created.
> + // In addition to splitting the string, converts IPv6 addresses to
> + // BSD-style.
> + private ArrayList<String> addressesToList(String str) {
> + // String is delimited by comma
> + String[] tokens = str.split(",");
> + ArrayList<String> l = new ArrayList<>(tokens.length);
> +
> + for (String s : tokens) {
> + if (!s.isEmpty()) {
> + if (s.indexOf(':') >= 0) {
> + // IPv6
> + if (s.charAt(0) != '[') {
> + // Not BSD style
> + s = '[' + s + ']';
> + }
> + if (s.startsWith("[fec0:")) {
> + // Deprecated site-local address. Windows adds
> + // such addresses to IPv6 adapters without DNS
> + // configured, but these should not actually be
> + // used for queries.
> + continue;
> + }
> + }
> + if (!l.contains(s)) {
> + l.add(s);
> + }
> + }
> + }
> + l.trimToSize();
> + return l;
> }
>
> // Load DNS configuration from OS
> @@ -81,28 +116,34 @@ public class ResolverConfigurationImpl
> private void loadConfig() {
> assert Thread.holdsLock(lock);
>
> - // if address have changed then DNS probably changed as well;
> - // otherwise check if cached settings have expired.
> - //
> + // A change in the network address of the machine usually
> indicates
> + // a change in DNS configuration too so we always refresh the
> config
> + // after such a change.
> if (changed) {
> changed = false;
> } else {
> + // Otherwise we refresh if TIMEOUT_NANOS has passed since
> last
> + // load.
> if (lastRefresh >= 0) {
> - long currTime = System.currentTimeMillis();
> - if ((currTime - lastRefresh) < TIMEOUT) {
> + long currTime = System.nanoTime();
> + if ((currTime - lastRefresh) < TIMEOUT_NANOS) {
> return;
> }
> }
> }
>
> - // load DNS configuration, update timestamp, create
> - // new HashMaps from the loaded configuration
> - //
> + // Native code that uses Windows API to find out the DNS server
> + // addresses and search suffixes. It builds a comma-delimited
> string
> + // of nameservers and domain suffixes and sets them to the static
> + // os_nameservers and os_searchlist. We then split these into
> Java
> + // Lists here.
> loadDNSconfig0();
>
> - lastRefresh = System.currentTimeMillis();
> + // Record the time of update and refresh the lists of addresses /
> + // domain suffixes.
> + lastRefresh = System.nanoTime();
> searchlist = stringToList(os_searchlist);
> - nameservers = stringToList(os_nameservers);
> + nameservers = addressesToList(os_nameservers);
> os_searchlist = null; // can be GC'ed
> os_nameservers = null;
> }
> diff --git
> a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
> b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
> index f2368bafcb..297a1561ef 100644
> --- a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
> +++ b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
> @@ -73,8 +73,8 @@ const int MAX_TRIES = 3;
> * for each adapter on the system. Returned in *adapters.
> * Buffer is malloc'd and must be freed (unless error returned)
> */
> -static int getAdapters (JNIEnv *env, IP_ADAPTER_ADDRESSES **adapters) {
> - DWORD ret, flags;
> +int getAdapters (JNIEnv *env, int flags, IP_ADAPTER_ADDRESSES
> **adapters) {
> + DWORD ret;
> IP_ADAPTER_ADDRESSES *adapterInfo;
> ULONG len;
> int try;
> @@ -87,9 +87,6 @@ static int getAdapters (JNIEnv *env,
> IP_ADAPTER_ADDRESSES **adapters) {
> }
>
> len = BUFF_SIZE;
> - flags = GAA_FLAG_SKIP_DNS_SERVER;
> - flags |= GAA_FLAG_SKIP_MULTICAST;
> - flags |= GAA_FLAG_INCLUDE_PREFIX;
> ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterInfo,
> &len);
>
> for (try = 0; ret == ERROR_BUFFER_OVERFLOW && try < MAX_TRIES;
> ++try) {
> @@ -240,7 +237,7 @@ static int ipinflen = 2048;
> */
> int getAllInterfacesAndAddresses (JNIEnv *env, netif **netifPP)
> {
> - DWORD ret;
> + DWORD ret, flags;
> MIB_IPADDRTABLE *tableP;
> IP_ADAPTER_ADDRESSES *ptr, *adapters=NULL;
> ULONG len=ipinflen, count=0;
> @@ -296,7 +293,11 @@ int getAllInterfacesAndAddresses (JNIEnv *env,
> netif **netifPP)
> }
> }
> free(tableP);
> - ret = getAdapters (env, &adapters);
> +
> + flags = GAA_FLAG_SKIP_DNS_SERVER;
> + flags |= GAA_FLAG_SKIP_MULTICAST;
> + flags |= GAA_FLAG_INCLUDE_PREFIX;
> + ret = getAdapters (env, flags, &adapters);
> if (ret != ERROR_SUCCESS) {
> goto err;
> }
> diff --git
> a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
> b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
> index 13b28044a5..427d7c85eb 100644
> --- a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
> +++ b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights
> reserved.
> + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights
> reserved.
> * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
> *
> * This code is free software; you can redistribute it and/or modify it
> @@ -30,6 +30,7 @@
> #include <iprtrmib.h>
> #include <time.h>
> #include <assert.h>
> +#include <winsock2.h>
> #include <iphlpapi.h>
>
> #include "jni_util.h"
> @@ -48,8 +49,10 @@
> static jfieldID searchlistID;
> static jfieldID nameserversID;
>
> +extern int getAdapters(JNIEnv *env, int flags, IP_ADAPTER_ADDRESSES
> **adapters);
> +
> /*
> - * Utility routine to append s2 to s1 with a space delimiter.
> + * Utility routine to append s2 to s1 with a comma delimiter.
> * strappend(s1="abc", "def") => "abc def"
> * strappend(s1="", "def") => "def
> */
> @@ -60,41 +63,30 @@ void strappend(char *s1, char *s2) {
> return;
>
> len = strlen(s1)+1;
> - if (s1[0] != 0) /* needs space character */
> + if (s1[0] != 0) /* needs comma character */
> len++;
> if (len + strlen(s2) > MAX_STR_LEN) /* insufficient space */
> return;
>
> if (s1[0] != 0) {
> - strcat(s1, " ");
> + strcat(s1, ",");
> }
> strcat(s1, s2);
> }
>
> /*
> - * Windows 2000/XP
> - *
> - * Use registry approach based on settings described in Appendix C
> - * of "Microsoft Windows 2000 TCP/IP Implementation Details".
> - *
> - * DNS suffix list is obtained from SearchList registry setting. If
> - * this is not specified we compile suffix list based on the
> - * per-connection domain suffix.
> - *
> - * DNS name servers and domain settings are on a per-connection
> - * basic. We therefore enumerate the network adapters to get the
> - * names of each adapter and then query the corresponding registry
> - * settings to obtain NameServer/DhcpNameServer and Domain/DhcpDomain.
> + * Use DNS server addresses returned by GetAdaptersAddresses for
> currently
> + * active interfaces.
> */
> -static int loadConfig(char *sl, char *ns) {
> - IP_ADAPTER_INFO *adapterP;
> - ULONG size;
> - DWORD ret;
> +static int loadConfig(JNIEnv *env, char *sl, char *ns) {
> + IP_ADAPTER_ADDRESSES *adapters, *adapter;
> + IP_ADAPTER_DNS_SERVER_ADDRESS *dnsServer;
> + WCHAR *suffix;
> + DWORD ret, flags;
> DWORD dwLen;
> ULONG ulType;
> char result[MAX_STR_LEN];
> HANDLE hKey;
> - int gotSearchList = 0;
>
> /*
> * First see if there is a global suffix list specified.
> @@ -112,122 +104,55 @@ static int loadConfig(char *sl, char *ns) {
> assert(ulType == REG_SZ);
> if (strlen(result) > 0) {
> strappend(sl, result);
> - gotSearchList = 1;
> }
> }
> RegCloseKey(hKey);
> }
>
> - /*
> - * Ask the IP Helper library to enumerate the adapters
> - */
> - size = sizeof(IP_ADAPTER_INFO);
> - adapterP = (IP_ADAPTER_INFO *)malloc(size);
> - if (adapterP == NULL) {
> - return STS_ERROR;
> - }
> - ret = GetAdaptersInfo(adapterP, &size);
> - if (ret == ERROR_BUFFER_OVERFLOW) {
> - IP_ADAPTER_INFO *newAdapterP = (IP_ADAPTER_INFO
> *)realloc(adapterP, size);
> - if (newAdapterP == NULL) {
> - free(adapterP);
> - return STS_ERROR;
> - }
> - adapterP = newAdapterP;
>
> - ret = GetAdaptersInfo(adapterP, &size);
> + // We only need DNS server addresses so skip everything else.
> + flags = GAA_FLAG_SKIP_UNICAST;
> + flags |= GAA_FLAG_SKIP_ANYCAST;
> + flags |= GAA_FLAG_SKIP_MULTICAST;
> + flags |= GAA_FLAG_SKIP_FRIENDLY_NAME;
> + ret = getAdapters(env, flags, &adapters);
> + if (ret != ERROR_SUCCESS) {
> + return STS_ERROR;
> }
>
> - /*
> - * Iterate through the list of adapters as registry settings are
> - * keyed on the adapter name (GUID).
> - */
> - if (ret == ERROR_SUCCESS) {
> - IP_ADAPTER_INFO *curr = adapterP;
> - while (curr != NULL) {
> - char key[MAX_STR_LEN];
> -
> - sprintf(key,
> -
> "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s",
> - curr->AdapterName);
> -
> - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
> - key,
> - 0,
> - KEY_READ,
> - (PHKEY)&hKey);
> - if (ret == ERROR_SUCCESS) {
> - DWORD enableDhcp = 0;
> -
> - /*
> - * Is DHCP enabled on this interface
> - */
> - dwLen = sizeof(enableDhcp);
> - ret = RegQueryValueEx(hKey, "EnableDhcp", NULL, &ulType,
> - (LPBYTE)&enableDhcp, &dwLen);
> -
> - /*
> - * If we don't have the suffix list when get the Domain
> - * or DhcpDomain. If DHCP is enabled then Domain overides
> - * DhcpDomain
> - */
> - if (!gotSearchList) {
> - result[0] = '\0';
> - dwLen = sizeof(result);
> - ret = RegQueryValueEx(hKey, "Domain", NULL, &ulType,
> - (LPBYTE)&result, &dwLen);
> - if (((ret != ERROR_SUCCESS) || (strlen(result) ==
> 0)) &&
> - enableDhcp) {
> - dwLen = sizeof(result);
> - ret = RegQueryValueEx(hKey, "DhcpDomain",
> NULL, &ulType,
> - (LPBYTE)&result, &dwLen);
> - }
> - if (ret == ERROR_SUCCESS) {
> - assert(ulType == REG_SZ);
> - strappend(sl, result);
> - }
> - }
> -
> - /*
> - * Get DNS servers based on NameServer or DhcpNameServer
> - * registry setting. If NameServer is set then it
> overrides
> - * DhcpNameServer (even if DHCP is enabled).
> - */
> - result[0] = '\0';
> + adapter = adapters;
> + while (adapter != NULL) {
> + // Only load config from enabled adapters.
> + if (adapter->OperStatus == IfOperStatusUp) {
> + dnsServer = adapter->FirstDnsServerAddress;
> + while (dnsServer != NULL) {
> dwLen = sizeof(result);
> - ret = RegQueryValueEx(hKey, "NameServer", NULL, &ulType,
> - (LPBYTE)&result, &dwLen);
> - if (((ret != ERROR_SUCCESS) || (strlen(result) == 0)) &&
> - enableDhcp) {
> - dwLen = sizeof(result);
> - ret = RegQueryValueEx(hKey, "DhcpNameServer",
> NULL, &ulType,
> - (LPBYTE)&result, &dwLen);
> - }
> - if (ret == ERROR_SUCCESS) {
> - assert(ulType == REG_SZ);
> + ret = WSAAddressToStringA(dnsServer->Address.lpSockaddr,
> + dnsServer->Address.iSockaddrLength, NULL,
> + result, &dwLen);
> + if (ret == 0) {
> strappend(ns, result);
> }
>
> - /*
> - * Finished with this registry key
> - */
> - RegCloseKey(hKey);
> + dnsServer = dnsServer->Next;
> }
>
> - /*
> - * Onto the next adapeter
> - */
> - curr = curr->Next;
> + // Add connection-specific search domains in addition to
> global one.
> + suffix = adapter->DnsSuffix;
> + if (suffix != NULL) {
> + ret = WideCharToMultiByte(CP_UTF8, 0, suffix, -1,
> + result, sizeof(result), NULL, NULL);
> + if (ret != 0) {
> + strappend(sl, result);
> + }
> + }
> }
> - }
>
> - /*
> - * Free the adpater structure
> - */
> - if (adapterP) {
> - free(adapterP);
> + adapter = adapter->Next;
> }
>
> + free(adapters);
> +
> return STS_SL_FOUND & STS_NS_FOUND;
> }
>
> @@ -260,7 +185,7 @@
> Java_sun_net_dns_ResolverConfigurationImpl_loadDNSconfig0(JNIEnv *env,
> jclass cl
> searchlist[0] = '\0';
> nameservers[0] = '\0';
>
> - if (loadConfig(searchlist, nameservers) != STS_ERROR) {
> + if (loadConfig(env, searchlist, nameservers) != STS_ERROR) {
>
> /*
> * Populate static fields in sun.net.DefaultResolverConfiguration
>
> On Tue, Dec 10, 2019 at 1:29 AM Aleks Efimov
> <aleksej.efimov at oracle.com <mailto:aleksej.efimov at oracle.com>> wrote:
>
> Hi Anuraag,
>
> The webrev with your latest changes can be found here:
> http://cr.openjdk.java.net/~aefimov/anuraaga/7006496/01
>
> The copyright changes looks good.
>
> The split by space also looks good with assumption that we will
> never get string with two spaces between IP addresses/DNS suffixes.
>
> I've also performed one test on Windows machine and it shows an
> issue with the retrieval of DNS suffixes:
> Method to call:
> sun.net.dns.ResolverConfiguration.open().searchlist()
>
> Old implementation returns:
> [test.domain.com <http://test.domain.com>]
>
> With the latest patch the suffixes list (searchlist) is empty:
> []
>
> I've collected the following information on the test host that
> might help you to fix the issue:
>
> The host OS: Microsoft Windows Server 2016 Standard
>
> Networking configuration (<---> is for masked addresses/info):
> $ ipconfig /all
>
> Windows IP Configuration
>
> Host Name . . . . . . . . . . . . : host-name-1
> Primary Dns Suffix . . . . . . . :
> Node Type . . . . . . . . . . . . : Hybrid
> IP Routing Enabled. . . . . . . . : No
> WINS Proxy Enabled. . . . . . . . : No
> DNS Suffix Search List. . . . . . : test.domain.com
> <http://test.domain.com>
>
> Ethernet adapter Ethernet:
>
> Connection-specific DNS Suffix . : test.domain.com
> <http://test.domain.com>
> Description . . . . . . . . . . . : Broadcom NetXtreme-E
> Virtual Function
> Physical Address. . . . . . . . . : <------------>
> DHCP Enabled. . . . . . . . . . . : Yes
> Autoconfiguration Enabled . . . . : Yes
> Link-local IPv6 Address . . . . . : <------------>
> IPv4 Address. . . . . . . . . . . : <------------>
> Subnet Mask . . . . . . . . . . . : <------------>
> Lease Obtained. . . . . . . . . . : <------------>
> Lease Expires . . . . . . . . . . : <------------>
> Default Gateway . . . . . . . . . : <------------>
> DHCP Server . . . . . . . . . . . : <------------>
> DHCPv6 Client DUID. . . . . . . . : <------------>
> DNS Servers . . . . . . . . . . . : <------------>
> <------------>
> NetBIOS over Tcpip. . . . . . . . : Disabled
>
> Tunnel adapter isatap.test.domain.com
> <http://isatap.test.domain.com>:
>
> Media State . . . . . . . . . . . : Media disconnected
> Connection-specific DNS Suffix . : test.domain.com
> <http://test.domain.com>
> Description . . . . . . . . . . . : Microsoft ISATAP Adapter #3
> Physical Address. . . . . . . . . : <------------>
> DHCP Enabled. . . . . . . . . . . : No
> Autoconfiguration Enabled . . . . : Yes
>
>
> - Aleksei
>
>
> On 07/12/2019 09:44, Anuraag Agrawal wrote:
>> Hi all,
>>
>> Thanks for the reviews. I've fixed the formatting errors, and I
>> hope I fixed the copyright issue. I couldn't find documentation
>> on the format of the copyright header, but assume the second
>> number is the year of modification to update. I
>> found ResolverConfigurationImpl.c to have an old one and updated
>> it to 2019.
>>
>> I also found the note in the docs that StringTokenizer is
>> deprecated and shouldn't be used - I agree it's a good time to
>> replace it. As we have a simple split by space, I have gone ahead
>> and used the simpler String.split instead of regex, hope that
>> makes sense. While in many code I'd be a little concerned with
>> the array allocation, this is only run very infrequently so I
>> think it should be ok.
>>
>> Inline diff follows
>>
>> diff --git
>> a/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
>> b/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
>> index 2250b3158e..47065e805e 100644
>> ---
>> a/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
>> +++
>> b/src/java.base/windows/classes/sun/net/dns/ResolverConfigurationImpl.java
>> @@ -25,9 +25,9 @@
>>
>> package sun.net.dns;
>>
>> +import java.util.ArrayList;
>> import java.util.List;
>> -import java.util.LinkedList;
>> -import java.util.StringTokenizer;
>> +import java.util.concurrent.TimeUnit;
>>
>> /*
>> * An implementation of sun.net.ResolverConfiguration for Windows.
>> @@ -50,30 +50,55 @@ public class ResolverConfigurationImpl
>>
>> // Cache timeout (120 seconds) - should be converted into
>> property
>> // or configured as preference in the future.
>> - private static final int TIMEOUT = 120000;
>> + private static final long TIMEOUT_NANOS =
>> TimeUnit.SECONDS.toNanos(120);
>>
>> // DNS suffix list and name servers populated by native method
>> private static String os_searchlist;
>> private static String os_nameservers;
>>
>> // Cached lists
>> - private static LinkedList<String> searchlist;
>> - private static LinkedList<String> nameservers;
>> -
>> - // Parse string that consists of token delimited by space or
>> commas
>> - // and return LinkedHashMap
>> - private LinkedList<String> stringToList(String str) {
>> - LinkedList<String> ll = new LinkedList<>();
>> -
>> - // comma and space are valid delimiters
>> - StringTokenizer st = new StringTokenizer(str, ", ");
>> - while (st.hasMoreTokens()) {
>> - String s = st.nextToken();
>> - if (!ll.contains(s)) {
>> - ll.add(s);
>> + private static ArrayList<String> searchlist;
>> + private static ArrayList<String> nameservers;
>> +
>> + // Parse string that consists of token delimited by space
>> + // and return ArrayList. Refer to
>> ResolverConfigurationImpl.c and
>> + // strappend to see how the string is created.
>> + private ArrayList<String> stringToList(String str) {
>> + // String is delimited by space.
>> + String[] tokens = str.split(" ");
>> + ArrayList<String> l = new ArrayList<>(tokens.length);
>> + for (String s : tokens) {
>> + if (!l.contains(s)) {
>> + l.add(s);
>> }
>> }
>> - return ll;
>> + l.trimToSize();
>> + return l;
>> + }
>> +
>> + // Parse string that consists of token delimited by space
>> + // and return ArrayList. Refer to
>> ResolverConfigurationImpl.c and
>> + // strappend to see how the string is created.
>> + // In addition to splitting the string, converts IPv6
>> addresses to
>> + // BSD-style.
>> + private ArrayList<String> addressesToList(String str) {
>> + // String is delimited by space
>> + String[] tokens = str.split(" ");
>> + ArrayList<String> l = new ArrayList<>(tokens.length);
>> +
>> + for (String s : tokens) {
>> + if (!s.isEmpty()) {
>> + if (s.indexOf(':') >= 0 && s.charAt(0) != '[') {
>> + // Not BSD style
>> + s = '[' + s + ']';
>> + }
>> + if (!l.contains(s)) {
>> + l.add(s);
>> + }
>> + }
>> + }
>> + l.trimToSize();
>> + return l;
>> }
>>
>> // Load DNS configuration from OS
>> @@ -81,28 +106,34 @@ public class ResolverConfigurationImpl
>> private void loadConfig() {
>> assert Thread.holdsLock(lock);
>>
>> - // if address have changed then DNS probably changed as
>> well;
>> - // otherwise check if cached settings have expired.
>> - //
>> + // A change in the network address of the machine
>> usually indicates
>> + // a change in DNS configuration too so we always
>> refresh the config
>> + // after such a change.
>> if (changed) {
>> changed = false;
>> } else {
>> + // Otherwise we refresh if TIMEOUT_NANOS has passed
>> since last
>> + // load.
>> if (lastRefresh >= 0) {
>> - long currTime = System.currentTimeMillis();
>> - if ((currTime - lastRefresh) < TIMEOUT) {
>> + long currTime = System.nanoTime();
>> + if ((currTime - lastRefresh) < TIMEOUT_NANOS) {
>> return;
>> }
>> }
>> }
>>
>> - // load DNS configuration, update timestamp, create
>> - // new HashMaps from the loaded configuration
>> - //
>> + // Native code that uses Windows API to find out the DNS
>> server
>> + // addresses and search suffixes. It builds a
>> space-delimited string
>> + // of nameservers and domain suffixes and sets them to
>> the static
>> + // os_nameservers and os_searchlist. We then split these
>> into Java
>> + // Lists here.
>> loadDNSconfig0();
>>
>> - lastRefresh = System.currentTimeMillis();
>> + // Record the time of update and refresh the lists of
>> addresses /
>> + // domain suffixes.
>> + lastRefresh = System.nanoTime();
>> searchlist = stringToList(os_searchlist);
>> - nameservers = stringToList(os_nameservers);
>> + nameservers = addressesToList(os_nameservers);
>> os_searchlist = null; // can be GC'ed
>> os_nameservers = null;
>> }
>> diff --git
>> a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
>> b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
>> index f2368bafcb..297a1561ef 100644
>> --- a/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
>> +++ b/src/java.base/windows/native/libnet/NetworkInterface_winXP.c
>> @@ -73,8 +73,8 @@ const int MAX_TRIES = 3;
>> * for each adapter on the system. Returned in *adapters.
>> * Buffer is malloc'd and must be freed (unless error returned)
>> */
>> -static int getAdapters (JNIEnv *env, IP_ADAPTER_ADDRESSES
>> **adapters) {
>> - DWORD ret, flags;
>> +int getAdapters (JNIEnv *env, int flags, IP_ADAPTER_ADDRESSES
>> **adapters) {
>> + DWORD ret;
>> IP_ADAPTER_ADDRESSES *adapterInfo;
>> ULONG len;
>> int try;
>> @@ -87,9 +87,6 @@ static int getAdapters (JNIEnv *env,
>> IP_ADAPTER_ADDRESSES **adapters) {
>> }
>>
>> len = BUFF_SIZE;
>> - flags = GAA_FLAG_SKIP_DNS_SERVER;
>> - flags |= GAA_FLAG_SKIP_MULTICAST;
>> - flags |= GAA_FLAG_INCLUDE_PREFIX;
>> ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL,
>> adapterInfo, &len);
>>
>> for (try = 0; ret == ERROR_BUFFER_OVERFLOW && try <
>> MAX_TRIES; ++try) {
>> @@ -240,7 +237,7 @@ static int ipinflen = 2048;
>> */
>> int getAllInterfacesAndAddresses (JNIEnv *env, netif **netifPP)
>> {
>> - DWORD ret;
>> + DWORD ret, flags;
>> MIB_IPADDRTABLE *tableP;
>> IP_ADAPTER_ADDRESSES *ptr, *adapters=NULL;
>> ULONG len=ipinflen, count=0;
>> @@ -296,7 +293,11 @@ int getAllInterfacesAndAddresses (JNIEnv
>> *env, netif **netifPP)
>> }
>> }
>> free(tableP);
>> - ret = getAdapters (env, &adapters);
>> +
>> + flags = GAA_FLAG_SKIP_DNS_SERVER;
>> + flags |= GAA_FLAG_SKIP_MULTICAST;
>> + flags |= GAA_FLAG_INCLUDE_PREFIX;
>> + ret = getAdapters (env, flags, &adapters);
>> if (ret != ERROR_SUCCESS) {
>> goto err;
>> }
>> diff --git
>> a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
>> b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
>> index 13b28044a5..85a7935314 100644
>> --- a/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
>> +++ b/src/java.base/windows/native/libnet/ResolverConfigurationImpl.c
>> @@ -1,5 +1,5 @@
>> /*
>> - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All
>> rights reserved.
>> + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All
>> rights reserved.
>> * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
>> *
>> * This code is free software; you can redistribute it and/or
>> modify it
>> @@ -30,6 +30,7 @@
>> #include <iprtrmib.h>
>> #include <time.h>
>> #include <assert.h>
>> +#include <winsock2.h>
>> #include <iphlpapi.h>
>>
>> #include "jni_util.h"
>> @@ -48,6 +49,8 @@
>> static jfieldID searchlistID;
>> static jfieldID nameserversID;
>>
>> +extern int getAdapters(JNIEnv *env, int flags,
>> IP_ADAPTER_ADDRESSES **adapters);
>> +
>> /*
>> * Utility routine to append s2 to s1 with a space delimiter.
>> * strappend(s1="abc", "def") => "abc def"
>> @@ -72,29 +75,19 @@ void strappend(char *s1, char *s2) {
>> }
>>
>> /*
>> - * Windows 2000/XP
>> - *
>> - * Use registry approach based on settings described in Appendix C
>> - * of "Microsoft Windows 2000 TCP/IP Implementation Details".
>> - *
>> - * DNS suffix list is obtained from SearchList registry setting. If
>> - * this is not specified we compile suffix list based on the
>> - * per-connection domain suffix.
>> - *
>> - * DNS name servers and domain settings are on a per-connection
>> - * basic. We therefore enumerate the network adapters to get the
>> - * names of each adapter and then query the corresponding registry
>> - * settings to obtain NameServer/DhcpNameServer and
>> Domain/DhcpDomain.
>> + * Use DNS server addresses returned by GetAdaptersAddresses for
>> currently
>> + * active interfaces.
>> */
>> -static int loadConfig(char *sl, char *ns) {
>> - IP_ADAPTER_INFO *adapterP;
>> - ULONG size;
>> - DWORD ret;
>> +static int loadConfig(JNIEnv *env, char *sl, char *ns) {
>> + IP_ADAPTER_ADDRESSES *adapters, *adapter;
>> + IP_ADAPTER_DNS_SERVER_ADDRESS *dnsServer;
>> + SOCKADDR *address;
>> + IP_ADAPTER_DNS_SUFFIX *suffix;
>> + DWORD ret, flags;
>> DWORD dwLen;
>> ULONG ulType;
>> char result[MAX_STR_LEN];
>> HANDLE hKey;
>> - int gotSearchList = 0;
>>
>> /*
>> * First see if there is a global suffix list specified.
>> @@ -112,122 +105,58 @@ static int loadConfig(char *sl, char *ns) {
>> assert(ulType == REG_SZ);
>> if (strlen(result) > 0) {
>> strappend(sl, result);
>> - gotSearchList = 1;
>> }
>> }
>> RegCloseKey(hKey);
>> }
>>
>> - /*
>> - * Ask the IP Helper library to enumerate the adapters
>> - */
>> - size = sizeof(IP_ADAPTER_INFO);
>> - adapterP = (IP_ADAPTER_INFO *)malloc(size);
>> - if (adapterP == NULL) {
>> - return STS_ERROR;
>> - }
>> - ret = GetAdaptersInfo(adapterP, &size);
>> - if (ret == ERROR_BUFFER_OVERFLOW) {
>> - IP_ADAPTER_INFO *newAdapterP = (IP_ADAPTER_INFO
>> *)realloc(adapterP, size);
>> - if (newAdapterP == NULL) {
>> - free(adapterP);
>> - return STS_ERROR;
>> - }
>> - adapterP = newAdapterP;
>>
>> - ret = GetAdaptersInfo(adapterP, &size);
>> + // We only need DNS server addresses so skip everything else.
>> + flags = GAA_FLAG_SKIP_UNICAST;
>> + flags |= GAA_FLAG_SKIP_ANYCAST;
>> + flags |= GAA_FLAG_SKIP_MULTICAST;
>> + flags |= GAA_FLAG_SKIP_FRIENDLY_NAME;
>> + ret = getAdapters(env, flags, &adapters);
>> + if (ret != ERROR_SUCCESS) {
>> + return STS_ERROR;
>> }
>>
>> - /*
>> - * Iterate through the list of adapters as registry settings are
>> - * keyed on the adapter name (GUID).
>> - */
>> - if (ret == ERROR_SUCCESS) {
>> - IP_ADAPTER_INFO *curr = adapterP;
>> - while (curr != NULL) {
>> - char key[MAX_STR_LEN];
>> -
>> - sprintf(key,
>> -
>> "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s",
>> - curr->AdapterName);
>> -
>> - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
>> - key,
>> - 0,
>> - KEY_READ,
>> - (PHKEY)&hKey);
>> - if (ret == ERROR_SUCCESS) {
>> - DWORD enableDhcp = 0;
>> -
>> - /*
>> - * Is DHCP enabled on this interface
>> - */
>> - dwLen = sizeof(enableDhcp);
>> - ret = RegQueryValueEx(hKey, "EnableDhcp", NULL,
>> &ulType,
>> - (LPBYTE)&enableDhcp, &dwLen);
>> -
>> - /*
>> - * If we don't have the suffix list when get the
>> Domain
>> - * or DhcpDomain. If DHCP is enabled then Domain
>> overides
>> - * DhcpDomain
>> - */
>> - if (!gotSearchList) {
>> - result[0] = '\0';
>> - dwLen = sizeof(result);
>> - ret = RegQueryValueEx(hKey, "Domain", NULL,
>> &ulType,
>> - (LPBYTE)&result, &dwLen);
>> - if (((ret != ERROR_SUCCESS) ||
>> (strlen(result) == 0)) &&
>> - enableDhcp) {
>> - dwLen = sizeof(result);
>> - ret = RegQueryValueEx(hKey,
>> "DhcpDomain", NULL, &ulType,
>> - (LPBYTE)&result, &dwLen);
>> - }
>> - if (ret == ERROR_SUCCESS) {
>> - assert(ulType == REG_SZ);
>> - strappend(sl, result);
>> - }
>> - }
>> -
>> - /*
>> - * Get DNS servers based on NameServer or
>> DhcpNameServer
>> - * registry setting. If NameServer is set then
>> it overrides
>> - * DhcpNameServer (even if DHCP is enabled).
>> - */
>> - result[0] = '\0';
>> + adapter = adapters;
>> + while (adapter != NULL) {
>> + // Only load config from enabled adapters.
>> + if (adapter->OperStatus == IfOperStatusUp) {
>> + dnsServer = adapter->FirstDnsServerAddress;
>> + while (dnsServer != NULL) {
>> + address = dnsServer->Address.lpSockaddr;
>> dwLen = sizeof(result);
>> - ret = RegQueryValueEx(hKey, "NameServer", NULL,
>> &ulType,
>> - (LPBYTE)&result, &dwLen);
>> - if (((ret != ERROR_SUCCESS) || (strlen(result)
>> == 0)) &&
>> - enableDhcp) {
>> - dwLen = sizeof(result);
>> - ret = RegQueryValueEx(hKey,
>> "DhcpNameServer", NULL, &ulType,
>> - (LPBYTE)&result, &dwLen);
>> - }
>> - if (ret == ERROR_SUCCESS) {
>> - assert(ulType == REG_SZ);
>> + ret =
>> WSAAddressToStringA(dnsServer->Address.lpSockaddr,
>> + dnsServer->Address.iSockaddrLength, NULL,
>> + result, &dwLen);
>> + if (ret == 0) {
>> strappend(ns, result);
>> }
>>
>> - /*
>> - * Finished with this registry key
>> - */
>> - RegCloseKey(hKey);
>> + dnsServer = dnsServer->Next;
>> }
>>
>> - /*
>> - * Onto the next adapeter
>> - */
>> - curr = curr->Next;
>> + // Add connection-specific search domains in
>> addition to global one.
>> + suffix = adapter->FirstDnsSuffix;
>> + while (suffix != NULL) {
>> + ret = WideCharToMultiByte(CP_UTF8, 0,
>> suffix->String, -1,
>> + result, sizeof(result), NULL, NULL);
>> + if (ret != 0) {
>> + strappend(sl, result);
>> + }
>> +
>> + suffix = suffix->Next;
>> + }
>> }
>> - }
>>
>> - /*
>> - * Free the adpater structure
>> - */
>> - if (adapterP) {
>> - free(adapterP);
>> + adapter = adapter->Next;
>> }
>>
>> + free(adapters);
>> +
>> return STS_SL_FOUND & STS_NS_FOUND;
>> }
>>
>> @@ -260,7 +189,7 @@
>> Java_sun_net_dns_ResolverConfigurationImpl_loadDNSconfig0(JNIEnv
>> *env, jclass cl
>> searchlist[0] = '\0';
>> nameservers[0] = '\0';
>>
>> - if (loadConfig(searchlist, nameservers) != STS_ERROR) {
>> + if (loadConfig(env, searchlist, nameservers) != STS_ERROR) {
>>
>> /*
>> * Populate static fields in
>> sun.net.DefaultResolverConfiguration
>>
>> On Sat, Dec 7, 2019 at 12:41 AM Alan Bateman
>> <Alan.Bateman at oracle.com <mailto:Alan.Bateman at oracle.com>> wrote:
>>
>> On 06/12/2019 15:34, Aleks Efimov wrote:
>> > :
>> >
>> > I've created webrev with your latest changes - it could be
>> convenient
>> > for other reviewers:
>> > http://cr.openjdk.java.net/~aefimov/anuraaga/7006496/00
>> >
>> > I will happily sponsor this change once it gets necessary
>> approvals
>> This could be split into two issues but since the code using
>> StringTokenizer is changing significantly then we should
>> replace it with
>> regex while we there.
>>
>> -Alan
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/net-dev/attachments/20191211/00c33948/attachment-0001.htm>
More information about the net-dev
mailing list