@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in @@ -18,11 +18,11 @@ #include #import "OFOpenSSLTLSStream.h" #import "OFData.h" -#import "OFAlreadyConnectedException.h" +#import "OFAlreadyOpenException.h" #import "OFInitializationFailedException.h" #import "OFNotOpenException.h" #import "OFReadFailedException.h" #import "OFTLSHandshakeFailedException.h" #import "OFWriteFailedException.h" @@ -92,10 +92,12 @@ SSL_shutdown(_SSL); SSL_free(_SSL); _SSL = NULL; + _handshakeDone = false; + [_host release]; _host = nil; [super close]; } @@ -106,26 +108,10 @@ size_t bytesRead; if (!_handshakeDone) @throw [OFNotOpenException exceptionWithObject: self]; - if (BIO_ctrl_pending(_readBIO) < 1) { - @try { - size_t tmp = [_underlyingStream - readIntoBuffer: _buffer - length: bufferSize]; - - OFEnsure(tmp <= INT_MAX); - /* Writing to a memory BIO must never fail. */ - OFEnsure(BIO_write(_readBIO, _buffer, (int)tmp) == - (int)tmp); - } @catch (OFReadFailedException *e) { - if (e.errNo != EWOULDBLOCK && e.errNo != EAGAIN) - @throw e; - } - } - ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); while (BIO_ctrl_pending(_writeBIO) > 0) { int tmp = BIO_read(_writeBIO, _buffer, bufferSize); @@ -133,30 +119,52 @@ [_underlyingStream writeBuffer: _buffer length: tmp]; [_underlyingStream flushWriteBuffer]; } - if (ret != 1) { - /* - * The underlying stream might have had data ready, but not - * enough for OpenSSL to return decrypted data. This means the - * caller might have observed the TLS stream for reading, got a - * ready signal and read - and expects the read to succeed, not - * to fail with EWOULDBLOCK, as it was signaled ready. - * Therefore, return 0, as we could read 0 decrypted bytes, but - * cleared the ready signal of the underlying stream. - */ + if (ret == 1) + return bytesRead; + + if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_READ) { + if (BIO_ctrl_pending(_readBIO) < 1) { + @try { + size_t tmp = [_underlyingStream + readIntoBuffer: _buffer + length: bufferSize]; + + OFEnsure(tmp <= INT_MAX); + /* Writing to a memory BIO must never fail. */ + OFEnsure(BIO_write(_readBIO, _buffer, + (int)tmp) == (int)tmp); + } @catch (OFReadFailedException *e) { + if (e.errNo == EWOULDBLOCK || e.errNo != EAGAIN) + return 0; + } + } + + ret = SSL_read_ex(_SSL, buffer, length, &bytesRead); + + while (BIO_ctrl_pending(_writeBIO) > 0) { + int tmp = BIO_read(_writeBIO, _buffer, bufferSize); + + OFEnsure(tmp >= 0); + + [_underlyingStream writeBuffer: _buffer length: tmp]; + [_underlyingStream flushWriteBuffer]; + } + + if (ret == 1) + return bytesRead; + if (SSL_get_error(_SSL, ret) == SSL_ERROR_WANT_READ) return 0; - - /* FIXME: Translate error to errNo */ - @throw [OFReadFailedException exceptionWithObject: self - requestedLength: length - errNo: 0]; } - return bytesRead; + /* FIXME: Translate error to errNo */ + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: length + errNo: 0]; } - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { int ret; @@ -188,16 +196,19 @@ } return bytesWritten; } -- (bool)hasDataInReadBuffer +- (bool)lowlevelHasDataInReadBuffer { - if (SSL_pending(_SSL) > 0 || BIO_ctrl_pending(_readBIO) > 0) - return true; - - return super.hasDataInReadBuffer; +#ifdef HAVE_SSL_HAS_PENDING + return (_underlyingStream.hasDataInReadBuffer || + SSL_has_pending(_SSL) || BIO_ctrl_pending(_readBIO) > 0); +#else + return (_underlyingStream.hasDataInReadBuffer || + SSL_pending(_SSL) > 0 || BIO_ctrl_pending(_readBIO) > 0); +#endif } - (void)asyncPerformClientHandshakeWithHost: (OFString *)host runLoopMode: (OFRunLoopMode)runLoopMode { @@ -205,11 +216,11 @@ OFTLSStreamErrorCodeInitializationFailed; id exception = nil; int status; if (_SSL != NULL) - @throw [OFAlreadyConnectedException exceptionWithSocket: self]; + @throw [OFAlreadyOpenException exceptionWithObject: self]; if ((_readBIO = BIO_new(BIO_s_mem())) == NULL) @throw [OFTLSHandshakeFailedException exceptionWithStream: self host: host @@ -276,13 +287,12 @@ length: bufferSize runLoopMode: runLoopMode]; [_delegate retain]; return; case SSL_ERROR_WANT_WRITE: - [_underlyingStream - asyncWriteData: [OFData dataWithItems: "" count: 0] - runLoopMode: runLoopMode]; + [_underlyingStream asyncWriteData: [OFData data] + runLoopMode: runLoopMode]; [_delegate retain]; return; default: /* FIXME: Map to better errors */ exception = [OFTLSHandshakeFailedException @@ -307,11 +317,10 @@ { if (exception == nil) { static const OFTLSStreamErrorCode unknownErrorCode = OFTLSStreamErrorCodeUnknown; int status; - OFData *data; OFEnsure(length <= INT_MAX); OFEnsure(BIO_write(_readBIO, buffer, (int)length) == (int)length); @@ -330,15 +339,14 @@ _handshakeDone = true; else { switch (SSL_get_error(_SSL, status)) { case SSL_ERROR_WANT_READ: return true; - case SSL_ERROR_WANT_WRITE: - data = [OFData dataWithItems: "" count: 0]; + case SSL_ERROR_WANT_WRITE:; OFRunLoopMode runLoopMode = [OFRunLoop currentRunLoop].currentMode; - [_underlyingStream asyncWriteData: data + [_underlyingStream asyncWriteData: [OFData data] runLoopMode: runLoopMode]; return false; default: exception = [OFTLSHandshakeFailedException exceptionWithStream: self