DnsNameResolver should not bind locally. Fixes #5457 Motivation: Dns resolution failures happen when using the DnsNameResolver and the JVM is not authorized to bind datagram channels. The current DnsNameResolver binds locally a DatagramChannel which is not necessary (and not always permitted).

Modifications:
The DnsNameResolver Bootstrap does not bind anymore, instead it registers and use the channel directly. The localAddress has also been removed from the DnsAddressResolverGroup, DnsNameResolver and DnsNameResolverBuilder as it is not necessary anymore and the API is marked as @UnstableApi.

Result:
Dns resolution does not require anymore to bind locally.
This commit is contained in:
Julien Viet 2016-06-28 11:44:04 +02:00 committed by Norman Maurer
parent 5e64985089
commit 804e058e27
4 changed files with 37 additions and 59 deletions

View File

@ -34,7 +34,6 @@ import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import static io.netty.resolver.dns.DnsNameResolver.ANY_LOCAL_ADDR;
import static io.netty.util.internal.PlatformDependent.newConcurrentHashMap; import static io.netty.util.internal.PlatformDependent.newConcurrentHashMap;
/** /**
@ -44,33 +43,21 @@ import static io.netty.util.internal.PlatformDependent.newConcurrentHashMap;
public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddress> { public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddress> {
private final ChannelFactory<? extends DatagramChannel> channelFactory; private final ChannelFactory<? extends DatagramChannel> channelFactory;
private final InetSocketAddress localAddress;
private final DnsServerAddresses nameServerAddresses; private final DnsServerAddresses nameServerAddresses;
private final ConcurrentMap<String, Promise<InetAddress>> resolvesInProgress = newConcurrentHashMap(); private final ConcurrentMap<String, Promise<InetAddress>> resolvesInProgress = newConcurrentHashMap();
private final ConcurrentMap<String, Promise<List<InetAddress>>> resolveAllsInProgress = newConcurrentHashMap(); private final ConcurrentMap<String, Promise<List<InetAddress>>> resolveAllsInProgress = newConcurrentHashMap();
public DnsAddressResolverGroup(
Class<? extends DatagramChannel> channelType, DnsServerAddresses nameServerAddresses) {
this(channelType, ANY_LOCAL_ADDR, nameServerAddresses);
}
public DnsAddressResolverGroup( public DnsAddressResolverGroup(
Class<? extends DatagramChannel> channelType, Class<? extends DatagramChannel> channelType,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) { DnsServerAddresses nameServerAddresses) {
this(new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses); this(new ReflectiveChannelFactory<DatagramChannel>(channelType), nameServerAddresses);
}
public DnsAddressResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory, DnsServerAddresses nameServerAddresses) {
this(channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);
} }
public DnsAddressResolverGroup( public DnsAddressResolverGroup(
ChannelFactory<? extends DatagramChannel> channelFactory, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) { DnsServerAddresses nameServerAddresses) {
this.channelFactory = channelFactory; this.channelFactory = channelFactory;
this.localAddress = localAddress;
this.nameServerAddresses = nameServerAddresses; this.nameServerAddresses = nameServerAddresses;
} }
@ -83,20 +70,20 @@ public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddr
" (expected: " + StringUtil.simpleClassName(EventLoop.class)); " (expected: " + StringUtil.simpleClassName(EventLoop.class));
} }
return newResolver((EventLoop) executor, channelFactory, localAddress, nameServerAddresses); return newResolver((EventLoop) executor, channelFactory, nameServerAddresses);
} }
/** /**
* @deprecated Override {@link #newNameResolver(EventLoop, ChannelFactory, InetSocketAddress, DnsServerAddresses)}. * @deprecated Override {@link #newNameResolver(EventLoop, ChannelFactory, DnsServerAddresses)}.
*/ */
@Deprecated @Deprecated
protected AddressResolver<InetSocketAddress> newResolver( protected AddressResolver<InetSocketAddress> newResolver(
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory, EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception { DnsServerAddresses nameServerAddresses) throws Exception {
final NameResolver<InetAddress> resolver = new InflightNameResolver<InetAddress>( final NameResolver<InetAddress> resolver = new InflightNameResolver<InetAddress>(
eventLoop, eventLoop,
newNameResolver(eventLoop, channelFactory, localAddress, nameServerAddresses), newNameResolver(eventLoop, channelFactory, nameServerAddresses),
resolvesInProgress, resolvesInProgress,
resolveAllsInProgress); resolveAllsInProgress);
@ -109,11 +96,9 @@ public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddr
*/ */
protected NameResolver<InetAddress> newNameResolver(EventLoop eventLoop, protected NameResolver<InetAddress> newNameResolver(EventLoop eventLoop,
ChannelFactory<? extends DatagramChannel> channelFactory, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress,
DnsServerAddresses nameServerAddresses) throws Exception { DnsServerAddresses nameServerAddresses) throws Exception {
return new DnsNameResolverBuilder(eventLoop) return new DnsNameResolverBuilder(eventLoop)
.channelFactory(channelFactory) .channelFactory(channelFactory)
.localAddress(localAddress)
.nameServerAddresses(nameServerAddresses) .nameServerAddresses(nameServerAddresses)
.build(); .build();
} }

View File

@ -17,12 +17,14 @@ package io.netty.resolver.dns;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.AddressedEnvelope;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory; import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.FixedRecvByteBufAllocator; import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannel;
@ -65,8 +67,6 @@ public class DnsNameResolver extends InetNameResolver {
private static final String LOCALHOST = "localhost"; private static final String LOCALHOST = "localhost";
private static final InetAddress LOCALHOST_ADDRESS; private static final InetAddress LOCALHOST_ADDRESS;
static final InetSocketAddress ANY_LOCAL_ADDR = new InetSocketAddress(0);
static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2]; static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2];
static { static {
@ -88,7 +88,7 @@ public class DnsNameResolver extends InetNameResolver {
private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder(); private static final DatagramDnsQueryEncoder ENCODER = new DatagramDnsQueryEncoder();
final DnsServerAddresses nameServerAddresses; final DnsServerAddresses nameServerAddresses;
final ChannelFuture bindFuture; final Future<Channel> channelFuture;
final DatagramChannel ch; final DatagramChannel ch;
/** /**
@ -123,7 +123,6 @@ public class DnsNameResolver extends InetNameResolver {
* *
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers * @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel} * @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
* @param localAddress the local address of the {@link DatagramChannel}
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from * @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
* this to determine which DNS server should be contacted for the next retry in case * this to determine which DNS server should be contacted for the next retry in case
* of failure. * of failure.
@ -140,9 +139,8 @@ public class DnsNameResolver extends InetNameResolver {
public DnsNameResolver( public DnsNameResolver(
EventLoop eventLoop, EventLoop eventLoop,
ChannelFactory<? extends DatagramChannel> channelFactory, ChannelFactory<? extends DatagramChannel> channelFactory,
InetSocketAddress localAddress,
DnsServerAddresses nameServerAddresses, DnsServerAddresses nameServerAddresses,
DnsCache resolveCache, final DnsCache resolveCache,
long queryTimeoutMillis, long queryTimeoutMillis,
InternetProtocolFamily[] resolvedAddressTypes, InternetProtocolFamily[] resolvedAddressTypes,
boolean recursionDesired, boolean recursionDesired,
@ -154,7 +152,6 @@ public class DnsNameResolver extends InetNameResolver {
super(eventLoop); super(eventLoop);
checkNotNull(channelFactory, "channelFactory"); checkNotNull(channelFactory, "channelFactory");
checkNotNull(localAddress, "localAddress");
this.nameServerAddresses = checkNotNull(nameServerAddresses, "nameServerAddresses"); this.nameServerAddresses = checkNotNull(nameServerAddresses, "nameServerAddresses");
this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis"); this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis");
this.resolvedAddressTypes = checkNonEmpty(resolvedAddressTypes, "resolvedAddressTypes"); this.resolvedAddressTypes = checkNonEmpty(resolvedAddressTypes, "resolvedAddressTypes");
@ -166,18 +163,11 @@ public class DnsNameResolver extends InetNameResolver {
this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver"); this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
this.resolveCache = resolveCache; this.resolveCache = resolveCache;
bindFuture = newChannel(channelFactory, localAddress);
ch = (DatagramChannel) bindFuture.channel();
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
}
private ChannelFuture newChannel(
ChannelFactory<? extends DatagramChannel> channelFactory, InetSocketAddress localAddress) {
Bootstrap b = new Bootstrap(); Bootstrap b = new Bootstrap();
b.group(executor()); b.group(executor());
b.channelFactory(channelFactory); b.channelFactory(channelFactory);
final DnsResponseHandler responseHandler = new DnsResponseHandler(); b.option(ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION, true);
final DnsResponseHandler responseHandler = new DnsResponseHandler(executor().<Channel>newPromise());
b.handler(new ChannelInitializer<DatagramChannel>() { b.handler(new ChannelInitializer<DatagramChannel>() {
@Override @Override
protected void initChannel(DatagramChannel ch) throws Exception { protected void initChannel(DatagramChannel ch) throws Exception {
@ -185,15 +175,16 @@ public class DnsNameResolver extends InetNameResolver {
} }
}); });
ChannelFuture bindFuture = b.bind(localAddress); channelFuture = responseHandler.channelActivePromise;
bindFuture.channel().closeFuture().addListener(new ChannelFutureListener() { ch = (DatagramChannel) b.register().channel();
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
ch.closeFuture().addListener(new ChannelFutureListener() {
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception { public void operationComplete(ChannelFuture future) throws Exception {
resolveCache.clear(); resolveCache.clear();
} }
}); });
return bindFuture;
} }
/** /**
@ -606,6 +597,13 @@ public class DnsNameResolver extends InetNameResolver {
} }
private final class DnsResponseHandler extends ChannelInboundHandlerAdapter { private final class DnsResponseHandler extends ChannelInboundHandlerAdapter {
private final Promise<Channel> channelActivePromise;
DnsResponseHandler(Promise<Channel> channelActivePromise) {
this.channelActivePromise = channelActivePromise;
}
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try { try {
@ -628,6 +626,12 @@ public class DnsNameResolver extends InetNameResolver {
} }
} }
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
channelActivePromise.setSuccess(ctx.channel());
}
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.warn("{} Unexpected exception: ", ch, cause); logger.warn("{} Unexpected exception: ", ch, cause);

View File

@ -26,7 +26,6 @@ import io.netty.resolver.HostsFileEntriesResolver;
import io.netty.util.internal.InternalThreadLocalMap; import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
@ -39,7 +38,6 @@ public final class DnsNameResolverBuilder {
private final EventLoop eventLoop; private final EventLoop eventLoop;
private ChannelFactory<? extends DatagramChannel> channelFactory; private ChannelFactory<? extends DatagramChannel> channelFactory;
private InetSocketAddress localAddress = DnsNameResolver.ANY_LOCAL_ADDR;
private DnsServerAddresses nameServerAddresses = DnsServerAddresses.defaultAddresses(); private DnsServerAddresses nameServerAddresses = DnsServerAddresses.defaultAddresses();
private DnsCache resolveCache; private DnsCache resolveCache;
private Integer minTtl; private Integer minTtl;
@ -86,17 +84,6 @@ public final class DnsNameResolverBuilder {
return channelFactory(new ReflectiveChannelFactory<DatagramChannel>(channelType)); return channelFactory(new ReflectiveChannelFactory<DatagramChannel>(channelType));
} }
/**
* Sets the local address of the {@link DatagramChannel}
*
* @param localAddress the local address
* @return {@code this}
*/
public DnsNameResolverBuilder localAddress(InetSocketAddress localAddress) {
this.localAddress = localAddress;
return this;
}
/** /**
* Sets the addresses of the DNS server. * Sets the addresses of the DNS server.
* *
@ -318,7 +305,6 @@ public final class DnsNameResolverBuilder {
return new DnsNameResolver( return new DnsNameResolver(
eventLoop, eventLoop,
channelFactory, channelFactory,
localAddress,
nameServerAddresses, nameServerAddresses,
cache, cache,
queryTimeoutMillis, queryTimeoutMillis,

View File

@ -17,6 +17,7 @@ package io.netty.resolver.dns;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.AddressedEnvelope;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.dns.DatagramDnsQuery; import io.netty.handler.codec.dns.DatagramDnsQuery;
@ -27,6 +28,8 @@ import io.netty.handler.codec.dns.DnsRecord;
import io.netty.handler.codec.dns.DnsRecordType; import io.netty.handler.codec.dns.DnsRecordType;
import io.netty.handler.codec.dns.DnsResponse; import io.netty.handler.codec.dns.DnsResponse;
import io.netty.handler.codec.dns.DnsSection; import io.netty.handler.codec.dns.DnsSection;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -107,12 +110,12 @@ final class DnsQueryContext {
} }
private void sendQuery(final DnsQuery query) { private void sendQuery(final DnsQuery query) {
if (parent.bindFuture.isDone()) { if (parent.channelFuture.isDone()) {
writeQuery(query); writeQuery(query);
} else { } else {
parent.bindFuture.addListener(new ChannelFutureListener() { parent.channelFuture.addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception { public void operationComplete(Future<? super Channel> future) throws Exception {
if (future.isSuccess()) { if (future.isSuccess()) {
writeQuery(query); writeQuery(query);
} else { } else {