ObjFW  Check-in [34cb121dc5]

Overview
Comment:Make OFTLSSocket an abstract class

This should make it easier to add TLS support using various
implementations.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 34cb121dc539a4a90083a14357549adb6ae859ea9aa37d2a21c304e4da7a79f0
User & Date: js on 2021-11-06 00:10:48
Other Links: manifest | tags
Context
2021-11-06
14:30
Doxyfile: Define _Nonnull and _Nullable check-in: 033ba56f36 user: js tags: trunk
00:10
Make OFTLSSocket an abstract class check-in: 34cb121dc5 user: js tags: trunk
2021-11-05
22:42
Remove TLS server support check-in: a5a3047210 user: js tags: trunk
Changes

Modified src/Makefile from [0d79d5eef0] to [099a565a95].

135
136
137
138
139
140
141

142
143
144
145
146
147
148
	       OFHTTPRequest.m			\
	       OFHTTPResponse.m			\
	       OFHTTPServer.m			\
	       OFSequencedPacketSocket.m	\
	       OFSocket.m			\
	       OFStreamSocket.m			\
	       OFTCPSocket.m			\

	       OFUDPSocket.m			\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_UNIX_SOCKETS}
SRCS_IPX = OFIPXSocket.m	\
	   OFSPXSocket.m	\
	   OFSPXStreamSocket.m
SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m	\







>







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
	       OFHTTPRequest.m			\
	       OFHTTPResponse.m			\
	       OFHTTPServer.m			\
	       OFSequencedPacketSocket.m	\
	       OFSocket.m			\
	       OFStreamSocket.m			\
	       OFTCPSocket.m			\
	       OFTLSSocket.m			\
	       OFUDPSocket.m			\
	       ${USE_SRCS_IPX}			\
	       ${USE_SRCS_UNIX_SOCKETS}
SRCS_IPX = OFIPXSocket.m	\
	   OFSPXSocket.m	\
	   OFSPXStreamSocket.m
SRCS_UNIX_SOCKETS = OFUNIXDatagramSocket.m	\
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
	    OFCollection.h			\
	    OFCryptographicHash.h		\
	    OFJSONRepresentation.h		\
	    OFKernelEventObserver.h		\
	    OFKeyValueCoding.h			\
	    OFLocking.h				\
	    OFMessagePackRepresentation.h	\
	    OFTLSSocket.h			\
	    ObjFW.h				\
	    macros.h				\
	    objfw-defs.h			\
	    platform.h				\
	    ${USE_INCLUDES_ATOMIC}

SRCS += OFAdjacentArray.m		\







<







169
170
171
172
173
174
175

176
177
178
179
180
181
182
	    OFCollection.h			\
	    OFCryptographicHash.h		\
	    OFJSONRepresentation.h		\
	    OFKernelEventObserver.h		\
	    OFKeyValueCoding.h			\
	    OFLocking.h				\
	    OFMessagePackRepresentation.h	\

	    ObjFW.h				\
	    macros.h				\
	    objfw-defs.h			\
	    platform.h				\
	    ${USE_INCLUDES_ATOMIC}

SRCS += OFAdjacentArray.m		\
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221
222
223
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
       ${UNICODE_M}
SRCS_FILES += OFFileURLHandler.m	\
	      OFINIFileSettings.m
SRCS_SOCKETS += OFDNSResolverSettings.m			\

		OFHTTPURLHandler.m			\
		OFHostAddressResolver.m			\
		OFIPSocketAsyncConnector.m		\
		OFKernelEventObserver.m			\
		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
		${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M}	\
		${OF_POLL_KERNEL_EVENT_OBSERVER_M}	\
		${OF_SELECT_KERNEL_EVENT_OBSERVER_M}	\
		OFTCPSocketSOCKS5Connector.m

OBJS_EXTRA = exceptions/exceptions.a	\
	     encodings/encodings.a	\







>




<







205
206
207
208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
       ${UNICODE_M}
SRCS_FILES += OFFileURLHandler.m	\
	      OFINIFileSettings.m
SRCS_SOCKETS += OFDNSResolverSettings.m			\
		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
		OFHTTPURLHandler.m			\
		OFHostAddressResolver.m			\
		OFIPSocketAsyncConnector.m		\
		OFKernelEventObserver.m			\

		${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M}	\
		${OF_POLL_KERNEL_EVENT_OBSERVER_M}	\
		${OF_SELECT_KERNEL_EVENT_OBSERVER_M}	\
		OFTCPSocketSOCKS5Connector.m

OBJS_EXTRA = exceptions/exceptions.a	\
	     encodings/encodings.a	\

Modified src/OFHTTPClient.m from [53c6cb3f12] to [95bd8d7b45].

27
28
29
30
31
32
33

34
35
36
37
38
39
40
#import "OFHTTPResponse.h"
#import "OFKernelEventObserver.h"
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFTCPSocket.h"

#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"







>







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#import "OFHTTPResponse.h"
#import "OFKernelEventObserver.h"
#import "OFNumber.h"
#import "OFRunLoop.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFTLSSocket.h"
#import "OFURL.h"

#import "OFAlreadyConnectedException.h"
#import "OFHTTPRequestFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidEncodingException.h"
#import "OFInvalidFormatException.h"
695
696
697
698
699
700
701

702


703
704
705
706
707
708
709
710
711
712
713
714
		uint16_t port;
		OFNumber *URLPort;

		[_client close];

		if ([URL.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame) {

			if (OFTLSSocketClass == Nil)


				@throw [OFUnsupportedProtocolException
				    exceptionWithURL: URL];

			sock = [[[OFTLSSocketClass alloc] init] autorelease];
			port = 443;
		} else {
			sock = [OFTCPSocket socket];
			port = 80;
		}

		URLPort = URL.port;
		if (URLPort != nil)







>
|
>
>


|
<
<







696
697
698
699
700
701
702
703
704
705
706
707
708
709


710
711
712
713
714
715
716
		uint16_t port;
		OFNumber *URLPort;

		[_client close];

		if ([URL.scheme caseInsensitiveCompare: @"https"] ==
		    OFOrderedSame) {
			@try {
				sock = [OFTLSSocket socket];
				port = 443;
			} @catch (OFNotImplementedException *e) {
				@throw [OFUnsupportedProtocolException
				    exceptionWithURL: URL];
			}


		} else {
			sock = [OFTCPSocket socket];
			port = 80;
		}

		URLPort = URL.port;
		if (URLPort != nil)

Modified src/OFTCPSocket.h from [7942720881] to [4ad589837f].

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern Class _Nullable OFTLSSocketClass;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







<
<
<
<
<
<
<
<

206
207
208
209
210
211
212








213
 * @param port The port to bind to. If the port is 0, an unused port will be
 *	       chosen, which can be obtained using the return value.
 * @return The port the socket was bound to
 */
- (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port;
@end









OF_ASSUME_NONNULL_END

Modified src/OFTLSSocket.h from [d519c5a780] to [38e4f875d1].

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
 *
 * 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 "OFObject.h"

OF_ASSUME_NONNULL_BEGIN

/**








 * @protocol OFTLSSocket OFTLSSocket.h ObjFW/OFTLSSocket.h
 *
 * @brief A protocol that should be implemented by 3rd-party libraries

















 *	  implementing TLS.





 */

@protocol OFTLSSocket

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

/**
 * @brief Initializes the TLS socket with the specified TCP socket as its
 *	  underlying socket.
 *
 * @param socket The TCP socket to use as underlying socket
 */
- (instancetype)initWithSocket: (OFTCPSocket *)socket;


































@end












OF_ASSUME_NONNULL_END







|




>
>
>
>
>
>
>
>
|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>

>
|
>














>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


>
>
>
>
>
>
>
>
>
>
>

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
 *
 * 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"

OF_ASSUME_NONNULL_BEGIN

/**
 * @protocol OFTLSSocketDelegate OFTLSSocket.h ObjFW/OFTLSSocket.h
 *
 * A delegate for OFTLSSocket.
 */
@protocol OFTLSSocketDelegate <OFTCPSocketDelegate>
@end

/**
 * @class OFTLSSocket OFTLSSocket.h ObjFW/OFTLSSocket.h
 *
 * @brief A class that provides Transport Layer Security on top of a TCP socket.
 *
 * This class is a class cluster and returns a suitable OFTLSSocket subclass,
 * if available.
 *
 * Subclasses need to override @ref accept, @ref lowlevelReadIntoBuffer:length:,
 * @ref lowlevelWriteBuffer:length:, @ref lowlevelIsAtEndOfStream and
 * @ref startTLSForHost:port:. 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:,
 * @ref lowlevelTCPWriteBuffer:length: and @ref lowlevelTCPIsAtEndOfStream are
 * provided.
 */
@interface OFTLSSocket: OFTCPSocket
{
	bool _verifiesCertificates;
	OF_RESERVE_IVARS(OFTLSSocket, 4)
}

/**
 * @brief The delegate for asynchronous operations on the socket.
 *
 * @note The delegate is retained for as long as asynchronous operations are
 *	 still ongoing.
 */
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
    id <OFTLSSocketDelegate> delegate;

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

/**
 * @brief Initializes the TLS socket with the specified TCP socket as its
 *	  underlying socket.
 *
 * @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;

/**
 * @brief This method should never be called directly. Only subclasses of
 *	  @ref OFTLSSocket are allowed to call it.
 */
- (bool)lowlevelTCPIsAtEndOfStream;
@end

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief The concrete subclass of OFTLSSocket that should be used.
 */
extern Class _Nullable OFTLSSocketImplementation;
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Added src/OFTLSSocket.m version [d6f41e909e].













































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
 * Copyright (c) 2008-2021 Jonathan Schleifer <js@nil.im>
 *
 * 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
 * the packaging of this file.
 *
 * 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.
 */

#include "config.h"

#import "OFTLSSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"

Class OFTLSSocketImplementation = Nil;

@interface OFTLSSocketAsyncConnector: OFObject <OFTLSSocketDelegate>
{
	OFTLSSocket *_socket;
	OFString *_host;
	uint16_t _port;
	id <OFTLSSocketDelegate> _delegate;
}

- (instancetype)initWithSocket: (OFTLSSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (id <OFTLSSocketDelegate>)delegate;
@end

@implementation OFTLSSocketAsyncConnector
- (instancetype)initWithSocket: (OFTLSSocket *)sock
			  host: (OFString *)host
			  port: (uint16_t)port
		      delegate: (id <OFTLSSocketDelegate>)delegate
{
	self = [super init];

	@try {
		_socket = [sock retain];
		_host = [host copy];
		_port = port;
		_delegate = [delegate retain];

		_socket.delegate = self;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	if (_socket.delegate == self)
		_socket.delegate = _delegate;

	[_socket release];
	[_delegate release];

	[super dealloc];
}

-     (void)socket: (OFTCPSocket *)sock
  didConnectToHost: (OFString *)host
	      port: (uint16_t)port
	 exception: (id)exception
{
	if (exception == nil) {
		@try {
			[(OFTLSSocket *)sock startTLSForHost: _host
							port: _port];
		} @catch (id e) {
			[self release];
			@throw e;
		}
	}

	_socket.delegate = _delegate;
	[_delegate    socket: sock
	    didConnectToHost: host
			port: port
		   exception: exception];
}
@end

@implementation OFTLSSocket
@dynamic delegate;
@synthesize verifiesCertificates = _verifiesCertificates;

+ (instancetype)alloc
{
	if (self == [OFTLSSocket class]) {
		if (OFTLSSocketImplementation == nil)
			@throw [OFNotImplementedException
			    exceptionWithSelector: _cmd
					   object: self];

		return [OFTLSSocketImplementation alloc];
	}

	return [super alloc];
}

- (instancetype)init
{
	self = [super init];

	_verifiesCertificates = true;

	return self;
}

- (instancetype)initWithSocket: (OFTCPSocket *)socket
{
	self = [super init];

	@try {
		if ([socket isKindOfClass: [OFTLSSocket class]])
			@throw [OFInvalidArgumentException exception];

		if ((_socket = dup(socket->_socket)) == OFInvalidSocketHandle)
			@throw [OFInitializationFailedException exception];

		_verifiesCertificates = true;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)startTLSForHost: (OFString *)host port: (uint16_t)port
{
	OF_UNRECOGNIZED_SELECTOR
}

- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
{
	void *pool = objc_autoreleasePoolPush();

	[[[OFTLSSocketAsyncConnector alloc]
	    initWithSocket: self
		      host: host
		      port: port
		  delegate: _delegate] autorelease];
	[super asyncConnectToHost: host port: port runLoopMode: runLoopMode];

	objc_autoreleasePoolPop(pool);
}

#ifdef OF_HAVE_BLOCKS
- (void)asyncConnectToHost: (OFString *)host
		      port: (uint16_t)port
	       runLoopMode: (OFRunLoopMode)runLoopMode
		     block: (OFTCPSocketAsyncConnectBlock)block
{
	[super asyncConnectToHost: host
			     port: port
		      runLoopMode: runLoopMode
			    block: ^ (id exception) {
		if (exception == nil) {
			@try {
				[self startTLSForHost: host port: port];
			} @catch (id e) {
				block(e);
				return;
			}
		}

		block(exception);
	}];
}
#endif

- (instancetype)accept
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelReadIntoBuffer: (void *)buffer length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length
{
	OF_UNRECOGNIZED_SELECTOR
}

- (bool)lowlevelIsAtEndOfStream
{
	OF_UNRECOGNIZED_SELECTOR
}

- (instancetype)TCPAccept
{
	return [super accept];
}

- (size_t)lowlevelTCPReadIntoBuffer: (void *)buffer length: (size_t)length
{
	return [super lowlevelReadIntoBuffer: buffer length: length];
}

- (size_t)lowlevelTCPWriteBuffer: (const void *)buffer length: (size_t)length
{
	return [super lowlevelWriteBuffer: buffer length: length];
}

- (bool)lowlevelTCPIsAtEndOfStream
{
	return [super lowlevelIsAtEndOfStream];
}
@end

Modified utils/ofhttp/OFHTTP.m from [cd70aeb710] to [2fe08b11b5].

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
	[self performSelector: @selector(downloadNextURL) afterDelay: 0];
}

-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)sock
	  request: (OFHTTPRequest *)request
{
	if (_insecure && [sock respondsToSelector:
	    @selector(setVerifiesCertificates:)])
		((id <OFTLSSocket>)sock).verifiesCertificates = false;
}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	/* TODO: Do asynchronously and print status */







|
<
|







583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
	[self performSelector: @selector(downloadNextURL) afterDelay: 0];
}

-    (void)client: (OFHTTPClient *)client
  didCreateSocket: (OFTCPSocket *)sock
	  request: (OFHTTPRequest *)request
{
	if (_insecure && [sock isKindOfClass: [OFTLSSocket class]])

		((OFTLSSocket *)sock).verifiesCertificates = false;
}

-     (void)client: (OFHTTPClient *)client
  wantsRequestBody: (OFStream *)body
	   request: (OFHTTPRequest *)request
{
	/* TODO: Do asynchronously and print status */