DnsClient now throws UnknownHostException if there is no DNS resolution.

Added a default timeout for DNS queries/etc + method with the timeout
 as a parameter
This commit is contained in:
nathan 2018-01-11 14:58:35 +01:00
parent 542092a7b3
commit 6d7ccb322e

View File

@ -27,6 +27,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -72,19 +73,23 @@ import io.netty.util.internal.PlatformDependent;
public public
class DnsClient extends EndPoint { class DnsClient extends EndPoint {
/* /*
http://bugs.java.com/view_bug.do?bug_id=8176361 * TODO: verify ResolverConfiguration works as expected!
Previous JDK releases documented how to configure `java.net.InetAddress` to use the JNDI DNS service provider as the name service. This mechanism, and the system properties to configure it, have been removed in JDK 9 * http://bugs.java.com/view_bug.do?bug_id=8176361
* Previous JDK releases documented how to configure `java.net.InetAddress` to use the JNDI DNS service provider as the name service.
A new mechanism to configure the use of a hosts file has been introduced. * This mechanism, and the system properties to configure it, have been removed in JDK 9
*
A new system property `jdk.net.hosts.file` has been defined. When this system property is set, the name and address resolution calls of `InetAddress`, i.e `getByXXX`, retrieve the relevant mapping from the specified file. The structure of this file is equivalent to that of the `/etc/hosts` file. * A new mechanism to configure the use of a hosts file has been introduced.
*
When the system property `jdk.net.hosts.file` is set, and the specified file doesn't exist, the name or address lookup will result in an UnknownHostException. Thus, a non existent hosts file is handled as if the file is empty. * A new system property `jdk.net.hosts.file` has been defined. When this system property is set, the name and address resolution calls
* of `InetAddress`, i.e `getByXXX`, retrieve the relevant mapping from the specified file. The structure of this file is equivalent to
* that of the `/etc/hosts` file.
UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfiguration *
*/ * When the system property `jdk.net.hosts.file` is set, and the specified file doesn't exist, the name or address lookup will result in
* an UnknownHostException. Thus, a non existent hosts file is handled as if the file is empty.
*
* UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfiguration
*/
/** /**
@ -108,7 +113,6 @@ UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfig
*/ */
public final static List<InetSocketAddress> DEFAULT_DNS_SERVER_LIST = DefaultDnsServerAddressStreamProvider.defaultAddressList(); public final static List<InetSocketAddress> DEFAULT_DNS_SERVER_LIST = DefaultDnsServerAddressStreamProvider.defaultAddressList();
private static final String ptrSuffix = ".in-addr.arpa";
public static final InetAddress[] INET_ADDRESSES = new InetAddress[0]; public static final InetAddress[] INET_ADDRESSES = new InetAddress[0];
/** /**
@ -602,41 +606,87 @@ UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfig
/** /**
* Resolves a specific hostname A/AAAA record. * Resolves a specific hostname A/AAAA record with the default timeout of 5 seconds
* *
* @param hostname the hostname, ie: google.com, that you want to resolve * @param hostname the hostname, ie: google.com, that you want to resolve
* @return the list of resolved InetAddress or null *
* @return the list of resolved InetAddress or throws an exception if the hostname cannot be resolved
* @throws UnknownHostException if the hostname cannot be resolved * @throws UnknownHostException if the hostname cannot be resolved
*/ */
public public
List<InetAddress> resolve(String hostname) throws UnknownHostException { List<InetAddress> resolve(String hostname) throws UnknownHostException {
return resolve(hostname, 5);
}
/**
* Resolves a specific hostname A/AAAA record.
*
* @param hostname the hostname, ie: google.com, that you want to resolve
* @param queryTimeoutSeconds the number of seconds to wait for host resolution
*
* @return the list of resolved InetAddress or throws an exception if the hostname cannot be resolved
* @throws UnknownHostException if the hostname cannot be resolved
*/
public
List<InetAddress> resolve(String hostname, int queryTimeoutSeconds) throws UnknownHostException {
if (hostname == null) {
throw new UnknownHostException("Cannot submit query for an unknown host");
}
if (resolver == null) { if (resolver == null) {
start(); start();
} }
// use "resolve", since it handles A/AAAA records + redirects correctly // use "resolve", since it handles A/AAAA records + redirects correctly
final Future<List<InetAddress>> resolve = resolver.resolveAll(hostname); final Future<List<InetAddress>> resolve = resolver.resolveAll(hostname);
final Future<List<InetAddress>> result = resolve.awaitUninterruptibly();
boolean finished = resolve.awaitUninterruptibly(queryTimeoutSeconds, TimeUnit.SECONDS);
// now return whatever value we had // now return whatever value we had
if (result.isSuccess() && result.isDone()) { if (finished && resolve.isSuccess() && resolve.isDone()) {
try { try {
List<InetAddress> now = result.getNow(); List<InetAddress> now = resolve.getNow();
return now; return now;
} catch (Exception e) { } catch (Exception e) {
logger.error("Could not ask question to DNS server", e); String msg = "Could not ask question to DNS server";
return null; logger.error(msg, e);
throw new UnknownHostException(msg);
} }
} }
logger.error("Could not ask question to DNS server for A/AAAA record: {}", hostname); String msg = "Could not ask question to DNS server for A/AAAA record: {}";
logger.error(msg, hostname);
UnknownHostException cause = (UnknownHostException) result.cause(); UnknownHostException cause = (UnknownHostException) resolve.cause();
if (cause != null) { if (cause != null) {
throw cause; throw cause;
} }
return null; throw new UnknownHostException(msg);
}
/**
* Resolves a specific hostname record, of the specified type (PTR, MX, TXT, etc) with the default timeout of 5 seconds
* <p/>
* <p/>
* Note: PTR queries absolutely MUST end in '.in-addr.arpa' in order for the DNS server to understand it.
* -- because of this, we will automatically fix this in case that clients are unaware of this requirement
* <p/>
* <p/>
* Note: A/AAAA queries absolutely MUST end in a '.' -- because of this we will automatically fix this in case that clients are
* unaware of this requirement
*
* @param hostname the hostname, ie: google.com, that you want to resolve
* @param type the DnsRecordType you want to resolve (PTR, MX, TXT, etc)
*
* @return the DnsRecords or throws an exception if the hostname cannot be resolved
*
* @throws @throws UnknownHostException if the hostname cannot be resolved
*/
@SuppressWarnings({"unchecked", "Duplicates"})
public
DnsRecord[] query(String hostname, final int type) throws UnknownHostException {
return query(hostname, type, 5);
} }
/** /**
@ -652,32 +702,32 @@ UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfig
* *
* @param hostname the hostname, ie: google.com, that you want to resolve * @param hostname the hostname, ie: google.com, that you want to resolve
* @param type the DnsRecordType you want to resolve (PTR, MX, TXT, etc) * @param type the DnsRecordType you want to resolve (PTR, MX, TXT, etc)
* @return the DnsRecords or null if there was an error resolving the hostname * @param queryTimeoutSeconds the number of seconds to wait for host resolution
*
* @return the DnsRecords or throws an exception if the hostname cannot be resolved
* *
* @throws @throws UnknownHostException if the hostname cannot be resolved * @throws @throws UnknownHostException if the hostname cannot be resolved
*/ */
@SuppressWarnings({"unchecked", "Duplicates"}) @SuppressWarnings({"unchecked", "Duplicates"})
public public
DnsRecord[] query(String hostname, final int type) throws UnknownHostException { DnsRecord[] query(String hostname, final int type, int queryTimeoutSeconds) throws UnknownHostException {
if (resolver == null) { if (hostname == null) {
start(); throw new UnknownHostException("Cannot submit query for an unknown host");
} }
if (type == DnsRecordType.PTR && !hostname.endsWith(ptrSuffix)) { if (resolver == null) {
// PTR absolutely MUST end in '.in-addr.arpa' in order for the DNS server to understand it. start();
// in this case, hostname is an ip address
hostname += ptrSuffix;
} }
// we use our own resolvers // we use our own resolvers
DnsQuestion dnsMessage = DnsQuestion.newQuery(hostname, type, recursionDesired); DnsQuestion dnsMessage = DnsQuestion.newQuery(hostname, type, recursionDesired);
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query = resolver.query(dnsMessage); final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query = resolver.query(dnsMessage);
final Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> result = query.awaitUninterruptibly(); boolean finished = query.awaitUninterruptibly(queryTimeoutSeconds, TimeUnit.SECONDS);
// now return whatever value we had // now return whatever value we had
if (result.isSuccess() && result.isDone()) { if (finished && query.isSuccess() && query.isDone()) {
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = result.getNow(); AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = query.getNow();
DnsResponse response = envelope.content(); DnsResponse response = envelope.content();
try { try {
final int code = response.getHeader().getRcode(); final int code = response.getHeader().getRcode();
@ -685,24 +735,24 @@ UP UNTIL java 1.8, one can use org/xbill/DNS/spi, ie: sun.net.dns.ResolverConfig
return response.getSectionArray(DnsSection.ANSWER); return response.getSectionArray(DnsSection.ANSWER);
} }
logger.error("Could not ask question to DNS server: Error code {} for type: {} - {}", String msg = "Could not ask question to DNS server: Error code " + code + " for type: " + type + " - " + DnsRecordType.string(type);
code, type, DnsRecordType.string(type)); logger.error(msg);
logger.error("Could not ask question to DNS server: Error code {}", code); throw new UnknownHostException(msg);
return null;
} finally { } finally {
response.release(); response.release();
} }
} }
logger.error("Could not ask question to DNS server for type: {}", DnsRecordType.string(type)); String msg = "Could not ask question to DNS server for type: " + DnsRecordType.string(type);
logger.error(msg);
UnknownHostException cause = (UnknownHostException) result.cause(); UnknownHostException cause = (UnknownHostException) query.cause();
if (cause != null) { if (cause != null) {
throw cause; throw cause;
} }
return null; throw new UnknownHostException(msg);
} }
} }