@@ -11,109 +11,146 @@ * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ -#import "OFTCPSocket.h" +#import "OFStream.h" +#import "OFRunLoop.h" OF_ASSUME_NONNULL_BEGIN +/** @file */ + +@class OFTLSStream; + +/** + * @protocol OFTLSStreamDelegate OFTLSStream.h ObjFW/OFTLSStream.h + * + * A delegate for OFTLSStream. + */ +@protocol OFTLSStreamDelegate /** - * @protocol OFTLSSocketDelegate OFTLSSocket.h ObjFW/OFTLSSocket.h + * @brief A method which is called when a TLS stream performed the client + * handshake. * - * A delegate for OFTLSSocket. + * @param stream The TLS stream which performed the handshake + * @param host The host for which the handshake was performed + * @param exception An exception that occurred during the handshake, or nil on + * success */ -@protocol OFTLSSocketDelegate +- (void)stream: (OFTLSStream *)stream + didPerformClientHandshakeWithHost: (OFString *)host + exception: (nullable id)exception; @end /** - * @class OFTLSSocket OFTLSSocket.h ObjFW/OFTLSSocket.h + * @class OFTLSStream OFTLSStream.h ObjFW/OFTLSStream.h * - * @brief A class that provides Transport Layer Security on top of a TCP socket. + * @brief A class that provides Transport Layer Security on top of a stream. * - * This class is a class cluster and returns a suitable OFTLSSocket subclass, + * This class is a class cluster and returns a suitable OFTLSStream subclass, * if available. * - * Subclasses need to override @ref accept, @ref lowlevelReadIntoBuffer:length:, - * @ref lowlevelWriteBuffer:length: and @ref startTLSForHost:port:. The method - * @ref hasDataInReadBuffer should be overridden to return `true` if the TLS - * socket has cached unprocessed data internally, while returning - * `[super hasDataInReadBuffer]` if it does not have any unprocessed data. In - * order to get access to the lowlevel TCP methods (you cannot call `super`, as - * the class is abstract), the private methods @ref TCPAccept, - * @ref lowlevelTCPReadIntoBuffer:length: and - * @ref lowlevelTCPWriteBuffer:length: are provided. - */ -@interface OFTLSSocket: OFTCPSocket -{ - bool _verifiesCertificates; - OF_RESERVE_IVARS(OFTLSSocket, 4) + * Subclasses need to override @ref lowlevelReadIntoBuffer:length:, + * @ref lowlevelWriteBuffer:length: and + * @ref asyncPerformClientHandshakeWithHost:runLoopMode:. The method + * @ref hasDataInReadBuffer should be overridden to return `true` if the TLS + * stream has cached unprocessed data internally, while returning + * `self.wrappedStream.hasDataInReadBuffer` if it does not have any unprocessed + * data. In order to get access to the wrapped stream, @ref wrappedStream can + * be used. + */ +@interface OFTLSStream: OFStream +{ + OFStream + *_wrappedStream; + bool _verifiesCertificates; + OF_RESERVE_IVARS(OFTLSStream, 4) } /** - * @brief The delegate for asynchronous operations on the socket. + * @brief The wrapped stream. + */ +@property (readonly, nonatomic) OFStream *wrappedStream; + +/** + * @brief The delegate for asynchronous operations on the stream. * * @note The delegate is retained for as long as asynchronous operations are * still ongoing. */ @property OF_NULLABLE_PROPERTY (assign, nonatomic) - id delegate; + id delegate; /** - * @brief Whether certificates are verified. - * - * The default is enabled. + * @brief Whether certificates are verified. Default is true. */ @property (nonatomic) bool verifiesCertificates; -/** - * @brief Initializes the TLS socket with the specified TCP socket as its - * underlying socket. - * - * The passed socket will become invalid, as the internal socket handle gets - * moved from the specified socket to the OFTLSSocket. - * - * @param socket The TCP socket to use as underlying socket - */ -- (instancetype)initWithSocket: (OFTCPSocket *)socket; - -/** - * @brief Start TLS on the underlying socket with the assumption that it is - * connected to the specified host and port. - * - * @param host The host the socket is connected to, which is also used for - * verification - * @param port The port the socket is connected to - */ -- (void)startTLSForHost: (OFString *)host port: (uint16_t)port; - -/** - * @brief This method should never be called directly. Only subclasses of - * @ref OFTLSSocket are allowed to call it. - */ -- (instancetype)TCPAccept; - -/** - * @brief This method should never be called directly. Only subclasses of - * @ref OFTLSSocket are allowed to call it. - */ -- (size_t)lowlevelTCPReadIntoBuffer: (void *)buffer length: (size_t)length; - -/** - * @brief This method should never be called directly. Only subclasses of - * @ref OFTLSSocket are allowed to call it. - */ -- (size_t)lowlevelTCPWriteBuffer: (const void *)buffer length: (size_t)length; +- (instancetype)init OF_UNAVAILABLE; + +/** + * @brief Creates a new TLS stream with the specified stream as its underlying + * stream. + * + * @param stream The stream to use as underlying stream. Must not be closed + * before the TLS stream is closed. + * @return A new, autoreleased TLS stream + */ ++ (instancetype)streamWithStream: (OFStream *)stream; + +/** + * @brief Initializes the TLS stream with the specified stream as its + * underlying stream. + * + * @param stream The stream to use as underlying stream. Must not be closed + * before the TLS stream is closed. + * @return An initialized TLS stream + */ +- (instancetype)initWithStream: (OFStream *)stream; + +/** + * @brief Asynchronously performs the TLS client handshake for the specified + * host and calls the delegate afterwards. + * + * @param host The host to perform the handshake with + */ +- (void)asyncPerformClientHandshakeWithHost: (OFString *)host; + +/** + * @brief Asynchronously performs the TLS client handshake for the specified + * host and calls the delegate afterwards. + * + * @param host The host to perform the handshake with + * @param runLoopMode The run loop mode in which to perform the async handshake + */ +- (void)asyncPerformClientHandshakeWithHost: (OFString *)host + runLoopMode: (OFRunLoopMode)runLoopMode; + +/** + * @brief Performs the TLS client handshake for the specified host. + * + * @param host The host to perform the handshake with + */ +- (void)performClientHandshakeWithHost: (OFString *)host; @end #ifdef __cplusplus extern "C" { #endif /** - * @brief The concrete subclass of OFTLSSocket that should be used. + * @brief The implementation for OFTLSStream to use. + * + * This can be set to a class that is always used for OFTLSStream. This is + * useful to either force a specific implementation or use one that ObjFW does + * not know about. */ -extern Class _Nullable OFTLSSocketImplementation; +extern Class OFTLSStreamImplementation; #ifdef __cplusplus } #endif OF_ASSUME_NONNULL_END