0

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!

user207421
  • 305,947
  • 44
  • 307
  • 483
R3tty
  • 39
  • 7

1 Answers1

0

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Hey EJP, thanks for your answer. But why does the upstream get closed only on Safari and IOS. And why does it not timeout with the other codesnippet. Shouldn't it behave the same there? – R3tty Dec 22 '17 at 04:03
  • I don't know, of course, but whatever the upstream peer does should be reflected faithfully to the downstream peer. – user207421 Dec 22 '17 at 05:21
  • Hey EJP, sry for my late response, i was on leave. So i fixed the the weird behaviour. The problem was/is, for whatever reason some unwraps of safari/ios data return OK when 760 Bytes are unwraped but there is still some data left in the peerNetData. In my code i would transfer the 760 Bytes in the destination buffer and return. That for sure is a wrong behaviour, i normally have to continue until there is no remaining Data in the peerNetData. For me i had to write a workaround for that, because if i would do that, streamdata would cause a OutOfMemory exception. I can post my code if you want – R3tty Jan 03 '18 at 00:42