aefimov-dns-client-branch: Cleanup and address ANY type failures aefimov-dns-client-branch
authoraefimov
Thu, 07 Nov 2019 18:46:06 +0000
branchaefimov-dns-client-branch
changeset 58971 465a15dd6bed
parent 58970 027e4cb87353
child 58983 956b1255a5ec
aefimov-dns-client-branch: Cleanup and address ANY type failures
src/jdk.dns.client/share/classes/jdk/dns/client/AddressFamily.java
src/jdk.dns.client/share/classes/jdk/dns/client/NetworkNamesResolver.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsCommunicationException.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsInvalidAttributeIdentifierException.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsInvalidNameException.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsNameNotFoundException.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsOperationNotSupportedException.java
src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsServiceUnavailableException.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/AddressFamily.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/AddressResolutionQueue.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsName.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsNameService.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsResolver.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/Header.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/HostsFileResolver.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/Resolver.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceClassType.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecord.java
src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecords.java
src/jdk.dns.client/share/classes/module-info.java
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/AddressFamily.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client;
-
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.ProtocolFamily;
-import java.net.StandardProtocolFamily;
-
-public enum AddressFamily {
-    IPv4,
-    IPv6,
-    ANY;
-
-    public static AddressFamily fromProtocolFamily(ProtocolFamily protocolFamily) {
-        if (protocolFamily == StandardProtocolFamily.INET) {
-            return IPv4;
-        } else if (protocolFamily == StandardProtocolFamily.INET6) {
-            return IPv6;
-        } else {
-            return ANY;
-        }
-
-    }
-
-    public boolean sameFamily(InetAddress inetAddress) {
-        switch (this) {
-            case IPv4:
-                return inetAddress instanceof Inet4Address;
-            case IPv6:
-                return inetAddress instanceof Inet6Address;
-            case ANY:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    public static AddressFamily fromInetAddress(InetAddress addr) {
-        return addr instanceof Inet4Address ? IPv4 : addr instanceof Inet6Address ? IPv6 : ANY;
-    }
-}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/NetworkNamesResolver.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/NetworkNamesResolver.java	Thu Nov 07 18:46:06 2019 +0000
@@ -26,76 +26,58 @@
 package jdk.dns.client;
 
 import jdk.dns.client.ex.DnsResolverException;
-import jdk.dns.client.internal.DnsName;
+import jdk.dns.client.internal.AddressResolutionQueue;
+import jdk.dns.client.internal.DnsResolver;
 import jdk.dns.client.internal.HostsFileResolver;
-import jdk.dns.client.internal.Resolver;
-import jdk.dns.client.internal.ResourceClassType;
-import jdk.dns.client.internal.ResourceRecord;
-import jdk.dns.client.internal.ResourceRecords;
-import jdk.dns.conf.DnsResolverConfiguration;
-import sun.net.util.IPAddressUtil;
 
 import java.net.InetAddress;
 import java.net.ProtocolFamily;
 import java.net.UnknownHostException;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 public class NetworkNamesResolver {
 
-    private final AddressFamily addressFamily;
+    private final ProtocolFamily protocolFamily;
 
     public static NetworkNamesResolver open() throws UnknownHostException {
-        return new NetworkNamesResolver(AddressFamily.ANY);
+        // null for any (IPv4+IPv6) addresses family
+        return new NetworkNamesResolver(null);
     }
 
     public static NetworkNamesResolver open(ProtocolFamily protocolFamily) throws UnknownHostException {
-        return new NetworkNamesResolver(AddressFamily.fromProtocolFamily(protocolFamily));
+        return new NetworkNamesResolver(protocolFamily);
     }
 
-    private NetworkNamesResolver(AddressFamily addressFamily) throws UnknownHostException {
-        this.addressFamily = addressFamily;
-        this.dnsResolver = new Resolver(dnsResolverConfiguration.nameservers(), 1000, 4);
+    private NetworkNamesResolver(ProtocolFamily protocolFamily) throws UnknownHostException {
+        this.protocolFamily = protocolFamily;
     }
 
-
     /**
      * Lookup the IP address of a host.
      * The family of required address needed can be specified with {@code addressFamily} parameter.
      *
      * @param hostname the specified host name
      * @return first IP address that matches requested {@code addressFamily}
-     * @throws UnknownHostException  if no IP address found for the specified host name and the specified address family
+     * @throws UnknownHostException if no IP address found for the specified host name and the specified address family
      */
     public InetAddress lookupHostAddr(String hostname) throws UnknownHostException {
-
         // First try hosts file
         // TODO: Add nsswitch.conf to select proper order
         try {
-            return hostsFileResolver.getHostAddress(hostname, addressFamily);
+            return hostsFileResolver.getHostAddress(hostname, protocolFamily);
         } catch (UnknownHostException uhe) {
             if (DEBUG) {
                 System.err.printf("Hosts file doesn't know '%s' host with '%s' address family%n",
-                        hostname, addressFamily.toString());
+                        hostname, protocolFamily == null ? "ANY" : protocolFamily.toString());
             }
         }
 
         // If no luck - try to ask name servers
-        try {
-            var results = lookup(hostname, addressFamily, true, true, 0);
-            if (results.isEmpty()) {
-                throw new UnknownHostException(hostname);
-            }
+        try (DnsResolver dnsResolver = new DnsResolver()) {
+            var results = lookup(dnsResolver, hostname, protocolFamily, true);
             return results.get(0);
-        } catch (DnsResolverException e) {
-            UnknownHostException uhe = new UnknownHostException(hostname);
-            uhe.initCause(e);
-            throw uhe;
         }
     }
 
@@ -111,22 +93,19 @@
         // First try hosts file
         // TODO: Add nsswitch.conf ReloadTracker and parser to select proper order
         try {
-            return List.of(hostsFileResolver.getHostAddress(hostname));
+            return List.of(hostsFileResolver.getHostAddress(hostname, protocolFamily));
         } catch (UnknownHostException uhe) {
             if (DEBUG) {
                 System.err.printf("Resolver API: Hosts file doesn't know '%s' host%n", hostname);
             }
         }
-        try {
-            var results = lookup(hostname, AddressFamily.ANY, false, true, 0);
+
+        try (var dnsResolver = new DnsResolver()) {
+            var results = lookup(dnsResolver, hostname, null, false);
             if (results.isEmpty()) {
                 throw new UnknownHostException(hostname + " unknown host name");
             }
             return results;
-        } catch (DnsResolverException e) {
-            UnknownHostException uhe = new UnknownHostException(hostname);
-            uhe.initCause(e);
-            throw uhe;
         }
     }
 
@@ -159,10 +138,9 @@
                 System.err.printf("Resolver API: No host in hosts file with %s address%n", address);
             }
         }
-        try {
+        try (DnsResolver dnsResolver = new DnsResolver()) {
             var literalIP = addressToLiteralIP(address);
-            var family = AddressFamily.fromInetAddress(address);
-            var results = rlookup(literalIP, family);
+            var results = dnsResolver.rlookup(literalIP, address);
             if (results.isEmpty()) {
                 throw new UnknownHostException();
             }
@@ -177,176 +155,6 @@
         }
     }
 
-    private Set<String> prepareDomainsSearchList() {
-        var domainsToSearch = new LinkedHashSet<String>();
-        var domain = dnsResolverConfiguration.domain();
-        // First will try the domain
-        if (!domain.isBlank()) {
-            domainsToSearch.add(domain);
-        }
-        // Then will iterate over search list
-        domainsToSearch.addAll(dnsResolverConfiguration.searchlist());
-        if (DEBUG) {
-            System.out.printf("Domains search list:%s%n", domainsToSearch);
-        }
-        return domainsToSearch;
-    }
-
-    private List<InetAddress> lookup(String host, AddressFamily addressFamily, boolean oneIsEnough, boolean checkDomains, int depth) throws UnknownHostException, DnsResolverException {
-
-        if (DEBUG) {
-            System.out.printf("Resolver API: internal lookup call - %s%n", host);
-        }
-
-        // Will remain the same during DNS queries execution
-        ResourceClassType ct = ResourceClassType.fromAddressFamily(addressFamily);
-        ResourceRecords rrs = null;
-
-        // First try to get the resource records with the requested DNS host name if it is FQDN
-        if (host.contains(".")) {
-            rrs = execAddrResolutionQuery(new DnsName(host), ct);
-        }
-        // Non-FQDN or not known host address and domains still can be checked
-        if ((rrs == null || rrs.answer.isEmpty()) && checkDomains) {
-            for (String domain : prepareDomainsSearchList()) {
-                String hostWithSuffix = host + "." + domain;
-                if (DEBUG) {
-                    System.err.printf("Resolver API: Trying to lookup:'%s'%n", hostWithSuffix);
-                }
-                rrs = execAddrResolutionQuery(new DnsName(hostWithSuffix), ct);
-                if (rrs != null && !rrs.answer.isEmpty()) {
-                    if (DEBUG) {
-                        System.err.printf("Resolver API: Found host in '%s' domain%n", domain);
-                    }
-                    break;
-                }
-            }
-        }
-
-        // If no answers then host is not known in registered domain
-        if (rrs == null || rrs.answer.isEmpty()) {
-            throw new UnknownHostException(host);
-        }
-
-        // Parse answers
-        List<InetAddress> results = new ArrayList<>();
-        for (int i = 0; i < rrs.answer.size(); i++) {
-            ResourceRecord rr = rrs.answer.elementAt(i);
-            int answerType = rr.getType();
-            String recordData = rr.getRdata().toString();
-            if (DEBUG) {
-                System.err.printf("Resolver API: Got %s type: %s%n", ResourceRecord.getTypeName(rr.getType()), rr.getRdata());
-            }
-            if (answerType == ResourceRecord.TYPE_CNAME) {
-                // We've got CNAME entry - issue another request to resolve the canonical host name
-                if (depth > MAX_CNAME_RESOLUTION_DEPTH) {
-                    throw new UnknownHostException(host);
-                }
-                List<InetAddress> cnameResults = lookup(recordData, addressFamily, oneIsEnough, false, depth + 1);
-                if (oneIsEnough) {
-                    return cnameResults;
-                } else {
-                    results.addAll(cnameResults);
-                }
-            } else {
-                byte[] addrBytes;
-                if (answerType == ResourceRecord.TYPE_A && (addressFamily == AddressFamily.IPv4 || addressFamily == AddressFamily.ANY)) {
-                    addrBytes = IPAddressUtil.textToNumericFormatV4(recordData);
-                    if (addrBytes == null) {
-                        if (DEBUG) {
-                            System.err.println("Incorrect A resource record answer");
-                        }
-                        return Collections.emptyList();
-                    }
-                } else if (answerType == ResourceRecord.TYPE_AAAA && (addressFamily == AddressFamily.IPv6 || addressFamily == AddressFamily.ANY)) {
-                    addrBytes = IPAddressUtil.textToNumericFormatV6(recordData);
-                    if (addrBytes == null) {
-                        if (DEBUG) {
-                            System.err.println("Incorrect AAAA resource record answer");
-                        }
-                        return Collections.emptyList();
-                    }
-                } else {
-                    continue;
-                }
-                // IPAddressUtil.textToNumeric can return null
-                if (addrBytes == null) {
-                    throw new RuntimeException("Internal error");
-                }
-                InetAddress address = InetAddress.getByAddress(host, addrBytes);
-                results.add(address);
-                if (oneIsEnough) {
-                    break;
-                }
-            }
-        }
-
-        return results;
-    }
-
-    // Can return null if query failed
-    private ResourceRecords execAddrResolutionQuery(DnsName name, ResourceClassType ct) {
-        // Not supporting authoritative requests [for now?]
-        try {
-            ResourceRecords rrs = dnsResolver.query(name, ct.getRecordClass(), ct.getRecordType(),
-                    true, false);
-            return rrs.hasAddressOrAlias() ? rrs : null;
-        } catch (DnsResolverException e) {
-            if (DEBUG) {
-                System.err.println("Query failed:" + e.getMessage());
-                e.printStackTrace();
-            }
-            return null;
-        }
-    }
-
-    private List<String> rlookup(String literalIP, AddressFamily family) throws DnsResolverException {
-        switch (family) {
-            case IPv4:
-                return ipv4rlookup(literalIP);
-            case IPv6:
-                return ipv6rlookup(literalIP);
-            default:
-                throw new RuntimeException("Only IPv4 and IPv6 addresses are supported");
-        }
-    }
-
-    private List<String> ipv4rlookup(String literalIP) throws DnsResolverException {
-        var request = literalIP + "IN-ADDR.ARPA.";
-        var hostNames = new ArrayList<String>();
-        var name = new DnsName(request);
-        var rrs = dnsResolver.query(name, ResourceRecord.CLASS_INTERNET,
-                ResourceRecord.TYPE_PTR, true, false);
-        for (int i = 0; i < rrs.answer.size(); i++) {
-            ResourceRecord rr = rrs.answer.elementAt(i);
-            hostNames.add(rr.getRdata().toString());
-        }
-        return hostNames;
-    }
-
-    private List<String> ipv6rlookup(String literalIP) throws DnsResolverException {
-        List<String> hostNames = new ArrayList<>();
-        DnsName name = new DnsName(literalIP + "IP6.ARPA.");
-        ResourceRecords rrs = dnsResolver.query(name, ResourceRecord.CLASS_INTERNET,
-                ResourceRecord.TYPE_PTR, true, false);
-        /**
-         * Because RFC 3152 changed the root domain name for reverse
-         * lookups from IP6.INT. to IP6.ARPA., we need to check
-         * both. I.E. first the new one, IP6.ARPA, then if it fails
-         * the older one, IP6.INT
-         */
-        if (rrs.answer.isEmpty()) {
-            name = new DnsName(literalIP + "IP6.INT.");
-            rrs = dnsResolver.query(name, ResourceRecord.CLASS_INTERNET,
-                    ResourceRecord.TYPE_PTR, true, false);
-        }
-        for (int i = 0; i < rrs.answer.size(); i++) {
-            ResourceRecord rr = rrs.answer.elementAt(i);
-            hostNames.add(rr.getRdata().toString());
-        }
-        return hostNames;
-    }
-
     private static String addressToLiteralIP(InetAddress address) {
         byte[] bytes = address.getAddress();
         StringBuilder addressBuff = new StringBuilder();
@@ -356,7 +164,7 @@
                 addressBuff.append(bytes[i] & 0xff);
                 addressBuff.append(".");
             }
-        // IPv6 address
+            // IPv6 address
         } else if (bytes.length == 16) {
             for (int i = bytes.length - 1; i >= 0; i--) {
                 addressBuff.append(Integer.toHexString((bytes[i] & 0x0f)));
@@ -370,13 +178,14 @@
         return addressBuff.toString();
     }
 
-    private static DnsResolverConfiguration dnsResolverConfiguration = new DnsResolverConfiguration();
-    private static HostsFileResolver hostsFileResolver = new HostsFileResolver();
+    private List<InetAddress> lookup(DnsResolver dnsResolver, String host, ProtocolFamily protocolFamily, boolean oneIsEnough) throws UnknownHostException {
+        if (DEBUG) {
+            System.out.printf("Resolver API: internal lookup call - %s%n", host);
+        }
+        return AddressResolutionQueue.resolve(dnsResolver, host, protocolFamily, oneIsEnough);
+    }
 
-    // TODO: Add system property with the sequence of resolution OR nsswitch.conf location to get the order:
-    //     file, name
-    private final Resolver dnsResolver;
+    private static HostsFileResolver hostsFileResolver = new HostsFileResolver();
     private static final boolean DEBUG = java.security.AccessController.doPrivileged(
             (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("jdk.dns.client.debug"));
-    private static final int MAX_CNAME_RESOLUTION_DEPTH = 4;
 }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsCommunicationException.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsCommunicationException.java	Thu Nov 07 18:46:06 2019 +0000
@@ -32,7 +32,4 @@
         super(message);
     }
 
-    public DnsCommunicationException(String message, Throwable cause) {
-        super(message, cause);
-    }
 }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsInvalidAttributeIdentifierException.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client.ex;
-
-public class DnsInvalidAttributeIdentifierException extends DnsResolverException {
-    private static final long serialVersionUID = 745256513458941901L;
-
-    public DnsInvalidAttributeIdentifierException(String message) {
-        super(message);
-    }
-
-    public DnsInvalidAttributeIdentifierException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsInvalidNameException.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client.ex;
-
-public class DnsInvalidNameException extends DnsResolverException {
-    private static final long serialVersionUID = -6375885343819861145L;
-
-    public DnsInvalidNameException(String message) {
-        super(message);
-    }
-
-    public DnsInvalidNameException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsNameNotFoundException.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsNameNotFoundException.java	Thu Nov 07 18:46:06 2019 +0000
@@ -26,14 +26,10 @@
 package jdk.dns.client.ex;
 
 public class DnsNameNotFoundException extends DnsResolverException {
-
     private static final long serialVersionUID = 2646341064300322397L;
 
     public DnsNameNotFoundException(String message) {
         super(message);
     }
 
-    public DnsNameNotFoundException(String message, Throwable cause) {
-        super(message, cause);
-    }
 }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsOperationNotSupportedException.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client.ex;
-
-public class DnsOperationNotSupportedException extends DnsResolverException {
-    private static final long serialVersionUID = -8739097013731823223L;
-
-    public DnsOperationNotSupportedException(String message) {
-        super(message);
-    }
-
-    public DnsOperationNotSupportedException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/ex/DnsServiceUnavailableException.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client.ex;
-
-public class DnsServiceUnavailableException extends DnsResolverException {
-    private static final long serialVersionUID = -4912814054659577232L;
-
-    public DnsServiceUnavailableException(String message) {
-        super(message);
-    }
-
-    public DnsServiceUnavailableException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/AddressFamily.java	Thu Nov 07 18:46:06 2019 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.dns.client.internal;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.ProtocolFamily;
+import java.net.StandardProtocolFamily;
+
+public enum AddressFamily {
+    IPv4,
+    IPv6,
+    ANY;
+
+    public static AddressFamily fromProtocolFamily(ProtocolFamily protocolFamily) {
+        if (protocolFamily == null) {
+            return ANY;
+        } else if (protocolFamily == StandardProtocolFamily.INET) {
+            return IPv4;
+        } else if (protocolFamily == StandardProtocolFamily.INET6) {
+            return IPv6;
+        } else {
+            return ANY;
+        }
+
+    }
+
+    public boolean sameFamily(InetAddress inetAddress) {
+        switch (this) {
+            case IPv4:
+                return inetAddress instanceof Inet4Address;
+            case IPv6:
+                return inetAddress instanceof Inet6Address;
+            case ANY:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    boolean matchesResourceRecord(ResourceRecord rr) {
+        int type = rr.getType();
+        switch (this) {
+            case IPv4:
+                return type == ResourceRecord.TYPE_A;
+            case IPv6:
+                return type == ResourceRecord.TYPE_AAAA;
+            case ANY:
+                return type == ResourceRecord.TYPE_A ||
+                        type == ResourceRecord.TYPE_AAAA;
+            default:
+                return false;
+        }
+    }
+
+    static AddressFamily fromResourceRecord(ResourceRecord rr) {
+        return rr.getType() == ResourceRecord.TYPE_AAAA ? IPv6 : IPv4;
+    }
+
+    static AddressFamily fromInetAddress(InetAddress addr) {
+        return addr instanceof Inet4Address ? IPv4 : addr instanceof Inet6Address ? IPv6 : ANY;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/AddressResolutionQueue.java	Thu Nov 07 18:46:06 2019 +0000
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.dns.client.internal;
+
+import jdk.dns.client.ex.DnsResolverException;
+import sun.net.util.IPAddressUtil;
+
+import java.net.InetAddress;
+import java.net.ProtocolFamily;
+import java.net.UnknownHostException;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.stream.Collectors;
+
+// This class handles DNS requests to perform host name to IP addresses resolutions.
+// Reverse-DNS requests are handled by DnsResolver itself
+public class AddressResolutionQueue {
+    /**
+     * Resolve hostname with provided @{code DnsResolver}
+     *
+     * @param dnsResolver      resolver that will be used to issue DNS requests
+     * @param hostName         host name to resolve
+     * @param family           address family to search
+     * @param needAllAddresses search for all available addresses
+     * @return list of found addresses
+     * @throws UnknownHostException host name cannot be resolved to an address
+     */
+    public static List<InetAddress> resolve(DnsResolver dnsResolver,
+                                            String hostName,
+                                            ProtocolFamily family,
+                                            boolean needAllAddresses) throws UnknownHostException {
+        AddressResolutionQueue rq = new AddressResolutionQueue(dnsResolver,
+                hostName,
+                AddressFamily.fromProtocolFamily(family),
+                needAllAddresses);
+        var rqResults = rq.resolve();
+        if (rqResults.isEmpty()) {
+            throw new UnknownHostException(hostName);
+        }
+
+        // Parse answers
+        var results = new ArrayList<InetAddress>();
+        for (var result : rqResults) {
+            byte[] addrBytes = null;
+            switch (result.getType()) {
+                case IPv4:
+                    addrBytes = IPAddressUtil.textToNumericFormatV4(result.getAddressString());
+                    break;
+                case IPv6:
+                    addrBytes = IPAddressUtil.textToNumericFormatV6(result.getAddressString());
+                    break;
+                default:
+                    break;
+            }
+
+            if (addrBytes == null) {
+                if (DEBUG) {
+                    System.err.println("Bad address format: " + result.getAddressString());
+                }
+                continue;
+            }
+            InetAddress address = InetAddress.getByAddress(hostName, addrBytes);
+            results.add(address);
+            if (needAllAddresses) {
+                break;
+            }
+        }
+        return results;
+    }
+
+    private AddressResolutionQueue(DnsResolver dnsResolver, String nameToResolve,
+                                   AddressFamily requestedAddressType, boolean needAllAddresses) {
+        this.dnsResolver = dnsResolver;
+        this.nameToResolve = nameToResolve;
+        this.requestedAddressType = requestedAddressType;
+        this.needAllAddresses = needAllAddresses;
+    }
+
+    private void fillQueue() {
+        if (nameToResolve.contains("."))
+            addQueriesForHostname(nameToResolve);
+        for (var domainName : dnsResolver.getDomainsSearchList()) {
+            addQueriesForHostname(nameToResolve + "." + domainName);
+        }
+    }
+
+    private List<ResolvedAddress> resolve() {
+        // Used in non-ANY mode and stores the host name that yielded some result (A or AAAA)
+        String gotResultsFor = "";
+        // Init queue with ANY requests first
+        isAnyMode = true;
+        fillQueue();
+        List<ResolvedAddress> addresses = new ArrayList<>();
+        while (true) {
+            if (queue.isEmpty()) {
+                if (isAnyMode) {
+                    // If previous request was for ANY address and no A/AAAA/CNAME replies were received
+                    // then populate queue with separate A/AAAA/CNAME requests
+                    isAnyMode = false;
+                    cnameCount = 0;
+                    fillQueue();
+                } else {
+                    // Tried everything - return what we have
+                    return Collections.unmodifiableList(addresses);
+                }
+            }
+            // Output queue for debugging purposes
+            if (DEBUG) {
+                System.out.println("================DNS Resolution queue==================");
+                System.out.println("Address resolution queue:" + queue);
+                System.out.println("======================================================");
+            }
+            // Execute request
+            var resRequest = queue.poll();
+            // If not in ANY mode and already found something - continue searches only for the same
+            // name and only for the left addresses and no need to process CNAME requests
+            if (gotResultsFor.isEmpty() || (resRequest.hostName.equals(gotResultsFor)
+                    && resRequest.resourceId != ResourceRecord.TYPE_CNAME)) {
+                var result = executeRequest(resRequest);
+                addresses.addAll(result);
+            }
+
+            // If found something and is in ANY mode - return the addresses
+            // If in separate A/AAAA requests - update host name with found address to filter out
+            // other requests
+            if (!addresses.isEmpty()) {
+                if (isAnyMode || !needAllAddresses) {
+                    return Collections.unmodifiableList(addresses);
+                } else if (gotResultsFor.isEmpty()) {
+                    gotResultsFor = resRequest.hostName;
+                }
+            }
+        }
+    }
+
+    private void addQueriesForHostname(String hostname) {
+        if (isAnyMode) {
+            queue.add(ResolutionRequest.of(hostname, ResourceRecord.TYPE_ANY));
+        } else {
+            if (requestedAddressType == AddressFamily.ANY || requestedAddressType == AddressFamily.IPv4) {
+                queue.add(ResolutionRequest.of(hostname, ResourceRecord.TYPE_A));
+            }
+            if (requestedAddressType == AddressFamily.ANY || requestedAddressType == AddressFamily.IPv6) {
+                queue.add(ResolutionRequest.of(hostname, ResourceRecord.TYPE_AAAA));
+            }
+            queue.add(ResolutionRequest.of(hostname, ResourceRecord.TYPE_CNAME));
+        }
+    }
+
+    private List<ResolvedAddress> executeRequest(ResolutionRequest request) {
+
+        try {
+            if (DEBUG) {
+                System.err.printf("Executing processing request:%s%n", request);
+            }
+            var rrs = dnsResolver.query(request.hostName, request.resourceId);
+            if (DEBUG) {
+                System.err.println("================DNS Resolution queue==================");
+                System.err.println("Got answers:" + rrs.answer);
+                System.err.println("======================================================");
+            }
+            // If no addresses or aliases - return
+            if (!rrs.hasAddressesOrAlias()) {
+                return Collections.emptyList();
+            }
+            // Has addresses
+            if (rrs.hasAddressOfFamily(AddressFamily.ANY)) {
+                // if no address of requested type but has other addresses
+                // clear the queue and return empty list
+                // Clear queue if address is found or doesn't match the
+                // requested address type - only for ANY requests
+                if (request.resourceId == ResourceRecord.TYPE_ANY) {
+                    queue.clear();
+                }
+                if (!rrs.hasAddressOfFamily(requestedAddressType)) {
+                    return Collections.emptyList();
+                }
+                // Parse addresses
+                return rrs.answer.stream()
+                        .filter(requestedAddressType::matchesResourceRecord)
+                        .map(ResolvedAddress::of)
+                        .collect(Collectors.toList());
+            }
+            // If has alias - clear the queue and add new requests withSystem.out.println("================DNS Resolution queue==================");
+            // the requested addresses
+            Optional<String> aliasO = rrs.answer.stream()
+                    .filter(rr -> rr.rrtype == ResourceRecord.TYPE_CNAME)
+                    .map(ResourceRecord::getRdata)
+                    .map(Object::toString)
+                    .findFirst();
+            if (aliasO.isPresent()) {
+                queue.clear();
+                String cname = aliasO.get();
+                if (DEBUG) {
+                    System.err.println("Found alias: " + cname);
+                }
+                cnameCount++;
+                if (cnameCount > MAX_CNAME_RESOLUTION_DEPTH) {
+                    throw new RuntimeException("CNAME loop detected");
+                }
+                addQueriesForHostname(cname);
+            }
+            // Filter the results depending on typeOfAddress
+            return Collections.emptyList();
+        } catch (DnsResolverException e) {
+            return Collections.emptyList();
+        }
+    }
+
+    static class ResolvedAddress {
+        final AddressFamily type;
+        final String addressString;
+
+        private ResolvedAddress(AddressFamily type, String addressString) {
+            this.type = type;
+            this.addressString = addressString;
+        }
+
+        public AddressFamily getType() {
+            return type;
+        }
+
+        public String getAddressString() {
+            return addressString;
+        }
+
+        public static ResolvedAddress of(ResourceRecord resourceRecord) {
+            AddressFamily type = AddressFamily.fromResourceRecord(resourceRecord);
+            String addressString = resourceRecord.getRdata().toString();
+            return new ResolvedAddress(type, addressString);
+        }
+
+        @Override
+        public String toString() {
+            return "[" + type.name() + "]:" + addressString;
+        }
+    }
+
+    static class ResolutionRequest {
+        final int resourceId;
+        final String hostName;
+
+        private ResolutionRequest(String hostName, int resourceId) {
+            this.resourceId = resourceId;
+            this.hostName = hostName;
+        }
+
+        public boolean isAny() {
+            return resourceId == ResourceRecord.TYPE_ANY;
+        }
+
+        public static ResolutionRequest of(String hostName, int resourceId) {
+            assert hostName != null && !hostName.isBlank();
+            return new ResolutionRequest(hostName, resourceId);
+        }
+
+        @Override
+        public String toString() {
+            return hostName + ":" + resourceId;
+        }
+    }
+
+    // DNS resolver instance to issue DNS queries
+    private final DnsResolver dnsResolver;
+    // Queue with address resolution requests (ANY/A/AAAA or CNAME)
+    private final Queue<ResolutionRequest> queue = new LinkedList<>();
+
+    // Internal state variables to control queue processing
+    // Type of requested address
+    private final AddressFamily requestedAddressType;
+    // Requested name to resolve
+    private final String nameToResolve;
+    // Is it getAllByName [true] or getByName [false]
+    private final boolean needAllAddresses;
+    // CNAME lookups counter needed to avoid alias loops
+    private int cnameCount = 0;
+    // Private resolution queue has two modes - ANY and single requests.
+    // The mode in use is tracked with this boolean flag
+    private boolean isAnyMode;
+    // Maximum number of sequential CNAME requests
+    private static final int MAX_CNAME_RESOLUTION_DEPTH = 4;
+    // Enable debug output
+    private static final boolean DEBUG = java.security.AccessController.doPrivileged(
+            (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("jdk.dns.client.debug"));
+
+
+}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsClient.java	Thu Nov 07 18:46:06 2019 +0000
@@ -27,11 +27,13 @@
 
 import jdk.dns.client.ex.DnsCommunicationException;
 import jdk.dns.client.ex.DnsNameNotFoundException;
-import jdk.dns.client.ex.DnsOperationNotSupportedException;
 import jdk.dns.client.ex.DnsResolverException;
-import jdk.dns.client.ex.DnsServiceUnavailableException;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.DatagramPacket;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -44,7 +46,6 @@
 import java.security.PrivilegedAction;
 import java.security.SecureRandom;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -99,7 +100,6 @@
     private int retries;                // number of UDP retries
 
 
-
     private static final SecureRandom random;
 
     static {
@@ -125,14 +125,13 @@
      * "timeout" is the initial timeout interval (in ms) for queries,
      * and "retries" gives the number of retries per server.
      */
-    public DnsClient(List<String> servers, int timeout, int retries)
-            throws UnknownHostException {
+    public DnsClient(List<String> servers, int timeout, int retries) {
         this.timeout = timeout;
         this.retries = retries;
         var serversList = new ArrayList<InetAddress>();
         var serverPortsList = new ArrayList<Integer>();
 
-        for (String serverString:servers) {
+        for (String serverString : servers) {
 
             // Is optional port given?
             int colon = serverString.indexOf(':',
@@ -155,8 +154,12 @@
             byte[] addr = System.getSecurityManager() == null ?
                     pa.run() : AccessController.doPrivileged(pa);
             if (addr != null) {
-                serversList.add(InetAddress.getByAddress(server, addr));
-                serverPortsList.add(serverPort);
+                try {
+                    serversList.add(InetAddress.getByAddress(server, addr));
+                    serverPortsList.add(serverPort);
+                } catch (UnknownHostException e) {
+                    // Malformed IP address is specified - will ignore it
+                }
             }
         }
         this.servers = Collections.unmodifiableList(serversList);
@@ -257,8 +260,7 @@
                         Header hdr = new Header(msg, msg.length);
 
                         if (auth && !hdr.authoritative) {
-                            caughtException = new DnsNameNotFoundException(
-                                    "DNS response not authoritative");
+                            caughtException = new DnsResolverException("DNS response not authoritative");
                             doNotRetry[i] = true;
                             continue;
                         }
@@ -283,7 +285,7 @@
                                     }
                                     Header hdr2 = new Header(msg2, msg2.length);
                                     if (hdr2.query) {
-                                        throw new DnsCommunicationException(
+                                        throw new DnsResolverException(
                                                 "DNS error: expecting response");
                                     }
                                     checkResponseCode(hdr2);
@@ -362,7 +364,10 @@
             DatagramPacket ipkt = new DatagramPacket(new byte[8000], 8000);
             // Packets may only be sent to or received from this server address
             // TODO: Revisit
-            var pa = (PrivilegedAction<Void>) () -> {dc.socket().connect(server, port); return null;};
+            var pa = (PrivilegedAction<Void>) () -> {
+                dc.socket().connect(server, port);
+                return null;
+            };
             if (System.getSecurityManager() == null) {
                 pa.run();
             } else {
@@ -461,6 +466,7 @@
         Packet pkt = new Packet(pktLen);
 
         short flags = recursion ? Header.RD_BIT : 0;
+        // flags = (short) (flags | Header.CD_BIT | Header.AD_BIT);
 
         pkt.putShort(xid, IDENT_OFFSET);
         pkt.putShort(flags, FLAGS_OFFSET);
@@ -536,7 +542,7 @@
 
         Header hdr = new Header(pkt, pkt.length);
         if (hdr.query) {
-            throw new DnsCommunicationException("DNS error: expecting response");
+            throw new DnsResolverException("DNS error: expecting response");
         }
 
         if (!reqs.containsKey(xid)) { // already received, ignore the response
@@ -562,7 +568,7 @@
 
                 // Check that the type/class/name in the query section of the
                 // response match those in the original query
-                if ((qtype == ResourceRecord.QTYPE_STAR ||
+                if ((qtype == ResourceRecord.TYPE_ANY ||
                         qtype == rr.getType()) &&
                         (qclass == ResourceRecord.QCLASS_STAR ||
                                 qclass == rr.getRrclass()) &&
@@ -632,19 +638,7 @@
                 : "DNS error";
 
         msg += " [response code " + rcode + "]";
-
-        switch (rcode) {
-            case SERVER_FAILURE:
-                throw new DnsServiceUnavailableException(msg);
-            case NAME_ERROR:
-                throw new DnsNameNotFoundException(msg);
-            case NOT_IMPL:
-            case REFUSED:
-                throw new DnsOperationNotSupportedException(msg);
-            case FORMAT_ERROR:
-            default:
-                throw new DnsResolverException(msg);
-        }
+        throw new DnsResolverException(msg);
     }
 
     //-------------------------------------------------------------------------
@@ -663,8 +657,8 @@
 class Tcp {
 
     private final Socket sock;
-    private final java.io.InputStream in;
-    final java.io.OutputStream out;
+    private final InputStream in;
+    final OutputStream out;
     private int timeoutLeft;
 
     Tcp(InetAddress server, int port, int timeout) throws IOException {
@@ -677,8 +671,8 @@
                 throw new SocketTimeoutException();
 
             sock.setTcpNoDelay(true);
-            out = new java.io.BufferedOutputStream(sock.getOutputStream());
-            in = new java.io.BufferedInputStream(sock.getInputStream());
+            out = new BufferedOutputStream(sock.getOutputStream());
+            in = new BufferedInputStream(sock.getInputStream());
         } catch (Exception e) {
             try {
                 sock.close();
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsName.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsName.java	Thu Nov 07 18:46:06 2019 +0000
@@ -25,7 +25,7 @@
 
 package jdk.dns.client.internal;
 
-import jdk.dns.client.ex.DnsInvalidNameException;
+import jdk.dns.client.ex.DnsResolverException;
 
 import java.util.ArrayList;
 
@@ -125,10 +125,10 @@
      * Constructs a {@code DnsName} representing a given domain name.
      *
      * @param name the domain name to parse
-     * @throws DnsInvalidNameException if {@code name} does not conform
-     *                                 to DNS syntax.
+     * @throws DnsResolverException if {@code name} does not conform
+     *                              to DNS syntax.
      */
-    public DnsName(String name) throws DnsInvalidNameException {
+    public DnsName(String name) throws DnsResolverException {
         parse(name);
     }
 
@@ -237,7 +237,7 @@
         return new DnsName(this, 0, size());
     }
 
-    public DnsName add(int pos, String comp) throws DnsInvalidNameException {
+    public DnsName add(int pos, String comp) throws DnsResolverException {
         if (pos < 0 || pos > size()) {
             throw new ArrayIndexOutOfBoundsException();
         }
@@ -245,13 +245,13 @@
         int len = comp.length();
         if ((pos > 0 && len == 0) ||
                 (pos == 0 && hasRootLabel())) {
-            throw new DnsInvalidNameException(
+            throw new DnsResolverException(
                     "Empty label must be the last label in a domain name");
         }
         // Check total name length.
         if (len > 0) {
             if (octets + len + 1 >= 256) {
-                throw new DnsInvalidNameException("Name too long");
+                throw new DnsResolverException("Name too long");
             }
             octets += (short) (len + 1);
         }
@@ -264,7 +264,7 @@
         return this;
     }
 
-    public DnsName addAll(int pos, DnsName n) throws DnsInvalidNameException {
+    public DnsName addAll(int pos, DnsName n) throws DnsResolverException {
         // "n" is a DnsName so we can insert it as a whole, rather than
         // verifying and inserting it component-by-component.
         // More code, but less work.
@@ -275,13 +275,13 @@
         // Check for empty labels:  may have only one, and only at end.
         if ((pos > 0 && n.hasRootLabel()) ||
                 (pos == 0 && hasRootLabel())) {
-            throw new DnsInvalidNameException(
+            throw new DnsResolverException(
                     "Empty label must be the last label in a domain name");
         }
 
         short newOctets = (short) (octets + n.octets - 1);
         if (newOctets > 255) {
-            throw new DnsInvalidNameException("Name too long");
+            throw new DnsResolverException("Name too long");
         }
         octets = newOctets;
         int i = size() - pos;       // index for insertion into "labels"
@@ -349,7 +349,7 @@
     /*
      * Parses a domain name, setting the values of instance vars accordingly.
      */
-    private void parse(String name) throws DnsInvalidNameException {
+    private void parse(String name) throws DnsResolverException {
 
         StringBuilder label = new StringBuilder();      // label being parsed
 
@@ -391,7 +391,7 @@
      * @throws InvalidNameException if a valid escape sequence is not found.
      */
     private static char getEscapedOctet(String name, int pos)
-            throws DnsInvalidNameException {
+            throws DnsResolverException {
         try {
             // assert (name.charAt(pos) == '\\');
             char c1 = name.charAt(++pos);
@@ -402,14 +402,14 @@
                     return (char)
                             ((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));
                 } else {
-                    throw new DnsInvalidNameException(
+                    throw new DnsResolverException(
                             "Invalid escape sequence in " + name);
                 }
             } else {                    // sequence is `\C'
                 return c1;
             }
         } catch (IndexOutOfBoundsException e) {
-            throw new DnsInvalidNameException(
+            throw new DnsResolverException(
                     "Invalid escape sequence in " + name);
         }
     }
@@ -418,16 +418,16 @@
      * Checks that this label is valid.
      * @throws InvalidNameException if label is not valid.
      */
-    private static void verifyLabel(String label) throws DnsInvalidNameException {
+    private static void verifyLabel(String label) throws DnsResolverException {
         if (label.length() > 63) {
-            throw new DnsInvalidNameException(
+            throw new DnsResolverException(
                     "Label exceeds 63 octets: " + label);
         }
         // Check for two-byte characters.
         for (int i = 0; i < label.length(); i++) {
             char c = label.charAt(i);
             if ((c & 0xFF00) != 0) {
-                throw new DnsInvalidNameException(
+                throw new DnsResolverException(
                         "Label has two-byte char: " + label);
             }
         }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsNameService.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsNameService.java	Thu Nov 07 18:46:06 2019 +0000
@@ -53,8 +53,9 @@
             if ("false".equals(value)) {
                 return IPv4First;
             }
+            // TODO: Decide if it is compatible way with default InetAddress resolver
             if ("system".equals(value)) {
-                return DontCare; // TODO: Decide if it is compatible way with default InetAddress resolver
+                return DontCare;
             }
             return IPv4First;
         }
@@ -80,13 +81,12 @@
             return NetworkNamesResolver.open()
                     .lookupAllHostAddr(host)
                     .stream()
-                    .sorted(
-                            Comparator.comparing(
-                                    ia -> (ia instanceof Inet4Address && order == AddressOrder.IPv4First)
-                                       || (ia instanceof Inet6Address && order == AddressOrder.IPv6First),
-                                    Boolean::compareTo)
-                                    .reversed()
-                    ).toArray(InetAddress[]::new);
+                    .sorted(Comparator.comparing(
+                            ia -> (ia instanceof Inet4Address && order == AddressOrder.IPv4First)
+                                    || (ia instanceof Inet6Address && order == AddressOrder.IPv6First),
+                            Boolean::compareTo)
+                            .reversed())
+                    .toArray(InetAddress[]::new);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/DnsResolver.java	Thu Nov 07 18:46:06 2019 +0000
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.dns.client.internal;
+
+import jdk.dns.client.ex.DnsResolverException;
+import jdk.dns.conf.DnsResolverConfiguration;
+
+import java.net.InetAddress;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DnsResolver implements AutoCloseable {
+    private DnsClient dnsClient;
+
+    /*
+     * Constructs a new Resolver given package oracle.dns.client.standalone;its servers and timeout parameters.
+     * Each server is of the form "server[:port]".
+     * IPv6 literal host names include delimiting brackets.
+     * There must be at least one server.
+     * "timeout" is the initial timeout interval (in ms) for UDP queries,
+     * and "retries" gives the number of retries per server.
+     */
+    public DnsResolver() {
+        dnsClient = new DnsClient(dnsResolverConfiguration.nameservers(), 1000, 4);
+    }
+
+    @Override
+    public void close() {
+        dnsClient.close();
+        dnsClient = null;
+    }
+
+    public Set<String> getDomainsSearchList() {
+        var domainsToSearch = new LinkedHashSet<String>();
+        var domain = dnsResolverConfiguration.domain();
+        // First, try the domain
+        if (!domain.isBlank()) {
+            domainsToSearch.add(domain);
+        }
+        // Then iterate over the search list
+        domainsToSearch.addAll(dnsResolverConfiguration.searchlist());
+        if (DEBUG) {
+            System.out.printf("Domains search list:%s%n", domainsToSearch);
+        }
+        return domainsToSearch;
+    }
+
+    /*
+     * Queries resource records of a particular class and type for a
+     * given domain name.
+     * Useful values of rrtype are ResourceRecord.[Q]TYPE_xxx.
+     */
+    public ResourceRecords query(String fqdn, int rrtype)
+            throws DnsResolverException {
+        return dnsClient.query(new DnsName(fqdn), ResourceRecord.CLASS_INTERNET, rrtype, true, false);
+    }
+
+    public List<String> rlookup(String literalIP, InetAddress address) throws DnsResolverException {
+
+        switch (AddressFamily.fromInetAddress(address)) {
+            case IPv4:
+                return ipv4rlookup(literalIP);
+            case IPv6:
+                return ipv6rlookup(literalIP);
+            default:
+                throw new RuntimeException("Only IPv4 and IPv6 addresses are supported");
+        }
+    }
+
+    private List<String> ipv4rlookup(String literalIP) throws DnsResolverException {
+        var request = literalIP + "IN-ADDR.ARPA.";
+        var hostNames = new ArrayList<String>();
+        var rrs = query(request, ResourceRecord.TYPE_PTR);
+        for (int i = 0; i < rrs.answer.size(); i++) {
+            ResourceRecord rr = rrs.answer.elementAt(i);
+            hostNames.add(rr.getRdata().toString());
+        }
+        return hostNames;
+    }
+
+    private List<String> ipv6rlookup(String literalIP) throws DnsResolverException {
+        List<String> hostNames = new ArrayList<>();
+        ResourceRecords rrs = query(literalIP + "IP6.ARPA.", ResourceRecord.TYPE_PTR);
+        /**
+         * Because RFC 3152 changed the root domain name for reverse
+         * lookups from IP6.INT. to IP6.ARPA., we need to check
+         * both. I.E. first the new one, IP6.ARPA, then if it fails
+         * the older one, IP6.INT
+         */
+        if (rrs.answer.isEmpty()) {
+            rrs = query(literalIP + "IP6.INT.", ResourceRecord.TYPE_PTR);
+        }
+        for (int i = 0; i < rrs.answer.size(); i++) {
+            ResourceRecord rr = rrs.answer.elementAt(i);
+            hostNames.add(rr.getRdata().toString());
+        }
+        return hostNames;
+    }
+
+
+    // Configuration file tracker
+    private static DnsResolverConfiguration dnsResolverConfiguration = new DnsResolverConfiguration();
+    private static final boolean DEBUG = java.security.AccessController.doPrivileged(
+            (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("jdk.dns.client.debug"));
+}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/Header.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/Header.java	Thu Nov 07 18:46:06 2019 +0000
@@ -36,6 +36,8 @@
     static final short OPCODE_MASK = (short) 0x7800;
     static final int OPCODE_SHIFT = 11;
     static final short AA_BIT = (short) 0x0400;
+    static final short AD_BIT = (short) 0x0020;
+    static final short CD_BIT = (short) 0x0010;
     static final short TC_BIT = (short) 0x0200;
     static final short RD_BIT = (short) 0x0100;
     static final short RA_BIT = (short) 0x0080;
@@ -48,6 +50,8 @@
     boolean truncated;          // TC
     boolean recursionDesired;   // RD
     boolean recursionAvail;     // RA
+    boolean authenticData;      // AD
+    boolean checkingDisabled;   // CD
     int rcode;                  // RCODE:  4-bit response code
     int numQuestions;
     int numAnswers;
@@ -88,6 +92,8 @@
             truncated = (flags & TC_BIT) != 0;
             recursionDesired = (flags & RD_BIT) != 0;
             recursionAvail = (flags & RA_BIT) != 0;
+            authenticData = (flags & AD_BIT) != 0;
+            checkingDisabled = (flags & CD_BIT) != 0;
             rcode = (flags & RCODE_MASK);
 
             // RR counts
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/HostsFileResolver.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/HostsFileResolver.java	Thu Nov 07 18:46:06 2019 +0000
@@ -25,11 +25,11 @@
 
 package jdk.dns.client.internal;
 
-import jdk.dns.client.AddressFamily;
 import jdk.dns.client.internal.util.ReloadTracker;
 import sun.net.util.IPAddressUtil;
 
 import java.net.InetAddress;
+import java.net.ProtocolFamily;
 import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -66,7 +66,7 @@
         LOCK.readLock().lock();
         var rsf = HOSTS_FILE_TRACKER.getReloadStatus();
         try {
-            if (!HOSTS_FILE_TRACKER.getReloadStatus().isReloadNeeded()) {
+            if (!rsf.isReloadNeeded()) {
                 return;
             }
         } finally {
@@ -118,7 +118,7 @@
 
         @Override
         public String toString() {
-            return names+"/"+address;
+            return names + "/" + address;
         }
 
         private HostFileEntry(String name, InetAddress address, boolean isHostname) {
@@ -131,7 +131,10 @@
         boolean isValid() {
             return isValid;
         }
-        boolean isHostname() {return isHostname;}
+
+        boolean isHostname() {
+            return isHostname;
+        }
 
         String getHostName() {
             return names.get(0);
@@ -210,11 +213,11 @@
     }
 
     public InetAddress getHostAddress(String hostName) throws UnknownHostException {
-        return getHostAddress(hostName, AddressFamily.ANY);
+        return getHostAddress(hostName, null);
     }
 
-    public InetAddress getHostAddress(String hostName, AddressFamily family) throws UnknownHostException {
-
+    public InetAddress getHostAddress(String hostName, ProtocolFamily family) throws UnknownHostException {
+        var af = AddressFamily.fromProtocolFamily(family);
         loadHostsAddresses();
         var map = HOST_ADDRESSES;
         var he = map.get(hostName);
@@ -222,7 +225,7 @@
             throw new UnknownHostException(hostName);
         }
         var addr = he.address;
-        if (!family.sameFamily(addr)) {
+        if (!af.sameFamily(addr)) {
             throw new UnknownHostException(hostName);
         }
         return addr;
@@ -241,14 +244,14 @@
         return entry.get().getHostName();
     }
 
-    private static boolean isAddressBytesTheSame(byte [] addr1, byte [] addr2) {
+    private static boolean isAddressBytesTheSame(byte[] addr1, byte[] addr2) {
         if (addr1 == null || addr2 == null) {
             return false;
         }
         if (addr1.length != addr2.length) {
             return false;
         }
-        for (int i=0; i<addr1.length; i++) {
+        for (int i = 0; i < addr1.length; i++) {
             if (addr1[i] != addr2[i])
                 return false;
         }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/Resolver.java	Thu Nov 07 18:44:09 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 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
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.dns.client.internal;
-
-import jdk.dns.client.ex.DnsResolverException;
-
-import java.net.UnknownHostException;
-import java.util.List;
-
-// TODO: Remove this one
-// TODO: All with close() implements AutoCloseable
-// TODO: Analyse exceptions and remove all different types if not needed to maintain code-flow
-public class Resolver implements AutoCloseable {
-    private DnsClient dnsClient;
-
-    /*
-     * Constructs a new Resolver given package oracle.dns.client.standalone;its servers and timeout parameters.
-     * Each server is of the form "server[:port]".
-     * IPv6 literal host names include delimiting brackets.
-     * There must be at least one server.
-     * "timeout" is the initial timeout interval (in ms) for UDP queries,
-     * and "retries" gives the number of retries per server.
-     */
-    public Resolver(List<String> servers, int timeout, int retries)
-            throws UnknownHostException {
-        dnsClient = new DnsClient(servers, timeout, retries);
-    }
-
-    public void close() {
-        dnsClient.close();
-        dnsClient = null;
-    }
-
-    /*
-     * Queries resource records of a particular class and type for a
-     * given domain name.
-     * Useful values of rrclass are ResourceRecord.[Q]CLASS_xxx.
-     * Useful values of rrtype are ResourceRecord.[Q]TYPE_xxx.
-     * If recursion is true, recursion is requested on the query.
-     * If auth is true, only authoritative responses are accepted.
-     */
-    public ResourceRecords query(DnsName fqdn, int rrclass, int rrtype,
-                                 boolean recursion, boolean auth)
-            throws DnsResolverException {
-        return dnsClient.query(fqdn, rrclass, rrtype, recursion, auth);
-    }
-}
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceClassType.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceClassType.java	Thu Nov 07 18:46:06 2019 +0000
@@ -25,8 +25,7 @@
 
 package jdk.dns.client.internal;
 
-import jdk.dns.client.AddressFamily;
-import jdk.dns.client.ex.DnsInvalidAttributeIdentifierException;
+import jdk.dns.client.ex.DnsResolverException;
 
 public class ResourceClassType {
     int rrclass;
@@ -49,8 +48,8 @@
         ResourceClassType[] cts;
         try {
             cts = ResourceClassType.attrIdsToClassesAndTypes(typeFromAddressFamily(addressFamily));
-        } catch (DnsInvalidAttributeIdentifierException e) {
-            return new ResourceClassType(ResourceRecord.QTYPE_STAR);
+        } catch (DnsResolverException e) {
+            return new ResourceClassType(ResourceRecord.TYPE_ANY);
         }
         return ResourceClassType.getClassAndTypeToQuery(cts);
     }
@@ -68,7 +67,7 @@
 
 
     public static ResourceClassType[] attrIdsToClassesAndTypes(String[] attrIds)
-            throws DnsInvalidAttributeIdentifierException {
+            throws DnsResolverException {
         if (attrIds == null) {
             return null;
         }
@@ -81,10 +80,10 @@
     }
 
     private static ResourceClassType fromAttrId(String attrId)
-            throws DnsInvalidAttributeIdentifierException {
+            throws DnsResolverException {
 
         if (attrId.isEmpty()) {
-            throw new DnsInvalidAttributeIdentifierException(
+            throw new DnsResolverException(
                     "Attribute ID cannot be empty");
         }
         int rrclass;
@@ -98,7 +97,7 @@
             String className = attrId.substring(0, space);
             rrclass = ResourceRecord.getRrclass(className);
             if (rrclass < 0) {
-                throw new DnsInvalidAttributeIdentifierException(
+                throw new DnsResolverException(
                         "Unknown resource record class '" + className + '\'');
             }
         }
@@ -107,7 +106,7 @@
         String typeName = attrId.substring(space + 1);
         rrtype = ResourceRecord.getType(typeName);
         if (rrtype < 0) {
-            throw new DnsInvalidAttributeIdentifierException(
+            throw new DnsResolverException(
                     "Unknown resource record type '" + typeName + '\'');
         }
 
@@ -128,7 +127,7 @@
             throw new RuntimeException("Internal DNS resolver error");
         } else if (cts.length == 0) {
             // No records are requested, but we need to ask for something.
-            rrtype = ResourceRecord.QTYPE_STAR;
+            rrtype = ResourceRecord.TYPE_ANY;
         } else {
             rrclass = ResourceRecord.CLASS_INTERNET;
             rrtype = cts[0].rrtype;
@@ -137,7 +136,7 @@
                     throw new RuntimeException("Internal error: Only CLASS_INTERNET is supported");
                 }
                 if (rrtype != cts[i].rrtype) {
-                    rrtype = ResourceRecord.QTYPE_STAR;
+                    rrtype = ResourceRecord.TYPE_ANY;
                 }
             }
         }
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecord.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecord.java	Thu Nov 07 18:46:06 2019 +0000
@@ -26,7 +26,7 @@
 package jdk.dns.client.internal;
 
 import jdk.dns.client.ex.DnsCommunicationException;
-import jdk.dns.client.ex.DnsInvalidNameException;
+import jdk.dns.client.ex.DnsResolverException;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -48,7 +48,7 @@
     public static final int TYPE_CNAME = 5;
     public static final int TYPE_AAAA = 28;
     public static final int TYPE_PTR = 12;
-    static final int QTYPE_STAR = 255;          // query type "*"
+    public static final int TYPE_ANY = 255;          // query type "*"
 
     private static final int TYPE_NS = 2;
     private static final int TYPE_SOA = 6;
@@ -57,7 +57,6 @@
     private static final int TYPE_TXT = 16;
     private static final int TYPE_SRV = 33;
     private static final int TYPE_NAPTR = 35;
-    //static final int QTYPE_AXFR = 252;          // zone transfer //Do not want to support that
 
     /*
      * Mapping from resource record type codes to type name strings.
@@ -183,7 +182,7 @@
         String name = null;
         if ((val > 0) && (val < names.length)) {
             name = names[val];
-        } else if (val == QTYPE_STAR) {         // QTYPE_STAR == QCLASS_STAR
+        } else if (val == TYPE_ANY) {         // QTYPE_STAR == QCLASS_STAR
             name = "*";
         }
         if (name == null) {
@@ -196,7 +195,7 @@
         if (name.isEmpty()) {
             return -1;                          // invalid name
         } else if (name.equals("*")) {
-            return QTYPE_STAR;                  // QTYPE_STAR == QCLASS_STAR
+            return TYPE_ANY;                  // QTYPE_STAR == QCLASS_STAR
         }
         if (Character.isDigit(name.charAt(0))) {
             try {
@@ -343,7 +342,7 @@
                 } else
                     throw new IOException("Invalid label type: " + typeAndLen);
             }
-        } catch (IOException | DnsInvalidNameException e) {
+        } catch (IOException | DnsResolverException e) {
             DnsCommunicationException ce = new DnsCommunicationException(
                     "DNS error: malformed packet");
             ce.initCause(e);
--- a/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecords.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/jdk/dns/client/internal/ResourceRecords.java	Thu Nov 07 18:46:06 2019 +0000
@@ -51,12 +51,20 @@
     // TODO: Try to reuse additional section to avoid additional CNAME lookups
     public Vector<ResourceRecord> additional = new Vector<>();
 
+    private boolean hasIpv4InAnswer;
+    private boolean hasIpv6InAnswer;
+    private boolean hasAliasInAnswer;
 
-    public boolean hasAddressOrAlias() {
-        return answer.stream().anyMatch(
-                rr -> rr.rrtype == ResourceRecord.TYPE_A
-                        || rr.rrtype == ResourceRecord.TYPE_AAAA
-                        || rr.rrtype == ResourceRecord.TYPE_CNAME);
+
+    public boolean hasAddressesOrAlias() {
+        return hasIpv4InAnswer || hasIpv6InAnswer || hasAliasInAnswer;
+    }
+
+    public boolean hasAddressOfFamily(AddressFamily af) {
+        if (af == AddressFamily.ANY) {
+            return hasIpv6InAnswer || hasIpv4InAnswer;
+        }
+        return af == AddressFamily.IPv4 ? hasIpv4InAnswer : hasIpv6InAnswer;
     }
 
     /*
@@ -82,28 +90,6 @@
     }
 
     /*
-     * Returns the type field of the first answer record, or -1 if
-     * there are no answer records.
-     */
-    int getFirstAnsType() {
-        if (answer.size() == 0) {
-            return -1;
-        }
-        return answer.firstElement().getType();
-    }
-
-    /*
-     * Returns the type field of the last answer record, or -1 if
-     * there are no answer records.
-     */
-    int getLastAnsType() {
-        if (answer.size() == 0) {
-            return -1;
-        }
-        return answer.lastElement().getType();
-    }
-
-    /*
      * Decodes the resource records in a DNS message and adds
      * them to this object.
      * Does not modify or store a reference to the msg array.
@@ -125,6 +111,9 @@
             for (int i = 0; i < hdr.numAnswers; i++) {
                 rr = new ResourceRecord(
                         msg, msgLen, pos, false, !zoneXfer);
+                hasIpv4InAnswer |= rr.rrtype == ResourceRecord.TYPE_A;
+                hasIpv6InAnswer |= rr.rrtype == ResourceRecord.TYPE_AAAA;
+                hasAliasInAnswer |= rr.rrtype == ResourceRecord.TYPE_CNAME;
                 answer.addElement(rr);
                 pos += rr.size();
             }
--- a/src/jdk.dns.client/share/classes/module-info.java	Thu Nov 07 18:44:09 2019 +0000
+++ b/src/jdk.dns.client/share/classes/module-info.java	Thu Nov 07 18:46:06 2019 +0000
@@ -30,6 +30,5 @@
 module jdk.dns.client {
     exports jdk.dns.client;
     exports jdk.dns.client.ex;
-    exports jdk.dns.client.internal; // For debugging
     provides InetAddress.NameService with DnsNameService;
 }