View Javadoc
1   /*
2    * Copyright 2017 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.resolver.dns;
17  
18  import io.netty.util.NetUtil;
19  import io.netty.util.internal.SocketUtils;
20  import io.netty.util.internal.UnstableApi;
21  import io.netty.util.internal.logging.InternalLogger;
22  import io.netty.util.internal.logging.InternalLoggerFactory;
23  
24  import javax.naming.Context;
25  import javax.naming.NamingException;
26  import javax.naming.directory.DirContext;
27  import javax.naming.directory.InitialDirContext;
28  import java.lang.reflect.Method;
29  import java.net.Inet6Address;
30  import java.net.InetSocketAddress;
31  import java.net.URI;
32  import java.net.URISyntaxException;
33  import java.util.ArrayList;
34  import java.util.Collections;
35  import java.util.Hashtable;
36  import java.util.List;
37  
38  import static io.netty.resolver.dns.DnsServerAddresses.sequential;
39  
40  /**
41   * A {@link DnsServerAddressStreamProvider} which will use predefined default DNS servers to use for DNS resolution.
42   * These defaults do not respect your host's machines defaults.
43   * <p>
44   * This may use the JDK's blocking DNS resolution to bootstrap the default DNS server addresses.
45   */
46  @UnstableApi
47  public final class DefaultDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
48      private static final InternalLogger logger =
49              InternalLoggerFactory.getInstance(DefaultDnsServerAddressStreamProvider.class);
50      public static final DefaultDnsServerAddressStreamProvider INSTANCE = new DefaultDnsServerAddressStreamProvider();
51  
52      private static final List<InetSocketAddress> DEFAULT_NAME_SERVER_LIST;
53      private static final DnsServerAddresses DEFAULT_NAME_SERVERS;
54      static final int DNS_PORT = 53;
55  
56      static {
57          final List<InetSocketAddress> defaultNameServers = new ArrayList<InetSocketAddress>(2);
58  
59          // Using jndi-dns to obtain the default name servers.
60          //
61          // See:
62          // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
63          // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
64          Hashtable<String, String> env = new Hashtable<String, String>();
65          env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
66          env.put("java.naming.provider.url", "dns://");
67          try {
68              DirContext ctx = new InitialDirContext(env);
69              String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url");
70              // Only try if not empty as otherwise we will produce an exception
71              if (dnsUrls != null && !dnsUrls.isEmpty()) {
72                  String[] servers = dnsUrls.split(" ");
73                  for (String server : servers) {
74                      try {
75                          URI uri = new URI(server);
76                          String host = new URI(server).getHost();
77  
78                          if (host == null || host.isEmpty()) {
79                              logger.debug(
80                                      "Skipping a nameserver URI as host portion could not be extracted: {}", server);
81                              // If the host portion can not be parsed we should just skip this entry.
82                              continue;
83                          }
84                          int port  = uri.getPort();
85                          defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ? DNS_PORT : port));
86                      } catch (URISyntaxException e) {
87                          logger.debug("Skipping a malformed nameserver URI: {}", server, e);
88                      }
89                  }
90              }
91          } catch (NamingException ignore) {
92              // Will try reflection if this fails.
93          }
94  
95          if (defaultNameServers.isEmpty()) {
96              try {
97                  Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
98                  Method open = configClass.getMethod("open");
99                  Method nameservers = configClass.getMethod("nameservers");
100                 Object instance = open.invoke(null);
101 
102                 @SuppressWarnings("unchecked")
103                 final List<String> list = (List<String>) nameservers.invoke(instance);
104                 for (String a: list) {
105                     if (a != null) {
106                         defaultNameServers.add(new InetSocketAddress(SocketUtils.addressByName(a), DNS_PORT));
107                     }
108                 }
109             } catch (Exception ignore) {
110                 // Failed to get the system name server list via reflection.
111                 // Will add the default name servers afterwards.
112             }
113         }
114 
115         if (!defaultNameServers.isEmpty()) {
116             if (logger.isDebugEnabled()) {
117                 logger.debug(
118                         "Default DNS servers: {} (sun.net.dns.ResolverConfiguration)", defaultNameServers);
119             }
120         } else {
121             // Depending if IPv6 or IPv4 is used choose the correct DNS servers provided by google:
122             // https://developers.google.com/speed/public-dns/docs/using
123             // https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html
124             if (NetUtil.isIpV6AddressesPreferred() ||
125                     (NetUtil.LOCALHOST instanceof Inet6Address && !NetUtil.isIpV4StackPreferred())) {
126                 Collections.addAll(
127                         defaultNameServers,
128                         SocketUtils.socketAddress("2001:4860:4860::8888", DNS_PORT),
129                         SocketUtils.socketAddress("2001:4860:4860::8844", DNS_PORT));
130             } else {
131                 Collections.addAll(
132                         defaultNameServers,
133                         SocketUtils.socketAddress("8.8.8.8", DNS_PORT),
134                         SocketUtils.socketAddress("8.8.4.4", DNS_PORT));
135             }
136 
137             if (logger.isWarnEnabled()) {
138                 logger.warn(
139                         "Default DNS servers: {} (Google Public DNS as a fallback)", defaultNameServers);
140             }
141         }
142 
143         DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers);
144         DEFAULT_NAME_SERVERS = sequential(DEFAULT_NAME_SERVER_LIST);
145     }
146 
147     private DefaultDnsServerAddressStreamProvider() {
148     }
149 
150     @Override
151     public DnsServerAddressStream nameServerAddressStream(String hostname) {
152         return DEFAULT_NAME_SERVERS.stream();
153     }
154 
155     /**
156      * Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server
157      * addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the
158      * Google public DNS servers.
159      */
160     public static List<InetSocketAddress> defaultAddressList() {
161         return DEFAULT_NAME_SERVER_LIST;
162     }
163 
164     /**
165      * Returns the {@link DnsServerAddresses} that yields the system DNS server addresses sequentially. If it failed to
166      * retrieve the list of the system DNS server addresses from the environment, it will use {@code "8.8.8.8"} and
167      * {@code "8.8.4.4"}, the addresses of the Google public DNS servers.
168      * <p>
169      * This method has the same effect with the following code:
170      * <pre>
171      * DnsServerAddresses.sequential(DnsServerAddresses.defaultAddressList());
172      * </pre>
173      * </p>
174      */
175     public static DnsServerAddresses defaultAddresses() {
176         return DEFAULT_NAME_SERVERS;
177     }
178 }