Java nio socketchannel read early eos in safari and IOS with TLS1.2

147 Views Asked by At

i have a really weird problem, which i am working on the last couple days.

I wrote a proxy application on my serverside. All the proxy does is managing TLS/nonTLS requests and responses from different Applications (WebApplications, IOSApps, Android Apps, etc.) and proxy the traffic to applications on the server.

The problem i run into is, when i access a WebApplication, which uses my proxy, via Safari and over https(TLSv1.2) i get randomly no responses from my proxy. I debugged a lot and this problem is caused in my read function of the connection. The timeout is very random, i my test i got an average of 8 timeouts out of 10 requests.

This is the code:

public int read(ByteBuffer dst) throws IOException {

    if (!dst.hasRemaining()) {
        return 0;
    }
    if (peerAppData.hasRemaining()) {
        peerAppData.flip();
        return ByteBufferUtils.transferByteBuffer(peerAppData, dst);
    }
    peerNetData.compact();

    int bytesRead = socketChannel.read(peerNetData);

    if (bytesRead > 0) {
        peerNetData.flip();
        while (peerNetData.hasRemaining()) {
            peerAppData.compact();
            SSLEngineResult result = null;
            try {
                result = sslInfo.sslEngine.unwrap(peerNetData, peerAppData);
            } catch (SSLException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
                // throw e;
            }
            switch (result.getStatus()) {
            case OK:
                peerAppData.flip();
                return ByteBufferUtils.transferByteBuffer(peerAppData, dst);
            case BUFFER_UNDERFLOW:
                peerAppData.flip();
                return ByteBufferUtils.transferByteBuffer(peerAppData, dst);
            case BUFFER_OVERFLOW:
                peerAppData = enlargeApplicationBuffer(peerAppData);
                break;
            case CLOSED:
                closeConnection();
                dst.clear();
                return -1;
            default:
                System.out.println("Invalid SSL status: " + result.getStatus());
                throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
            }
        }
    } else if (bytesRead < 0) {
        handleEndOfStream();
    }
    ByteBufferUtils.transferByteBuffer(peerAppData, dst);
    return bytesRead;
}

This code works fine for Chrome, Opera and Android. But on Safari and IOS, randomly the second read of the SocketChannel returns -1. That causes, that the connection gets closed. And that explains why i get timeouts on the safari/IOS side.

I have a working piece of code, but i cannot use this, because this does not allow me to proxy a stream of data like a file upload. And this does not handle the SSLEngineResult correctly.

public int read(ByteBuffer dst) throws IOException {

    int bytesRead = 1;
    int totalBytesRead = 0;

    peerNetData.clear();

    while (bytesRead > 0) {
        bytesRead = socketChannel.read(peerNetData);

        if (bytesRead > 0) {
            totalBytesRead = totalBytesRead + bytesRead;
        }
    }

    peerNetData.flip();

    if (totalBytesRead < 0) {
        return bytesRead;
    }

    while (peerNetData.hasRemaining()) {
        SSLEngineResult result = sslInfo.sslEngine.unwrap(peerNetData, dst);
        if (result.getStatus() != SSLEngineResult.Status.OK) {
            return -1;
        }
    }

    peerNetData.compact();

    return totalBytesRead;
}

I really don't know why this code only fails on request of the safari browser or any IOS device.

Has any of you ever had the same problem?

Thanks in advance and please let me know if i missed something!

1

There are 1 best solutions below

3
On

If your proxy's upstream read() returned -1, the upstream peer closed the connection, and you should do the same to the downstream peer, instead of letting it time out.