1

We have an applet that connects to our game server. Since two days we get complaints from our users about not being able to connect. I tracked the issue down to the following code:

System.err.println("<PO> creating socket host=" + host + ", port=" + port);
try {
    socket = new Socket(host, port);
} catch (Throwable t) {
    System.err.println("<PO> OOPS: " + t.getMessage());
    t.printStackTrace();
}
System.err.println("<PO> socket created");

The output is as follows:

<PO> creating socket host=<host deleted>, port=7754

And then the applet just hangs (no cpu usage). No exception, but also no 'socket created' print! Note that this code has been running successfully for many years (without the prints and try/catch of course). With java 1.7 it still runs fine but with java 1.8.0_25 the code hangs.

Under what conditions can a "new Socket" call hang?

Does anyone have any suggestions on how to debug this issue further?

I have found the relevant system calls with strace:

socket(PF_INET6, SOCK_STREAM, IPPROTO_IP) = 53
setsockopt(53, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
fcntl(53, F_GETFL)                      = 0x2 (flags O_RDWR)
fcntl(53, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
connect(53, {sa_family=AF_INET6, sin6_port=htons(7754), inet_pton(AF_INET6, "::ffff:<server ip in v4 format>", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=53, events=POLLOUT}], 1, 120000) = 1 ([{fd=53, revents=POLLOUT}])
getsockopt(53, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
fcntl(53, F_GETFL)                      = 0x802 (flags O_RDWR|O_NONBLOCK)
fcntl(53, F_SETFL, O_RDWR)              = 0
getsockname(53, {sa_family=AF_INET6, sin6_port=htons(52535), inet_pton(AF_INET6, "::ffff:<client ip in v4 format>", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
setsockopt(53, SOL_TCP, TCP_NODELAY, [1], 4) = 0

I suspect the 'setsocketopt(... IPV6_V6ONLY ...)' call: the server is definitely IPV4 only! Any idea how to force Java to use IPv4 (or at least stop Java from using IPV6_V6ONLY)?

Pieter
  • 221
  • 2
  • 10
  • `new Socket` may hang if `host` parameter is not an IP address but a host name - in that case name resolution may hang the thread, or it may even make it fail in case name resolution is broken. On Linux, you can use `strace` to see which system call is the last one. – Laszlo Valko Oct 16 '14 at 16:07
  • @Laslo I have refactored the code and specified an explicit IP address (using InetAddress.getByAddress, followed by a 'new InetSocketAddress(...)', and finally an explict 'socket.connect(address)'. The code still hangs, now on the 'socket.connect' call. – Pieter Oct 16 '14 at 16:59
  • 1
    You may disable the IPv6 stack on your OS completely (on Linux, you can use `ipv6.disable=1` kernel option), or you may ask Java not to use IPv6: http://stackoverflow.com/questions/11850655/how-can-i-disable-ipv6-stack-use-for-ipv4-ips-on-jre – Laszlo Valko Oct 16 '14 at 18:51
  • The server runs Ubuntu linux. The clients can run anything. Unfortunately System.setProperty("java.net.preferIPv4Stack" , "true"); will not work as by the time our applet gets control (Applet.init method) the network stack has been initialized already. – Pieter Oct 17 '14 at 05:18
  • I found this page explaining the issue: https://bugs.openjdk.java.net/browse/JDK-8037938 Unfortunately this does not help me much as I cannot demand from all our users to edit their policy file. It looks like I have to switch from sandbox to all-permissions :-( – Pieter Oct 17 '14 at 07:25

1 Answers1

2

I have found the cause and a solution, so it is time to answer my own question in the hope of helping other poor souls who stumble on this page with the same problem.

The root of the problem is that in Java 8 signed applets in 'sandbox' mode no longer receive the 'SocketPermission', only the 'URLPermission'.

When you call 'socket.connect()' (or 'new Socket(host, port)' which does a connect), some magic happens and Java decides that you must be opening a http connection. It then tries to get the '/crossdomain.xml' file through your socket using the http protocol.

This is what tripped up my code: my custom game server does not speak http and did not provide an answer while keeping the socket open. Java tried to read the response that did not come, and the 'socket.connect()' call never returned.

The 'solution' is that the applet must be run in 'all-permissions' mode to open regular sockets. Ironically, the tightened security in Java 8 actually forces us to enable 'all-permissions'.

Pieter
  • 221
  • 2
  • 10