We've many servers which host multiple IP addresses. The additional ones are so-called Virtual IPs ("VIPs").
We'd like to enable a Java app to make an outgoing connection which lists as source address a designated Virtual IP rather than the host ip address.
Here's what we've tried. The ip address listed in InetAddress#getByAddress
is the virtual ip address. The property url
is a user-specified target url to connect to.
public void attemptConnection() {
try {
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(InetAddress.getByAddress(new byte[]{(byte)10,(byte)252,(byte)47,(byte)33}), 0));
final URL _url = new URL(url);
final URLConnection conn = _url.openConnection(proxy);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println(inputLine);
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
The code above was modelled on this answer
We're testing this with Python's SimpleHTTPServer
, simply running python -m SimpleHTTPServer
then trying to connect.
If we use no proxy in our Java code then the connection works correctly.
However, as soon as the VIP proxy is defined we get connection refused:
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:327)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:193)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:180)
at java.net.Socket.connect(Socket.java:546)
at java.net.Socket.connect(Socket.java:495)
at sun.net.NetworkClient.doConnect(NetworkClient.java:178)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:409)
at sun.net.www.http.HttpClient$2.run(HttpClient.java:457)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.http.HttpClient.privilegedOpenServer(HttpClient.java:454)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:521)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:240)
at sun.net.www.http.HttpClient.New(HttpClient.java:321)
at sun.net.www.http.HttpClient.New(HttpClient.java:338)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:935)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:914)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:801)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1139)
at com.ocado.dsi.ConnectToUrl.attemptConnection(ConnectToUrl.java:38)
at com.ocado.dsi.ConnectToUrl.run(ConnectToUrl.java:56)
at com.ocado.dsi.ConnectToUrl.main(ConnectToUrl.java:64)
Any ideas why?
Update
When specifying the local address on a socket directly the process works fine.
I setup a Python simple socket server listening on port 8000 just as the SimpleHTTPServer
used above does. Connecting to this from Java using a socket with the desired local address worked as expected: the python server recognised the incoming connection source address as that defined by the user.
Socket s = new Socket(InetAddress.getByAddress(new byte[]{(byte)10,(byte)252,(byte)47,(byte)33}), 8000, InetAddress.getByAddress(new byte[]{(byte)10,(byte)97,(byte)5,(byte)147}), 0);
s.close();
I'm going to dig into Socket#setSocketImplFactory
to see whether the problem can be resolved by customising every created socket.
The problem is that both Socket#setSocketImplFactory
and URL.setURLStreamHandlerFactory
enable you to override the default implementation but do not give you access to the default implementation, so they seem to be very much all-or-nothing overrides. This is very annoying when you only wish tor add logic on top.