1

I'm debugging a strange connection refused error thrown from my Java application (JDK 1.8.0_65).

I have got Wireshark capture, but it includes both normal and error TCP segments and I have no way to distinguish which is which.

I want to know if I can make Java log out source port number when it throws ConnectException, so I can search using it. Can I use tools like AspectJ to do this?

FYI, here's the stack trace:

Caused by: java.net.ConnectException: Connection refused: connect
                at java.net.DualStackPlainSocketImpl.connect0(Native Method) ~[na:1.8.0_65]
                at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_65]
                at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[na:1.8.0_65]
                at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_65]
                at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_65]
...
stackoverflower
  • 3,885
  • 10
  • 47
  • 71

1 Answers1

2

If the client socket was created without an explicit bind(), there's no way to find out which source port was used to try and establish the connection. So in order to be able to report on the source port for debugging purposes, you need to do an explicit bind before the connect call. If you have access to the source of the client code, you could update it in a similar way:

SocketAddress target = new InetSocketAddress(host, port);
int localPort = -1;
try (Socket socket = new Socket()) {
    socket.bind(null);
    localPort = socket.getLocalPort();
    socket.connect(target, timeout);
} catch (IOException e) {
    throw new IOException(
            String.format("couldn't connect to %s from port %d", target, localPort), e);
}

If you can't change the client source code, you could use the following aspect to weave the client code. It works by intercepting method calls to Socket.connect(...), determines whether the socket was already bound to a port and if not, will do a bind(null), which should find an available port to bind to.

import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public aspect SocketExceptionLoggerAspect {

    private static final Logger logger = LoggerFactory
            .getLogger(SocketExceptionLoggerAspect.class);

    pointcut callToSocketConnect(SocketAddress target): 
        call(void Socket+.connect(..)) && args(target, ..);

    void around(SocketAddress target, Socket socket) : 
        callToSocketConnect(target) && target(socket) {

        int localPort = socket.getLocalPort();
        if (localPort == -1) {
            try {
                socket.bind(null);
                localPort = socket.getLocalPort();
            } catch (IOException e) {
            }
        }
        try {
            proceed(target, socket);
        } catch (IOException e) {
            logger.error(String.format("couldn't connect to %s from port %d", target,
                    localPort), e);
        }
    }
}

Example output:
SocketExceptionLoggerAspect - couldn't connect to stackoverflow.com/104.16.33.249:123 from port 49840 <stack trace...>

Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • You don't need all this nonsense to find an available port. Just bind to port zero and then get the local socket address or local port from the socket, to see what Oort the OS allocated. – user207421 Mar 18 '16 at 01:54
  • @EJP you're right. I updated my answer based on your suggestion. – Nándor Előd Fekete Mar 18 '16 at 02:14
  • I have a concern: since Socket is in JDK, any special care is need for class loading? – stackoverflower Mar 18 '16 at 10:57
  • That means you want to go with the AspectJ solution? That one uses a `call()` pointcut, which means it only has to change the call sites where the `Socket.connect()` is actually getting called. If that's your own source code, it's enough to apply weaving to it during or after the compilation process. If it's some kind of library code, you will need to weave the library code or use load-time weaving. If it's JDK code, only load-time weaving will work, and you'll need to make sure that LTW is enabled at JVM start, so all code which calls `Socket.connect()` gets weaved at load time. – Nándor Előd Fekete Mar 18 '16 at 13:08