Use MacOSDnsServerAddressStreamProvider when on the classpath and we … (#10079)

Motivation:

939e928312 introduced MacOSDnsServerAddressStreamProvider which will ensure the right nameservers are selected when running on MacOS. To ensure this is done automatically on MacOS we should use it by default on these platforms.

Modifications:

Try to use MacOSDnsServerAddressStreamProvider when on MacOS via reflection and fallback if not possible

Result:

Ensure the right nameservers are used on MacOS even when a VPN (for example) is used.
This commit is contained in:
Norman Maurer 2020-03-06 10:41:35 +01:00 committed by GitHub
parent 161c237fb9
commit 660759b997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 33 deletions

View File

@ -42,10 +42,6 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
// Fallback provider
private static final DnsServerAddressStreamProvider DEFAULT_PROVIDER =
DnsServerAddressStreamProviders.platformDefault();
private static final Throwable UNAVAILABILITY_CAUSE;
private static final InternalLogger logger =
@ -167,7 +163,7 @@ public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddre
if (addresses != null) {
return addresses.stream();
}
return DEFAULT_PROVIDER.nameServerAddressStream(originalHostname);
return DnsServerAddressStreamProviders.unixDefault().nameServerAddressStream(originalHostname);
}
DnsServerAddresses addresses = resolverMap.get(hostname);

View File

@ -17,6 +17,8 @@ package io.netty.resolver.dns.macos;
import io.netty.resolver.dns.DnsServerAddressStream;
import io.netty.resolver.dns.DnsServerAddressStreamProvider;
import io.netty.resolver.dns.DnsServerAddressStreamProviders;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
@ -30,7 +32,7 @@ public class MacOSDnsServerAddressStreamProviderTest {
}
@Test
public void test() {
public void testStream() {
DnsServerAddressStreamProvider provider = new MacOSDnsServerAddressStreamProvider();
DnsServerAddressStream stream = provider.nameServerAddressStream("netty.io");
Assert.assertNotNull(stream);
@ -40,4 +42,11 @@ public class MacOSDnsServerAddressStreamProviderTest {
Assert.assertNotEquals(0, stream.next().getPort());
}
}
@Test
public void testDefaultUseCorrectInstance() {
Assert.assertThat(DnsServerAddressStreamProviders.platformDefault(),
Matchers.instanceOf(MacOSDnsServerAddressStreamProvider.class));
}
}

View File

@ -16,7 +16,14 @@
package io.netty.resolver.dns;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@ -24,37 +31,49 @@ import java.util.concurrent.atomic.AtomicLong;
* Utility methods related to {@link DnsServerAddressStreamProvider}.
*/
public final class DnsServerAddressStreamProviders {
// We use 5 minutes which is the same as what OpenJDK is using in sun.net.dns.ResolverConfigurationImpl.
private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(5);
// TODO(scott): how is this done on Windows? This may require a JNI call to GetNetworkParams
// https://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx.
private static final DnsServerAddressStreamProvider DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER =
new DnsServerAddressStreamProvider() {
private volatile DnsServerAddressStreamProvider currentProvider = provider();
private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime());
private static final InternalLogger LOGGER =
InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class);
private static final Constructor<? extends DnsServerAddressStreamProvider> STREAM_PROVIDER_CONSTRUCTOR;
@Override
public DnsServerAddressStream nameServerAddressStream(String hostname) {
long last = lastRefresh.get();
DnsServerAddressStreamProvider current = currentProvider;
if (System.nanoTime() - last > REFRESH_INTERVAL) {
// This is slightly racy which means it will be possible still use the old configuration for a small
// amount of time, but that's ok.
if (lastRefresh.compareAndSet(last, System.nanoTime())) {
current = currentProvider = provider();
static {
Constructor<? extends DnsServerAddressStreamProvider> constructor = null;
if (PlatformDependent.isOsx()) {
try {
// As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar
// we use reflection to use it if its on the classpath.
Object maybeProvider = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return Class.forName(
"io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider",
true,
DnsServerAddressStreamProviders.class.getClassLoader());
} catch (Throwable cause) {
return cause;
}
}
});
if (maybeProvider instanceof Class) {
@SuppressWarnings("unchecked")
Class<? extends DnsServerAddressStreamProvider> providerClass =
(Class<? extends DnsServerAddressStreamProvider>) maybeProvider;
Method method = providerClass.getMethod("ensureAvailability");
method.invoke(null);
constructor = providerClass.getConstructor();
constructor.newInstance();
} else if (!(maybeProvider instanceof ClassNotFoundException)) {
throw (Throwable) maybeProvider;
}
} catch (Throwable cause) {
LOGGER.debug(
"Unable to use MacOSDnsServerAddressStreamProvider, fallback to system defaults", cause);
constructor = null;
}
return current.nameServerAddressStream(hostname);
}
private DnsServerAddressStreamProvider provider() {
// If on windows just use the DefaultDnsServerAddressStreamProvider.INSTANCE as otherwise
// we will log some error which may be confusing.
return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE :
UnixResolverDnsServerAddressStreamProvider.parseSilently();
}
};
STREAM_PROVIDER_CONSTRUCTOR = constructor;
}
private DnsServerAddressStreamProviders() {
}
@ -67,6 +86,57 @@ public final class DnsServerAddressStreamProviders {
* configuration.
*/
public static DnsServerAddressStreamProvider platformDefault() {
return DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER;
if (STREAM_PROVIDER_CONSTRUCTOR != null) {
try {
return STREAM_PROVIDER_CONSTRUCTOR.newInstance();
} catch (IllegalAccessException e) {
// ignore
} catch (InstantiationException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
}
return unixDefault();
}
public static DnsServerAddressStreamProvider unixDefault() {
return DefaultProviderHolder.DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER;
}
// We use a Holder class to only initialize DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER if we really
// need it.
private static final class DefaultProviderHolder {
// We use 5 minutes which is the same as what OpenJDK is using in sun.net.dns.ResolverConfigurationImpl.
private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(5);
// TODO(scott): how is this done on Windows? This may require a JNI call to GetNetworkParams
// https://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx.
static final DnsServerAddressStreamProvider DEFAULT_DNS_SERVER_ADDRESS_STREAM_PROVIDER =
new DnsServerAddressStreamProvider() {
private volatile DnsServerAddressStreamProvider currentProvider = provider();
private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime());
@Override
public DnsServerAddressStream nameServerAddressStream(String hostname) {
long last = lastRefresh.get();
DnsServerAddressStreamProvider current = currentProvider;
if (System.nanoTime() - last > REFRESH_INTERVAL) {
// This is slightly racy which means it will be possible still use the old configuration
// for a small amount of time, but that's ok.
if (lastRefresh.compareAndSet(last, System.nanoTime())) {
current = currentProvider = provider();
}
}
return current.nameServerAddressStream(hostname);
}
private DnsServerAddressStreamProvider provider() {
// If on windows just use the DefaultDnsServerAddressStreamProvider.INSTANCE as otherwise
// we will log some error which may be confusing.
return PlatformDependent.isWindows() ? DefaultDnsServerAddressStreamProvider.INSTANCE :
UnixResolverDnsServerAddressStreamProvider.parseSilently();
}
};
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/package io.netty.resolver.dns;
import org.junit.Assert;
import org.junit.Test;
public class DnsServerAddressStreamProvidersTest {
@Test
public void testUseCorrectProvider() {
Assert.assertSame(DnsServerAddressStreamProviders.unixDefault(),
DnsServerAddressStreamProviders.platformDefault());
}
}