Repository: netty/netty
Issue: Blocking call in UnixResolverDnsServerAddressStreamProvider.parse

### Expected behavior

No blocking calls reported by BlockHound

### Actual behavior

The exception below appears when BlockHound is installed

```
reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes
     	at java.io.FileInputStream.readBytes(FileInputStream.java)
     	at java.io.FileInputStream.read(FileInputStream.java:255)
     	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
     	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
     	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
     	at java.io.InputStreamReader.read(InputStreamReader.java:184)
     	at java.io.BufferedReader.fill(BufferedReader.java:161)
     	at java.io.BufferedReader.readLine(BufferedReader.java:324)
     	at java.io.BufferedReader.readLine(BufferedReader.java:389)
     	at io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parse(UnixResolverDnsServerAddressStreamProvider.java:175)
     	at io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.<init>(UnixResolverDnsServerAddressStreamProvider.java:98)
     	at io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.<init>(UnixResolverDnsServerAddressStreamProvider.java:133)
     	at io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseSilently(UnixResolverDnsServerAddressStreamProvider.java:72)
     	at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.provider(DnsServerAddressStreamProviders.java:139)
     	at io.netty.resolver.dns.DnsServerAddressStreamProviders$DefaultProviderHolder$1.nameServerAddressStream(DnsServerAddressStreamProviders.java:129)
     	at io.netty.resolver.dns.DnsNameResolver.doResolveAllUncached0(DnsNameResolver.java:1070)
     	at io.netty.resolver.dns.DnsNameResolver.access$600(DnsNameResolver.java:90)
     	at io.netty.resolver.dns.DnsNameResolver$6.run(DnsNameResolver.java:1053)
     	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
     	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
     	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:500)
    	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
     	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
     	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
     	at java.lang.Thread.run(Thread.java:748)
```

### Netty version

4.1.57.Final

### JVM version (e.g. `java -version`)

JDK 8

### OS version (e.g. `uname -a`)

MacOS

Issue comments:
@violetagg can you provide a PR ?
@normanmaurer I was thinking for allowing blocking calls when loading `etcResolverFiles`, is that what you are thinking also?
This sounds good 

Pull request review comments:
you need to also shutdown the executor 
oops ... done

Patch that resolved the issue:
diff --git a/common/src/main/java/io/netty/util/internal/Hidden.java b/common/src/main/java/io/netty/util/internal/Hidden.java
index ac97323573e..18a03c68ead 100644
--- a/common/src/main/java/io/netty/util/internal/Hidden.java
+++ b/common/src/main/java/io/netty/util/internal/Hidden.java
@@ -115,6 +115,10 @@ public void applyTo(BlockHound.Builder builder) {
                     "sun.security.ssl.SSLEngineImpl",
                     "wrap");
 
+            builder.allowBlockingCallsInside(
+                    "io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
+                    "parse");
+
             builder.nonBlockingThreadPredicate(new Function<Predicate<Thread>, Predicate<Thread>>() {
                 @Override
                 public Predicate<Thread> apply(final Predicate<Thread> p) {
diff --git a/transport-blockhound-tests/pom.xml b/transport-blockhound-tests/pom.xml
index 81240d329d9..77c36f40e90 100644
--- a/transport-blockhound-tests/pom.xml
+++ b/transport-blockhound-tests/pom.xml
@@ -89,6 +89,11 @@
       <artifactId>netty-handler</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>netty-resolver-dns</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>${tcnative.artifactId}</artifactId>


Test patch:
diff --git a/transport-blockhound-tests/src/test/java/io/netty/util/internal/NettyBlockHoundIntegrationTest.java b/transport-blockhound-tests/src/test/java/io/netty/util/internal/NettyBlockHoundIntegrationTest.java
index ade2def4703..2d5121a0781 100644
--- a/transport-blockhound-tests/src/test/java/io/netty/util/internal/NettyBlockHoundIntegrationTest.java
+++ b/transport-blockhound-tests/src/test/java/io/netty/util/internal/NettyBlockHoundIntegrationTest.java
@@ -35,6 +35,8 @@
 import io.netty.handler.ssl.SslProvider;
 import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
 import io.netty.handler.ssl.util.SelfSignedCertificate;
+import io.netty.resolver.dns.DnsServerAddressStreamProvider;
+import io.netty.resolver.dns.DnsServerAddressStreamProviders;
 import io.netty.util.HashedWheelTimer;
 import io.netty.util.ReferenceCountUtil;
 import io.netty.util.concurrent.DefaultThreadFactory;
@@ -53,6 +55,8 @@
 import reactor.blockhound.integration.BlockHoundIntegration;
 
 import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Queue;
 import java.util.ServiceLoader;
 import java.util.concurrent.CountDownLatch;
@@ -69,6 +73,7 @@
 
 import static io.netty.buffer.Unpooled.wrappedBuffer;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -314,6 +319,40 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
         }
     }
 
+    @Test(timeout = 5000L)
+    public void testParseEtcResolverFilesAllowsBlockingCalls() throws InterruptedException {
+        SingleThreadEventExecutor executor =
+                new SingleThreadEventExecutor(null, new DefaultThreadFactory("test"), true) {
+                    @Override
+                    protected void run() {
+                        while (!confirmShutdown()) {
+                            Runnable task = takeTask();
+                            if (task != null) {
+                                task.run();
+                            }
+                        }
+                    }
+                };
+        try {
+            CountDownLatch latch = new CountDownLatch(1);
+            List<DnsServerAddressStreamProvider> result = new ArrayList<>();
+            List<Throwable> error = new ArrayList<>();
+            executor.execute(() -> {
+                try {
+                    result.add(DnsServerAddressStreamProviders.unixDefault());
+                } catch (Throwable t) {
+                    error.add(t);
+                }
+                latch.countDown();
+            });
+            latch.await();
+            assertEquals(0, error.size());
+            assertEquals(1, result.size());
+        } finally {
+            executor.shutdownGracefully();
+        }
+    }
+
     private static void testTrustManagerVerify(String tlsVersion) throws Exception {
         final SslContext sslClientCtx =
                 SslContextBuilder.forClient()