[PATCH] 7006496: Use modern Windows API to retrieve OS DNS servers
Aleks Efimov
aleksej.efimov at oracle.com
Tue Dec 24 10:12:17 UTC 2019
Hi Anuraag,
We need additional approval from openjdk reviewer. After that I will
sponsor your change.
Merry Christmas and a Happy New Year,
Aleksei
On 18/12/2019 06:08, Anuraag Agrawal wrote:
> Hi Aleksei,
>
> Thanks for all the reviews, it's really helped a lot. Just wondering,
> is there anything we need to go to finish getting approvals?
>
> Thanks,
> - Anuraag
>
> On Sat, Dec 14, 2019 at 12:35 AM Aleks Efimov
> <aleksej.efimov at oracle.com <mailto:aleksej.efimov at oracle.com>> wrote:
>
> Hi Anuraag,
>
> Thank you for moving the site-local/link-local check to the native
> code.
> The latest version of your changes looks good to me. Webrev of it
> can be viewed here:
> http://cr.openjdk.java.net/~aefimov/anuraaga/7006496/03/
>
> Our CI system is also happy with it.
>
> With Best Regards,
> Aleksei
>
>
> On 12/12/2019 13:26, Anuraag Agrawal wrote:
>> Hi Aleksei,
>>
>> I've gone ahead and moved the site-local check to native code -
>> it does reduce the chance of filling up our buffer, though the
>> code is a bit more complicated. To get sockaddr_in6, I went ahead
>> and switched the imports to net_util.h, which I noticed was used
>> in other files. Let me know if it looks ok.
>>
>> 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..c3ebe09776 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 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 && 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
>> 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..580347540d 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
>> @@ -24,14 +24,12 @@
>> */
>>
>> #include <stdlib.h>
>> -#include <windows.h>
>> #include <stdio.h>
>> #include <stddef.h>
>> -#include <iprtrmib.h>
>> #include <time.h>
>> #include <assert.h>
>> -#include <iphlpapi.h>
>>
>> +#include "net_util.h"
>> #include "jni_util.h"
>>
>> #define MAX_STR_LEN 256
>> @@ -48,8 +46,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 +60,32 @@ 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;
>> + SOCKADDR *sockAddr;
>> + struct sockaddr_in6 *sockAddrIpv6;
>>
>> /*
>> * First see if there is a global suffix list specified.
>> @@ -112,122 +103,67 @@ 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);
>> + adapter = adapters;
>> + while (adapter != NULL) {
>> + // Only load config from enabled adapters.
>> + if (adapter->OperStatus == IfOperStatusUp) {
>> + dnsServer = adapter->FirstDnsServerAddress;
>> + while (dnsServer != NULL) {
>> + sockAddr = dnsServer->Address.lpSockaddr;
>> + if (sockAddr->sa_family == AF_INET6) {
>> + sockAddrIpv6 = (struct sockaddr_in6 *)sockAddr;
>> + if (sockAddrIpv6->sin6_scope_id != 0) {
>> + // An address with a scope is either
>> link-local or
>> + // site-local, which aren't valid for
>> DNS queries so
>> + // we can skip them.
>> + dnsServer = dnsServer->Next;
>> + continue;
>> }
>> }
>>
>> - /*
>> - * 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';
>> 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(sockAddr,
>> + 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 +196,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 Thu, Dec 12, 2019 at 1:49 AM Anuraag Agrawal
>> <anuraaga at gmail.com <mailto:anuraaga at gmail.com>> wrote:
>>
>> Hi Aleksei,
>>
>> Thanks for the feedback. I had thought about it but felt that
>> it might be better to leave as much business logic to Java vs
>> native as possible as a general principle. But if it makes
>> more sense to do it native here happy to make the change :)
>>
>> Thanks,
>> - Anuraag
>>
>> On Thu, Dec 12, 2019, 01:18 Aleks Efimov
>> <aleksej.efimov at oracle.com
>> <mailto:aleksej.efimov at oracle.com>> wrote:
>>
>> 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 <http://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 <http://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/20191224/250b1f96/attachment-0001.htm>
More information about the net-dev
mailing list