Repository: netty/netty
Issue: DnsNameResolver uses public dns servers in java 9
Labels: improvement

I realise this is known, but it seems broken enough to deserve a tracking issue.

### Expected behavior

Should use system dns servers 

### Actual behavior

Logs a warning "Default DNS servers: [/8.8.8.8:53, /8.8.4.4:53] (Google Public DNS as a fallback)"
Then uses the google servers.

n.b. It logs the warning even *if* you manually set the nameServerAddresses

        .nameServerAddresses(
            DnsServerAddresses.sequential(new InetSocketAddress("x.x.x.x", 53),
                new InetSocketAddress("x.x.x.x", 53)))

### Steps to reproduce

### Minimal yet complete reproducer code (or URL to code)

### Netty version

### JVM version (e.g. `java -version`)

java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+149)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+149, mixed mode)

### OS version (e.g. `uname -a`)


### Workaround

set on command line

--add-opens=java.base/sun.net.dns=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED  --add-opens=java.base/java.nio=ALL-UNNAMED  --add-opens=java.base/sun.nio.ch=ALL-UNNAMED

Issue comments:
Somewhat related with Java 9 without these options I get

```
java.lang.NullPointerException
	at io.netty.resolver.dns.DnsNameResolver.<init>(DnsNameResolver.java:250)
	at io.netty.resolver.dns.DnsNameResolverBuilder.build(DnsNameResolverBuilder.java:347)
	at com.baulsupp.oksocial.network.NettyDns.<init>(NettyDns.java:49)
	at com.baulsupp.oksocial.network.NettyDns.byName(NettyDns.java:92)
	at com.baulsupp.oksocial.Main.createClientBuilder(Main.java:589)
	at com.baulsupp.oksocial.Main.initialise(Main.java:425)
	at com.baulsupp.oksocial.Main.run(Main.java:277)
	at com.baulsupp.oksocial.Main.main(Main.java:106)
	at com.baulsupp.oksocial.TestMain.main(TestMain.java:5)
```

Because b.register() returns a failed ChannelFuture with null channel

```
        channelFuture = responseHandler.channelActivePromise;
        ch = (DatagramChannel) b.register().channel();
        ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
```
@yschimke thanks for reporting... working on a fix
@yschimke fixed by https://github.com/netty/netty/pull/6590 . Please check
As commented on the other issue, this worked for me
should have just commented...
Fixed by https://github.com/netty/netty/pull/6590

Pull request review comments:
How about: `Skipping a malformed nameserver URI: {}` ?
done
Not possible as the constructor expects a `Hashtable` 👎 
nope...
![MAJOR](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/severity-major.png) Extract this nested try block into a separate method. [![rule](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/rule.png)](https://garage.netty.io/sonarqube/coding_rules#rule_key=squid%3AS1141)

![MAJOR](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/severity-major.png) Replace the synchronized class "Hashtable" by an unsynchronized one such as "HashMap". [![rule](https://raw.githubusercontent.com/SonarCommunity/sonar-github/master/images/rule.png)](https://garage.netty.io/sonarqube/coding_rules#rule_key=squid%3AS1149)


Patch that resolved the issue:
diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
index 505ecfb4ae2..b7c5bd719fa 100644
--- a/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
+++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DefaultDnsServerAddressStreamProvider.java
@@ -20,10 +20,17 @@
 import io.netty.util.internal.logging.InternalLogger;
 import io.netty.util.internal.logging.InternalLoggerFactory;
 
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
 import java.lang.reflect.Method;
 import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Hashtable;
 import java.util.List;
 
 import static io.netty.resolver.dns.DnsServerAddresses.sequential;
@@ -47,22 +54,48 @@ public final class DefaultDnsServerAddressStreamProvider implements DnsServerAdd
 
     static {
         final List<InetSocketAddress> defaultNameServers = new ArrayList<InetSocketAddress>(2);
+
+        // Using jndi-dns to obtain the default name servers.
+        //
+        // See:
+        // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
+        // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
+        env.put("java.naming.provider.url", "dns://");
         try {
-            Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
-            Method open = configClass.getMethod("open");
-            Method nameservers = configClass.getMethod("nameservers");
-            Object instance = open.invoke(null);
-
-            @SuppressWarnings("unchecked")
-            final List<String> list = (List<String>) nameservers.invoke(instance);
-            for (String a: list) {
-                if (a != null) {
-                    defaultNameServers.add(new InetSocketAddress(SocketUtils.addressByName(a), DNS_PORT));
+            DirContext ctx = new InitialDirContext(env);
+            String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url");
+            String[] servers = dnsUrls.split(" ");
+            for (String server : servers) {
+                try {
+                    defaultNameServers.add(SocketUtils.socketAddress(new URI(server).getHost(), DNS_PORT));
+                } catch (URISyntaxException e) {
+                    logger.debug("Skipping a malformed nameserver URI: {}", server, e);
+                }
+            }
+        } catch (NamingException ignore) {
+            // Will try reflection if this fails.
+        }
+
+        if (defaultNameServers.isEmpty()) {
+            try {
+                Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
+                Method open = configClass.getMethod("open");
+                Method nameservers = configClass.getMethod("nameservers");
+                Object instance = open.invoke(null);
+
+                @SuppressWarnings("unchecked")
+                final List<String> list = (List<String>) nameservers.invoke(instance);
+                for (String a: list) {
+                    if (a != null) {
+                        defaultNameServers.add(new InetSocketAddress(SocketUtils.addressByName(a), DNS_PORT));
+                    }
                 }
+            } catch (Exception ignore) {
+                // Failed to get the system name server list via reflection.
+                // Will add the default name servers afterwards.
             }
-        } catch (Exception ignore) {
-            // Failed to get the system name server list.
-            // Will add the default name servers afterwards.
         }
 
         if (!defaultNameServers.isEmpty()) {


Test patch: