JDK-8257080 : Java does not try all DNS results when opening a socket
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 8
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2020-11-19
  • Updated: 2021-03-10
  • Resolved: 2021-01-12
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
Any OS (see sources above). Tested with all major hotspot and OpenJ9 versions from Java 8 onwards.

A DESCRIPTION OF THE PROBLEM :
When opening a socket, for example for an https or ldaps connection, the dns is being queried before the target port known. From the DNS, only the first result is ever used when querying an IP for a hostname.

This is due two code paths:
1. in InetAddress.java (ref: https://github.com/openjdk/jdk/blob/270674ce1b1b8d44bbe92949c3f7db7b7c767cac/src/java.base/share/classes/java/net/InetAddress.java#L1236-L1239)
Java will always return getAllByName(host)[0]

2. getAddress() being called in https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/net/Socket.java#L634-L637
and connect methods in AbstractPlainSocketImpl.java.

This is also true for the more recent implementations of AbstractPlainSocketImpl.java in Java 14+.

All other IPs are discarded.

Other languages, like python and .NET-languages, wait until the the target port is known. They will then iterate through all the IPs, trying to open the socket. If the destination is not reachable, the next IP:Port will be tried.
This way, outages and regional unavailability can be mitigated.

As java does not implement this behaviour, it is the only platform which does not provide a similar failsafe dns resolution.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Connect to a host (e.g. via ldaps, https) where the dns resolution will provide multiple answers. Make sure the first target IP of the DNS response is not reachable.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Java will pick the second (or any other) IP from the dns result after discovering the first ip cannot be used to open a socket.
ACTUAL -
Java takes the first dns result without checking, resulting in an exception (unreachable).
No non-java applications are affected, as only Java implements this behaviour.
As only java implements this behaviour, I consider this a bug.

CUSTOMER SUBMITTED WORKAROUND :
None possible. It is not possible to write an agent which modifies any classes in java.net, as they are protected.

FREQUENCY : always



Comments
Closing as a duplicate of JDK-8170568.
12-01-2021

> I gather the problem is not actually InetAddress.getByName itself, because InetAddress.getAllByName could be used instead, but that some other JDK code uses getByName? Yes, that is my understanding also. > I can see this happening in Socket.connect(String,int), but I'd like to be sure this is actually the problematic method before we try to find a fix. Yes, the` connect` method that accepts a String hostname is the "problematic" method. ( Though, I do not necessarily agree that it is a problem ;-) ). > The reference to the port being known is confusing, as the port is not used in DNS requests. Agreed. It is confusing. > Presumably, this could be worked around manually, by doing the resolution locally using InetAddress.getAllByName and then using Socket.connect(InetAddress,int) until a connection is established? Yes, that should be possible. > Or maybe this is a problem because of higher-level code using Socket.connect(String,int)? Much implementation and library code is using the connect(String,int) method, so it may not be so straightforward to change each and every use, hence the desire to "fix it at the source". The problem is better described by JDK-8170568. I'm going to close this issue as a duplicate of JDK-8170568.
22-12-2020

Some recent discussion here: https://mail.openjdk.java.net/pipermail/net-dev/2020-December/015061.html Exploration and prototypes needed before there is a proposal and some idea of the scope of the API changes needed.
22-12-2020

Is there a specific reproducer for the issue here? It's not completely clear which JDK call is problematic. I gather the problem is not actually InetAddress.getByName itself, because InetAddress.getAllByName could be used instead, but that some other JDK code uses getByName? I can see this happening in Socket.connect(String,int), but I'd like to be sure this is actually the problematic method before we try to find a fix. The reference to the port being known is confusing, as the port is not used in DNS requests. Presumably, this could be worked around manually, by doing the resolution locally using InetAddress.getAllByName and then using Socket.connect(InetAddress,int) until a connection is established? Or maybe this is a problem because of higher-level code using Socket.connect(String,int)?
22-12-2020

Is there any progress on this?
22-12-2020

The code snips have been provided and the root causes are also provided. Moved to JDK for more evaluations.
20-11-2020