Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Socket fails to connect to "0.0.0.0" with NoRouteToHostException instead of ConnectionRefused

Problem

When opening a socket to IP: 0.0.0.0 and Port: 37845 (just a random closed port) with java's socket class , the socket connect fails with a java.net.NoRouteToHostException on Machine 1

Exception in thread "main" java.net.NoRouteToHostException: No route to host (Host unreachable)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at Test.main(Test.java:26)

I'm using this testcode:

import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

public class Test {

 public static void main(String[] args) throws Exception {

  Socket socket;

  // create a socket with a timeout
  SocketAddress socketAddress = new InetSocketAddress("0.0.0.0", 37845);

  // create a socket
  socket = new Socket();

  // this method will block no more than timeout ms.
  int timeoutInMs = 10 * 1000; // 10 seconds
  socket.connect(socketAddress, timeoutInMs);
  System.err.println("SUCCESS");
 }
}

Expected

What , I'm actually expecting is a java.net.ConnectException : Connection refused (Connection refused) , which is also what I'm getting with another Cent OS machine, let's call it Machine2:

Exception in thread "main" java.net.ConnectException: Connection refused (Connection refused)
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:204)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at Test.main(Test.java:26)

Setup

Machine1:

[qa@jenkins-staging ~]$ cat /etc/centos-release
CentOS Linux release 7.6.1810 (Core)
[qa@jenkins-staging ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
[qa@jenkins-staging ~]$ uname -a
Linux jenkins-staging.fancydomain 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Machine2:

[qa@localhost ~]$ cat /etc/centos-release
CentOS Linux release 7.6.1810 (Core)
[qa@localhost ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
[qa@localhost ~]$ uname -a
Linux localhost.localdomain 3.10.0-957.1.3.el7.x86_64 #1 SMP Thu Nov 29 14:49:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

So it seems like the only difference is the kernel version.

Additional things i've tried:

  • I tried the "same" code with python , there i always get a ConnectionRefused (on Machine1 + Machine2)

    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("0.0.0.0", 37845))
    
  • Pinging 0.0.0.0 on Machine1 works as well and resolves to 127.0.0.1
  • Replacing 0.0.0.0 with 127.0.0.1 in the source code resolves the problem , and ConnectionRefused (expected) is raised instead of NoRouteToHostException
  • Disabling Firewalld, Disabling SELinux etc.

Questions

  1. Is this a java bug ? If so why is it working on Machine2 even though they are using the same jdk and same java version ?
  2. Is this a Linux Kernel Bug ? If so why is it working with Python when i open a socket to 0.0.0.0 but not with java ? I would assume the underlying syscalls are the same.

Clarification

In the example above i used a port which is closed , just for demonstration purposes. The same applies if there is an actual listening port on the machine then it will be ConnectionRefused vs SUCCESS

like image 961
Mino_e Avatar asked Feb 11 '19 18:02

Mino_e


2 Answers

0.0.0.0 is a special address, part of the special 0.0.0.0/8 range that means "current network" or "unspecified". You can't connect to it since it's undefined as a destination.

This is why you're getting a NoRouteToHostException - the address is simply not routable. You'll get a similar failure if you try to run ping 0.0.0.0 or a similar command.

ConnectionRefused occurs when the remote machine actually refuses the connection, which is usually a sign that the remote machine doesn't have a listening socket or is behind a firewall.

like image 56
Malt Avatar answered Oct 26 '22 09:10

Malt


I would definitely install Wireshark on both machines, and compare all scenarios.

Specifically:

https://superuser.com/questions/720851/connection-refused-vs-no-route-to-host

"Connection refused" means that the target machine actively rejected the connection... one of the following things is likely the reason:

  • Nothing is listening on the port
  • The firewall is blocking the connection with REJECT

The ICMP message, "no route to host," means that ARP cannot find the layer-2 address for the destination host. Usually, this means that that the host with that IP address is not online or responding.

Of course, this begs the question why Python behaves one way, and Java a different way ... on the same machine.

Again - I'd encourage you to look at Wireshark. In particular, look at 1) the three-way TCP handshake, and 2) the ARP call that precedes it.


PS: As malt says above:

0.0.0.0 ... the address is simply not routable.

On Windows, you might get WSAEADDRNOTAVAIL -The remote address is not a valid address

Which begs the question why you're ever getting "ConnectionRefused".

Again - I'm definitely curious what Wireshark shows you.

like image 43
paulsm4 Avatar answered Oct 26 '22 09:10

paulsm4