DNS Cache?

First of all, I couldn’t really find the correct place to post this, if someone can move this to a better place, please do.

  • Platform information:
    • Hardware: x86-64
    • OS: ubuntu 18.04 / Docker
    • Java Runtime Environment: java 8
    • openHAB version: 2.3.0
  • Issue of the topic: please be detailed explaining your issue
    It seems dns queries are cached indefinitely. I’ve created a binding which connects to a Solar inverter every 10 seconds and fetches the latest stats from the machine. Every now and then my DHCP gives out a new IP to the inverter’s wifi module. Yet, the binding still uses the old IP until I restart openhab. I’ve read up on this and it seems Java is notorious in this regard.

Does the Openhab community have a general solution to this problem?

Please mind that this solution should be on the side of Openhab or the binding I am creating; a solution outside of Openhab itself would be to give the solar inverter a static IP, however I wish to make this binding as generally applicable as possible.

You could try a network scan a get the mac address.
The best way is to assign a static IP.

What does that mean exactly? The user would still have to input the mac address which is not the easiest thing to get for everyone. The easiest thing would be to assign a static IP.
There are several topics around this subject already and the consensus is static IPs.

where? in the java space or in the O/S space?
does the O/S resolve the fqdn/hostname to the newly assigned IP while your binding still caches the old dns resolution?

I am no Java developer, so I don’t know if Java caches it’s own dns resolutions/table.

edit: it seems it’s in the Java space:

@vzorglub I think that he wants to use a hostname (for the Solar inverter host/endpoint) in his binding. It’s not clear to me if he uses discovery in his binding or sets manually the (hostname of the) target.

That’s fine but from which name resolver? localhost hosts file? The router? the device itself (If the router passes it along)?

Recently I ran into the same DNS caching issue with a Java application in a Docker stack where containers can get recreated and get a different IP.

What works is to configure the networkaddress.cache.ttl property in the security policy defined in $JAVA_HOME/jre/lib/security/java.security

See also the Oracle Networking Properties documentation.

This security policy configuration is actually depending on the JVM implementation. Though after a quick check it seems that the Oracle/OpenJDK and Zulu JVM all use the same configuration file and mechanism for this.

The DNS caching issue shouldn’t be solved in the binding itself. If possible the binding could implement a DiscoveryService to discover the Solar inverter.

Other ways to solve it could be:

  1. Instruct people who have this issue to read this topic. :slight_smile:
  2. Some documentation is added about this configuration on the official documentation website.
  3. Distributions that also configure the JVM provide a better default for this value (openHABian, Docker).

It’s also possible to get or set the value from within Java with for instance:

  • java.security.Security.getProperty("networkaddress.cache.ttl")
  • java.security.Security.setProperty("networkaddress.cache.ttl", "600")

So if that always works well in all security contexts it could perhaps also be made a configuration option in openHAB/Eclipse SmartHome. Though it is questionable if an application itself should manage this setting.

1 Like

So to answer someone’s question above, the configuration of my binding is done by giving the binding a hostname of the inverter. The hostname is correctly communicated by the inverter to the dhcp server. The dhcp servers adds the hostname as hostnamewhatever.lan to its DNS database. This is pretty common with SOHO routers nowadays. I think it’s overkill to implement a DiscoveryService just because java’s braindead decision to not honor DNS TTLs. DNS is fine service discovery method on its own.
Also, even if I would implement the DiscoveryService, is that also meant to do continuous updates to binding configs, in this case the config would be inverter’s hostname. I interpreted the DiscoveryService as a one-time discovery for initial setup of a thing.

I will verify if the java.security.Security.getProperty("networkaddress.cache.ttl") is indeed the culprit.

1 Like

Yes you are correct. But with for instance UPnP the UDN (Unique Device Name) is stored in the Thing configuration instead of the hostname/IP. That way the device can be found regardless of its IP.

Some devices also have their own proprietary discovery protocol that can be used. E.g. LIFX has its own proprietary multicast based discovery protocol where the MAC address of a light is used to uniquely identify it. So in that case storing the MAC address in the Thing configuration is sufficient to rediscover the light at a later moment.

These discovery protocols also have their own issues of course. Devices that are not part of the subnet will not be discoverable because they will not receive the broadcast packets.

You can always decide later on to add some discovery goodness (when possible) for end users instead of building it immediately. :slight_smile:

Then that will not solve my issue when the IP of the inverter changes after the initial setup due to a new DHCP lease, yet its hostname remains intact. Basically I’m expecting to rely DNS, but that’s apparently a no-go. In this case with a local inverter, sure, I can do something about it, e.g., set a static IP. But what about 3rd party servers which change their IPs, but keep using the same hostname; do we expect users to also restart Openhab in that case?
The only thing I can come up with all these types of devices which rely on DNS to be resolved, is to reinvent a DNS client in my binding which does the sane thing: respect the upstream DNS’ TTL.

I think the options in my first reply are a good start for addressing that.

It’s perfectly OK to rely on DNS and I agree it should work without any issues so you don’t have to implement your own DNS client. :slight_smile: There are many other bindings that also rely on DNS and use a hostname/IP in their Thing configuration. Implementing a DNS client in one binding will not solve the issue for the other parts of the application that also rely on DNS.

For some users discovery really helps a lot because they are on a network using DHCP where they can not configure DNS on their router. E.g. within company networks or when they use routers that are heavily restricted by their ISP or which only allow for a limited number of DNS entries.

Some bindings also allow users to configure if communication with a device should be done using a configured hostname/IP or by discovering the device IP.

It looks like openHAB is not using a security manager at all so it defaults to the default behavior of caching DNS entries for 30 seconds as is documented in the java.security file:

# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#    
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior in this implementation
# is to cache for 30 seconds.
# 
# NOTE: setting this to anything other than the default value can have
#       serious security implications. Do not set it unless
#       you are sure you are not exposed to DNS spoofing attack.
#    
#networkaddress.cache.ttl=-1

I tested this on the Oracle JVM and it seems to resolve the hostname to new IPs when I change the IP of testhost in /etc/hosts and create the following rule:

import java.net.InetAddress
import java.net.UnknownHostException

rule "Resolve hostname"
when
    System started or
    Time cron "*/15 * * * * ? *"
then 
    var hostname = "testhost";
    try {
        var address = InetAddress.getByName(hostname)
        logInfo("test.rules", "Resolved {} to {}", hostname, address.getHostAddress())
    } catch (UnknownHostException e) {
        logInfo("test.rules", "Failed to resolve: {}", hostname)
    }
end

This showed the following output:

16:26:00.024 [INFO ] [pse.smarthome.model.script.test.rules] - Failed to resolve: testhost
16:26:15.005 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.1
16:26:30.006 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.1
16:26:45.006 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.2
16:27:00.005 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.2
16:27:15.006 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.2
16:27:30.006 [INFO ] [pse.smarthome.model.script.test.rules] - Resolved testhost to 123.123.1.100

So if you still have issues maybe some library or the OS is doing the caching

That’s really weird then. Definitely will debug this and update here. Thanks for looking at this so far.