Open jonquark opened 6 years ago
A practical example that caused me some headaches and I believe woulde be solved by fixing this: My router (AVM FritzBox) has an integrated DynDNS feature provided by the manufacturer. Hence with every reconnect my hostname will be updated with the new public IP. The problem however is, that I have native IPv4 and IPv6. As a result the DNS A record has my public IPv4 address, and the DNS AAAA has the IP of the router. The problem is, that my MQTT broker of course isn't running on the router, but instead on a Pi for which I have port forwarding for IPv4 enabled. For IPv6 there is no port forwarding, so for this case the firewall (just the port to be more precise) of the router is opened up to the globally valid IPv6 address of my Pi. This however does not match the IPv6 address associated with my hostname (which has the IPv6 of the router).
Technically all this is working as intended. It would be my responsibility to modify the AAAA record to point to my Pi if I wanted this to work. But sadly my router does not allow this.
In practice the problem that I currently have is that I can't connect to my broker from IPv6 enabled devices because the hosename resolves to 2 addresses. Since IPv6 generally is available and functional on my smartphone, the MQTT client chooses to connect to the IPv6 address. But that's my router, not the Pi. So the solutions for that would be:
This is awful for IPv6-only devices and dualstack servers.
https://stackoverflow.com/questions/11974232/return-ipv6-in-java
This -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=true
solves the issue for one device.
But the solution should work for every device. We have a lot of IOT devices, and we do not know what kind of IP addresses will be provided by mobile network providers.
You can use opts.setServerURIs()
to use working IP/host out of many.
This is how I overcame my problem in IPv6 only network.
Because you cannot use IPv6 literals (https://github.com/eclipse/paho.mqtt.java/issues/322):
-Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=true
to prefer IPv6 addresses)
and literals for IPv4 addresses.This code solved my issue:
URI uri = new URI(mqttProperties.getUri());
String[] serverURIs = Stream.concat(
Arrays.stream(InetAddress.getAllByName(uri.getHost()))
.filter(inetAddress -> inetAddress instanceof Inet4Address)
.map(InetAddress::getHostAddress), // IPv4 addresses
Stream.of(uri.getHost())) // Hostname - now preferres IPv6
.map(host -> {
try {
return new URI(uri.getScheme(), uri.getUserInfo(), host, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}).toArray(String[]::new);
opts.setServerURIs(serverURIs); // overrides serverURI constructor parameter
This is not a bug (but I think it applies to all current branches).
When the client connects to a server, under the covers a DNS lookup is performed. At the moment if multiple IP addresses are returned, the client will attempt to connect to just one.
If we cannot connect to that address, if the other addresses were attempted in turn as well then the client would be more fault tolerant of a server with multiple IP addresses.
One subtlety is the interaction of this feature with the HA feature. I propose that if two server names are provided, each of which resolves to multiple IP addresses then all the addresses of server1 would be attempted before attempting to connect to server2.
Here are a couple of links I have found describing handling connections to multiple IP addresses: https://bugs.openjdk.java.net/browse/JDK-8051854 https://stackoverflow.com/questions/4648803/java-outgoing-tcp-connection-failover-based-on-multiple-dns-results