miloxe
miloxe

Reputation: 463

DNS query freezes with DnsContextFactory in java

The program below performs a DNS lookup. It works good except for one particular combination of host name and dns:

import javax.naming.directory.InitialDirContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public final class StackOverflow {
 public static void main(String args[]) throws NamingException {
  Hashtable<String, Object> env = new Hashtable<String, Object>();
  env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
  env.put("java.naming.provider.url", "dns://ns.dnssek.org");
  System.out.println(new InitialDirContext(env).getAttributes("dnsseccert.us", new String[]{"NS","A"}));
 }
}

I tried to set timeout:

env.put("com.sun.jndi.dns.timeout.initial", "220");

however it behaves strange. It works sometimes for small values when it throws:

DNS error [Root exception is java.net.SocketTimeoutException: Receive timed out];

but for most of the times the program just freezes and hangs in the memory.

Have someone else had the same problem and solved it? Are there any other settings I could try to prevent it? Is there some other alternative java.naming.factory.initial which I could try?

Upvotes: 1

Views: 2854

Answers (2)

chegar999
chegar999

Reputation: 146

Please try setting both the com.sun.jndi.dns.timeout.initial and com.sun.jndi.dns.timeout.retries values. For example, I see consistent timeout with the following:

$ cat StackOverflow.java 
import javax.naming.directory.InitialDirContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public final class StackOverflow {
 public static void main(String args[]) throws NamingException {
  Hashtable<String, Object> env = new Hashtable<String, Object>();
  env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
  env.put("java.naming.provider.url", "dns://ns.dnssek.org");

  env.put("com.sun.jndi.dns.timeout.initial", "220");
  env.put("com.sun.jndi.dns.timeout.retries", "1");

  System.out.println(new InitialDirContext(env).getAttributes("dnsseccert.us", new String[]{"NS","A"}));
 }
}

$ java -version
java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)

$ java StackOverflow
Exception in thread "main" javax.naming.CommunicationException: DNS error [Root exception is java.net.SocketTimeoutException: Receive timed out]; remaining name    'dnsseccert.us'
    at jdk.naming.dns/com.sun.jndi.dns.DnsClient.query(DnsClient.java:313)
    at jdk.naming.dns/com.sun.jndi.dns.Resolver.query(Resolver.java:81)
    at jdk.naming.dns/com.sun.jndi.dns.DnsContext.c_getAttributes(DnsContext.java:434)
    at java.naming/com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:235)
    at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:141)
    at java.naming/com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:129)
    at java.naming/javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:142)
    at StackOverflow.main(StackOverflow.java:14)
    Caused by: java.net.SocketTimeoutException: Receive timed out
    at java.base/java.net.PlainDatagramSocketImpl.receive0(Native Method)
    at java.base/java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:181)
    at java.base/java.net.DatagramSocket.receive(DatagramSocket.java:814)
    at jdk.naming.dns/com.sun.jndi.dns.DnsClient.doUdpQuery(DnsClient.java:423)
    at jdk.naming.dns/com.sun.jndi.dns.DnsClient.query(DnsClient.java:212)
    ... 7 more

You will of course want to configure timeout and retry values that make sense in your own environment. The above values are just for demonstration purposes.

Upvotes: 0

miloxe
miloxe

Reputation: 463

It took me a few hours but I finally found an acceptable solution. I downloaded sources of the package com.sun.jndi.dns and added one line into the class DnsClient:

...
Tcp(InetAddress server, int port, int timeout) throws IOException {
    sock = new Socket(server, port);
    sock.setSoTimeout(timeout);  // <-- missing timeout
    sock.setTcpNoDelay(true);
    out = new java.io.BufferedOutputStream(sock.getOutputStream());
    in = new java.io.BufferedInputStream(sock.getInputStream());
}
...

I set timout of the socket. I actually also added a parameter into the Tcp constructor and modified its calls so it is now initialized by the value from com.sun.jndi.dns.timeout.initial.

Upvotes: 4

Related Questions