Fixed illegal reflective access by not relying on a sun.net.dns class. (#8318) (#8319)

Motivation

Applications should not depend on internal packages with Java 9 and later. This cause a warning now, but will break in future versions of Java.

Modification

This change adds methods to UnixResolverDnsServerAddressStreamProvider (following after #6844) that parse /etc/resolv.conf for domain and search entries. Then DnsNameResolver does not need to rely on sun.net.dns.ResolverConfiguration to do this.

Result

Fixes #8318. Furthermore, at least in my testing with Java 11, this also makes multiple search entries work properly (previously I was only getting the first entry).
This commit is contained in:
Matt Ayres 2018-09-26 11:55:46 -07:00 committed by Norman Maurer
parent 618a98fdb5
commit ba594bcf4a
3 changed files with 108 additions and 7 deletions

View File

@ -125,13 +125,9 @@ public class DnsNameResolver extends InetNameResolver {
static {
String[] searchDomains;
try {
Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
Method open = configClass.getMethod("open");
Method nameservers = configClass.getMethod("searchlist");
Object instance = open.invoke(null);
@SuppressWarnings("unchecked")
List<String> list = (List<String>) nameservers.invoke(instance);
List<String> list = PlatformDependent.isWindows()
? getSearchDomainsHack()
: UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains();
searchDomains = list.toArray(new String[0]);
} catch (Exception ignore) {
// Failed to get the system name search domain list.
@ -148,6 +144,18 @@ public class DnsNameResolver extends InetNameResolver {
DEFAULT_NDOTS = ndots;
}
@SuppressWarnings("unchecked")
private static List<String> getSearchDomainsHack() throws Exception {
// This code on Java 9+ yields a warning about illegal reflective access that will be denied in
// a future release. There doesn't seem to be a better way to get search domains for Windows yet.
Class<?> configClass = Class.forName("sun.net.dns.ResolverConfiguration");
Method open = configClass.getMethod("open");
Method nameservers = configClass.getMethod("searchlist");
Object instance = open.invoke(null);
return (List<String>) nameservers.invoke(instance);
}
private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder();
private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder();

View File

@ -27,7 +27,9 @@ import java.io.FileReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -51,6 +53,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
private static final String SORTLIST_ROW_LABEL = "sortlist";
private static final String OPTIONS_ROW_LABEL = "options";
private static final String DOMAIN_ROW_LABEL = "domain";
private static final String SEARCH_ROW_LABEL = "search";
private static final String PORT_ROW_LABEL = "port";
private static final String NDOTS_LABEL = "ndots:";
static final int DEFAULT_NDOTS = 1;
@ -284,4 +287,57 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
}
return DEFAULT_NDOTS;
}
/**
* Parse a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return the
* list of search domains found in it or an empty list if not found.
* @return List of search domains.
* @throws IOException If a failure occurs parsing the file.
*/
static List<String> parseEtcResolverSearchDomains() throws IOException {
return parseEtcResolverSearchDomains(new File(ETC_RESOLV_CONF_FILE));
}
/**
* Parse a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return the
* list of search domains found in it or an empty list if not found.
* @param etcResolvConf a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>.
* @return List of search domains.
* @throws IOException If a failure occurs parsing the file.
*/
static List<String> parseEtcResolverSearchDomains(File etcResolvConf) throws IOException {
String localDomain = null;
List<String> searchDomains = new ArrayList<String>();
FileReader fr = new FileReader(etcResolvConf);
BufferedReader br = null;
try {
br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
if (localDomain == null && line.startsWith(DOMAIN_ROW_LABEL)) {
int i = indexOfNonWhiteSpace(line, DOMAIN_ROW_LABEL.length());
if (i >= 0) {
localDomain = line.substring(i);
}
} else if (line.startsWith(SEARCH_ROW_LABEL)) {
int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length());
if (i >= 0) {
searchDomains.add(line.substring(i));
}
}
}
} finally {
if (br == null) {
fr.close();
} else {
br.close();
}
}
// return what was on the 'domain' line only if there were no 'search' lines
return localDomain != null && searchDomains.isEmpty()
? Collections.singletonList(localDomain)
: searchDomains;
}
}

View File

@ -25,6 +25,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.DEFAULT_NDOTS;
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverFirstNdots;
@ -111,6 +114,40 @@ public class UnixResolverDnsServerAddressStreamProviderTest {
assertHostNameEquals("127.0.0.2", stream.next());
}
@Test
public void searchDomainsWithOnlyDomain() throws IOException {
File f = buildFile("domain linecorp.local\n" +
"nameserver 127.0.0.2\n");
List<String> domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f);
assertEquals(Collections.singletonList("linecorp.local"), domains);
}
@Test
public void searchDomainsWithOnlySearch() throws IOException {
File f = buildFile("search linecorp.local\n" +
"nameserver 127.0.0.2\n");
List<String> domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f);
assertEquals(Collections.singletonList("linecorp.local"), domains);
}
@Test
public void searchDomainsWithMultipleSearch() throws IOException {
File f = buildFile("search linecorp.local\n" +
"search squarecorp.local\n" +
"nameserver 127.0.0.2\n");
List<String> domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f);
assertEquals(Arrays.asList("linecorp.local", "squarecorp.local"), domains);
}
@Test
public void searchDomainsPrecedence() throws IOException {
File f = buildFile("domain linecorp.local\n" +
"search squarecorp.local\n" +
"nameserver 127.0.0.2\n");
List<String> domains = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains(f);
assertEquals(Collections.singletonList("squarecorp.local"), domains);
}
private File buildFile(String contents) throws IOException {
File f = folder.newFile();
OutputStream out = new FileOutputStream(f);