ObjFW  Diff

Differences From Artifact [5dd4f959ef]:

To Artifact [c91283e053]:

  • File src/OFTLSStream.h — part of check-in [d30efa8bbf] at 2021-11-13 13:04:13 on branch trunk — Completely rework the TLS/SSL API

    The previous API could never work cleanly and would always require
    hacks, as it needed intercepting all interactions of OFTCPSocket with
    the raw socket and did not work at all if the OFTCPSocket had anything
    in its read buffer before starting the TLS handshake. This also could
    not be fixed easily, as it would have required the object to contain two
    read buffers, one for the unencrypted connection and one for the
    encrypted connection. There was also no clean way to perform the
    handshake in a non-blocking way.

    The new API is a lot cleaner and requires none of the hacks, but using
    it requires slightly more work. But this is more than made up for by
    making a fully asynchronous handshake possible. It uses the concept of a
    stream wrapping another stream, meaning the entire connecting part is
    being handled by OFTCPSocket and then the connected socket is passed off
    to OFTLSStream to wrap it. This also makes for a lot cleaner separation
    of concerns. (user: js, size: 4764) [annotate] [blame] [check-ins using]


9
10
11
12
13
14
15
16


17
18
19




20
21

22
23

24
25













26
27
28
29

30
31

32
33

34
35
36
37



38
39
40
41



42
43

44
45
46


47


48
49

50
51
52






53

54
55
56
57
58
59

60
61
62

63
64
65
66
67


68
69
70


71
72
73
74
75



76
77


78
79

80

81
82
83

84
85


86
87


88
89
90
91




92
93

94
95
96

97




98
99


100
101
102

103


104
105

106
107
108
109
110
111
112





113
114

115
116
117
118
119
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25

26
27

28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47

48
49

50
51
52


53
54
55
56



57
58
59


60

61

62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83

84
85
86

87


88
89
90
91
92
93


94
95
96




97
98
99
100

101
102
103
104
105

106

107

108


109
110
111

112
113
114
115


116
117
118
119
120

121
122
123

124

125
126
127
128
129

130
131
132
133

134

135
136
137

138
139
140
141
142
143
144

145
146
147
148
149
150

151
152
153
154
155
156







-
+
+



+
+
+
+

-
+

-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
+

-
+


-
-
+
+
+

-
-
-
+
+
+
-
-
+
-

-
+
+

+
+

-
+



+
+
+
+
+
+
-
+





-
+


-
+
-
-



+
+

-
-
+
+

-
-
-
-
+
+
+

-
+
+


+
-
+
-

-
+
-
-
+
+

-
+
+


-
-
+
+
+
+

-
+


-
+
-
+
+
+
+

-
+
+


-
+
-
+
+

-
+






-
+
+
+
+
+

-
+





 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * 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 OFTLSSocketDelegate OFTLSSocket.h ObjFW/OFTLSSocket.h
 * @protocol OFTLSStreamDelegate OFTLSStream.h ObjFW/OFTLSStream.h
 *
 * A delegate for OFTLSSocket.
 * A delegate for OFTLSStream.
 */
@protocol OFTLSSocketDelegate <OFTCPSocketDelegate>
@protocol OFTLSStreamDelegate <OFStreamDelegate>
/**
 * @brief A method which is called when a TLS stream performed the client
 *	  handshake.
 *
 * @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
 */
-		       (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
 * 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
 * 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
 * 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
 * the class is abstract), the private methods @ref TCPAccept,
 * @ref lowlevelTCPReadIntoBuffer:length: and
 * be used.
 * @ref lowlevelTCPWriteBuffer:length: are provided.
 */
@interface OFTLSSocket: OFTCPSocket
@interface OFTLSStream: OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	OFStream <OFReadyForReadingObserving, OFReadyForWritingObserving>
	    *_wrappedStream;
	bool _verifiesCertificates;
	OF_RESERVE_IVARS(OFTLSSocket, 4)
	OF_RESERVE_IVARS(OFTLSStream, 4)
}

/**
 * @brief The wrapped stream.
 */
@property (readonly, nonatomic) OFStream <OFReadyForReadingObserving,
    OFReadyForWritingObserving> *wrappedStream;

/**
 * @brief The delegate for asynchronous operations on the socket.
 * @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 <OFTLSSocketDelegate> delegate;
    id <OFTLSStreamDelegate> delegate;

/**
 * @brief Whether certificates are verified.
 * @brief Whether certificates are verified. Default is true.
 *
 * The default is enabled.
 */
@property (nonatomic) bool verifiesCertificates;

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Initializes the TLS socket with the specified TCP socket as its
 *	  underlying socket.
 * @brief Creates a new TLS stream with the specified stream as its underlying
 *	  stream.
 *
 * 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
 * @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)initWithSocket: (OFTCPSocket *)socket;
+ (instancetype)streamWithStream: (OFStream <OFReadyForReadingObserving,
				       OFReadyForWritingObserving> *)stream;

/**
 * @brief Initializes the TLS stream with the specified stream as its
 * @brief Start TLS on the underlying socket with the assumption that it is
 *	  underlying stream.
 *	  connected to the specified host and port.
 *
 * @param host The host the socket is connected to, which is also used for
 * @param stream The stream to use as underlying stream. Must not be closed
 *	       verification
 * @param port The port the socket is connected to
 *		 before the TLS stream is closed.
 * @return An initialized TLS stream
 */
- (void)startTLSForHost: (OFString *)host port: (uint16_t)port;
- (instancetype)initWithStream: (OFStream <OFReadyForReadingObserving,
				     OFReadyForWritingObserving> *)stream;

/**
 * @brief This method should never be called directly. Only subclasses of
 *	  @ref OFTLSSocket are allowed to call it.
 * @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
 */
- (instancetype)TCPAccept;
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host;

/**
 * @brief This method should never be called directly. Only subclasses of
 * @brief Asynchronously performs the TLS client handshake for the specified
 *	  @ref OFTLSSocket are allowed to call it.
 *	  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
 */
- (size_t)lowlevelTCPReadIntoBuffer: (void *)buffer length: (size_t)length;
- (void)asyncPerformClientHandshakeWithHost: (OFString *)host
				runLoopMode: (OFRunLoopMode)runLoopMode;

/**
 * @brief This method should never be called directly. Only subclasses of
 * @brief Performs the TLS client handshake for the specified host.
 *	  @ref OFTLSSocket are allowed to call it.
 *
 * @param host The host to perform the handshake with
 */
- (size_t)lowlevelTCPWriteBuffer: (const void *)buffer length: (size_t)length;
- (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