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 InetSocketAddress[] DEFAULT_NAME_SERVER_ARRAY;
54      private static final DnsServerAddresses DEFAULT_NAME_SERVERS;
55      static final int DNS_PORT = 53;
56  
57      static {
58          final List<InetSocketAddress> defaultNameServers = new ArrayList<InetSocketAddress>(2);
59  
60          // Using jndi-dns to obtain the default name servers.
61          //
62          // See:
63          // - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
64          // - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
65          Hashtable<String, String> env = new Hashtable<String, String>();
66          env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
67          env.put("java.naming.provider.url", "dns://");
68          try {
69              DirContext ctx = new InitialDirContext(env);
70              String dnsUrls = (String) ctx.getEnvironment().get("java.naming.provider.url");
71              // Only try if not empty as otherwise we will produce an exception
72              if (dnsUrls != null && !dnsUrls.isEmpty()) {
73                  String[] servers = dnsUrls.split(" ");
74                  for (String server : servers) {
75                      try {
76                          URI uri = new URI(server);
77                          String host = new URI(server).getHost();
78  
79                          if (host == null || host.isEmpty()) {
80                              logger.debug(
81                                      "Skipping a nameserver URI as host portion could not be extracted: {}", server);
82                              // If the host portion can not be parsed we should just skip this entry.
83                              continue;
84                          }
85                          int port  = uri.getPort();
86                          defaultNameServers.add(SocketUtils.socketAddress(uri.getHost(), port == -1 ? DNS_PORT : port));
87                      } catch (URISyntaxException e) {
88                          logger.debug("Skipping a malformed nameserver URI: {}", server, e);
89                      }
90                  }
91              }
92          } catch (NamingException ignore) {
93              // Will try reflection if this fails.
94          }
95  
96          if (defaultNameServers.isEmpty()) {
97              try {
98                  Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
99                  Method open = configClass.getMethod("open");
100                 Method nameservers = configClass.getMethod("nameservers");
101                 Object instance = open.invoke(null);
102 
103                 @SuppressWarnings("unchecked")
104                 final List<String> list = (List<String>) nameservers.invoke(instance);
105                 for (String a: list) {
106                     if (a != null) {
107                         defaultNameServers.add(new InetSocketAddress(SocketUtils.addressByName(a), DNS_PORT));
108                     }
109                 }
110             } catch (Exception ignore) {
111                 // Failed to get the system name server list via reflection.
112                 // Will add the default name servers afterwards.
113             }
114         }
115 
116         if (!defaultNameServers.isEmpty()) {
117             if (logger.isDebugEnabled()) {
118                 logger.debug(
119                         "Default DNS servers: {} (sun.net.dns.ResolverConfiguration)", defaultNameServers);
120             }
121         } else {
122             // Depending if IPv6 or IPv4 is used choose the correct DNS servers provided by google:
123             // https://developers.google.com/speed/public-dns/docs/using
124             // https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html
125             if (NetUtil.isIpV6AddressesPreferred() ||
126                     (NetUtil.LOCALHOST instanceof Inet6Address && !NetUtil.isIpV4StackPreferred())) {
127                 Collections.addAll(
128                         defaultNameServers,
129                         SocketUtils.socketAddress("2001:4860:4860::8888", DNS_PORT),
130                         SocketUtils.socketAddress("2001:4860:4860::8844", DNS_PORT));
131             } else {
132                 Collections.addAll(
133                         defaultNameServers,
134                         SocketUtils.socketAddress("8.8.8.8", DNS_PORT),
135                         SocketUtils.socketAddress("8.8.4.4", DNS_PORT));
136             }
137 
138             if (logger.isWarnEnabled()) {
139                 logger.warn(
140                         "Default DNS servers: {} (Google Public DNS as a fallback)", defaultNameServers);
141             }
142         }
143 
144         DEFAULT_NAME_SERVER_LIST = Collections.unmodifiableList(defaultNameServers);
145         DEFAULT_NAME_SERVER_ARRAY = defaultNameServers.toArray(new InetSocketAddress[defaultNameServers.size()]);
146         DEFAULT_NAME_SERVERS = sequential(DEFAULT_NAME_SERVER_ARRAY);
147     }
148 
149     private DefaultDnsServerAddressStreamProvider() {
150     }
151 
152     @Override
153     public DnsServerAddressStream nameServerAddressStream(String hostname) {
154         return DEFAULT_NAME_SERVERS.stream();
155     }
156 
157     /**
158      * Returns the list of the system DNS server addresses. If it failed to retrieve the list of the system DNS server
159      * addresses from the environment, it will return {@code "8.8.8.8"} and {@code "8.8.4.4"}, the addresses of the
160      * Google public DNS servers.
161      */
162     public static List<InetSocketAddress> defaultAddressList() {
163         return DEFAULT_NAME_SERVER_LIST;
164     }
165 
166     /**
167      * Returns the {@link DnsServerAddresses} that yields the system DNS server addresses sequentially. If it failed to
168      * retrieve the list of the system DNS server addresses from the environment, it will use {@code "8.8.8.8"} and
169      * {@code "8.8.4.4"}, the addresses of the Google public DNS servers.
170      * <p>
171      * This method has the same effect with the following code:
172      * <pre>
173      * DnsServerAddresses.sequential(DnsServerAddresses.defaultAddressList());
174      * </pre>
175      * </p>
176      */
177     public static DnsServerAddresses defaultAddresses() {
178         return DEFAULT_NAME_SERVERS;
179     }
180 
181     /**
182      * Get the array form of {@link #defaultAddressList()}.
183      * @return The array form of {@link #defaultAddressList()}.
184      */
185     static InetSocketAddress[] defaultAddressArray() {
186         return DEFAULT_NAME_SERVER_ARRAY.clone();
187     }
188 }