From 660759b99757748dc416b52028b31f97d80f7d08 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 6 Mar 2020 10:41:35 +0100 Subject: [PATCH] =?UTF-8?q?Use=20MacOSDnsServerAddressStreamProvider=20whe?= =?UTF-8?q?n=20on=20the=20classpath=20and=20we=20=E2=80=A6=20(#10079)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: 939e928312f1099373582f9811817a6226550987 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. --- .../MacOSDnsServerAddressStreamProvider.java | 6 +- ...cOSDnsServerAddressStreamProviderTest.java | 11 +- .../dns/DnsServerAddressStreamProviders.java | 124 ++++++++++++++---- .../DnsServerAddressStreamProvidersTest.java | 27 ++++ 4 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java index f82f571511..6cdcff6535 100644 --- a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java +++ b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java @@ -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); diff --git a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java index aafec23727..424716f939 100644 --- a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java +++ b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java @@ -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)); + } + } diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java index 2983ccbad9..8df2492c02 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddressStreamProviders.java @@ -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 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 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() { + @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 providerClass = + (Class) 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(); + } + }; } } diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java new file mode 100644 index 0000000000..57be3adc68 --- /dev/null +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/DnsServerAddressStreamProvidersTest.java @@ -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()); + } +}